From ed9b10d2ea86c03cb636575a6ae9999d27d50b40 Mon Sep 17 00:00:00 2001 From: Lai Power Date: Fri, 8 Dec 2023 23:23:11 +0000 Subject: [PATCH] updated plugin `ActivityPub` version 1.3.0 --- .../plugins/activitypub/activitypub.php | 5 +- .../build/follow-me/index.asset.php | 2 +- .../activitypub/build/follow-me/index.js | 2 +- .../build/follow-me/style-index.css | 2 +- .../build/follow-me/view.asset.php | 2 +- .../activitypub/build/follow-me/view.js | 2 +- .../build/followers/index.asset.php | 2 +- .../activitypub/build/followers/index.js | 2 +- .../build/followers/view.asset.php | 2 +- .../activitypub/build/followers/view.js | 2 +- .../includes/activity/class-activity.php | 6 + .../includes/activity/class-base-object.php | 4 +- .../includes/class-activitypub.php | 84 ++++- .../activitypub/includes/class-handler.php | 33 ++ .../activitypub/includes/class-hashtag.php | 4 + .../activitypub/includes/class-http.php | 24 +- .../activitypub/includes/class-mention.php | 4 + .../activitypub/includes/class-migration.php | 21 ++ .../activitypub/includes/class-scheduler.php | 2 +- .../activitypub/includes/class-shortcodes.php | 26 +- .../activitypub/includes/class-signature.php | 29 +- .../activitypub/includes/class-webfinger.php | 5 + .../includes/collection/class-followers.php | 203 ++--------- .../collection/class-interactions.php | 235 ++++++++++++ .../plugins/activitypub/includes/compat.php | 12 + .../activitypub/includes/functions.php | 239 ++++++++++++- .../includes/handler/class-create.php | 61 ++++ .../includes/handler/class-delete.php | 165 +++++++++ .../includes/handler/class-follow.php | 81 +++++ .../includes/handler/class-undo.php | 31 ++ .../includes/handler/class-update.php | 89 +++++ .../plugins/activitypub/includes/help.php | 4 +- .../includes/model/class-application-user.php | 8 +- .../includes/model/class-follower.php | 2 +- .../activitypub/includes/model/class-user.php | 18 - .../includes/rest/class-collection.php | 19 +- .../includes/rest/class-followers.php | 6 +- .../includes/rest/class-following.php | 9 +- .../activitypub/includes/rest/class-inbox.php | 178 ++-------- .../includes/rest/class-nodeinfo.php | 34 +- .../includes/rest/class-outbox.php | 7 +- .../includes/rest/class-server.php | 51 ++- .../activitypub/includes/rest/class-users.php | 6 +- .../includes/rest/class-webfinger.php | 6 + .../includes/table/class-followers.php | 72 ++-- .../includes/transformer/class-post.php | 334 +++++++++++------- .../integration/class-nodeinfo.php | 15 + .../integration/class-webfinger.php | 4 + wp-content/plugins/activitypub/readme.txt | 52 ++- .../templates/blog-user-followers-list.php | 2 +- .../activitypub/templates/settings.php | 15 +- .../templates/user-followers-list.php | 2 +- 52 files changed, 1618 insertions(+), 607 deletions(-) create mode 100644 wp-content/plugins/activitypub/includes/class-handler.php create mode 100644 wp-content/plugins/activitypub/includes/collection/class-interactions.php create mode 100644 wp-content/plugins/activitypub/includes/handler/class-create.php create mode 100644 wp-content/plugins/activitypub/includes/handler/class-delete.php create mode 100644 wp-content/plugins/activitypub/includes/handler/class-follow.php create mode 100644 wp-content/plugins/activitypub/includes/handler/class-undo.php create mode 100644 wp-content/plugins/activitypub/includes/handler/class-update.php diff --git a/wp-content/plugins/activitypub/activitypub.php b/wp-content/plugins/activitypub/activitypub.php index 521a3795..abab5f09 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.0.7 + * Version: 1.3.0 * Author: Matthias Pfefferle & Automattic * Author URI: https://automattic.com/ * License: MIT @@ -66,10 +66,9 @@ function plugin_init() { \add_action( 'init', array( __NAMESPACE__ . '\Migration', 'init' ) ); \add_action( 'init', array( __NAMESPACE__ . '\Activitypub', 'init' ) ); \add_action( 'init', array( __NAMESPACE__ . '\Activity_Dispatcher', 'init' ) ); - \add_action( 'init', array( __NAMESPACE__ . '\Collection\Followers', 'init' ) ); + \add_action( 'init', array( __NAMESPACE__ . '\Handler', 'init' ) ); \add_action( 'init', array( __NAMESPACE__ . '\Admin', 'init' ) ); \add_action( 'init', array( __NAMESPACE__ . '\Hashtag', 'init' ) ); - \add_action( 'init', array( __NAMESPACE__ . '\Shortcodes', 'init' ) ); \add_action( 'init', array( __NAMESPACE__ . '\Mention', 'init' ) ); \add_action( 'init', array( __NAMESPACE__ . '\Health_Check', 'init' ) ); \add_action( 'init', array( __NAMESPACE__ . '\Scheduler', 'init' ) ); 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 4e87a6f4..760f4336 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' => '2a185b1c488886051601'); + array('wp-api-fetch', 'wp-block-editor', 'wp-blocks', 'wp-components', 'wp-compose', 'wp-data', 'wp-element', 'wp-i18n', 'wp-primitives'), 'version' => '6aeec6336fd28aa836a7'); diff --git a/wp-content/plugins/activitypub/build/follow-me/index.js b/wp-content/plugins/activitypub/build/follow-me/index.js index 89618f03..1320f15d 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}),(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={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 diff --git a/wp-content/plugins/activitypub/build/follow-me/style-index.css b/wp-content/plugins/activitypub/build/follow-me/style-index.css index 0a435681..7c6db77c 100644 --- a/wp-content/plugins/activitypub/build/follow-me/style-index.css +++ b/wp-content/plugins/activitypub/build/follow-me/style-index.css @@ -1 +1 @@ -.activitypub-follow-me-block-wrapper{width:100%}.activitypub-follow-me-block-wrapper.has-background .activitypub-profile,.activitypub-follow-me-block-wrapper.has-border-color .activitypub-profile{padding-left:1rem;padding-right:1rem}.activitypub-follow-me-block-wrapper .activitypub-profile{align-items:center;display:flex;padding:1rem 0}.activitypub-follow-me-block-wrapper .activitypub-profile .activitypub-profile__avatar{border-radius:50%;height:75px;margin-right:1rem;width:75px}.activitypub-follow-me-block-wrapper .activitypub-profile .activitypub-profile__content{flex:1;min-width:0}.activitypub-follow-me-block-wrapper .activitypub-profile .activitypub-profile__handle,.activitypub-follow-me-block-wrapper .activitypub-profile .activitypub-profile__name{line-height:1.2;margin:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.activitypub-follow-me-block-wrapper .activitypub-profile .activitypub-profile__name{font-size:1.25em}.activitypub-follow-me-block-wrapper .activitypub-profile .activitypub-profile__follow{align-self:center;background-color:var(--wp--preset--color--black);color:var(--wp--preset--color--white);margin-left:1rem}.activitypub-profile__confirm.components-modal__frame{background-color:#f7f7f7;color:#333}.activitypub-profile__confirm.components-modal__frame .components-modal__header-heading,.activitypub-profile__confirm.components-modal__frame h4{color:#333;letter-spacing:inherit;word-spacing:inherit}.activitypub-follow-me__dialog{max-width:30em}.activitypub-follow-me__dialog h4{line-height:1;margin:0}.activitypub-follow-me__dialog .apmfd__section{margin-bottom:2em}.activitypub-follow-me__dialog .apfmd-description{font-size:var(--wp--preset--font-size--normal,.75rem);margin:.33em 0 1em}.activitypub-follow-me__dialog .apfmd__button-group{display:flex;justify-content:flex-end}.activitypub-follow-me__dialog .apfmd__button-group svg{height:21px;margin-right:.5em;width:21px}.activitypub-follow-me__dialog .apfmd__button-group input{flex:1;padding-left:1em;padding-right:1em} +.activitypub-follow-me-block-wrapper{width:100%}.activitypub-follow-me-block-wrapper.has-background .activitypub-profile,.activitypub-follow-me-block-wrapper.has-border-color .activitypub-profile{padding-left:1rem;padding-right:1rem}.activitypub-follow-me-block-wrapper .activitypub-profile{align-items:center;display:flex;padding:1rem 0}.activitypub-follow-me-block-wrapper .activitypub-profile .activitypub-profile__avatar{border-radius:50%;height:75px;margin-right:1rem;width:75px}.activitypub-follow-me-block-wrapper .activitypub-profile .activitypub-profile__content{flex:1;min-width:0}.activitypub-follow-me-block-wrapper .activitypub-profile .activitypub-profile__handle,.activitypub-follow-me-block-wrapper .activitypub-profile .activitypub-profile__name{line-height:1.2;margin:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.activitypub-follow-me-block-wrapper .activitypub-profile .activitypub-profile__name{font-size:1.25em}.activitypub-follow-me-block-wrapper .activitypub-profile .activitypub-profile__follow{align-self:center;background-color:var(--wp--preset--color--black);color:var(--wp--preset--color--white);margin-left:1rem}.activitypub-profile__confirm.components-modal__frame{background-color:#f7f7f7;color:#333}.activitypub-profile__confirm.components-modal__frame .components-modal__header-heading,.activitypub-profile__confirm.components-modal__frame h4{color:#333;letter-spacing:inherit;word-spacing:inherit}.activitypub-follow-me__dialog{max-width:30em}.activitypub-follow-me__dialog h4{line-height:1;margin:0}.activitypub-follow-me__dialog .apmfd__section{margin-bottom:2em}.activitypub-follow-me__dialog .apfmd-description{font-size:var(--wp--preset--font-size--normal,.75rem);margin:.33em 0 1em}.activitypub-follow-me__dialog .apfmd__button-group{align-items:flex-end;display:flex;justify-content:flex-end}.activitypub-follow-me__dialog .apfmd__button-group svg{height:21px;margin-right:.5em;width:21px}.activitypub-follow-me__dialog .apfmd__button-group input{background-color:var(--wp--preset--color--white);border:1px solid var(--wp--preset--color--black);color:var(--wp--preset--color--black);flex:1;padding:6px 12px} 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 3d8b16e1..76495efb 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' => '17a158ceced1355cc8ea'); + array('wp-api-fetch', 'wp-components', 'wp-compose', 'wp-dom-ready', 'wp-element', 'wp-i18n', 'wp-primitives'), 'version' => '5b48281e37700a970a66'); diff --git a/wp-content/plugins/activitypub/build/follow-me/view.js b/wp-content/plugins/activitypub/build/follow-me/view.js index 7e0a8070..62d6d05a 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}),(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={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 diff --git a/wp-content/plugins/activitypub/build/followers/index.asset.php b/wp-content/plugins/activitypub/build/followers/index.asset.php index 40f86715..324ac5ed 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' => '1cbd9cbfcbd7fc813429'); + array('react', 'wp-api-fetch', 'wp-block-editor', 'wp-blocks', 'wp-components', 'wp-data', 'wp-element', 'wp-i18n', 'wp-primitives', 'wp-url'), 'version' => '59d9702e06860a6d13e4'); diff --git a/wp-content/plugins/activitypub/build/followers/index.js b/wp-content/plugins/activitypub/build/followers/index.js index 972f56b5..bcbcf3f1 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"}),(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 +(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 diff --git a/wp-content/plugins/activitypub/build/followers/view.asset.php b/wp-content/plugins/activitypub/build/followers/view.asset.php index 6a045bb0..ba6c3228 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' => 'f0e21057f7ec615290d6'); + array('react', 'wp-api-fetch', 'wp-components', 'wp-dom-ready', 'wp-element', 'wp-i18n', 'wp-url'), 'version' => '04e51e7562fe28b0b2c3'); diff --git a/wp-content/plugins/activitypub/build/followers/view.js b/wp-content/plugins/activitypub/build/followers/view.js index 3d0cbc1f..7c66f054 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"}),(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 +(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 diff --git a/wp-content/plugins/activitypub/includes/activity/class-activity.php b/wp-content/plugins/activitypub/includes/activity/class-activity.php index 6c598662..96ee095b 100644 --- a/wp-content/plugins/activitypub/includes/activity/class-activity.php +++ b/wp-content/plugins/activitypub/includes/activity/class-activity.php @@ -194,6 +194,12 @@ class Activity extends Base_Object { * @return void */ public function set_object( $object ) { + // convert array to object + if ( is_array( $object ) ) { + $object = Base_Object::init_from_array( $object ); + } + + // set object $this->set( 'object', $object ); if ( ! is_object( $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 a75ed16d..b73c621c 100644 --- a/wp-content/plugins/activitypub/includes/activity/class-base-object.php +++ b/wp-content/plugins/activitypub/includes/activity/class-base-object.php @@ -585,7 +585,7 @@ class Base_Object { foreach ( $array as $key => $value ) { $key = camel_to_snake_case( $key ); - $object->set( $key, $value ); + call_user_func( array( $object, 'set_' . $key ), $value ); } return $object; @@ -611,7 +611,7 @@ class Base_Object { foreach ( $array as $key => $value ) { if ( $value ) { $key = camel_to_snake_case( $key ); - $this->set( $key, $value ); + call_user_func( array( $this, 'set_' . $key ), $value ); } } } diff --git a/wp-content/plugins/activitypub/includes/class-activitypub.php b/wp-content/plugins/activitypub/includes/class-activitypub.php index 24228f43..6f654c5c 100644 --- a/wp-content/plugins/activitypub/includes/class-activitypub.php +++ b/wp-content/plugins/activitypub/includes/class-activitypub.php @@ -1,8 +1,12 @@ array( + 'name' => _x( 'Followers', 'post_type plural name', 'activitypub' ), + 'singular_name' => _x( 'Follower', 'post_type single name', 'activitypub' ), + ), + 'public' => false, + 'hierarchical' => false, + 'rewrite' => false, + 'query_var' => false, + 'delete_with_user' => false, + 'can_export' => true, + 'supports' => array(), + ) + ); + + register_post_meta( + Followers::POST_TYPE, + 'activitypub_inbox', + array( + 'type' => 'string', + 'single' => true, + 'sanitize_callback' => 'sanitize_url', + ) + ); + + register_post_meta( + Followers::POST_TYPE, + 'activitypub_errors', + array( + 'type' => 'string', + 'single' => false, + 'sanitize_callback' => function( $value ) { + if ( ! is_string( $value ) ) { + throw new Exception( 'Error message is no valid string' ); + } + + return esc_sql( $value ); + }, + ) + ); + + register_post_meta( + Followers::POST_TYPE, + 'activitypub_user_id', + array( + 'type' => 'string', + 'single' => false, + 'sanitize_callback' => function( $value ) { + return esc_sql( $value ); + }, + ) + ); + + register_post_meta( + Followers::POST_TYPE, + 'activitypub_actor_json', + array( + 'type' => 'string', + 'single' => true, + 'sanitize_callback' => function( $value ) { + return sanitize_text_field( $value ); + }, + ) + ); + + do_action( 'activitypub_after_register_post_type' ); + } } diff --git a/wp-content/plugins/activitypub/includes/class-handler.php b/wp-content/plugins/activitypub/includes/class-handler.php new file mode 100644 index 00000000..fcabd63c --- /dev/null +++ b/wp-content/plugins/activitypub/includes/class-handler.php @@ -0,0 +1,33 @@ + MB_IN_BYTES ) { + return $the_content; + } $tag_stack = array(); $protected_tags = array( 'pre', diff --git a/wp-content/plugins/activitypub/includes/class-http.php b/wp-content/plugins/activitypub/includes/class-http.php index f9140bed..79519b3b 100644 --- a/wp-content/plugins/activitypub/includes/class-http.php +++ b/wp-content/plugins/activitypub/includes/class-http.php @@ -20,7 +20,7 @@ class Http { * @return array|WP_Error The POST Response or an WP_ERROR */ public static function post( $url, $body, $user_id ) { - do_action( 'activitypub_pre_http_post', $url, $body, $user_id ); + \do_action( 'activitypub_pre_http_post', $url, $body, $user_id ); $date = \gmdate( 'D, d M Y H:i:s T' ); $digest = Signature::generate_digest( $body ); @@ -70,7 +70,7 @@ class Http { * @return array|WP_Error The GET Response or an WP_ERROR */ public static function get( $url ) { - do_action( 'activitypub_pre_http_get', $url ); + \do_action( 'activitypub_pre_http_get', $url ); $date = \gmdate( 'D, d M Y H:i:s T' ); $signature = Signature::generate_signature( Users::APPLICATION_USER_ID, 'get', $url, $date ); @@ -108,4 +108,24 @@ class Http { return $response; } + + /** + * Check for URL for Tombstone. + * + * @param string $url The URL to check. + * + * @return bool True if the URL is a tombstone. + */ + public static function is_tombstone( $url ) { + \do_action( 'activitypub_pre_http_is_tombstone', $url ); + + $response = \wp_safe_remote_get( $url ); + $code = \wp_remote_retrieve_response_code( $response ); + + if ( in_array( (int) $code, array( 404, 410 ), true ) ) { + return true; + } + + return false; + } } diff --git a/wp-content/plugins/activitypub/includes/class-mention.php b/wp-content/plugins/activitypub/includes/class-mention.php index d55e5f20..beb62468 100644 --- a/wp-content/plugins/activitypub/includes/class-mention.php +++ b/wp-content/plugins/activitypub/includes/class-mention.php @@ -26,6 +26,10 @@ class Mention { * @return string the filtered post-content */ public static function the_content( $the_content ) { + // small protection against execution timeouts: limit to 1 MB + if ( mb_strlen( $the_content ) > MB_IN_BYTES ) { + return $the_content; + } $tag_stack = array(); $protected_tags = array( 'pre', diff --git a/wp-content/plugins/activitypub/includes/class-migration.php b/wp-content/plugins/activitypub/includes/class-migration.php index cd13f8d2..adebb7e9 100644 --- a/wp-content/plugins/activitypub/includes/class-migration.php +++ b/wp-content/plugins/activitypub/includes/class-migration.php @@ -114,6 +114,9 @@ class Migration { if ( version_compare( $version_from_db, '1.0.0', '<' ) ) { self::migrate_from_0_17(); } + if ( version_compare( $version_from_db, '1.3.0', '<' ) ) { + self::migrate_from_1_2_0(); + } update_option( 'activitypub_db_version', self::get_target_version() ); @@ -176,4 +179,22 @@ class Migration { \update_option( 'activitypub_custom_post_content', $content ); } } + + /** + * Clear the cache after updating to 1.3.0 + * + * @return void + */ + private static function migrate_from_1_2_0() { + $user_ids = get_users( + array( + 'fields' => 'ID', + 'capability__in' => array( 'publish_posts' ), + ) + ); + + foreach ( $user_ids as $user_id ) { + wp_cache_delete( sprintf( Followers::CACHE_KEY_INBOXES, $user_id ), 'activitypub' ); + } + } } diff --git a/wp-content/plugins/activitypub/includes/class-scheduler.php b/wp-content/plugins/activitypub/includes/class-scheduler.php index 63f92732..11f40daf 100644 --- a/wp-content/plugins/activitypub/includes/class-scheduler.php +++ b/wp-content/plugins/activitypub/includes/class-scheduler.php @@ -114,7 +114,7 @@ class Scheduler { $followers = Followers::get_outdated_followers( $number ); foreach ( $followers as $follower ) { - $meta = get_remote_metadata_by_actor( $follower->get_url(), false ); + $meta = get_remote_metadata_by_actor( $follower->get_id(), false ); if ( empty( $meta ) || ! is_array( $meta ) || is_wp_error( $meta ) ) { Followers::add_error( $follower->get__id(), $meta ); diff --git a/wp-content/plugins/activitypub/includes/class-shortcodes.php b/wp-content/plugins/activitypub/includes/class-shortcodes.php index 43be17b0..491a6add 100644 --- a/wp-content/plugins/activitypub/includes/class-shortcodes.php +++ b/wp-content/plugins/activitypub/includes/class-shortcodes.php @@ -5,14 +5,9 @@ use function Activitypub\esc_hashtag; class Shortcodes { /** - * Class constructor, registering WordPress then Shortcodes + * Register the shortcodes */ - public static function init() { - // do not load on admin pages - if ( is_admin() ) { - return; - } - + public static function register() { foreach ( get_class_methods( self::class ) as $shortcode ) { if ( 'init' !== $shortcode ) { add_shortcode( 'ap_' . $shortcode, array( self::class, $shortcode ) ); @@ -20,6 +15,17 @@ class Shortcodes { } } + /** + * Unregister the shortcodes + */ + public static function unregister() { + foreach ( get_class_methods( self::class ) as $shortcode ) { + if ( 'init' !== $shortcode ) { + remove_shortcode( 'ap_' . $shortcode ); + } + } + } + /** * Generates output for the 'ap_hashtags' shortcode * @@ -384,7 +390,8 @@ class Shortcodes { return ''; } - $name = \get_the_author_meta( 'display_name', $item->post_author ); + $author_id = \get_post_field( 'post_author', $item->ID ); + $name = \get_the_author_meta( 'display_name', $author_id ); if ( ! $name ) { return ''; @@ -409,7 +416,8 @@ class Shortcodes { return ''; } - $url = \get_the_author_meta( 'user_url', $item->post_author ); + $author_id = \get_post_field( 'post_author', $item->ID ); + $url = \get_the_author_meta( 'user_url', $author_id ); if ( ! $url ) { return ''; diff --git a/wp-content/plugins/activitypub/includes/class-signature.php b/wp-content/plugins/activitypub/includes/class-signature.php index 1789dd67..d021cf0e 100644 --- a/wp-content/plugins/activitypub/includes/class-signature.php +++ b/wp-content/plugins/activitypub/includes/class-signature.php @@ -4,6 +4,7 @@ namespace Activitypub; use WP_Error; use DateTime; use DateTimeZone; +use WP_REST_Request; use Activitypub\Collection\Users; /** @@ -226,7 +227,7 @@ class Signature { /** * Verifies the http signatures * - * @param WP_REQUEST|array $request The request object or $_SERVER array. + * @param WP_REST_Request|array $request The request object or $_SERVER array. * * @return mixed A boolean or WP_Error. */ @@ -259,7 +260,7 @@ class Signature { } if ( ! isset( $headers['signature'] ) ) { - return new WP_Error( 'activitypub_signature', __( 'Request not signed', 'activitypub' ), array( 'status' => 403 ) ); + return new WP_Error( 'activitypub_signature', __( 'Request not signed', 'activitypub' ), array( 'status' => 401 ) ); } if ( array_key_exists( 'signature', $headers ) ) { @@ -269,7 +270,7 @@ class Signature { } if ( ! isset( $signature_block ) || ! $signature_block ) { - return new WP_Error( 'activitypub_signature', __( 'Incompatible request signature. keyId and signature are required', 'activitypub' ), array( 'status' => 403 ) ); + return new WP_Error( 'activitypub_signature', __( 'Incompatible request signature. keyId and signature are required', 'activitypub' ), array( 'status' => 401 ) ); } $signed_headers = $signature_block['headers']; @@ -279,12 +280,12 @@ class Signature { $signed_data = self::get_signed_data( $signed_headers, $signature_block, $headers ); if ( ! $signed_data ) { - return new WP_Error( 'activitypub_signature', __( 'Signed request date outside acceptable time window', 'activitypub' ), array( 'status' => 403 ) ); + return new WP_Error( 'activitypub_signature', __( 'Signed request date outside acceptable time window', 'activitypub' ), array( 'status' => 401 ) ); } $algorithm = self::get_signature_algorithm( $signature_block ); if ( ! $algorithm ) { - return new WP_Error( 'activitypub_signature', __( 'Unsupported signature algorithm (only rsa-sha256 and hs2019 are supported)', 'activitypub' ), array( 'status' => 403 ) ); + return new WP_Error( 'activitypub_signature', __( 'Unsupported signature algorithm (only rsa-sha256 and hs2019 are supported)', 'activitypub' ), array( 'status' => 401 ) ); } if ( \in_array( 'digest', $signed_headers, true ) && isset( $body ) ) { @@ -300,7 +301,7 @@ class Signature { } if ( \base64_encode( \hash( $hashalg, $body, true ) ) !== $digest[1] ) { // phpcs:ignore - return new WP_Error( 'activitypub_signature', __( 'Invalid Digest header', 'activitypub' ), array( 'status' => 403 ) ); + return new WP_Error( 'activitypub_signature', __( 'Invalid Digest header', 'activitypub' ), array( 'status' => 401 ) ); } } @@ -313,7 +314,7 @@ class Signature { $verified = \openssl_verify( $signed_data, $signature_block['signature'], $public_key, $algorithm ) > 0; if ( ! $verified ) { - return new WP_Error( 'activitypub_signature', __( 'Invalid signature', 'activitypub' ), array( 'status' => 403 ) ); + return new WP_Error( 'activitypub_signature', __( 'Invalid signature', 'activitypub' ), array( 'status' => 401 ) ); } return $verified; } @@ -323,17 +324,25 @@ class Signature { * * @param string $key_id The URL to the public key. * - * @return WP_Error|string The public key. + * @return WP_Error|string The public key or WP_Error. */ public static function get_remote_key( $key_id ) { // phpcs:ignore $actor = get_remote_metadata_by_actor( strip_fragment_from_url( $key_id ) ); // phpcs:ignore if ( \is_wp_error( $actor ) ) { - return $actor; + return new WP_Error( + 'activitypub_no_remote_profile_found', + __( 'No Profile found or Profile not accessible', 'activitypub' ), + array( 'status' => 401 ) + ); } if ( isset( $actor['publicKey']['publicKeyPem'] ) ) { return \rtrim( $actor['publicKey']['publicKeyPem'] ); // phpcs:ignore } - return new WP_Error( 'activitypub_no_remote_key_found', __( 'No Public-Key found', 'activitypub' ), array( 'status' => 403 ) ); + return new WP_Error( + 'activitypub_no_remote_key_found', + __( 'No Public-Key found', 'activitypub' ), + array( 'status' => 401 ) + ); } /** diff --git a/wp-content/plugins/activitypub/includes/class-webfinger.php b/wp-content/plugins/activitypub/includes/class-webfinger.php index b6b0a64e..75f7ff69 100644 --- a/wp-content/plugins/activitypub/includes/class-webfinger.php +++ b/wp-content/plugins/activitypub/includes/class-webfinger.php @@ -41,9 +41,14 @@ class Webfinger { * @return string|WP_Error The URL or WP_Error */ public static function resolve( $resource ) { + if ( ! $resource ) { + return null; + } + if ( ! preg_match( '/^@?' . ACTIVITYPUB_USERNAME_REGEXP . '$/i', $resource, $m ) ) { return null; } + $transient_key = 'activitypub_resolve_' . ltrim( $resource, '@' ); $link = \get_transient( $transient_key ); diff --git a/wp-content/plugins/activitypub/includes/collection/class-followers.php b/wp-content/plugins/activitypub/includes/collection/class-followers.php index a9fe2981..be98a46c 100644 --- a/wp-content/plugins/activitypub/includes/collection/class-followers.php +++ b/wp-content/plugins/activitypub/includes/collection/class-followers.php @@ -2,14 +2,10 @@ namespace Activitypub\Collection; use WP_Error; -use Exception; use WP_Query; use Activitypub\Http; use Activitypub\Webfinger; use Activitypub\Model\Follower; -use Activitypub\Collection\Users; -use Activitypub\Activity\Activity; -use Activitypub\Activity\Base_Object; use function Activitypub\is_tombstone; use function Activitypub\get_remote_metadata_by_actor; @@ -24,136 +20,6 @@ class Followers { const POST_TYPE = 'ap_follower'; const CACHE_KEY_INBOXES = 'follower_inboxes_%s'; - /** - * Register WordPress hooks/actions and register Taxonomy - * - * @return void - */ - public static function init() { - // register "followers" post_type - self::register_post_type(); - - \add_action( 'activitypub_inbox_follow', array( self::class, 'handle_follow_request' ), 10, 2 ); - \add_action( 'activitypub_inbox_undo', array( self::class, 'handle_undo_request' ), 10, 2 ); - - \add_action( 'activitypub_followers_post_follow', array( self::class, 'send_follow_response' ), 10, 4 ); - } - - /** - * Register the "Followers" Taxonomy - * - * @return void - */ - private static function register_post_type() { - register_post_type( - self::POST_TYPE, - array( - 'labels' => array( - 'name' => _x( 'Followers', 'post_type plural name', 'activitypub' ), - 'singular_name' => _x( 'Follower', 'post_type single name', 'activitypub' ), - ), - 'public' => false, - 'hierarchical' => false, - 'rewrite' => false, - 'query_var' => false, - 'delete_with_user' => false, - 'can_export' => true, - 'supports' => array(), - ) - ); - - register_post_meta( - self::POST_TYPE, - 'activitypub_inbox', - array( - 'type' => 'string', - 'single' => true, - 'sanitize_callback' => array( self::class, 'sanitize_url' ), - ) - ); - - register_post_meta( - self::POST_TYPE, - 'activitypub_errors', - array( - 'type' => 'string', - 'single' => false, - 'sanitize_callback' => function( $value ) { - if ( ! is_string( $value ) ) { - throw new Exception( 'Error message is no valid string' ); - } - - return esc_sql( $value ); - }, - ) - ); - - register_post_meta( - self::POST_TYPE, - 'activitypub_user_id', - array( - 'type' => 'string', - 'single' => false, - 'sanitize_callback' => function( $value ) { - return esc_sql( $value ); - }, - ) - ); - - register_post_meta( - self::POST_TYPE, - 'activitypub_actor_json', - array( - 'type' => 'string', - 'single' => true, - 'sanitize_callback' => function( $value ) { - return sanitize_text_field( $value ); - }, - ) - ); - - do_action( 'activitypub_after_register_post_type' ); - } - - public static function sanitize_url( $value ) { - if ( filter_var( $value, FILTER_VALIDATE_URL ) === false ) { - return null; - } - - return esc_url_raw( $value ); - } - - /** - * Handle the "Follow" Request - * - * @param array $object The JSON "Follow" Activity - * @param int $user_id The ID of the ID of the WordPress User - * - * @return void - */ - public static function handle_follow_request( $object, $user_id ) { - // save follower - $follower = self::add_follower( $user_id, $object['actor'] ); - - do_action( 'activitypub_followers_post_follow', $object['actor'], $object, $user_id, $follower ); - } - - /** - * Handle "Unfollow" requests - * - * @param array $object The JSON "Undo" Activity - * @param int $user_id The ID of the ID of the WordPress User - */ - public static function handle_undo_request( $object, $user_id ) { - if ( - isset( $object['object'] ) && - isset( $object['object']['type'] ) && - 'Follow' === $object['object']['type'] - ) { - self::remove_follower( $user_id, $object['actor'] ); - } - } - /** * Add new Follower * @@ -173,8 +39,6 @@ class Followers { return new WP_Error( 'activitypub_invalid_follower', __( 'Invalid Follower', 'activitypub' ), array( 'status' => 400 ) ); } - $error = null; - $follower = new Follower(); $follower->from_array( $meta ); @@ -184,14 +48,10 @@ class Followers { return $id; } - $meta = get_post_meta( $id, 'activitypub_user_id' ); - - if ( $error ) { - self::add_error( $id, $error ); - } + $post_meta = get_post_meta( $id, 'activitypub_user_id' ); // phpcs:ignore WordPress.PHP.StrictInArray.MissingTrueStrict - if ( is_array( $meta ) && ! in_array( $user_id, $meta ) ) { + if ( is_array( $post_meta ) && ! in_array( $user_id, $post_meta ) ) { add_post_meta( $id, 'activitypub_user_id', $user_id ); wp_cache_delete( sprintf( self::CACHE_KEY_INBOXES, $user_id ), 'activitypub' ); } @@ -220,16 +80,17 @@ class Followers { } /** - * Get a Follower + * Get a Follower. * * @param int $user_id The ID of the WordPress User * @param string $actor The Actor URL * - * @return \Activitypub\Model\Follower The Follower object + * @return \Activitypub\Model\Follower|null The Follower object or null */ public static function get_follower( $user_id, $actor ) { global $wpdb; + // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching $post_id = $wpdb->get_var( $wpdb->prepare( "SELECT DISTINCT p.ID FROM $wpdb->posts p INNER JOIN $wpdb->postmeta pm ON p.ID = pm.post_id WHERE p.post_type = %s AND pm.meta_key = 'activitypub_user_id' AND pm.meta_value = %d AND p.guid = %s", @@ -250,51 +111,29 @@ class Followers { } /** - * Send Accept response + * Get a Follower by Actor indepenent from the User. * - * @param string $actor The Actor URL - * @param array $object The Activity object - * @param int $user_id The ID of the WordPress User - * @param Activitypub\Model\Follower $follower The Follower object + * @param string $actor The Actor URL. * - * @return void + * @return \Activitypub\Model\Follower|null The Follower object or null */ - public static function send_follow_response( $actor, $object, $user_id, $follower ) { - if ( is_wp_error( $follower ) ) { - // it is not even possible to send a "Reject" because - // we can not get the Remote-Inbox - return; - } + public static function get_follower_by_actor( $actor ) { + global $wpdb; - // only send minimal data - $object = array_intersect_key( - $object, - array_flip( - array( - 'id', - 'type', - 'actor', - 'object', - ) + // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching + $post_id = $wpdb->get_var( + $wpdb->prepare( + "SELECT ID FROM $wpdb->posts WHERE guid=%s", + esc_sql( $actor ) ) ); - $user = Users::get_by_id( $user_id ); + if ( $post_id ) { + $post = get_post( $post_id ); + return Follower::init_from_cpt( $post ); + } - // get inbox - $inbox = $follower->get_shared_inbox(); - - // send "Accept" activity - $activity = new Activity(); - $activity->set_type( 'Accept' ); - $activity->set_object( $object ); - $activity->set_actor( $user->get_id() ); - $activity->set_to( $actor ); - $activity->set_id( $user->get_id() . '#follow-' . \preg_replace( '~^https?://~', '', $actor ) . '-' . \time() ); - - $activity = $activity->to_json(); - - Http::post( $inbox, $activity, $user_id ); + return null; } /** @@ -360,6 +199,7 @@ class Followers { */ public static function get_all_followers() { $args = array( + 'nopaging' => true, // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_query 'meta_query' => array( 'relation' => 'AND', @@ -428,6 +268,7 @@ class Followers { // get all Followers of a ID of the WordPress User $posts = new WP_Query( array( + 'nopaging' => true, 'post_type' => self::POST_TYPE, 'fields' => 'ids', // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_query diff --git a/wp-content/plugins/activitypub/includes/collection/class-interactions.php b/wp-content/plugins/activitypub/includes/collection/class-interactions.php new file mode 100644 index 00000000..7a0fdcf9 --- /dev/null +++ b/wp-content/plugins/activitypub/includes/collection/class-interactions.php @@ -0,0 +1,235 @@ +comment_post_ID; + } + + // not a reply to a post or comment + if ( ! $comment_post_id ) { + return false; + } + + $meta = get_remote_metadata_by_actor( $activity['actor'] ); + + if ( ! $meta || \is_wp_error( $meta ) ) { + return false; + } + + $commentdata = array( + 'comment_post_ID' => $comment_post_id, + 'comment_author' => \esc_attr( $meta['name'] ), + '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_meta' => array( + 'source_id' => \esc_url_raw( $activity['object']['id'] ), + 'source_url' => \esc_url_raw( $activity['object']['url'] ), + 'protocol' => 'activitypub', + ), + ); + + if ( isset( $meta['icon']['url'] ) ) { + $commentdata['comment_meta']['avatar_url'] = \esc_url_raw( $meta['icon']['url'] ); + } + + // disable flood control + \remove_action( 'check_comment_flood', 'check_comment_flood_db', 10 ); + // do not require email for AP entries + \add_filter( 'pre_option_require_name_email', '__return_false' ); + // No nonce possible for this submission route + \add_filter( + 'akismet_comment_nonce', + function() { + return 'inactive'; + } + ); + \add_filter( 'wp_kses_allowed_html', array( self::class, 'allowed_comment_html' ), 10, 2 ); + + $comment = \wp_new_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; + } + + /** + * Update a comment + * + * @param array $activity The activity-object + * + * @return array|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'] ) ); + + if ( ! $object_comment_id ) { + 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'] ) ) { + $commentdata['comment_meta']['avatar_url'] = \esc_url_raw( $meta['icon']['url'] ); + } + + // disable flood control + \remove_action( 'check_comment_flood', 'check_comment_flood_db', 10 ); + // do not require email for AP entries + \add_filter( 'pre_option_require_name_email', '__return_false' ); + // No nonce possible for this submission route + \add_filter( + 'akismet_comment_nonce', + function() { + return 'inactive'; + } + ); + \add_filter( 'wp_kses_allowed_html', array( self::class, 'allowed_comment_html' ), 10, 2 ); + + $comment = \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; + } + + /** + * Get interaction(s) for a given URL/ID. + * + * @param strin $url The URL/ID to get interactions for. + * + * @return array The interactions as WP_Comment objects. + */ + public static function get_interaction_by_id( $url ) { + $args = array( + 'nopaging' => true, + // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_query + 'meta_query' => array( + 'relation' => 'AND', + array( + 'key' => 'protocol', + 'value' => 'activitypub', + ), + array( + 'relation' => 'OR', + array( + 'key' => 'source_url', + 'value' => $url, + ), + array( + 'key' => 'source_id', + 'value' => $url, + ), + ), + ), + ); + + $query = new WP_Comment_Query( $args ); + return $query->comments; + } + + /** + * Get interaction(s) for a given actor. + * + * @param string $actor The Actor-URL. + * + * @return array The interactions as WP_Comment objects. + */ + public static function get_interactions_by_actor( $actor ) { + $meta = get_remote_metadata_by_actor( $actor ); + + // get URL, because $actor seems to be the ID + if ( $meta && ! is_wp_error( $meta ) && isset( $meta['url'] ) ) { + $actor = $meta['url']; + } + + $args = array( + 'nopaging' => true, + 'author_url' => $actor, + // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_query + 'meta_query' => array( + array( + 'key' => 'protocol', + 'value' => 'activitypub', + 'compare' => '=', + ), + ), + ); + $comment_query = new WP_Comment_Query( $args ); + return $comment_query->comments; + } + + /** + * Adds line breaks to the list of allowed comment tags. + * + * @param array $allowed_tags Allowed HTML tags. + * @param string $context Context. + * + * @return array Filtered tag list. + */ + public static function allowed_comment_html( $allowed_tags, $context = '' ) { + if ( 'pre_comment_content' !== $context ) { + // Do nothing. + return $allowed_tags; + } + + // Add `p` and `br` to the list of allowed tags. + if ( ! array_key_exists( 'br', $allowed_tags ) ) { + $allowed_tags['br'] = array(); + } + + if ( ! array_key_exists( 'p', $allowed_tags ) ) { + $allowed_tags['p'] = array(); + } + + return $allowed_tags; + } +} diff --git a/wp-content/plugins/activitypub/includes/compat.php b/wp-content/plugins/activitypub/includes/compat.php index 4bee6402..c0996af8 100644 --- a/wp-content/plugins/activitypub/includes/compat.php +++ b/wp-content/plugins/activitypub/includes/compat.php @@ -35,3 +35,15 @@ if ( ! function_exists( 'get_self_link' ) ) { return esc_url( apply_filters( 'self_link', set_url_scheme( 'http://' . $host['host'] . $path ) ) ); } } + +if ( ! function_exists( 'is_countable' ) ) { + /** + * Polyfill for `is_countable()` function added in PHP 7.3. + * + * @param mixed $value The value to check. + * @return bool True if `$value` is countable, otherwise false. + */ + function is_countable( $value ) { + return is_array( $value ) || $value instanceof \Countable; + } +} diff --git a/wp-content/plugins/activitypub/includes/functions.php b/wp-content/plugins/activitypub/includes/functions.php index d5cd9f67..9b2c64d0 100644 --- a/wp-content/plugins/activitypub/includes/functions.php +++ b/wp-content/plugins/activitypub/includes/functions.php @@ -2,9 +2,11 @@ namespace Activitypub; use WP_Error; +use WP_Comment_Query; use Activitypub\Http; use Activitypub\Activity\Activity; use Activitypub\Collection\Followers; +use Activitypub\Collection\Users; /** * Returns the ActivityPub default JSON-context @@ -42,7 +44,7 @@ function get_webfinger_resource( $user_id ) { * @param string $actor The Actor URL. * @param bool $cached If the result should be cached. * - * @return array The Actor profile as array + * @return array|WP_Error The Actor profile as array or WP_Error on failure. */ function get_remote_metadata_by_actor( $actor, $cached = true ) { $pre = apply_filters( 'pre_get_remote_metadata_by_actor', false, $actor ); @@ -74,32 +76,25 @@ function get_remote_metadata_by_actor( $actor, $cached = true ) { if ( ! \wp_http_validate_url( $actor ) ) { $metadata = new WP_Error( 'activitypub_no_valid_actor_url', \__( 'The "actor" is no valid URL', 'activitypub' ), array( 'status' => 400, 'actor' => $actor ) ); - \set_transient( $transient_key, $metadata, HOUR_IN_SECONDS ); // Cache the error for a shorter period. return $metadata; } - $short_timeout = function() { - return 3; - }; - add_filter( 'activitypub_remote_get_timeout', $short_timeout ); $response = Http::get( $actor ); - remove_filter( 'activitypub_remote_get_timeout', $short_timeout ); + if ( \is_wp_error( $response ) ) { - \set_transient( $transient_key, $response, HOUR_IN_SECONDS ); // Cache the error for a shorter period. return $response; } $metadata = \wp_remote_retrieve_body( $response ); $metadata = \json_decode( $metadata, true ); - \set_transient( $transient_key, $metadata, WEEK_IN_SECONDS ); - if ( ! $metadata ) { $metadata = new WP_Error( 'activitypub_invalid_json', \__( 'No valid JSON data', 'activitypub' ), array( 'status' => 400, 'actor' => $actor ) ); - \set_transient( $transient_key, $metadata, HOUR_IN_SECONDS ); // Cache the error for a shorter period. return $metadata; } + \set_transient( $transient_key, $metadata, WEEK_IN_SECONDS ); + return $metadata; } @@ -285,6 +280,16 @@ function is_activitypub_request() { return false; } + // Check if the current post type supports ActivityPub. + if ( \is_singular() ) { + $queried_object = \get_queried_object(); + $post_type = \get_post_type( $queried_object ); + + if ( ! \post_type_supports( $post_type, 'activitypub' ) ) { + return false; + } + } + // One can trigger an ActivityPub request by adding ?activitypub to the URL. // phpcs:ignore VariableAnalysis.CodeAnalysis.VariableAnalysis.VariableRedeclaration global $wp_query; @@ -481,3 +486,215 @@ function is_json( $data ) { function is_blog_public() { return (bool) apply_filters( 'activitypub_is_blog_public', \get_option( 'blog_public', 1 ) ); } + +/** + * Sanitize a URL + * + * @param string $value The URL to sanitize + * + * @return string|null The sanitized URL or null if invalid + */ +function sanitize_url( $value ) { + if ( filter_var( $value, FILTER_VALIDATE_URL ) === false ) { + return null; + } + + return esc_url_raw( $value ); +} + +/** + * Extract recipient URLs from Activity object + * + * @param array $data + * + * @return array The list of user URLs + */ +function extract_recipients_from_activity( $data ) { + $recipient_items = array(); + + foreach ( array( 'to', 'bto', 'cc', 'bcc', 'audience' ) as $i ) { + if ( array_key_exists( $i, $data ) ) { + if ( is_array( $data[ $i ] ) ) { + $recipient = $data[ $i ]; + } else { + $recipient = array( $data[ $i ] ); + } + $recipient_items = array_merge( $recipient_items, $recipient ); + } + + if ( is_array( $data['object'] ) && array_key_exists( $i, $data['object'] ) ) { + if ( is_array( $data['object'][ $i ] ) ) { + $recipient = $data['object'][ $i ]; + } else { + $recipient = array( $data['object'][ $i ] ); + } + $recipient_items = array_merge( $recipient_items, $recipient ); + } + } + + $recipients = array(); + + // flatten array + foreach ( $recipient_items as $recipient ) { + if ( is_array( $recipient ) ) { + // check if recipient is an object + if ( array_key_exists( 'id', $recipient ) ) { + $recipients[] = $recipient['id']; + } + } else { + $recipients[] = $recipient; + } + } + + return array_unique( $recipients ); +} + +/** + * Check if passed Activity is Public + * + * @param array $data The Activity object as array + * + * @return boolean True if public, false if not + */ +function is_activity_public( $data ) { + $recipients = extract_recipients_from_activity( $data ); + + return in_array( 'https://www.w3.org/ns/activitystreams#Public', $recipients, true ); +} + +/** + * Get active users based on a given duration + * + * @param int $duration The duration to check in month(s) + * + * @return int The number of active users + */ +function get_active_users( $duration = 1 ) { + + $duration = intval( $duration ); + $transient_key = sprintf( 'monthly_active_users_%d', $duration ); + $count = get_transient( $transient_key ); + + if ( false === $count ) { + 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 + + set_transient( $transient_key, $count, DAY_IN_SECONDS ); + } + + // if 0 authors where active + if ( 0 === $count ) { + return 0; + } + + // if single user mode + if ( is_single_user() ) { + return 1; + } + + // if blog user is disabled + if ( is_user_disabled( Users::BLOG_USER_ID ) ) { + return $count; + } + + // also count blog user + return $count + 1; +} + +/** + * Get the total number of users + * + * @return int The total number of users + */ +function get_total_users() { + // if single user mode + if ( is_single_user() ) { + return 1; + } + + $users = \get_users( + array( + 'capability__in' => array( 'publish_posts' ), + ) + ); + + if ( is_array( $users ) ) { + $users = count( $users ); + } else { + $users = 1; + } + + // if blog user is disabled + if ( is_user_disabled( Users::BLOG_USER_ID ) ) { + return $users; + } + + return $users + 1; +} + +/** + * Examine a comment ID and look up an existing comment it represents. + * + * @param string $id ActivityPub object ID (usually a URL) to check. + * + * @return int|boolean Comment ID, or false on failure. + */ +function object_id_to_comment( $id ) { + $comment_query = new WP_Comment_Query( + array( + 'meta_key' => 'source_id', // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_key + 'meta_value' => $id, // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_value + ) + ); + + if ( ! $comment_query->comments ) { + return false; + } + + if ( count( $comment_query->comments ) > 1 ) { + return false; + } + + return $comment_query->comments[0]; +} + +/** + * Verify if URL is a local comment, + * Or if it is a previously received remote comment + * (For threading comments locally) + * + * @param string $url The URL to check. + * + * @return int comment_ID or null if not found + */ +function url_to_commentid( $url ) { + if ( ! $url || ! filter_var( $url, FILTER_VALIDATE_URL ) ) { + return null; + } + + $args = array( + // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_query + 'meta_query' => array( + 'relation' => 'OR', + array( + 'key' => 'source_url', + 'value' => $url, + ), + array( + 'key' => 'source_id', + 'value' => $url, + ), + ), + ); + + $query = new \WP_Comment_Query(); + $comments = $query->query( $args ); + + if ( $comments && is_array( $comments ) ) { + return $comments[0]->comment_ID; + } + + return null; +} diff --git a/wp-content/plugins/activitypub/includes/handler/class-create.php b/wp-content/plugins/activitypub/includes/handler/class-create.php new file mode 100644 index 00000000..2e5d76ad --- /dev/null +++ b/wp-content/plugins/activitypub/includes/handler/class-create.php @@ -0,0 +1,61 @@ +delete(); + } + } + + /** + * Delete Reactions if Actor-URL is a Tombstone. + * + * @param array $activity The delete activity. + */ + public static function maybe_delete_interactions( $activity ) { + // verify if Actor is deleted. + if ( Http::is_tombstone( $activity['actor'] ) ) { + \wp_schedule_single_event( + \time(), + 'activitypub_delete_actor_interactions', + array( $activity['actor'] ) + ); + } + } + + /** + * Delete comments from an Actor. + * + * @param array $comments The comments to delete. + */ + public static function delete_interactions( $actor ) { + $comments = Interactions::get_interactions_by_actor( $actor ); + + if ( is_array( $comments ) ) { + foreach ( $comments as $comment ) { + wp_delete_comment( $comment->comment_ID ); + } + } + } + + /** + * Delete a Reaction if URL is a Tombstone. + * + * @param array $activity The delete activity. + * + * @return void + */ + public static function maybe_delete_interaction( $activity ) { + if ( is_array( $activity['object'] ) ) { + $id = $activity['object']['id']; + } else { + $id = $activity['object']; + } + + $comments = Interactions::get_interaction_by_id( $id ); + + if ( $comments && Http::is_tombstone( $id ) ) { + foreach ( $comments as $comment ) { + wp_delete_comment( $comment->comment_ID, true ); + } + } + } + + /** + * Defer signature verification for `Delete` requests. + * + * @param bool $defer Whether to defer signature verification. + * @param WP_REST_Request $request The request object. + * + * @return bool Whether to defer signature verification. + */ + public static function defer_signature_verification( $defer, $request ) { + $json = $request->get_json_params(); + + if ( isset( $json['type'] ) && 'Delete' === $json['type'] ) { + return true; + } + + return false; + } +} diff --git a/wp-content/plugins/activitypub/includes/handler/class-follow.php b/wp-content/plugins/activitypub/includes/handler/class-follow.php new file mode 100644 index 00000000..6855dbd8 --- /dev/null +++ b/wp-content/plugins/activitypub/includes/handler/class-follow.php @@ -0,0 +1,81 @@ +get_shared_inbox(); + + // send "Accept" activity + $activity = new Activity(); + $activity->set_type( 'Accept' ); + $activity->set_object( $object ); + $activity->set_actor( $user->get_id() ); + $activity->set_to( $actor ); + $activity->set_id( $user->get_id() . '#follow-' . \preg_replace( '~^https?://~', '', $actor ) . '-' . \time() ); + + $activity = $activity->to_json(); + + Http::post( $inbox, $activity, $user_id ); + } +} diff --git a/wp-content/plugins/activitypub/includes/handler/class-undo.php b/wp-content/plugins/activitypub/includes/handler/class-undo.php new file mode 100644 index 00000000..13c06f3d --- /dev/null +++ b/wp-content/plugins/activitypub/includes/handler/class-undo.php @@ -0,0 +1,31 @@ +' . \wp_kses( __( 'The post\'s title.', 'activitypub' ), array( 'code' => array() ) ) . '' . '
[ap_content apply_filters="yes"]
' . '
' . \wp_kses( __( 'The post\'s content. With apply_filters you can decide if filters (apply_filters( \'the_content\', $content )) should be applied or not (default is yes). The values can be yes or no. apply_filters attribute is optional.', 'activitypub' ), array( 'code' => array() ) ) . '
' . - '
[ap_excerpt lenght="400"]
' . - '
' . \wp_kses( __( 'The post\'s excerpt (default 400 chars). length attribute is optional.', 'activitypub' ), array( 'code' => array() ) ) . '
' . + '
[ap_excerpt length="400"]
' . + '
' . \wp_kses( __( 'The post\'s excerpt (uses the_excerpt if that is set). If no excerpt is provided, will truncate at length (optional, default = 400).', 'activitypub' ), array( 'code' => array() ) ) . '
' . '
[ap_permalink type="url"]
' . '
' . \wp_kses( __( 'The post\'s permalink. type can be either: url or html (an <a /> tag). type attribute is optional.', 'activitypub' ), array( 'code' => array() ) ) . '
' . '
[ap_shortlink type="url"]
' . 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 1cfcec0e..cf4d9cc4 100644 --- a/wp-content/plugins/activitypub/includes/model/class-application-user.php +++ b/wp-content/plugins/activitypub/includes/model/class-application-user.php @@ -58,10 +58,6 @@ class Application_User extends Blog_User { return null; } - public function get_featured_tags() { - return null; - } - public function get_featured() { return null; } @@ -69,4 +65,8 @@ class Application_User extends Blog_User { public function get_moderators() { return null; } + + public function get_indexable() { + return false; + } } diff --git a/wp-content/plugins/activitypub/includes/model/class-follower.php b/wp-content/plugins/activitypub/includes/model/class-follower.php index 7cf6dd46..b2833e9c 100644 --- a/wp-content/plugins/activitypub/includes/model/class-follower.php +++ b/wp-content/plugins/activitypub/includes/model/class-follower.php @@ -315,7 +315,7 @@ class Follower extends Actor { $object->set_id( $post->guid ); $object->set_name( $post->post_title ); $object->set_summary( $post->post_excerpt ); - $object->set_published( gmdate( 'Y-m-d H:i:s', strtotime( $post->post_published ) ) ); + $object->set_published( gmdate( 'Y-m-d H:i:s', strtotime( $post->post_date ) ) ); $object->set_updated( gmdate( 'Y-m-d H:i:s', strtotime( $post->post_modified ) ) ); return $object; diff --git a/wp-content/plugins/activitypub/includes/model/class-user.php b/wp-content/plugins/activitypub/includes/model/class-user.php index f62772d3..95c83d73 100644 --- a/wp-content/plugins/activitypub/includes/model/class-user.php +++ b/wp-content/plugins/activitypub/includes/model/class-user.php @@ -18,15 +18,6 @@ class User extends Actor { */ protected $_id; // phpcs:ignore PSR2.Classes.PropertyDeclaration.Underscore - /** - * The Featured-Tags. - * - * @see https://docs.joinmastodon.org/spec/activitypub/#featuredTags - * - * @var string - */ - protected $featured_tags; - /** * The Featured-Posts. * @@ -235,15 +226,6 @@ class User extends Actor { return get_rest_url_by_path( sprintf( 'users/%d/collections/featured', $this->get__id() ) ); } - /** - * Returns the Featured-Tags-API-Endpoint. - * - * @return string The Featured-Tags-Endpoint. - */ - public function get_featured_tags() { - return get_rest_url_by_path( sprintf( 'users/%d/collections/tags', $this->get__id() ) ); - } - /** * Extend the User-Output with Attachments. * diff --git a/wp-content/plugins/activitypub/includes/rest/class-collection.php b/wp-content/plugins/activitypub/includes/rest/class-collection.php index 5fa585dd..365641c2 100644 --- a/wp-content/plugins/activitypub/includes/rest/class-collection.php +++ b/wp-content/plugins/activitypub/includes/rest/class-collection.php @@ -105,7 +105,7 @@ class Collection { '@context' => Activity::CONTEXT, 'id' => get_rest_url_by_path( sprintf( 'users/%d/collections/tags', $user->get__id() ) ), 'type' => 'Collection', - 'totalItems' => count( $tags ), + 'totalItems' => is_countable( $tags ) ? count( $tags ) : 0, 'items' => array(), ); @@ -117,7 +117,10 @@ class Collection { ); } - return new WP_REST_Response( $response, 200 ); + $rest_response = new WP_REST_Response( $response, 200 ); + $rest_response->header( 'Content-Type', 'application/activity+json; charset=' . get_option( 'blog_charset' ) ); + + return $rest_response; } /** @@ -160,7 +163,7 @@ class Collection { '@context' => Activity::CONTEXT, 'id' => get_rest_url_by_path( sprintf( 'users/%d/collections/featured', $user_id ) ), 'type' => 'OrderedCollection', - 'totalItems' => count( $posts ), + 'totalItems' => is_countable( $posts ) ? count( $posts ) : 0, 'orderedItems' => array(), ); @@ -168,7 +171,10 @@ class Collection { $response['orderedItems'][] = Post::transform( $post )->to_object()->to_array(); } - return new WP_REST_Response( $response, 200 ); + $rest_response = new WP_REST_Response( $response, 200 ); + $rest_response->header( 'Content-Type', 'application/activity+json; charset=' . get_option( 'blog_charset' ) ); + + return $rest_response; } /** @@ -192,7 +198,10 @@ class Collection { $response['orderedItems'][] = $user->get_url(); } - return new WP_REST_Response( $response, 200 ); + $rest_response = new WP_REST_Response( $response, 200 ); + $rest_response->header( 'Content-Type', 'application/activity+json; charset=' . get_option( 'blog_charset' ) ); + + return $rest_response; } /** diff --git a/wp-content/plugins/activitypub/includes/rest/class-followers.php b/wp-content/plugins/activitypub/includes/rest/class-followers.php index b7be9d02..71e48400 100644 --- a/wp-content/plugins/activitypub/includes/rest/class-followers.php +++ b/wp-content/plugins/activitypub/includes/rest/class-followers.php @@ -103,10 +103,10 @@ class Followers { $data['followers'] ); - $response = new WP_REST_Response( $json, 200 ); - $response->header( 'Content-Type', 'application/activity+json' ); + $rest_response = new WP_REST_Response( $json, 200 ); + $rest_response->header( 'Content-Type', 'application/activity+json; charset=' . get_option( 'blog_charset' ) ); - return $response; + return $rest_response; } /** diff --git a/wp-content/plugins/activitypub/includes/rest/class-following.php b/wp-content/plugins/activitypub/includes/rest/class-following.php index 33bdf650..58e4375c 100644 --- a/wp-content/plugins/activitypub/includes/rest/class-following.php +++ b/wp-content/plugins/activitypub/includes/rest/class-following.php @@ -1,6 +1,7 @@ totalItems = count( $items ); // phpcs:ignore + $json->totalItems = is_countable( $items ) ? count( $items ) : 0; // phpcs:ignore $json->orderedItems = $items; // phpcs:ignore $json->first = $json->partOf; // phpcs:ignore - $response = new \WP_REST_Response( $json, 200 ); - $response->header( 'Content-Type', 'application/activity+json' ); + $rest_response = new WP_REST_Response( $json, 200 ); + $rest_response->header( 'Content-Type', 'application/activity+json; charset=' . get_option( 'blog_charset' ) ); - return $response; + return $rest_response; } /** diff --git a/wp-content/plugins/activitypub/includes/rest/class-inbox.php b/wp-content/plugins/activitypub/includes/rest/class-inbox.php index 5d65b9b8..d38ffb51 100644 --- a/wp-content/plugins/activitypub/includes/rest/class-inbox.php +++ b/wp-content/plugins/activitypub/includes/rest/class-inbox.php @@ -11,6 +11,7 @@ use function Activitypub\get_context; use function Activitypub\url_to_authorid; use function Activitypub\get_rest_url_by_path; use function Activitypub\get_remote_metadata_by_actor; +use function Activitypub\extract_recipients_from_activity; /** * ActivityPub Inbox REST-Class @@ -25,8 +26,6 @@ class Inbox { */ public static function init() { self::register_routes(); - - \add_action( 'activitypub_inbox_create', array( self::class, 'handle_create' ), 10, 2 ); } /** @@ -109,11 +108,10 @@ class Inbox { */ \do_action( 'activitypub_inbox_post' ); - $response = new WP_REST_Response( $json, 200 ); + $rest_response = new WP_REST_Response( $json, 200 ); + $rest_response->header( 'Content-Type', 'application/activity+json; charset=' . get_option( 'blog_charset' ) ); - $response->header( 'Content-Type', 'application/activity+json' ); - - return $response; + return $rest_response; } /** @@ -131,14 +129,18 @@ class Inbox { return $user; } - $data = $request->get_json_params(); - $type = $request->get_param( 'type' ); - $type = \strtolower( $type ); + $data = $request->get_json_params(); + $activity = Activity::init_from_array( $data ); + $type = $request->get_param( 'type' ); + $type = \strtolower( $type ); - \do_action( 'activitypub_inbox', $data, $user->get__id(), $type ); - \do_action( "activitypub_inbox_{$type}", $data, $user->get__id() ); + \do_action( 'activitypub_inbox', $data, $user->get__id(), $type, $activity ); + \do_action( "activitypub_inbox_{$type}", $data, $user->get__id(), $activity ); - return new WP_REST_Response( array(), 202 ); + $rest_response = new WP_REST_Response( array(), 202 ); + $rest_response->header( 'Content-Type', 'application/activity+json; charset=' . get_option( 'blog_charset' ) ); + + return $rest_response; } /** @@ -149,16 +151,17 @@ class Inbox { * @return WP_REST_Response */ public static function shared_inbox_post( $request ) { - $data = $request->get_json_params(); - $type = $request->get_param( 'type' ); - $users = self::extract_recipients( $data ); + $data = $request->get_json_params(); + $activity = Activity::init_from_array( $data ); + $type = $request->get_param( 'type' ); + $users = self::get_recipients( $data ); if ( ! $users ) { return new WP_Error( 'rest_invalid_param', \__( 'No recipients found', 'activitypub' ), array( - 'status' => 404, + 'status' => 400, 'params' => array( 'to' => \__( 'Please check/validate "to" field', 'activitypub' ), 'bto' => \__( 'Please check/validate "bto" field', 'activitypub' ), @@ -179,11 +182,14 @@ class Inbox { $type = \strtolower( $type ); - \do_action( 'activitypub_inbox', $data, $user->ID, $type ); - \do_action( "activitypub_inbox_{$type}", $data, $user->ID ); + \do_action( 'activitypub_inbox', $data, $user->ID, $type, $activity ); + \do_action( "activitypub_inbox_{$type}", $data, $user->ID, $activity ); } - return new WP_REST_Response( array(), 202 ); + $rest_response = new WP_REST_Response( array(), 202 ); + $rest_response->header( 'Content-Type', 'application/activity+json; charset=' . get_option( 'blog_charset' ) ); + + return $rest_response; } /** @@ -231,8 +237,12 @@ class Inbox { $params['actor'] = array( 'required' => true, 'sanitize_callback' => function( $param, $request, $key ) { - if ( ! \is_string( $param ) ) { - $param = $param['id']; + if ( \is_array( $param ) ) { + if ( isset( $param['id'] ) ) { + $param = $param['id']; + } else { + $param = $param['url']; + } } return \esc_url_raw( $param ); }, @@ -331,118 +341,6 @@ class Inbox { return $params; } - /** - * Handles "Create" requests - * - * @param array $object The activity-object - * @param int $user_id The id of the local blog-user - */ - public static function handle_create( $object, $user_id ) { - $meta = get_remote_metadata_by_actor( $object['actor'] ); - - if ( ! isset( $object['object']['inReplyTo'] ) ) { - return; - } - - // check if Activity is public or not - if ( ! self::is_activity_public( $object ) ) { - // @todo maybe send email - return; - } - - $comment_post_id = \url_to_postid( $object['object']['inReplyTo'] ); - - // save only replys and reactions - if ( ! $comment_post_id ) { - return false; - } - - $commentdata = array( - 'comment_post_ID' => $comment_post_id, - 'comment_author' => \esc_attr( $meta['name'] ), - 'comment_author_url' => \esc_url_raw( $object['actor'] ), - 'comment_content' => \wp_filter_kses( $object['object']['content'] ), - 'comment_type' => 'comment', - 'comment_author_email' => '', - 'comment_parent' => 0, - 'comment_meta' => array( - 'source_url' => \esc_url_raw( $object['object']['url'] ), - 'avatar_url' => \esc_url_raw( $meta['icon']['url'] ), - 'protocol' => 'activitypub', - ), - ); - - // disable flood control - \remove_action( 'check_comment_flood', 'check_comment_flood_db', 10 ); - - // do not require email for AP entries - \add_filter( 'pre_option_require_name_email', '__return_false' ); - - // No nonce possible for this submission route - \add_filter( - 'akismet_comment_nonce', - function() { - return 'inactive'; - } - ); - - $state = \wp_new_comment( $commentdata, true ); - - \remove_filter( 'pre_option_require_name_email', '__return_false' ); - - // re-add flood control - \add_action( 'check_comment_flood', 'check_comment_flood_db', 10, 4 ); - - do_action( 'activitypub_handled_create', $object, $user_id, $state, $commentdata ); - } - - /** - * Extract recipient URLs from Activity object - * - * @param array $data - * - * @return array The list of user URLs - */ - public static function extract_recipients( $data ) { - $recipient_items = array(); - - foreach ( array( 'to', 'bto', 'cc', 'bcc', 'audience' ) as $i ) { - if ( array_key_exists( $i, $data ) ) { - if ( is_array( $data[ $i ] ) ) { - $recipient = $data[ $i ]; - } else { - $recipient = array( $data[ $i ] ); - } - $recipient_items = array_merge( $recipient_items, $recipient ); - } - - if ( array_key_exists( $i, $data['object'] ) ) { - if ( is_array( $data['object'][ $i ] ) ) { - $recipient = $data['object'][ $i ]; - } else { - $recipient = array( $data['object'][ $i ] ); - } - $recipient_items = array_merge( $recipient_items, $recipient ); - } - } - - $recipients = array(); - - // flatten array - foreach ( $recipient_items as $recipient ) { - if ( is_array( $recipient ) ) { - // check if recipient is an object - if ( array_key_exists( 'id', $recipient ) ) { - $recipients[] = $recipient['id']; - } - } else { - $recipients[] = $recipient; - } - } - - return array_unique( $recipients ); - } - /** * Get local user recipients * @@ -451,7 +349,7 @@ class Inbox { * @return array The list of local users */ public static function get_recipients( $data ) { - $recipients = self::extract_recipients( $data ); + $recipients = extract_recipients_from_activity( $data ); $users = array(); foreach ( $recipients as $recipient ) { @@ -466,16 +364,4 @@ class Inbox { return $users; } - - /** - * Check if passed Activity is Public - * - * @param array $data - * @return boolean - */ - public static function is_activity_public( $data ) { - $recipients = self::extract_recipients( $data ); - - return in_array( 'https://www.w3.org/ns/activitystreams#Public', $recipients, true ); - } } diff --git a/wp-content/plugins/activitypub/includes/rest/class-nodeinfo.php b/wp-content/plugins/activitypub/includes/rest/class-nodeinfo.php index 1f6277af..62151fff 100644 --- a/wp-content/plugins/activitypub/includes/rest/class-nodeinfo.php +++ b/wp-content/plugins/activitypub/includes/rest/class-nodeinfo.php @@ -3,6 +3,8 @@ namespace Activitypub\Rest; use WP_REST_Response; +use function Activitypub\get_total_users; +use function Activitypub\get_active_users; use function Activitypub\get_rest_url_by_path; /** @@ -82,24 +84,14 @@ class Nodeinfo { 'version' => \get_bloginfo( 'version' ), ); - $users = \get_users( - array( - 'capability__in' => array( 'publish_posts' ), - ) - ); - - if ( is_array( $users ) ) { - $users = count( $users ); - } else { - $users = 1; - } - $posts = \wp_count_posts(); $comments = \wp_count_comments(); $nodeinfo['usage'] = array( 'users' => array( - 'total' => $users, + 'total' => get_total_users(), + 'activeMonth' => get_active_users( '1 month ago' ), + 'activeHalfyear' => get_active_users( '6 month ago' ), ), 'localPosts' => (int) $posts->publish, 'localComments' => (int) $comments->approved, @@ -139,24 +131,14 @@ class Nodeinfo { 'version' => \get_bloginfo( 'version' ), ); - $users = \get_users( - array( - 'capability__in' => array( 'publish_posts' ), - ) - ); - - if ( is_array( $users ) ) { - $users = count( $users ); - } else { - $users = 1; - } - $posts = \wp_count_posts(); $comments = \wp_count_comments(); $nodeinfo['usage'] = array( 'users' => array( - 'total' => (int) $users, + 'total' => get_total_users(), + 'activeMonth' => get_active_users( 1 ), + 'activeHalfyear' => get_active_users( 6 ), ), 'localPosts' => (int) $posts->publish, 'localComments' => (int) $comments->approved, diff --git a/wp-content/plugins/activitypub/includes/rest/class-outbox.php b/wp-content/plugins/activitypub/includes/rest/class-outbox.php index eb35e865..d640d173 100644 --- a/wp-content/plugins/activitypub/includes/rest/class-outbox.php +++ b/wp-content/plugins/activitypub/includes/rest/class-outbox.php @@ -123,11 +123,10 @@ class Outbox { */ \do_action( 'activitypub_outbox_post' ); - $response = new WP_REST_Response( $json, 200 ); + $rest_response = new WP_REST_Response( $json, 200 ); + $rest_response->header( 'Content-Type', 'application/activity+json; charset=' . get_option( 'blog_charset' ) ); - $response->header( 'Content-Type', 'application/activity+json' ); - - return $response; + return $rest_response; } /** diff --git a/wp-content/plugins/activitypub/includes/rest/class-server.php b/wp-content/plugins/activitypub/includes/rest/class-server.php index e1a10379..1bad5a73 100644 --- a/wp-content/plugins/activitypub/includes/rest/class-server.php +++ b/wp-content/plugins/activitypub/includes/rest/class-server.php @@ -2,6 +2,7 @@ namespace Activitypub\Rest; use stdClass; +use WP_Error; use WP_REST_Response; use Activitypub\Signature; use Activitypub\Model\Application_User; @@ -54,11 +55,10 @@ class Server { $json = $user->to_array(); - $response = new WP_REST_Response( $json, 200 ); + $rest_response = new WP_REST_Response( $json, 200 ); + $rest_response->header( 'Content-Type', 'application/activity+json; charset=' . get_option( 'blog_charset' ) ); - $response->header( 'Content-Type', 'application/activity+json' ); - - return $response; + return $rest_response; } /** @@ -74,6 +74,10 @@ class Server { * @return mixed|WP_Error The response, error, or modified response. */ public static function authorize_activitypub_requests( $response, $handler, $request ) { + if ( 'HEAD' === $request->get_method() ) { + return $response; + } + $route = $request->get_route(); // check if it is an activitypub request and exclude webfinger and nodeinfo endpoints @@ -85,18 +89,41 @@ class Server { return $response; } + /** + * Filter to defer signature verification + * + * Skip signature verification for debugging purposes or to reduce load for + * certain Activity-Types, like "Delete". + * + * @param bool $defer Whether to defer signature verification. + * @param WP_REST_Request $request The request used to generate the response. + * + * @return bool Whether to defer signature verification. + */ + $defer = \apply_filters( 'activitypub_defer_signature_verification', false, $request ); + + if ( $defer ) { + return $response; + } + // POST-Requets are always signed - if ( 'get' !== \strtolower( $request->get_method() ) ) { + if ( 'GET' !== $request->get_method() ) { $verified_request = Signature::verify_http_signature( $request ); if ( \is_wp_error( $verified_request ) ) { - return $verified_request; + return new WP_Error( + 'activitypub_signature_verification', + $verified_request->get_error_message(), + array( 'status' => 401 ) + ); } - } elseif ( 'get' === \strtolower( $request->get_method() ) ) { // GET-Requests are only signed in secure mode - if ( ACTIVITYPUB_AUTHORIZED_FETCH ) { - $verified_request = Signature::verify_http_signature( $request ); - if ( \is_wp_error( $verified_request ) ) { - return $verified_request; - } + } elseif ( 'GET' === $request->get_method() && ACTIVITYPUB_AUTHORIZED_FETCH ) { // GET-Requests are only signed in secure mode + $verified_request = Signature::verify_http_signature( $request ); + if ( \is_wp_error( $verified_request ) ) { + return new WP_Error( + 'activitypub_signature_verification', + $verified_request->get_error_message(), + array( 'status' => 401 ) + ); } } diff --git a/wp-content/plugins/activitypub/includes/rest/class-users.php b/wp-content/plugins/activitypub/includes/rest/class-users.php index 31a92ddd..9fb10bab 100644 --- a/wp-content/plugins/activitypub/includes/rest/class-users.php +++ b/wp-content/plugins/activitypub/includes/rest/class-users.php @@ -95,10 +95,10 @@ class Users { $json = $user->to_array(); - $response = new WP_REST_Response( $json, 200 ); - $response->header( 'Content-Type', 'application/activity+json' ); + $rest_response = new WP_REST_Response( $json, 200 ); + $rest_response->header( 'Content-Type', 'application/activity+json; charset=' . get_option( 'blog_charset' ) ); - return $response; + return $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 f69593f1..34ae3929 100644 --- a/wp-content/plugins/activitypub/includes/rest/class-webfinger.php +++ b/wp-content/plugins/activitypub/includes/rest/class-webfinger.php @@ -113,6 +113,12 @@ class Webfinger { ), ); + if ( 'Group' === $user->get_type() ) { + $profile['links'][0]['properties'] = array( + 'https://www.w3.org/ns/activitystreams#type' => 'Group', + ); + } + return $profile; } } diff --git a/wp-content/plugins/activitypub/includes/table/class-followers.php b/wp-content/plugins/activitypub/includes/table/class-followers.php index 289a1945..3045dddf 100644 --- a/wp-content/plugins/activitypub/includes/table/class-followers.php +++ b/wp-content/plugins/activitypub/includes/table/class-followers.php @@ -30,19 +30,24 @@ class Followers extends WP_List_Table { public function get_columns() { return array( - 'cb' => '', - 'avatar' => \__( 'Avatar', 'activitypub' ), - 'name' => \__( 'Name', 'activitypub' ), - 'username' => \__( 'Username', 'activitypub' ), - 'url' => \__( 'URL', 'activitypub' ), - 'updated' => \__( 'Last updated', 'activitypub' ), - //'errors' => \__( 'Errors', 'activitypub' ), - //'latest-error' => \__( 'Latest Error Message', 'activitypub' ), + 'cb' => '', + 'avatar' => \__( 'Avatar', 'activitypub' ), + 'post_title' => \__( 'Name', 'activitypub' ), + 'username' => \__( 'Username', 'activitypub' ), + 'url' => \__( 'URL', 'activitypub' ), + 'published' => \__( 'Followed', 'activitypub' ), + 'modified' => \__( 'Last updated', 'activitypub' ), ); } public function get_sortable_columns() { - return array(); + $sortable_columns = array( + 'post_title' => array( 'post_title', true ), + 'modified' => array( 'modified', false ), + 'published' => array( 'published', false ), + ); + + return $sortable_columns; } public function prepare_items() { @@ -55,8 +60,32 @@ class Followers extends WP_List_Table { $page_num = $this->get_pagenum(); $per_page = 20; - $followers = FollowerCollection::get_followers( $this->user_id, $per_page, $page_num ); - $counter = FollowerCollection::count_followers( $this->user_id ); + $args = array(); + + // phpcs:ignore WordPress.Security.NonceVerification.Recommended + if ( isset( $_GET['orderby'] ) ) { + // phpcs:ignore WordPress.Security.NonceVerification.Recommended + $args['orderby'] = sanitize_text_field( wp_unslash( $_GET['orderby'] ) ); + } + + // phpcs:ignore WordPress.Security.NonceVerification.Recommended + if ( isset( $_GET['order'] ) ) { + // phpcs:ignore WordPress.Security.NonceVerification.Recommended + $args['order'] = sanitize_text_field( wp_unslash( $_GET['order'] ) ); + } + + // phpcs:ignore WordPress.Security.NonceVerification.Recommended + if ( isset( $_GET['s'] ) && isset( $_REQUEST['_wpnonce'] ) ) { + $nonce = sanitize_text_field( wp_unslash( $_REQUEST['_wpnonce'] ) ); + if ( wp_verify_nonce( $nonce, 'bulk-' . $this->_args['plural'] ) ) { + // phpcs:ignore WordPress.Security.NonceVerification.Recommended + $args['s'] = sanitize_text_field( wp_unslash( $_GET['s'] ) ); + } + } + + $followers_with_count = FollowerCollection::get_followers_with_count( $this->user_id, $per_page, $page_num, $args ); + $followers = $followers_with_count['followers']; + $counter = $followers_with_count['total']; $this->items = array(); $this->set_pagination_args( @@ -69,14 +98,13 @@ class Followers extends WP_List_Table { foreach ( $followers as $follower ) { $item = array( - 'icon' => esc_attr( $follower->get_icon_url() ), - 'name' => esc_attr( $follower->get_name() ), - 'username' => esc_attr( $follower->get_preferred_username() ), - 'url' => esc_attr( $follower->get_url() ), - 'identifier' => esc_attr( $follower->get_id() ), - 'updated' => esc_attr( $follower->get_updated() ), - 'errors' => $follower->count_errors(), - 'latest-error' => $follower->get_latest_error_message(), + '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() ), + 'identifier' => esc_attr( $follower->get_id() ), + 'published' => esc_attr( $follower->get_published() ), + 'modified' => esc_attr( $follower->get_updated() ), ); $this->items[] = $item; @@ -116,11 +144,11 @@ class Followers extends WP_List_Table { } public function process_action() { - if ( ! isset( $_REQUEST['followers'] ) || ! isset( $_REQUEST['_apnonce'] ) ) { + if ( ! isset( $_REQUEST['followers'] ) || ! isset( $_REQUEST['_wpnonce'] ) ) { return false; } - $nonce = sanitize_text_field( wp_unslash( $_REQUEST['_apnonce'] ) ); - if ( ! wp_verify_nonce( $nonce, 'activitypub-followers-list' ) ) { + $nonce = sanitize_text_field( wp_unslash( $_REQUEST['_wpnonce'] ) ); + if ( ! wp_verify_nonce( $nonce, 'bulk-' . $this->_args['plural'] ) ) { return false; } diff --git a/wp-content/plugins/activitypub/includes/transformer/class-post.php b/wp-content/plugins/activitypub/includes/transformer/class-post.php index 6e2f0aa9..721bbec0 100644 --- a/wp-content/plugins/activitypub/includes/transformer/class-post.php +++ b/wp-content/plugins/activitypub/includes/transformer/class-post.php @@ -5,6 +5,7 @@ use WP_Post; use Activitypub\Collection\Users; use Activitypub\Model\Blog_User; use Activitypub\Activity\Base_Object; +use Activitypub\Shortcodes; use function Activitypub\esc_hashtag; use function Activitypub\is_single_user; @@ -81,7 +82,7 @@ class Post { $object->set_content( $this->get_content() ); $object->set_content_map( array( - \strstr( \get_locale(), '_', true ) => $this->get_content(), + $this->get_locale() => $this->get_content(), ) ); $path = sprintf( 'users/%d/followers', intval( $wp_post->post_author ) ); @@ -142,78 +143,64 @@ class Post { } /** - * Returns the Image Attachments for this Post, parsed from blocks. - * @param int $max_images The maximum number of images to return. - * @param array $image_ids The image IDs to append new IDs to. + * Generates all Media Attachments for a Post. * - * @return array The image IDs. - */ - protected function get_block_image_ids( $max_images, $image_ids = [] ) { - $blocks = \parse_blocks( $this->wp_post->post_content ); - return self::get_image_ids_from_blocks( $blocks, $image_ids, $max_images ); - } - - /** - * Recursively get image IDs from blocks. - * @param array $blocks The blocks to search for image IDs - * @param array $image_ids The image IDs to append new IDs to - * @param int $max_images The maximum number of images to return. - * - * @return array The image IDs. - */ - protected static function get_image_ids_from_blocks( $blocks, $image_ids, $max_images ) { - foreach ( $blocks as $block ) { - // recurse into inner blocks - if ( ! empty( $block['innerBlocks'] ) ) { - $image_ids = self::get_image_ids_from_blocks( $block['innerBlocks'], $image_ids, $max_images ); - } - - switch ( $block['blockName'] ) { - case 'core/image': - case 'core/cover': - if ( ! empty( $block['attrs']['id'] ) ) { - $image_ids[] = $block['attrs']['id']; - } - break; - case 'jetpack/slideshow': - case 'jetpack/tiled-gallery': - if ( ! empty( $block['attrs']['ids'] ) ) { - $image_ids = array_merge( $image_ids, $block['attrs']['ids'] ); - } - break; - case 'jetpack/image-compare': - if ( ! empty( $block['attrs']['beforeImageId'] ) ) { - $image_ids[] = $block['attrs']['beforeImageId']; - } - if ( ! empty( $block['attrs']['afterImageId'] ) ) { - $image_ids[] = $block['attrs']['afterImageId']; - } - break; - } - - // we could be at or over max, stop unneeded work - if ( count( $image_ids ) >= $max_images ) { - break; - } - } - - // still need to slice it because one gallery could knock us over the limit - return \array_slice( $image_ids, 0, $max_images ); - } - - /** - * Generates all Image Attachments for a Post. - * - * @return array The Image Attachments. + * @return array The Attachments. */ protected function get_attachments() { - $max_images = intval( \apply_filters( 'activitypub_max_image_attachments', \get_option( 'activitypub_max_image_attachments', ACTIVITYPUB_MAX_IMAGE_ATTACHMENTS ) ) ); + // 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 ) ) ); - $images = array(); + if ( site_supports_blocks() && \has_blocks( $this->wp_post->post_content ) ) { + return $this->get_block_attachments( $max_media ); + } + return $this->get_classic_editor_images( $max_media ); + } + + /** + * Get media attachments from blocks. They will be formatted as ActivityPub attachments, not as WP attachments. + * + * @param int $max_media The maximum number of attachments to return. + * + * @return array The attachments. + */ + protected function get_block_attachments( $max_media ) { + // max media can't be negative or zero + if ( $max_media <= 0 ) { + return array(); + } + + $id = $this->wp_post->ID; + + $media_ids = array(); + + // list post thumbnail first if this post has one + if ( \function_exists( 'has_post_thumbnail' ) && \has_post_thumbnail( $id ) ) { + $media_ids[] = \get_post_thumbnail_id( $id ); + } + + if ( $max_media > 0 ) { + $blocks = \parse_blocks( $this->wp_post->post_content ); + $media_ids = self::get_media_ids_from_blocks( $blocks, $media_ids, $max_media ); + } + + return \array_filter( \array_map( array( self::class, 'wp_attachment_to_activity_attachment' ), $media_ids ) ); + } + + /** + * Get image attachments 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. + * + * @return array The attachments. + */ + protected function get_classic_editor_images( $max_images ) { // max images can't be negative or zero if ( $max_images <= 0 ) { - return $images; + return array(); } $id = $this->wp_post->ID; @@ -227,68 +214,147 @@ class Post { } if ( $max_images > 0 ) { - // first try to get images that are actually in the post content - if ( site_supports_blocks() && \has_blocks( $this->wp_post->post_content ) ) { - $block_image_ids = $this->get_block_image_ids( $max_images, $image_ids ); - $image_ids = \array_merge( $image_ids, $block_image_ids ); - } else { - // fallback to images attached to the post - $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; - } + $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; } } } - $image_ids = \array_unique( $image_ids ); - // get URLs for each image - foreach ( $image_ids as $id ) { - $image_size = 'full'; + return \array_filter( \array_map( array( self::class, 'wp_attachment_to_activity_attachment' ), $image_ids ) ); + } - /** - * Filter the image URL returned for each post. - * - * @param array|false $thumbnail The image URL, or false if no image is available. - * @param int $id The attachment ID. - * @param string $image_size The image size to retrieve. Set to 'full' by default. - */ - $thumbnail = apply_filters( - 'activitypub_get_image', - $this->get_image( $id, $image_size ), - $id, - $image_size - ); + /** + * Recursively get media IDs from blocks. + * @param array $blocks The blocks to search for media IDs + * @param array $media_ids The media IDs to append new IDs to + * @param int $max_media The maximum number of media to return. + * + * @return array The image IDs. + */ + protected static function get_media_ids_from_blocks( $blocks, $media_ids, $max_media ) { - if ( $thumbnail ) { - $mimetype = \get_post_mime_type( $id ); - $alt = \get_post_meta( $id, '_wp_attachment_image_alt', true ); - $image = array( - 'type' => 'Image', - 'url' => $thumbnail[0], - 'mediaType' => $mimetype, - ); + foreach ( $blocks as $block ) { + // recurse into inner blocks + if ( ! empty( $block['innerBlocks'] ) ) { + $media_ids = self::get_media_ids_from_blocks( $block['innerBlocks'], $media_ids, $max_media ); + } - if ( $alt ) { - $image['name'] = $alt; - } - $images[] = $image; + switch ( $block['blockName'] ) { + case 'core/image': + case 'core/cover': + case 'core/audio': + case 'core/video': + case 'videopress/video': + if ( ! empty( $block['attrs']['id'] ) ) { + $media_ids[] = $block['attrs']['id']; + } + break; + case 'jetpack/slideshow': + case 'jetpack/tiled-gallery': + if ( ! empty( $block['attrs']['ids'] ) ) { + $media_ids = array_merge( $media_ids, $block['attrs']['ids'] ); + } + break; + case 'jetpack/image-compare': + if ( ! empty( $block['attrs']['beforeImageId'] ) ) { + $media_ids[] = $block['attrs']['beforeImageId']; + } + if ( ! empty( $block['attrs']['afterImageId'] ) ) { + $media_ids[] = $block['attrs']['afterImageId']; + } + break; + } + + // depupe + $media_ids = \array_unique( $media_ids ); + + // stop doing unneeded work + if ( count( $media_ids ) >= $max_media ) { + break; } } - return $images; + // still need to slice it because one gallery could knock us over the limit + return array_slice( $media_ids, 0, $max_media ); + } + + /** + * Converts a WordPress Attachment to an ActivityPub Attachment. + * + * @param int $id The Attachment ID. + * + * @return array The ActivityPub Attachment. + */ + public static function wp_attachment_to_activity_attachment( $id ) { + $attachment = array(); + $mime_type = \get_post_mime_type( $id ); + $mime_type_parts = \explode( '/', $mime_type ); + // switching on image/audio/video + switch ( $mime_type_parts[0] ) { + case 'image': + $image_size = 'full'; + + /** + * Filter the image URL returned for each post. + * + * @param array|false $thumbnail The image URL, or false if no image is available. + * @param int $id The attachment ID. + * @param string $image_size The image size to retrieve. Set to 'full' by default. + */ + $thumbnail = apply_filters( + 'activitypub_get_image', + self::get_image( $id, $image_size ), + $id, + $image_size + ); + + if ( $thumbnail ) { + $alt = \get_post_meta( $id, '_wp_attachment_image_alt', true ); + $image = array( + 'type' => 'Image', + 'url' => $thumbnail[0], + 'mediaType' => $mime_type, + ); + + if ( $alt ) { + $image['name'] = $alt; + } + $attachment = $image; + } + break; + + case 'audio': + case 'video': + $attachment = array( + 'type' => 'Document', + 'mediaType' => $mime_type, + 'url' => \wp_get_attachment_url( $id ), + 'name' => \get_the_title( $id ), + ); + $meta = wp_get_attachment_metadata( $id ); + // height and width for videos + if ( isset( $meta['width'] ) && isset( $meta['height'] ) ) { + $attachment['width'] = $meta['width']; + $attachment['height'] = $meta['height']; + } + // @todo: add `icon` support for audio/video attachments. Maybe use post thumbnail? + break; + } + + return \apply_filters( 'activitypub_attachment', $attachment, $id ); } /** @@ -299,7 +365,7 @@ class Post { * * @return array|false Array of image data, or boolean false if no image is available. */ - protected function get_image( $id, $image_size = 'full' ) { + protected static function get_image( $id, $image_size = 'full' ) { /** * Hook into the image retrieval process. Before image retrieval. * @@ -308,7 +374,7 @@ class Post { */ do_action( 'activitypub_get_image_pre', $id, $image_size ); - $thumbnail = \wp_get_attachment_image_src( $id, $image_size ); + $image = \wp_get_attachment_image_src( $id, $image_size ); /** * Hook into the image retrieval process. After image retrieval. @@ -318,7 +384,7 @@ class Post { */ do_action( 'activitypub_get_image_post', $id, $image_size ); - return $thumbnail; + return $image; } /** @@ -334,6 +400,8 @@ class Post { return \ucfirst( \get_option( 'activitypub_object_type', 'note' ) ); } + // Default to Article. + $object_type = 'Article'; $post_type = \get_post_type( $this->wp_post ); switch ( $post_type ) { case 'post': @@ -466,6 +534,8 @@ class Post { $post = $this->wp_post; $content = $this->get_post_content_template(); + // Register our shortcodes just in time. + Shortcodes::register(); // Fill in the shortcodes. setup_postdata( $post ); $content = do_shortcode( $content ); @@ -477,6 +547,9 @@ class Post { $content = \apply_filters( 'activitypub_the_content', $content, $post ); + // Don't need these any more, should never appear in a post. + Shortcodes::unregister(); + return $content; } @@ -509,4 +582,25 @@ class Post { protected function get_mentions() { return apply_filters( 'activitypub_extract_mentions', array(), $this->wp_post->post_content, $this->wp_post ); } + + /** + * Returns the locale of the post. + * + * @return string The locale of the post. + */ + public function get_locale() { + $post_id = $this->wp_post->ID; + $lang = \strtolower( \strtok( \get_locale(), '_-' ) ); + + /** + * Filter the locale of the post. + * + * @param string $lang The locale of the post. + * @param int $post_id The post ID. + * @param WP_Post $post The post object. + * + * @return string The filtered locale of the post. + */ + return apply_filters( 'activitypub_post_locale', $lang, $post_id, $this->wp_post ); + } } diff --git a/wp-content/plugins/activitypub/integration/class-nodeinfo.php b/wp-content/plugins/activitypub/integration/class-nodeinfo.php index f1e2506a..dea6c678 100644 --- a/wp-content/plugins/activitypub/integration/class-nodeinfo.php +++ b/wp-content/plugins/activitypub/integration/class-nodeinfo.php @@ -1,6 +1,9 @@ get_total_users(), + 'activeMonth' => get_active_users( '1 month ago' ), + 'activeHalfyear' => get_active_users( '6 month ago' ), + ); + return $nodeinfo; } @@ -44,6 +53,12 @@ class Nodeinfo { public static function add_nodeinfo2_discovery( $nodeinfo ) { $nodeinfo['protocols'][] = 'activitypub'; + $nodeinfo['usage']['users'] = array( + 'total' => get_total_users(), + 'activeMonth' => get_active_users( '1 month ago' ), + 'activeHalfyear' => get_active_users( '6 month ago' ), + ); + return $nodeinfo; } } diff --git a/wp-content/plugins/activitypub/integration/class-webfinger.php b/wp-content/plugins/activitypub/integration/class-webfinger.php index 177b4173..c9dd565e 100644 --- a/wp-content/plugins/activitypub/integration/class-webfinger.php +++ b/wp-content/plugins/activitypub/integration/class-webfinger.php @@ -30,6 +30,10 @@ class Webfinger { public static function add_user_discovery( $array, $resource, $user ) { $user = User_Collection::get_by_id( $user->ID ); + if ( ! $user || is_wp_error( $user ) ) { + return $array; + } + $array['links'][] = array( 'rel' => 'self', 'type' => 'application/activity+json', diff --git a/wp-content/plugins/activitypub/readme.txt b/wp-content/plugins/activitypub/readme.txt index 03e9b39a..b7c9b67a 100644 --- a/wp-content/plugins/activitypub/readme.txt +++ b/wp-content/plugins/activitypub/readme.txt @@ -2,8 +2,8 @@ Contributors: automattic, pfefferle, mediaformat, mattwiebe, akirk, jeherve, nuriapena, cavalierlife Tags: OStatus, fediverse, activitypub, activitystream Requires at least: 4.7 -Tested up to: 6.3 -Stable tag: 1.0.7 +Tested up to: 6.4 +Stable tag: 1.3.0 Requires PHP: 5.6 License: MIT License URI: http://opensource.org/licenses/MIT @@ -105,6 +105,54 @@ Where 'blog' is the path to the subdirectory at which your blog resides. Project maintained on GitHub at [automattic/wordpress-activitypub](https://github.com/automattic/wordpress-activitypub). += 1.3.0 = + +* Added: Threaded-Comments support +* Improved: alt text for avatars in Follow Me/Followers blocks +* Improved: `Delete`, `Update` and `Follow` Activities +* Improved: better/more effective handling of `Delete` Activities +* Improved: allow `

` and `
` for Comments +* Fixed: removed default limit of WP_Query to send updates to all Inboxes and not only to the first 10 + += 1.2.0 = + +* Add: Search and order followerer lists +* Add: Have a filter to defer signature verification +* Improved: "Follow Me" styles for dark themes +* Improved: Allow `p` and `br` tags only for AP comments +* Fixed: Deduplicate attachments earlier to prevent incorrect max_media + + += 1.1.0 = + +* Improved: audio and video attachments are now supported! +* Improved: better error messages if remote profile is not accessible +* Improved: PHP 8.1 compatibility +* Fixed: don't try to parse mentions or hashtags for very large (>1MB) posts to prevent timeouts +* Fixed: better handling of ISO-639-1 locale codes +* Improved: more reliable [ap_author], props @uk3 +* Improved: NodeInfo statistics + += 1.0.10 = + +* Improved: better error messages if remote profile is not accessible + += 1.0.9 = + +* Fixed: broken following endpoint + += 1.0.8 = + +* Fixed: blocking of HEAD requests +* Fixed: PHP fatal error +* Fixed: several typos +* Fixed: error codes +* Improved: loading of shortcodes +* Updated: caching of followers +* Updated: Application-User is no longer "indexable" +* Updated: more consistent usage of the `application/activity+json` Content-Type +* Removed: featured tags endpoint + = 1.0.7 = * Fixed: broken function call diff --git a/wp-content/plugins/activitypub/templates/blog-user-followers-list.php b/wp-content/plugins/activitypub/templates/blog-user-followers-list.php index 42d1b8b3..1eaa7ee9 100644 --- a/wp-content/plugins/activitypub/templates/blog-user-followers-list.php +++ b/wp-content/plugins/activitypub/templates/blog-user-followers-list.php @@ -21,8 +21,8 @@ $followers_template = _n( 'Your blog profile currently has %s follower.', 'Your prepare_items(); + $table->search_box( 'Search', 'search' ); $table->display(); ?> - diff --git a/wp-content/plugins/activitypub/templates/settings.php b/wp-content/plugins/activitypub/templates/settings.php index fd801458..642ab164 100644 --- a/wp-content/plugins/activitypub/templates/settings.php +++ b/wp-content/plugins/activitypub/templates/settings.php @@ -93,7 +93,7 @@ - - +

@@ -125,7 +125,7 @@
  • [ap_title] -
  • [ap_content] -
  • -
  • [ap_excerpt] -
  • +
  • [ap_excerpt] -
  • [ap_permalink] -
  • [ap_shortlink] - Hum.', 'activitypub' ), 'default' ); ?>
  • [ap_hashtags] -
  • @@ -138,7 +138,7 @@ - + @@ -147,13 +147,20 @@ echo \wp_kses( \sprintf( // translators: - \__( 'The number of images to attach to posts. Default: %s', 'activitypub' ), + \__( 'The number of media (images, audio, video) to attach to posts. Default: %s', 'activitypub' ), \esc_html( ACTIVITYPUB_MAX_IMAGE_ATTACHMENTS ) ), 'default' ); ?>

    +

    + + + +

    diff --git a/wp-content/plugins/activitypub/templates/user-followers-list.php b/wp-content/plugins/activitypub/templates/user-followers-list.php index ec19be90..8ea35a73 100644 --- a/wp-content/plugins/activitypub/templates/user-followers-list.php +++ b/wp-content/plugins/activitypub/templates/user-followers-list.php @@ -14,8 +14,8 @@ $followers_template = _n( 'Your author profile currently has %s follower.', 'You prepare_items(); + $table->search_box( 'Search', 'search' ); $table->display(); ?> -