updated plugin ActivityPub
version 2.4.0
This commit is contained in:
parent
eeef5ad6e0
commit
4e493c268e
@ -1,42 +0,0 @@
|
|||||||
.DS_Store
|
|
||||||
.editorconfig
|
|
||||||
.git
|
|
||||||
.gitignore
|
|
||||||
.github
|
|
||||||
.travis.yml
|
|
||||||
.codeclimate.yml
|
|
||||||
.data
|
|
||||||
.svnignore
|
|
||||||
.wordpress-org
|
|
||||||
.php_cs
|
|
||||||
Gruntfile.js
|
|
||||||
LINGUAS
|
|
||||||
Makefile
|
|
||||||
README.md
|
|
||||||
readme.md
|
|
||||||
CHANGELOG.md
|
|
||||||
CODE_OF_CONDUCT.md
|
|
||||||
FEDERATION.md
|
|
||||||
SECURITY.md
|
|
||||||
LICENSE.md
|
|
||||||
_site
|
|
||||||
_config.yml
|
|
||||||
bin
|
|
||||||
composer.json
|
|
||||||
composer.lock
|
|
||||||
docker-compose.yml
|
|
||||||
docker-compose-test.yml
|
|
||||||
Dockerfile
|
|
||||||
gulpfile.js
|
|
||||||
package.json
|
|
||||||
node_modules
|
|
||||||
npm-debug.log
|
|
||||||
phpcs.xml
|
|
||||||
package.json
|
|
||||||
package-lock.json
|
|
||||||
phpunit.xml
|
|
||||||
phpunit.xml.dist
|
|
||||||
tests
|
|
||||||
node_modules
|
|
||||||
vendor
|
|
||||||
src
|
|
@ -3,7 +3,7 @@
|
|||||||
* Plugin Name: ActivityPub
|
* Plugin Name: ActivityPub
|
||||||
* Plugin URI: https://github.com/pfefferle/wordpress-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.
|
* Description: The ActivityPub protocol is a decentralized social networking protocol based upon the ActivityStreams 2.0 data format.
|
||||||
* Version: 2.3.1
|
* Version: 2.4.0
|
||||||
* Author: Matthias Pfefferle & Automattic
|
* Author: Matthias Pfefferle & Automattic
|
||||||
* Author URI: https://automattic.com/
|
* Author URI: https://automattic.com/
|
||||||
* License: MIT
|
* License: MIT
|
||||||
@ -21,7 +21,7 @@ use function Activitypub\site_supports_blocks;
|
|||||||
require_once __DIR__ . '/includes/compat.php';
|
require_once __DIR__ . '/includes/compat.php';
|
||||||
require_once __DIR__ . '/includes/functions.php';
|
require_once __DIR__ . '/includes/functions.php';
|
||||||
|
|
||||||
\define( 'ACTIVITYPUB_PLUGIN_VERSION', '2.3.1' );
|
\define( 'ACTIVITYPUB_PLUGIN_VERSION', '2.4.0' );
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Initialize the plugin constants.
|
* Initialize the plugin constants.
|
||||||
@ -50,7 +50,7 @@ require_once __DIR__ . '/includes/functions.php';
|
|||||||
* Initialize REST routes.
|
* Initialize REST routes.
|
||||||
*/
|
*/
|
||||||
function rest_init() {
|
function rest_init() {
|
||||||
Rest\Users::init();
|
Rest\Actors::init();
|
||||||
Rest\Outbox::init();
|
Rest\Outbox::init();
|
||||||
Rest\Inbox::init();
|
Rest\Inbox::init();
|
||||||
Rest\Followers::init();
|
Rest\Followers::init();
|
||||||
@ -100,6 +100,11 @@ function plugin_init() {
|
|||||||
|
|
||||||
require_once __DIR__ . '/integration/class-enable-mastodon-apps.php';
|
require_once __DIR__ . '/integration/class-enable-mastodon-apps.php';
|
||||||
Integration\Enable_Mastodon_Apps::init();
|
Integration\Enable_Mastodon_Apps::init();
|
||||||
|
|
||||||
|
if ( \defined( 'JETPACK__VERSION' ) && ! \defined( 'IS_WPCOM' ) ) {
|
||||||
|
require_once __DIR__ . '/integration/class-jetpack.php';
|
||||||
|
Integration\Jetpack::init();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
\add_action( 'plugins_loaded', __NAMESPACE__ . '\plugin_init' );
|
\add_action( 'plugins_loaded', __NAMESPACE__ . '\plugin_init' );
|
||||||
|
|
||||||
|
@ -197,3 +197,8 @@ input.blog-user-identifier {
|
|||||||
border-bottom: none;
|
border-bottom: none;
|
||||||
margin-bottom: 0;
|
margin-bottom: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#dashboard_right_now li a.activitypub-followers::before {
|
||||||
|
content: "\f307";
|
||||||
|
font-family: dashicons;
|
||||||
|
}
|
||||||
|
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@ -1 +1 @@
|
|||||||
<?php return array('dependencies' => array('react', 'wp-api-fetch', 'wp-block-editor', 'wp-blocks', 'wp-components', 'wp-data', 'wp-element', 'wp-i18n', 'wp-primitives', 'wp-url'), 'version' => '536d43b3eaab93a5c9ef');
|
<?php return array('dependencies' => array('react', 'wp-api-fetch', 'wp-block-editor', 'wp-blocks', 'wp-components', 'wp-data', 'wp-element', 'wp-i18n', 'wp-primitives', 'wp-url'), 'version' => 'a351235e5feab398a954');
|
||||||
|
@ -1,3 +1,3 @@
|
|||||||
(()=>{var e={942:(e,t)=>{var a;!function(){"use strict";var n={}.hasOwnProperty;function r(){for(var e="",t=0;t<arguments.length;t++){var a=arguments[t];a&&(e=o(e,l(a)))}return e}function l(e){if("string"==typeof e||"number"==typeof e)return e;if("object"!=typeof e)return"";if(Array.isArray(e))return r.apply(null,e);if(e.toString!==Object.prototype.toString&&!e.toString.toString().includes("[native code]"))return e.toString();var t="";for(var a in e)n.call(e,a)&&e[a]&&(t=o(t,a));return t}function o(e,t){return t?e?e+" "+t:e+t:e}e.exports?(r.default=r,e.exports=r):void 0===(a=function(){return r}.apply(t,[]))||(e.exports=a)}()}},t={};function a(n){var r=t[n];if(void 0!==r)return r.exports;var l=t[n]={exports:{}};return e[n](l,l.exports,a),l.exports}a.n=e=>{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.React,n=window.wp.primitives,r=(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"})),l=window.wp.components,o=window.wp.element,i=window.wp.blockEditor,c=window.wp.i18n,s=window.wp.apiFetch;var p=a.n(s);const u=window.wp.url;var v=a(942),m=a.n(v);function w({active:e,children:a,page:n,pageClick:r,className:l}){const o=m()("wp-block activitypub-pager",l,{current:e});return(0,t.createElement)("a",{className:o,onClick:t=>{t.preventDefault(),!e&&r(n)}},a)}const b={outlined:"outlined",minimal:"minimal"};function d({compact:e,nextLabel:a,page:n,pageClick:r,perPage:l,prevLabel:o,total:i,variant:c=b.outlined}){const s=((e,t)=>{let a=[1,e-2,e-1,e,e+1,e+2,t];a.sort(((e,t)=>e-t)),a=a.filter(((e,a,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})(n,Math.ceil(i/l)),p=m()("alignwide wp-block-query-pagination is-content-justification-space-between is-layout-flex wp-block-query-pagination-is-layout-flex",`is-${c}`,{"is-compact":e});return(0,t.createElement)("nav",{className:p},o&&(0,t.createElement)(w,{key:"prev",page:n-1,pageClick:r,active:1===n,"aria-label":o,className:"wp-block-query-pagination-previous block-editor-block-list__block"},o),!e&&(0,t.createElement)("div",{className:"block-editor-block-list__block wp-block wp-block-query-pagination-numbers"},s.map((e=>(0,t.createElement)(w,{key:e,page:e,pageClick:r,active:e===n,className:"page-numbers"},e)))),a&&(0,t.createElement)(w,{key:"next",page:n+1,pageClick:r,active:n===Math.ceil(i/l),"aria-label":a,className:"wp-block-query-pagination-next block-editor-block-list__block"},a))}const{namespace:f}=window._activityPubOptions;function g({selectedUser:e,per_page:a,order:n,title:r,page:l,setPage:i,className:s="",followLinks:v=!0,followerData:m=!1}){const w="site"===e?0:e,[b,g]=(0,t.useState)([]),[k,h]=(0,t.useState)(0),[E,_]=(0,t.useState)(0),[x,C]=function(){const[e,a]=(0,t.useState)(1);return[e,a]}(),S=l||x,N=i||C,P=(0,o.createInterpolateElement)(/* translators: arrow for previous followers link */ /* translators: arrow for previous followers link */
|
(()=>{var e={184:(e,t)=>{var a;!function(){"use strict";var l={}.hasOwnProperty;function n(){for(var e=[],t=0;t<arguments.length;t++){var a=arguments[t];if(a){var r=typeof a;if("string"===r||"number"===r)e.push(a);else if(Array.isArray(a)){if(a.length){var o=n.apply(null,a);o&&e.push(o)}}else if("object"===r){if(a.toString!==Object.prototype.toString&&!a.toString.toString().includes("[native code]")){e.push(a.toString());continue}for(var i in a)l.call(a,i)&&a[i]&&e.push(i)}}}return e.join(" ")}e.exports?(n.default=n,e.exports=n):void 0===(a=function(){return n}.apply(t,[]))||(e.exports=a)}()}},t={};function a(l){var n=t[l];if(void 0!==n)return n.exports;var r=t[l]={exports:{}};return e[l](r,r.exports,a),r.exports}a.n=e=>{var t=e&&e.__esModule?()=>e.default:()=>e;return a.d(t,{a:t}),t},a.d=(e,t)=>{for(var l in t)a.o(t,l)&&!a.o(e,l)&&Object.defineProperty(e,l,{enumerable:!0,get:t[l]})},a.o=(e,t)=>Object.prototype.hasOwnProperty.call(e,t),(()=>{"use strict";const e=window.wp.blocks,t=window.wp.element,l=window.wp.primitives,n=(0,t.createElement)(l.SVG,{xmlns:"http://www.w3.org/2000/svg",viewBox:"0 0 24 24"},(0,t.createElement)(l.Path,{d:"M15.5 9.5a1 1 0 100-2 1 1 0 000 2zm0 1.5a2.5 2.5 0 100-5 2.5 2.5 0 000 5zm-2.25 6v-2a2.75 2.75 0 00-2.75-2.75h-4A2.75 2.75 0 003.75 15v2h1.5v-2c0-.69.56-1.25 1.25-1.25h4c.69 0 1.25.56 1.25 1.25v2h1.5zm7-2v2h-1.5v-2c0-.69-.56-1.25-1.25-1.25H15v-1.5h2.5A2.75 2.75 0 0120.25 15zM9.5 8.5a1 1 0 11-2 0 1 1 0 012 0zm1.5 0a2.5 2.5 0 11-5 0 2.5 2.5 0 015 0z",fillRule:"evenodd"})),r=window.wp.components,o=window.wp.blockEditor,i=window.wp.i18n,c=window.React,s=window.wp.apiFetch;var p=a.n(s);const u=window.wp.url;var v=a(184),m=a.n(v);function w({active:e,children:a,page:l,pageClick:n,className:r}){const o=m()("wp-block activitypub-pager",r,{current:e});return(0,t.createElement)("a",{className:o,onClick:t=>{t.preventDefault(),!e&&n(l)}},a)}const b={outlined:"outlined",minimal:"minimal"};function d({compact:e,nextLabel:a,page:l,pageClick:n,perPage:r,prevLabel:o,total:i,variant:c=b.outlined}){const s=((e,t)=>{let a=[1,e-2,e-1,e,e+1,e+2,t];a.sort(((e,t)=>e-t)),a=a.filter(((e,a,l)=>e>=1&&e<=t&&l.lastIndexOf(e)===a));for(let e=a.length-2;e>=0;e--)a[e]===a[e+1]&&a.splice(e+1,1);return a})(l,Math.ceil(i/r)),p=m()("alignwide wp-block-query-pagination is-content-justification-space-between is-layout-flex wp-block-query-pagination-is-layout-flex",`is-${c}`,{"is-compact":e});return(0,t.createElement)("nav",{className:p},o&&(0,t.createElement)(w,{key:"prev",page:l-1,pageClick:n,active:1===l,"aria-label":o,className:"wp-block-query-pagination-previous block-editor-block-list__block"},o),!e&&(0,t.createElement)("div",{className:"block-editor-block-list__block wp-block wp-block-query-pagination-numbers"},s.map((e=>(0,t.createElement)(w,{key:e,page:e,pageClick:n,active:e===l,className:"page-numbers"},e)))),a&&(0,t.createElement)(w,{key:"next",page:l+1,pageClick:n,active:l===Math.ceil(i/r),"aria-label":a,className:"wp-block-query-pagination-next block-editor-block-list__block"},a))}const{namespace:g}=window._activityPubOptions;function f({selectedUser:e,per_page:a,order:l,title:n,page:r,setPage:o,className:s="",followLinks:v=!0,followerData:m=!1}){const w="site"===e?0:e,[b,f]=(0,c.useState)([]),[h,k]=(0,c.useState)(0),[E,_]=(0,c.useState)(0),[x,C]=function(){const[e,t]=(0,c.useState)(1);return[e,t]}(),S=r||x,N=o||C,P=(0,t.createInterpolateElement)(/* translators: arrow for previous followers link */
|
||||||
(0,c.__)("<span>←</span> Less","activitypub"),{span:(0,t.createElement)("span",{class:"wp-block-query-pagination-previous-arrow is-arrow-arrow","aria-hidden":"true"})}),L=(0,o.createInterpolateElement)(/* translators: arrow for next followers link */ /* translators: arrow for next followers link */
|
(0,i.__)("<span>←</span> Less","activitypub"),{span:(0,t.createElement)("span",{class:"wp-block-query-pagination-previous-arrow is-arrow-arrow","aria-hidden":"true"})}),L=(0,t.createInterpolateElement)(/* translators: arrow for next followers link */
|
||||||
(0,c.__)("More <span>→</span>","activitypub"),{span:(0,t.createElement)("span",{class:"wp-block-query-pagination-next-arrow is-arrow-arrow","aria-hidden":"true"})}),O=(e,t)=>{g(e),_(t),h(Math.ceil(t/a))};return(0,t.useEffect)((()=>{if(m&&1===S)return O(m.followers,m.total);const e=function(e,t,a,n){const r=`/${f}/users/${e}/followers`,l={per_page:t,order:a,page:n,context:"full"};return(0,u.addQueryArgs)(r,l)}(w,a,n,S);p()({path:e}).then((e=>O(e.orderedItems,e.totalItems))).catch((()=>{}))}),[w,a,n,S,m]),(0,t.createElement)("div",{className:"activitypub-follower-block "+s},(0,t.createElement)("h3",null,r),(0,t.createElement)("ul",null,b&&b.map((e=>(0,t.createElement)("li",{key:e.url},(0,t.createElement)(y,{...e,followLinks:v}))))),k>1&&(0,t.createElement)(d,{page:S,perPage:a,total:E,pageClick:N,nextLabel:L,prevLabel:P,compact:"is-style-compact"===s}))}function y({name:e,icon:a,url:n,preferredUsername:r,followLinks:o=!0}){const i=`@${r}`,c={};return o||(c.onClick=e=>e.preventDefault()),(0,t.createElement)(l.ExternalLink,{className:"activitypub-link",href:n,title:i,...c},(0,t.createElement)("img",{width:"40",height:"40",src:a.url,class:"avatar activitypub-avatar",alt:e}),(0,t.createElement)("span",{class:"activitypub-actor"},(0,t.createElement)("strong",{className:"activitypub-name"},e),(0,t.createElement)("span",{class:"sep"},"/"),(0,t.createElement)("span",{class:"activitypub-handle"},i)))}const k=window.wp.data,h=window._activityPubOptions?.enabled;(0,e.registerBlockType)("activitypub/followers",{edit:function({attributes:e,setAttributes:a}){const{order:n,per_page:r,selectedUser:s,title:p}=e,u=(0,i.useBlockProps)(),[v,m]=(0,o.useState)(1),w=[{label:(0,c.__)("New to old","activitypub"),value:"desc"},{label:(0,c.__)("Old to new","activitypub"),value:"asc"}],b=function(){const e=h?.users?(0,k.useSelect)((e=>e("core").getUsers({who:"authors"}))):[];return(0,o.useMemo)((()=>{if(!e)return[];const t=h?.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])}(),d=e=>t=>{m(1),a({[e]:t})};return(0,o.useEffect)((()=>{b.length&&(b.find((({value:e})=>e===s))||a({selectedUser:b[0].value}))}),[s,b]),(0,t.createElement)("div",{...u},(0,t.createElement)(i.InspectorControls,{key:"setting"},(0,t.createElement)(l.PanelBody,{title:(0,c.__)("Followers Options","activitypub")},(0,t.createElement)(l.TextControl,{label:(0,c.__)("Title","activitypub"),help:(0,c.__)("Title to display above the list of followers. Blank for none.","activitypub"),value:p,onChange:e=>a({title:e})}),b.length>1&&(0,t.createElement)(l.SelectControl,{label:(0,c.__)("Select User","activitypub"),value:s,options:b,onChange:d("selectedUser")}),(0,t.createElement)(l.SelectControl,{label:(0,c.__)("Sort","activitypub"),value:n,options:w,onChange:d("order")}),(0,t.createElement)(l.RangeControl,{label:(0,c.__)("Number of Followers","activitypub"),value:r,onChange:d("per_page"),min:1,max:10}))),(0,t.createElement)(g,{...e,page:v,setPage:m,followLinks:!1}))},save:()=>null,icon:r})})()})();
|
(0,i.__)("More <span>→</span>","activitypub"),{span:(0,t.createElement)("span",{class:"wp-block-query-pagination-next-arrow is-arrow-arrow","aria-hidden":"true"})}),O=(e,t)=>{f(e),_(t),k(Math.ceil(t/a))};return(0,c.useEffect)((()=>{if(m&&1===S)return O(m.followers,m.total);const e=function(e,t,a,l){const n=`/${g}/actors/${e}/followers`,r={per_page:t,order:a,page:l,context:"full"};return(0,u.addQueryArgs)(n,r)}(w,a,l,S);p()({path:e}).then((e=>O(e.orderedItems,e.totalItems))).catch((()=>{}))}),[w,a,l,S,m]),(0,t.createElement)("div",{className:"activitypub-follower-block "+s},(0,t.createElement)("h3",null,n),(0,t.createElement)("ul",null,b&&b.map((e=>(0,t.createElement)("li",{key:e.url},(0,t.createElement)(y,{...e,followLinks:v}))))),h>1&&(0,t.createElement)(d,{page:S,perPage:a,total:E,pageClick:N,nextLabel:L,prevLabel:P,compact:"is-style-compact"===s}))}function y({name:e,icon:a,url:l,preferredUsername:n,followLinks:o=!0}){const i=`@${n}`,c={};return o||(c.onClick=e=>e.preventDefault()),(0,t.createElement)(r.ExternalLink,{className:"activitypub-link",href:l,title:i,...c},(0,t.createElement)("img",{width:"40",height:"40",src:a.url,class:"avatar activitypub-avatar",alt:e}),(0,t.createElement)("span",{class:"activitypub-actor"},(0,t.createElement)("strong",{className:"activitypub-name"},e),(0,t.createElement)("span",{class:"sep"},"/"),(0,t.createElement)("span",{class:"activitypub-handle"},i)))}const h=window.wp.data,k=window._activityPubOptions?.enabled;(0,e.registerBlockType)("activitypub/followers",{edit:function({attributes:e,setAttributes:a}){const{order:l,per_page:n,selectedUser:c,title:s}=e,p=(0,o.useBlockProps)(),[u,v]=(0,t.useState)(1),m=[{label:(0,i.__)("New to old","activitypub"),value:"desc"},{label:(0,i.__)("Old to new","activitypub"),value:"asc"}],w=function(){const e=k?.users?(0,h.useSelect)((e=>e("core").getUsers({who:"authors"}))):[];return(0,t.useMemo)((()=>{if(!e)return[];const t=k?.site?[{label:(0,i.__)("Whole Site","activitypub"),value:"site"}]:[];return e.reduce(((e,t)=>(e.push({label:t.name,value:`${t.id}`}),e)),t)}),[e])}(),b=e=>t=>{v(1),a({[e]:t})};return(0,t.useEffect)((()=>{w.length&&(w.find((({value:e})=>e===c))||a({selectedUser:w[0].value}))}),[c,w]),(0,t.createElement)("div",{...p},(0,t.createElement)(o.InspectorControls,{key:"setting"},(0,t.createElement)(r.PanelBody,{title:(0,i.__)("Followers Options","activitypub")},(0,t.createElement)(r.TextControl,{label:(0,i.__)("Title","activitypub"),help:(0,i.__)("Title to display above the list of followers. Blank for none.","activitypub"),value:s,onChange:e=>a({title:e})}),w.length>1&&(0,t.createElement)(r.SelectControl,{label:(0,i.__)("Select User","activitypub"),value:c,options:w,onChange:b("selectedUser")}),(0,t.createElement)(r.SelectControl,{label:(0,i.__)("Sort","activitypub"),value:l,options:m,onChange:b("order")}),(0,t.createElement)(r.RangeControl,{label:(0,i.__)("Number of Followers","activitypub"),value:n,onChange:b("per_page"),min:1,max:10}))),(0,t.createElement)(f,{...e,page:u,setPage:v,followLinks:!1}))},save:()=>null,icon:n})})()})();
|
@ -1 +1 @@
|
|||||||
<?php return array('dependencies' => array('react', 'wp-api-fetch', 'wp-components', 'wp-dom-ready', 'wp-element', 'wp-i18n', 'wp-url'), 'version' => '23bc54443801976420cd');
|
<?php return array('dependencies' => array('react', 'wp-api-fetch', 'wp-components', 'wp-dom-ready', 'wp-element', 'wp-i18n', 'wp-url'), 'version' => '8c01e26171636c3b698f');
|
||||||
|
@ -1,3 +1,3 @@
|
|||||||
(()=>{var e,t={250:(e,t,a)=>{"use strict";const r=window.React,n=window.wp.apiFetch;var l=a.n(n);const o=window.wp.url,i=window.wp.element,c=window.wp.i18n;var s=a(942),p=a.n(s);function u({active:e,children:t,page:a,pageClick:n,className:l}){const o=p()("wp-block activitypub-pager",l,{current:e});return(0,r.createElement)("a",{className:o,onClick:t=>{t.preventDefault(),!e&&n(a)}},t)}const m={outlined:"outlined",minimal:"minimal"};function f({compact:e,nextLabel:t,page:a,pageClick:n,perPage:l,prevLabel:o,total:i,variant:c=m.outlined}){const s=((e,t)=>{let a=[1,e-2,e-1,e,e+1,e+2,t];a.sort(((e,t)=>e-t)),a=a.filter(((e,a,r)=>e>=1&&e<=t&&r.lastIndexOf(e)===a));for(let e=a.length-2;e>=0;e--)a[e]===a[e+1]&&a.splice(e+1,1);return a})(a,Math.ceil(i/l)),f=p()("alignwide wp-block-query-pagination is-content-justification-space-between is-layout-flex wp-block-query-pagination-is-layout-flex",`is-${c}`,{"is-compact":e});return(0,r.createElement)("nav",{className:f},o&&(0,r.createElement)(u,{key:"prev",page:a-1,pageClick:n,active:1===a,"aria-label":o,className:"wp-block-query-pagination-previous block-editor-block-list__block"},o),!e&&(0,r.createElement)("div",{className:"block-editor-block-list__block wp-block wp-block-query-pagination-numbers"},s.map((e=>(0,r.createElement)(u,{key:e,page:e,pageClick:n,active:e===a,className:"page-numbers"},e)))),t&&(0,r.createElement)(u,{key:"next",page:a+1,pageClick:n,active:a===Math.ceil(i/l),"aria-label":t,className:"wp-block-query-pagination-next block-editor-block-list__block"},t))}const v=window.wp.components,{namespace:b}=window._activityPubOptions;function d({selectedUser:e,per_page:t,order:a,title:n,page:s,setPage:p,className:u="",followLinks:m=!0,followerData:v=!1}){const d="site"===e?0:e,[g,y]=(0,r.useState)([]),[k,h]=(0,r.useState)(0),[E,x]=(0,r.useState)(0),[_,O]=function(){const[e,t]=(0,r.useState)(1);return[e,t]}(),N=s||_,S=p||O,C=(0,i.createInterpolateElement)(/* translators: arrow for previous followers link */ /* translators: arrow for previous followers link */
|
(()=>{var e,t={142:(e,t,a)=>{"use strict";const r=window.wp.element,n=window.React,l=window.wp.apiFetch;var o=a.n(l);const i=window.wp.url,c=window.wp.i18n;var s=a(184),p=a.n(s);function u({active:e,children:t,page:a,pageClick:n,className:l}){const o=p()("wp-block activitypub-pager",l,{current:e});return(0,r.createElement)("a",{className:o,onClick:t=>{t.preventDefault(),!e&&n(a)}},t)}const m={outlined:"outlined",minimal:"minimal"};function f({compact:e,nextLabel:t,page:a,pageClick:n,perPage:l,prevLabel:o,total:i,variant:c=m.outlined}){const s=((e,t)=>{let a=[1,e-2,e-1,e,e+1,e+2,t];a.sort(((e,t)=>e-t)),a=a.filter(((e,a,r)=>e>=1&&e<=t&&r.lastIndexOf(e)===a));for(let e=a.length-2;e>=0;e--)a[e]===a[e+1]&&a.splice(e+1,1);return a})(a,Math.ceil(i/l)),f=p()("alignwide wp-block-query-pagination is-content-justification-space-between is-layout-flex wp-block-query-pagination-is-layout-flex",`is-${c}`,{"is-compact":e});return(0,r.createElement)("nav",{className:f},o&&(0,r.createElement)(u,{key:"prev",page:a-1,pageClick:n,active:1===a,"aria-label":o,className:"wp-block-query-pagination-previous block-editor-block-list__block"},o),!e&&(0,r.createElement)("div",{className:"block-editor-block-list__block wp-block wp-block-query-pagination-numbers"},s.map((e=>(0,r.createElement)(u,{key:e,page:e,pageClick:n,active:e===a,className:"page-numbers"},e)))),t&&(0,r.createElement)(u,{key:"next",page:a+1,pageClick:n,active:a===Math.ceil(i/l),"aria-label":t,className:"wp-block-query-pagination-next block-editor-block-list__block"},t))}const v=window.wp.components,{namespace:d}=window._activityPubOptions;function b({selectedUser:e,per_page:t,order:a,title:l,page:s,setPage:p,className:u="",followLinks:m=!0,followerData:v=!1}){const b="site"===e?0:e,[g,k]=(0,n.useState)([]),[y,h]=(0,n.useState)(0),[E,x]=(0,n.useState)(0),[_,O]=function(){const[e,t]=(0,n.useState)(1);return[e,t]}(),N=s||_,S=p||O,C=(0,r.createInterpolateElement)(/* translators: arrow for previous followers link */
|
||||||
(0,c.__)("<span>←</span> Less","activitypub"),{span:(0,r.createElement)("span",{class:"wp-block-query-pagination-previous-arrow is-arrow-arrow","aria-hidden":"true"})}),L=(0,i.createInterpolateElement)(/* translators: arrow for next followers link */ /* translators: arrow for next followers link */
|
(0,c.__)("<span>←</span> Less","activitypub"),{span:(0,r.createElement)("span",{class:"wp-block-query-pagination-previous-arrow is-arrow-arrow","aria-hidden":"true"})}),L=(0,r.createInterpolateElement)(/* translators: arrow for next followers link */
|
||||||
(0,c.__)("More <span>→</span>","activitypub"),{span:(0,r.createElement)("span",{class:"wp-block-query-pagination-next-arrow is-arrow-arrow","aria-hidden":"true"})}),q=(e,a)=>{y(e),x(a),h(Math.ceil(a/t))};return(0,r.useEffect)((()=>{if(v&&1===N)return q(v.followers,v.total);const e=function(e,t,a,r){const n=`/${b}/users/${e}/followers`,l={per_page:t,order:a,page:r,context:"full"};return(0,o.addQueryArgs)(n,l)}(d,t,a,N);l()({path:e}).then((e=>q(e.orderedItems,e.totalItems))).catch((()=>{}))}),[d,t,a,N,v]),(0,r.createElement)("div",{className:"activitypub-follower-block "+u},(0,r.createElement)("h3",null,n),(0,r.createElement)("ul",null,g&&g.map((e=>(0,r.createElement)("li",{key:e.url},(0,r.createElement)(w,{...e,followLinks:m}))))),k>1&&(0,r.createElement)(f,{page:N,perPage:t,total:E,pageClick:S,nextLabel:L,prevLabel:C,compact:"is-style-compact"===u}))}function w({name:e,icon:t,url:a,preferredUsername:n,followLinks:l=!0}){const o=`@${n}`,i={};return l||(i.onClick=e=>e.preventDefault()),(0,r.createElement)(v.ExternalLink,{className:"activitypub-link",href:a,title:o,...i},(0,r.createElement)("img",{width:"40",height:"40",src:t.url,class:"avatar activitypub-avatar",alt:e}),(0,r.createElement)("span",{class:"activitypub-actor"},(0,r.createElement)("strong",{className:"activitypub-name"},e),(0,r.createElement)("span",{class:"sep"},"/"),(0,r.createElement)("span",{class:"activitypub-handle"},o)))}const g=window.wp.domReady;a.n(g)()((()=>{[].forEach.call(document.querySelectorAll(".activitypub-follower-block"),(e=>{const t=JSON.parse(e.dataset.attrs);(0,i.render)((0,r.createElement)(d,{...t}),e)}))}))},942:(e,t)=>{var a;!function(){"use strict";var r={}.hasOwnProperty;function n(){for(var e="",t=0;t<arguments.length;t++){var a=arguments[t];a&&(e=o(e,l(a)))}return e}function l(e){if("string"==typeof e||"number"==typeof e)return e;if("object"!=typeof e)return"";if(Array.isArray(e))return n.apply(null,e);if(e.toString!==Object.prototype.toString&&!e.toString.toString().includes("[native code]"))return e.toString();var t="";for(var a in e)r.call(e,a)&&e[a]&&(t=o(t,a));return t}function o(e,t){return t?e?e+" "+t:e+t:e}e.exports?(n.default=n,e.exports=n):void 0===(a=function(){return n}.apply(t,[]))||(e.exports=a)}()}},a={};function r(e){var n=a[e];if(void 0!==n)return n.exports;var l=a[e]={exports:{}};return t[e](l,l.exports,r),l.exports}r.m=t,e=[],r.O=(t,a,n,l)=>{if(!a){var o=1/0;for(p=0;p<e.length;p++){for(var[a,n,l]=e[p],i=!0,c=0;c<a.length;c++)(!1&l||o>=l)&&Object.keys(r.O).every((e=>r.O[e](a[c])))?a.splice(c--,1):(i=!1,l<o&&(o=l));if(i){e.splice(p--,1);var s=n();void 0!==s&&(t=s)}}return t}l=l||0;for(var p=e.length;p>0&&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={996:0,528: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);s<o.length;s++)l=o[s],r.o(e,l)&&e[l]&&e[l][0](),e[l]=0;return r.O(p)},a=globalThis.webpackChunkwordpress_activitypub=globalThis.webpackChunkwordpress_activitypub||[];a.forEach(t.bind(null,0)),a.push=t.bind(null,a.push.bind(a))})();var n=r.O(void 0,[528],(()=>r(250)));n=r.O(n)})();
|
(0,c.__)("More <span>→</span>","activitypub"),{span:(0,r.createElement)("span",{class:"wp-block-query-pagination-next-arrow is-arrow-arrow","aria-hidden":"true"})}),j=(e,a)=>{k(e),x(a),h(Math.ceil(a/t))};return(0,n.useEffect)((()=>{if(v&&1===N)return j(v.followers,v.total);const e=function(e,t,a,r){const n=`/${d}/actors/${e}/followers`,l={per_page:t,order:a,page:r,context:"full"};return(0,i.addQueryArgs)(n,l)}(b,t,a,N);o()({path:e}).then((e=>j(e.orderedItems,e.totalItems))).catch((()=>{}))}),[b,t,a,N,v]),(0,r.createElement)("div",{className:"activitypub-follower-block "+u},(0,r.createElement)("h3",null,l),(0,r.createElement)("ul",null,g&&g.map((e=>(0,r.createElement)("li",{key:e.url},(0,r.createElement)(w,{...e,followLinks:m}))))),y>1&&(0,r.createElement)(f,{page:N,perPage:t,total:E,pageClick:S,nextLabel:L,prevLabel:C,compact:"is-style-compact"===u}))}function w({name:e,icon:t,url:a,preferredUsername:n,followLinks:l=!0}){const o=`@${n}`,i={};return l||(i.onClick=e=>e.preventDefault()),(0,r.createElement)(v.ExternalLink,{className:"activitypub-link",href:a,title:o,...i},(0,r.createElement)("img",{width:"40",height:"40",src:t.url,class:"avatar activitypub-avatar",alt:e}),(0,r.createElement)("span",{class:"activitypub-actor"},(0,r.createElement)("strong",{className:"activitypub-name"},e),(0,r.createElement)("span",{class:"sep"},"/"),(0,r.createElement)("span",{class:"activitypub-handle"},o)))}const g=window.wp.domReady;a.n(g)()((()=>{[].forEach.call(document.querySelectorAll(".activitypub-follower-block"),(e=>{const t=JSON.parse(e.dataset.attrs);(0,r.render)((0,r.createElement)(b,{...t}),e)}))}))},184:(e,t)=>{var a;!function(){"use strict";var r={}.hasOwnProperty;function n(){for(var e=[],t=0;t<arguments.length;t++){var a=arguments[t];if(a){var l=typeof a;if("string"===l||"number"===l)e.push(a);else if(Array.isArray(a)){if(a.length){var o=n.apply(null,a);o&&e.push(o)}}else if("object"===l){if(a.toString!==Object.prototype.toString&&!a.toString.toString().includes("[native code]")){e.push(a.toString());continue}for(var i in a)r.call(a,i)&&a[i]&&e.push(i)}}}return e.join(" ")}e.exports?(n.default=n,e.exports=n):void 0===(a=function(){return n}.apply(t,[]))||(e.exports=a)}()}},a={};function r(e){var n=a[e];if(void 0!==n)return n.exports;var l=a[e]={exports:{}};return t[e](l,l.exports,r),l.exports}r.m=t,e=[],r.O=(t,a,n,l)=>{if(!a){var o=1/0;for(p=0;p<e.length;p++){a=e[p][0],n=e[p][1],l=e[p][2];for(var i=!0,c=0;c<a.length;c++)(!1&l||o>=l)&&Object.keys(r.O).every((e=>r.O[e](a[c])))?a.splice(c--,1):(i=!1,l<o&&(o=l));if(i){e.splice(p--,1);var s=n();void 0!==s&&(t=s)}}return t}l=l||0;for(var p=e.length;p>0&&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=a[0],i=a[1],c=a[2],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);s<o.length;s++)l=o[s],r.o(e,l)&&e[l]&&e[l][0](),e[l]=0;return r.O(p)},a=self.webpackChunkwordpress_activitypub=self.webpackChunkwordpress_activitypub||[];a.forEach(t.bind(null,0)),a.push=t.bind(null,a.push.bind(a))})();var n=r.O(void 0,[962],(()=>r(142)));n=r.O(n)})();
|
File diff suppressed because one or more lines are too long
@ -162,7 +162,13 @@ class Activity extends Base_Object {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if ( $object->get_id() && ! $this->get_id() ) {
|
if ( $object->get_id() && ! $this->get_id() ) {
|
||||||
$this->set( 'id', $object->get_id() . '#activity' );
|
$id = strtok( $object->get_id(), '#' );
|
||||||
|
if ( $object->get_updated() ) {
|
||||||
|
$updated = $object->get_updated();
|
||||||
|
} else {
|
||||||
|
$updated = $object->get_published();
|
||||||
|
}
|
||||||
|
$this->set( 'id', $id . '#activity-' . strtolower( $this->get_type() ) . '-' . $updated );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -64,7 +64,11 @@ class Activity_Dispatcher {
|
|||||||
* @return void
|
* @return void
|
||||||
*/
|
*/
|
||||||
public static function send_activity( $wp_object, $type, $user_id = null ) {
|
public static function send_activity( $wp_object, $type, $user_id = null ) {
|
||||||
$transformer = Factory::get_transformer( $wp_object );
|
$transformer = Factory::get_transformer( $wp_object ); // Could potentially return a `\WP_Error` instance.
|
||||||
|
|
||||||
|
if ( \is_wp_error( $transformer ) ) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if ( null !== $user_id ) {
|
if ( null !== $user_id ) {
|
||||||
$transformer->change_wp_user_id( $user_id );
|
$transformer->change_wp_user_id( $user_id );
|
||||||
@ -98,18 +102,22 @@ class Activity_Dispatcher {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// do not announce replies
|
$transformer = Factory::get_transformer( $wp_object );
|
||||||
if ( $wp_object instanceof WP_Comment ) {
|
|
||||||
|
if ( \is_wp_error( $transformer ) ) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
$transformer = Factory::get_transformer( $wp_object );
|
$user_id = Users::BLOG_USER_ID;
|
||||||
$transformer->change_wp_user_id( Users::BLOG_USER_ID );
|
$activity = $transformer->to_activity( $type );
|
||||||
|
$user = Users::get_by_id( Users::BLOG_USER_ID );
|
||||||
|
|
||||||
$user_id = $transformer->get_wp_user_id();
|
$announce = new Activity();
|
||||||
$activity = $transformer->to_activity( 'Announce' );
|
$announce->set_type( 'Announce' );
|
||||||
|
$announce->set_object( $activity );
|
||||||
|
$announce->set_actor( $user->get_id() );
|
||||||
|
|
||||||
self::send_activity_to_followers( $activity, $user_id, $wp_object );
|
self::send_activity_to_followers( $announce, $user_id, $wp_object );
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -9,6 +9,7 @@ use Activitypub\Collection\Followers;
|
|||||||
use function Activitypub\is_comment;
|
use function Activitypub\is_comment;
|
||||||
use function Activitypub\sanitize_url;
|
use function Activitypub\sanitize_url;
|
||||||
use function Activitypub\is_local_comment;
|
use function Activitypub\is_local_comment;
|
||||||
|
use function Activitypub\is_user_type_disabled;
|
||||||
use function Activitypub\is_activitypub_request;
|
use function Activitypub\is_activitypub_request;
|
||||||
use function Activitypub\should_comment_be_federated;
|
use function Activitypub\should_comment_be_federated;
|
||||||
|
|
||||||
@ -95,37 +96,39 @@ class Activitypub {
|
|||||||
|
|
||||||
$json_template = false;
|
$json_template = false;
|
||||||
|
|
||||||
// check if user can publish posts
|
if ( \is_author() && ! is_user_disabled( \get_the_author_meta( 'ID' ) ) ) {
|
||||||
if ( \is_author() && is_wp_error( Users::get_by_id( \get_the_author_meta( 'ID' ) ) ) ) {
|
|
||||||
return $template;
|
|
||||||
}
|
|
||||||
|
|
||||||
// check if blog-user is enabled
|
|
||||||
if ( \is_home() && is_wp_error( Users::get_by_id( Users::BLOG_USER_ID ) ) ) {
|
|
||||||
return $template;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ( \is_author() ) {
|
|
||||||
$json_template = ACTIVITYPUB_PLUGIN_DIR . '/templates/author-json.php';
|
$json_template = ACTIVITYPUB_PLUGIN_DIR . '/templates/author-json.php';
|
||||||
} elseif ( is_comment() ) {
|
} elseif ( is_comment() ) {
|
||||||
$json_template = ACTIVITYPUB_PLUGIN_DIR . '/templates/comment-json.php';
|
$json_template = ACTIVITYPUB_PLUGIN_DIR . '/templates/comment-json.php';
|
||||||
} elseif ( \is_singular() ) {
|
} elseif ( \is_singular() ) {
|
||||||
$json_template = ACTIVITYPUB_PLUGIN_DIR . '/templates/post-json.php';
|
$json_template = ACTIVITYPUB_PLUGIN_DIR . '/templates/post-json.php';
|
||||||
} elseif ( \is_home() ) {
|
} elseif ( \is_home() && ! is_user_type_disabled( 'blog' ) ) {
|
||||||
$json_template = ACTIVITYPUB_PLUGIN_DIR . '/templates/blog-json.php';
|
$json_template = ACTIVITYPUB_PLUGIN_DIR . '/templates/blog-json.php';
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( ACTIVITYPUB_AUTHORIZED_FETCH ) {
|
/*
|
||||||
|
* Check if the request is authorized.
|
||||||
|
*
|
||||||
|
* @see https://www.w3.org/wiki/SocialCG/ActivityPub/Primer/Authentication_Authorization#Authorized_fetch
|
||||||
|
* @see https://swicg.github.io/activitypub-http-signature/#authorized-fetch
|
||||||
|
*/
|
||||||
|
if ( $json_template && ACTIVITYPUB_AUTHORIZED_FETCH ) {
|
||||||
$verification = Signature::verify_http_signature( $_SERVER );
|
$verification = Signature::verify_http_signature( $_SERVER );
|
||||||
if ( \is_wp_error( $verification ) ) {
|
if ( \is_wp_error( $verification ) ) {
|
||||||
|
header( 'HTTP/1.1 401 Unauthorized' );
|
||||||
|
|
||||||
// fallback as template_loader can't return http headers
|
// fallback as template_loader can't return http headers
|
||||||
return $template;
|
return $template;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if ( $json_template ) {
|
||||||
return $json_template;
|
return $json_template;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return $template;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Custom redirects for ActivityPub requests.
|
* Custom redirects for ActivityPub requests.
|
||||||
*
|
*
|
||||||
@ -292,7 +295,7 @@ class Activitypub {
|
|||||||
|
|
||||||
\add_rewrite_rule(
|
\add_rewrite_rule(
|
||||||
'^@([\w\-\.]+)',
|
'^@([\w\-\.]+)',
|
||||||
'index.php?rest_route=/' . ACTIVITYPUB_REST_NAMESPACE . '/users/$matches[1]',
|
'index.php?rest_route=/' . ACTIVITYPUB_REST_NAMESPACE . '/actors/$matches[1]',
|
||||||
'top'
|
'top'
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -2,8 +2,10 @@
|
|||||||
namespace Activitypub;
|
namespace Activitypub;
|
||||||
|
|
||||||
use WP_User_Query;
|
use WP_User_Query;
|
||||||
use Activitypub\Model\Blog_User;
|
use Activitypub\Model\Blog;
|
||||||
|
use Activitypub\Collection\Users;
|
||||||
|
|
||||||
|
use function Activitypub\count_followers;
|
||||||
use function Activitypub\is_user_disabled;
|
use function Activitypub\is_user_disabled;
|
||||||
use function Activitypub\was_comment_received;
|
use function Activitypub\was_comment_received;
|
||||||
use function Activitypub\is_comment_federatable;
|
use function Activitypub\is_comment_federatable;
|
||||||
@ -37,6 +39,8 @@ class Admin {
|
|||||||
if ( ! is_user_disabled( get_current_user_id() ) ) {
|
if ( ! is_user_disabled( get_current_user_id() ) ) {
|
||||||
\add_action( 'show_user_profile', array( self::class, 'add_profile' ) );
|
\add_action( 'show_user_profile', array( self::class, 'add_profile' ) );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
\add_filter( 'dashboard_glance_items', array( self::class, 'dashboard_glance_items' ) );
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -216,7 +220,7 @@ class Admin {
|
|||||||
'type' => 'string',
|
'type' => 'string',
|
||||||
'description' => \esc_html__( 'The Identifier of the Blog-User', 'activitypub' ),
|
'description' => \esc_html__( 'The Identifier of the Blog-User', 'activitypub' ),
|
||||||
'show_in_rest' => true,
|
'show_in_rest' => true,
|
||||||
'default' => Blog_User::get_default_username(),
|
'default' => Blog::get_default_username(),
|
||||||
'sanitize_callback' => function ( $value ) {
|
'sanitize_callback' => function ( $value ) {
|
||||||
// hack to allow dots in the username
|
// hack to allow dots in the username
|
||||||
$parts = explode( '.', $value );
|
$parts = explode( '.', $value );
|
||||||
@ -247,7 +251,7 @@ class Admin {
|
|||||||
'error'
|
'error'
|
||||||
);
|
);
|
||||||
|
|
||||||
return Blog_User::get_default_username();
|
return Blog::get_default_username();
|
||||||
}
|
}
|
||||||
|
|
||||||
return $sanitized;
|
return $sanitized;
|
||||||
@ -313,8 +317,12 @@ class Admin {
|
|||||||
|
|
||||||
public static function enqueue_scripts( $hook_suffix ) {
|
public static function enqueue_scripts( $hook_suffix ) {
|
||||||
if ( false !== strpos( $hook_suffix, 'activitypub' ) ) {
|
if ( false !== strpos( $hook_suffix, 'activitypub' ) ) {
|
||||||
wp_enqueue_style( 'activitypub-admin-styles', plugins_url( 'assets/css/activitypub-admin.css', ACTIVITYPUB_PLUGIN_FILE ), array(), '1.0.0' );
|
wp_enqueue_style( 'activitypub-admin-styles', plugins_url( 'assets/css/activitypub-admin.css', ACTIVITYPUB_PLUGIN_FILE ), array(), get_plugin_version() );
|
||||||
wp_enqueue_script( 'activitypub-admin-styles', plugins_url( 'assets/js/activitypub-admin.js', ACTIVITYPUB_PLUGIN_FILE ), array( 'jquery' ), '1.0.0', false );
|
wp_enqueue_script( 'activitypub-admin-script', plugins_url( 'assets/js/activitypub-admin.js', ACTIVITYPUB_PLUGIN_FILE ), array( 'jquery' ), get_plugin_version(), false );
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( 'index.php' === $hook_suffix ) {
|
||||||
|
wp_enqueue_style( 'activitypub-admin-styles', plugins_url( 'assets/css/activitypub-admin.css', ACTIVITYPUB_PLUGIN_FILE ), array(), get_plugin_version() );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -469,4 +477,57 @@ class Admin {
|
|||||||
|
|
||||||
return $sendback;
|
return $sendback;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add ActivityPub infos to the dashboard glance items
|
||||||
|
*
|
||||||
|
* @param array $items The existing glance items.
|
||||||
|
*
|
||||||
|
* @return array The extended glance items.
|
||||||
|
*/
|
||||||
|
public static function dashboard_glance_items( $items ) {
|
||||||
|
\add_filter( 'number_format_i18n', '\Activitypub\custom_large_numbers', 10, 3 );
|
||||||
|
|
||||||
|
if ( ! is_user_disabled( get_current_user_id() ) ) {
|
||||||
|
$follower_count = sprintf(
|
||||||
|
// translators: %s: number of followers
|
||||||
|
_n(
|
||||||
|
'%s Follower',
|
||||||
|
'%s Followers',
|
||||||
|
count_followers( \get_current_user_id() ),
|
||||||
|
'activitypub'
|
||||||
|
),
|
||||||
|
\number_format_i18n( count_followers( \get_current_user_id() ) )
|
||||||
|
);
|
||||||
|
$items['activitypub-followers-user'] = sprintf(
|
||||||
|
'<a class="activitypub-followers" href="%1$s" title="%2$s">%3$s</a>',
|
||||||
|
\esc_url( \admin_url( 'users.php?page=activitypub-followers-list' ) ),
|
||||||
|
\esc_attr__( 'Your followers', 'activitypub' ),
|
||||||
|
\esc_html( $follower_count )
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( ! is_user_type_disabled( 'blog' ) && current_user_can( 'manage_options' ) ) {
|
||||||
|
$follower_count = sprintf(
|
||||||
|
// translators: %s: number of followers
|
||||||
|
_n(
|
||||||
|
'%s Follower (Blog)',
|
||||||
|
'%s Followers (Blog)',
|
||||||
|
count_followers( Users::BLOG_USER_ID ),
|
||||||
|
'activitypub'
|
||||||
|
),
|
||||||
|
\number_format_i18n( count_followers( Users::BLOG_USER_ID ) )
|
||||||
|
);
|
||||||
|
$items['activitypub-followers-blog'] = sprintf(
|
||||||
|
'<a class="activitypub-followers" href="%1$s" title="%2$s">%3$s</a>',
|
||||||
|
\esc_url( \admin_url( 'options-general.php?page=activitypub&tab=followers' ) ),
|
||||||
|
\esc_attr__( 'The Blog\'s followers', 'activitypub' ),
|
||||||
|
\esc_html( $follower_count )
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
\remove_filter( 'number_format_i18n', '\Activitypub\custom_large_numbers', 10, 3 );
|
||||||
|
|
||||||
|
return $items;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -379,7 +379,7 @@ class Comment {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// generate URI based on comment ID
|
// generate URI based on comment ID
|
||||||
return \add_query_arg( 'c', $comment->comment_ID, \home_url() );
|
return \add_query_arg( 'c', $comment->comment_ID, \trailingslashit( \home_url() ) );
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
<?php
|
<?php
|
||||||
namespace Activitypub;
|
namespace Activitypub;
|
||||||
|
|
||||||
|
use Activitypub\Handler\Announce;
|
||||||
use Activitypub\Handler\Create;
|
use Activitypub\Handler\Create;
|
||||||
use Activitypub\Handler\Delete;
|
use Activitypub\Handler\Delete;
|
||||||
use Activitypub\Handler\Follow;
|
use Activitypub\Handler\Follow;
|
||||||
@ -22,6 +23,7 @@ class Handler {
|
|||||||
* Register handlers.
|
* Register handlers.
|
||||||
*/
|
*/
|
||||||
public static function register_handlers() {
|
public static function register_handlers() {
|
||||||
|
Announce::init();
|
||||||
Create::init();
|
Create::init();
|
||||||
Delete::init();
|
Delete::init();
|
||||||
Follow::init();
|
Follow::init();
|
||||||
|
@ -154,4 +154,98 @@ class Http {
|
|||||||
public static function generate_cache_key( $url ) {
|
public static function generate_cache_key( $url ) {
|
||||||
return 'activitypub_http_' . \md5( $url );
|
return 'activitypub_http_' . \md5( $url );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Requests the Data from the Object-URL or Object-Array
|
||||||
|
*
|
||||||
|
* @param array|string $url_or_object The Object or the Object URL.
|
||||||
|
* @param bool $cached If the result should be cached.
|
||||||
|
*
|
||||||
|
* @return array|WP_Error The Object data as array or WP_Error on failure.
|
||||||
|
*/
|
||||||
|
public static function get_remote_object( $url_or_object, $cached = true ) {
|
||||||
|
if ( is_array( $url_or_object ) ) {
|
||||||
|
if ( array_key_exists( 'id', $url_or_object ) ) {
|
||||||
|
$url = $url_or_object['id'];
|
||||||
|
} elseif ( array_key_exists( 'url', $url_or_object ) ) {
|
||||||
|
$url = $url_or_object['url'];
|
||||||
|
} else {
|
||||||
|
return new WP_Error(
|
||||||
|
'activitypub_no_valid_actor_identifier',
|
||||||
|
\__( 'The "actor" identifier is not valid', 'activitypub' ),
|
||||||
|
array(
|
||||||
|
'status' => 404,
|
||||||
|
'object' => $url_or_object,
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
$url = $url_or_object;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( preg_match( '/^@?' . ACTIVITYPUB_USERNAME_REGEXP . '$/i', $url ) ) {
|
||||||
|
$url = Webfinger::resolve( $url );
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( ! $url ) {
|
||||||
|
return new WP_Error(
|
||||||
|
'activitypub_no_valid_actor_identifier',
|
||||||
|
\__( 'The "actor" identifier is not valid', 'activitypub' ),
|
||||||
|
array(
|
||||||
|
'status' => 404,
|
||||||
|
'object' => $url,
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( is_wp_error( $url ) ) {
|
||||||
|
return $url;
|
||||||
|
}
|
||||||
|
|
||||||
|
$transient_key = self::generate_cache_key( $url );
|
||||||
|
|
||||||
|
// only check the cache if needed.
|
||||||
|
if ( $cached ) {
|
||||||
|
$data = \get_transient( $transient_key );
|
||||||
|
|
||||||
|
if ( $data ) {
|
||||||
|
return $data;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( ! \wp_http_validate_url( $url ) ) {
|
||||||
|
return new WP_Error(
|
||||||
|
'activitypub_no_valid_object_url',
|
||||||
|
\__( 'The "object" is/has no valid URL', 'activitypub' ),
|
||||||
|
array(
|
||||||
|
'status' => 400,
|
||||||
|
'object' => $url,
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
$response = self::get( $url );
|
||||||
|
|
||||||
|
if ( \is_wp_error( $response ) ) {
|
||||||
|
return $response;
|
||||||
|
}
|
||||||
|
|
||||||
|
$data = \wp_remote_retrieve_body( $response );
|
||||||
|
$data = \json_decode( $data, true );
|
||||||
|
|
||||||
|
if ( ! $data ) {
|
||||||
|
return new WP_Error(
|
||||||
|
'activitypub_invalid_json',
|
||||||
|
\__( 'No valid JSON data', 'activitypub' ),
|
||||||
|
array(
|
||||||
|
'status' => 400,
|
||||||
|
'object' => $url,
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
\set_transient( $transient_key, $data, WEEK_IN_SECONDS );
|
||||||
|
|
||||||
|
return $data;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -4,6 +4,8 @@ namespace Activitypub;
|
|||||||
use WP_Error;
|
use WP_Error;
|
||||||
use Activitypub\Webfinger;
|
use Activitypub\Webfinger;
|
||||||
|
|
||||||
|
use function Activitypub\object_to_uri;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* ActivityPub Mention Class
|
* ActivityPub Mention Class
|
||||||
*
|
*
|
||||||
@ -93,7 +95,11 @@ class Mention {
|
|||||||
public static function replace_with_links( $result ) {
|
public static function replace_with_links( $result ) {
|
||||||
$metadata = get_remote_metadata_by_actor( $result[0] );
|
$metadata = get_remote_metadata_by_actor( $result[0] );
|
||||||
|
|
||||||
if ( ! empty( $metadata ) && ! is_wp_error( $metadata ) && ! empty( $metadata['url'] ) ) {
|
if (
|
||||||
|
! empty( $metadata ) &&
|
||||||
|
! is_wp_error( $metadata ) &&
|
||||||
|
( ! empty( $metadata['id'] ) || ! empty( $metadata['url'] ) )
|
||||||
|
) {
|
||||||
$username = ltrim( $result[0], '@' );
|
$username = ltrim( $result[0], '@' );
|
||||||
if ( ! empty( $metadata['name'] ) ) {
|
if ( ! empty( $metadata['name'] ) ) {
|
||||||
$username = $metadata['name'];
|
$username = $metadata['name'];
|
||||||
@ -102,11 +108,7 @@ class Mention {
|
|||||||
$username = $metadata['preferredUsername'];
|
$username = $metadata['preferredUsername'];
|
||||||
}
|
}
|
||||||
|
|
||||||
$url = isset( $metadata['url'] ) ? $metadata['url'] : $metadata['id'];
|
$url = isset( $metadata['url'] ) ? object_to_uri( $metadata['url'] ) : object_to_uri( $metadata['id'] );
|
||||||
|
|
||||||
if ( \is_array( $url ) ) {
|
|
||||||
$url = $url[0];
|
|
||||||
}
|
|
||||||
|
|
||||||
return \sprintf( '<a rel="mention" class="u-url mention" href="%s">@<span>%s</span></a>', esc_url( $url ), esc_html( $username ) );
|
return \sprintf( '<a rel="mention" class="u-url mention" href="%s">@<span>%s</span></a>', esc_url( $url ), esc_html( $username ) );
|
||||||
}
|
}
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
namespace Activitypub;
|
namespace Activitypub;
|
||||||
|
|
||||||
use Activitypub\Activitypub;
|
use Activitypub\Activitypub;
|
||||||
use Activitypub\Model\Blog_User;
|
use Activitypub\Model\Blog;
|
||||||
use Activitypub\Collection\Followers;
|
use Activitypub\Collection\Followers;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -0,0 +1,58 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Activitypub;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Notification class.
|
||||||
|
*/
|
||||||
|
class Notification {
|
||||||
|
/**
|
||||||
|
* The type of the notification.
|
||||||
|
*
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
public $type;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The actor URL.
|
||||||
|
*
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
public $actor;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The Activity object.
|
||||||
|
*
|
||||||
|
* @var array
|
||||||
|
*/
|
||||||
|
public $object;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The WordPress User-Id.
|
||||||
|
*
|
||||||
|
* @var int
|
||||||
|
*/
|
||||||
|
public $target;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Notification constructor.
|
||||||
|
*
|
||||||
|
* @param string $type The type of the notification.
|
||||||
|
* @param string $actor The actor URL.
|
||||||
|
* @param array $object The Activity object.
|
||||||
|
* @param int $target The WordPress User-Id.
|
||||||
|
*/
|
||||||
|
public function __construct( $type, $actor, $object, $target ) { // phpcs:ignore Universal.NamingConventions.NoReservedKeywordParameterNames.objectFound
|
||||||
|
$this->type = $type;
|
||||||
|
$this->actor = $actor;
|
||||||
|
$this->object = $object;
|
||||||
|
$this->target = $target;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Send the notification.
|
||||||
|
*/
|
||||||
|
public function send() {
|
||||||
|
do_action( 'activitypub_notification', $this );
|
||||||
|
}
|
||||||
|
}
|
@ -4,8 +4,8 @@ namespace Activitypub\Collection;
|
|||||||
use WP_Error;
|
use WP_Error;
|
||||||
use WP_User_Query;
|
use WP_User_Query;
|
||||||
use Activitypub\Model\User;
|
use Activitypub\Model\User;
|
||||||
use Activitypub\Model\Blog_User;
|
use Activitypub\Model\Blog;
|
||||||
use Activitypub\Model\Application_User;
|
use Activitypub\Model\Application;
|
||||||
|
|
||||||
use function Activitypub\object_to_uri;
|
use function Activitypub\object_to_uri;
|
||||||
use function Activitypub\url_to_authorid;
|
use function Activitypub\url_to_authorid;
|
||||||
@ -47,9 +47,9 @@ class Users {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if ( self::BLOG_USER_ID === $user_id ) {
|
if ( self::BLOG_USER_ID === $user_id ) {
|
||||||
return Blog_User::from_wp_user( $user_id );
|
return new Blog();
|
||||||
} elseif ( self::APPLICATION_USER_ID === $user_id ) {
|
} elseif ( self::APPLICATION_USER_ID === $user_id ) {
|
||||||
return Application_User::from_wp_user( $user_id );
|
return new Application();
|
||||||
} elseif ( $user_id > 0 ) {
|
} elseif ( $user_id > 0 ) {
|
||||||
return User::from_wp_user( $user_id );
|
return User::from_wp_user( $user_id );
|
||||||
}
|
}
|
||||||
@ -70,12 +70,12 @@ class Users {
|
|||||||
*/
|
*/
|
||||||
public static function get_by_username( $username ) {
|
public static function get_by_username( $username ) {
|
||||||
// check for blog user.
|
// check for blog user.
|
||||||
if ( Blog_User::get_default_username() === $username ) {
|
if ( Blog::get_default_username() === $username ) {
|
||||||
return self::get_by_id( self::BLOG_USER_ID );
|
return new Blog();
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( get_option( 'activitypub_blog_user_identifier' ) === $username ) {
|
if ( get_option( 'activitypub_blog_user_identifier' ) === $username ) {
|
||||||
return self::get_by_id( self::BLOG_USER_ID );
|
return new Blog();
|
||||||
}
|
}
|
||||||
|
|
||||||
// check for application user.
|
// check for application user.
|
||||||
@ -144,25 +144,32 @@ class Users {
|
|||||||
// try to extract the scheme and the host
|
// try to extract the scheme and the host
|
||||||
if ( preg_match( '/^([a-zA-Z^:]+):(.*)$/i', $resource, $match ) ) {
|
if ( preg_match( '/^([a-zA-Z^:]+):(.*)$/i', $resource, $match ) ) {
|
||||||
// extract the scheme
|
// extract the scheme
|
||||||
$scheme = esc_attr( $match[1] );
|
$scheme = \esc_attr( $match[1] );
|
||||||
}
|
}
|
||||||
|
|
||||||
switch ( $scheme ) {
|
switch ( $scheme ) {
|
||||||
// check for http(s) URIs
|
// check for http(s) URIs
|
||||||
case 'http':
|
case 'http':
|
||||||
case 'https':
|
case 'https':
|
||||||
$url_parts = wp_parse_url( $resource );
|
$resource_path = \wp_parse_url( $resource, PHP_URL_PATH );
|
||||||
|
|
||||||
|
if ( $resource_path ) {
|
||||||
|
$blog_path = \wp_parse_url( \home_url(), PHP_URL_PATH );
|
||||||
|
|
||||||
|
if ( $blog_path ) {
|
||||||
|
$resource_path = \str_replace( $blog_path, '', $resource_path );
|
||||||
|
}
|
||||||
|
|
||||||
|
$resource_path = \trim( $resource_path, '/' );
|
||||||
|
|
||||||
// check for http(s)://blog.example.com/@username
|
// check for http(s)://blog.example.com/@username
|
||||||
if (
|
if ( str_starts_with( $resource_path, '@' ) ) {
|
||||||
isset( $url_parts['path'] ) &&
|
$identifier = \str_replace( '@', '', $resource_path );
|
||||||
str_starts_with( $url_parts['path'], '/@' )
|
$identifier = \trim( $identifier, '/' );
|
||||||
) {
|
|
||||||
$identifier = str_replace( '/@', '', $url_parts['path'] );
|
|
||||||
$identifier = untrailingslashit( $identifier );
|
|
||||||
|
|
||||||
return self::get_by_username( $identifier );
|
return self::get_by_username( $identifier );
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// check for http(s)://blog.example.com/author/username
|
// check for http(s)://blog.example.com/author/username
|
||||||
$user_id = url_to_authorid( $resource );
|
$user_id = url_to_authorid( $resource );
|
||||||
@ -222,18 +229,26 @@ class Users {
|
|||||||
* @return \Acitvitypub\Model\User The User.
|
* @return \Acitvitypub\Model\User The User.
|
||||||
*/
|
*/
|
||||||
public static function get_by_various( $id ) {
|
public static function get_by_various( $id ) {
|
||||||
|
$user = null;
|
||||||
|
|
||||||
if ( is_numeric( $id ) ) {
|
if ( is_numeric( $id ) ) {
|
||||||
return self::get_by_id( $id );
|
$user = self::get_by_id( $id );
|
||||||
} elseif (
|
} elseif (
|
||||||
// is URL
|
// is URL
|
||||||
filter_var( $id, FILTER_VALIDATE_URL ) ||
|
filter_var( $id, FILTER_VALIDATE_URL ) ||
|
||||||
// is acct
|
// is acct
|
||||||
str_starts_with( $id, 'acct:' )
|
str_starts_with( $id, 'acct:' ) ||
|
||||||
|
// is email
|
||||||
|
filter_var( $id, FILTER_VALIDATE_EMAIL )
|
||||||
) {
|
) {
|
||||||
return self::get_by_resource( $id );
|
$user = self::get_by_resource( $id );
|
||||||
} else {
|
|
||||||
return self::get_by_username( $id );
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if ( $user && ! is_wp_error( $user ) ) {
|
||||||
|
return $user;
|
||||||
|
}
|
||||||
|
|
||||||
|
return self::get_by_username( $id );
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -854,3 +854,118 @@ function get_masked_wp_version() {
|
|||||||
|
|
||||||
return implode( '.', $version );
|
return implode( '.', $version );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the enclosures of a post.
|
||||||
|
*
|
||||||
|
* @param int $post_id The post ID.
|
||||||
|
*
|
||||||
|
* @return array The enclosures.
|
||||||
|
*/
|
||||||
|
function get_enclosures( $post_id ) {
|
||||||
|
$enclosures = get_post_meta( $post_id, 'enclosure' );
|
||||||
|
|
||||||
|
if ( ! $enclosures ) {
|
||||||
|
return array();
|
||||||
|
}
|
||||||
|
|
||||||
|
$enclosures = array_map(
|
||||||
|
function ( $enclosure ) {
|
||||||
|
$attributes = explode( "\n", $enclosure );
|
||||||
|
|
||||||
|
if ( ! isset( $attributes[0] ) || ! \wp_http_validate_url( $attributes[0] ) ) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return array(
|
||||||
|
'url' => $attributes[0],
|
||||||
|
'length' => isset( $attributes[1] ) ? trim( $attributes[1] ) : null,
|
||||||
|
'mediaType' => isset( $attributes[2] ) ? trim( $attributes[2] ) : null,
|
||||||
|
);
|
||||||
|
},
|
||||||
|
$enclosures
|
||||||
|
);
|
||||||
|
|
||||||
|
return array_filter( $enclosures );
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves the IDs of the ancestors of a comment.
|
||||||
|
*
|
||||||
|
* Adaption of `get_post_ancestors` from WordPress core.
|
||||||
|
*
|
||||||
|
* @see https://developer.wordpress.org/reference/functions/get_post_ancestors/
|
||||||
|
*
|
||||||
|
* @param int|WP_Comment $comment Comment ID or comment object.
|
||||||
|
*
|
||||||
|
* @return WP_Comment[] Array of ancestor comments or empty array if there are none.
|
||||||
|
*/
|
||||||
|
function get_comment_ancestors( $comment ) {
|
||||||
|
$comment = \get_comment( $comment );
|
||||||
|
|
||||||
|
// phpcs:ignore Universal.Operators.StrictComparisons.LooseEqual
|
||||||
|
if ( ! $comment || empty( $comment->comment_parent ) || $comment->comment_parent == $comment->comment_ID ) {
|
||||||
|
return array();
|
||||||
|
}
|
||||||
|
|
||||||
|
$ancestors = array();
|
||||||
|
|
||||||
|
$id = (int) $comment->comment_parent;
|
||||||
|
$ancestors[] = $id;
|
||||||
|
|
||||||
|
// phpcs:ignore Generic.CodeAnalysis.AssignmentInCondition.FoundInWhileCondition
|
||||||
|
while ( $id > 0 ) {
|
||||||
|
$ancestor = \get_comment( $id );
|
||||||
|
$parent_id = (int) $ancestor->comment_parent;
|
||||||
|
|
||||||
|
// Loop detection: If the ancestor has been seen before, break.
|
||||||
|
if ( empty( $parent_id ) || ( $parent_id === (int) $comment->comment_ID ) || in_array( $parent_id, $ancestors, true ) ) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
$id = $parent_id;
|
||||||
|
$ancestors[] = $id;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $ancestors;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Change the display of large numbers on the site.
|
||||||
|
*
|
||||||
|
* @author Jeremy Herve
|
||||||
|
*
|
||||||
|
* @see https://wordpress.org/support/topic/abbreviate-numbers-with-k/
|
||||||
|
*
|
||||||
|
* @param string $formatted Converted number in string format.
|
||||||
|
* @param float $number The number to convert based on locale.
|
||||||
|
* @param int $decimals Precision of the number of decimal places.
|
||||||
|
*
|
||||||
|
* @return string Converted number in string format.
|
||||||
|
*/
|
||||||
|
function custom_large_numbers( $formatted, $number, $decimals ) {
|
||||||
|
global $wp_locale;
|
||||||
|
|
||||||
|
$decimals = 0;
|
||||||
|
$decimal_point = '.';
|
||||||
|
$thousands_sep = ',';
|
||||||
|
|
||||||
|
if ( isset( $wp_locale ) ) {
|
||||||
|
$decimals = (int) $wp_locale->number_format['decimal_point'];
|
||||||
|
$decimal_point = $wp_locale->number_format['decimal_point'];
|
||||||
|
$thousands_sep = $wp_locale->number_format['thousands_sep'];
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( $number < 1000 ) { // any number less than a Thousand.
|
||||||
|
return \number_format( $number, $decimals, $decimal_point, $thousands_sep );
|
||||||
|
} elseif ( $number < 1000000 ) { // any number less than a million
|
||||||
|
return \number_format( $number / 1000, $decimals, $decimal_point, $thousands_sep ) . 'K';
|
||||||
|
} elseif ( $number < 1000000000 ) { // any number less than a billion
|
||||||
|
return \number_format( $number / 1000000, $decimals, $decimal_point, $thousands_sep ) . 'M';
|
||||||
|
} else { // at least a billion
|
||||||
|
return \number_format( $number / 1000000000, $decimals, $decimal_point, $thousands_sep ) . 'B';
|
||||||
|
}
|
||||||
|
|
||||||
|
// Default fallback. We should not get here.
|
||||||
|
return $formatted;
|
||||||
|
}
|
||||||
|
@ -0,0 +1,69 @@
|
|||||||
|
<?php
|
||||||
|
namespace Activitypub\Handler;
|
||||||
|
|
||||||
|
use Activitypub\Http;
|
||||||
|
|
||||||
|
use function Activitypub\is_activity_public;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handle Create requests
|
||||||
|
*/
|
||||||
|
class Announce {
|
||||||
|
/**
|
||||||
|
* Initialize the class, registering WordPress hooks
|
||||||
|
*/
|
||||||
|
public static function init() {
|
||||||
|
\add_action(
|
||||||
|
'activitypub_inbox_announce',
|
||||||
|
array( self::class, 'handle_announce' ),
|
||||||
|
10,
|
||||||
|
3
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handles "Announce" requests
|
||||||
|
*
|
||||||
|
* @param array $array The activity-object
|
||||||
|
* @param int $user_id The id of the local blog-user
|
||||||
|
* @param Activitypub\Activity $activity The activity object
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public static function handle_announce( $array, $user_id, $activity = null ) { // phpcs:ignore Universal.NamingConventions.NoReservedKeywordParameterNames.arrayFound
|
||||||
|
if ( ACTIVITYPUB_DISABLE_INCOMING_INTERACTIONS ) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( ! isset( $array['object'] ) ) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// check if Activity is public or not
|
||||||
|
if ( ! is_activity_public( $array ) ) {
|
||||||
|
// @todo maybe send email
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// @todo save the `Announce`-Activity itself
|
||||||
|
|
||||||
|
if ( is_string( $array['object'] ) ) {
|
||||||
|
$object = Http::get_remote_object( $array['object'] );
|
||||||
|
} else {
|
||||||
|
$object = $array['object'];
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( ! $object || is_wp_error( $object ) ) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( ! isset( $object['type'] ) ) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$type = \strtolower( $object['type'] );
|
||||||
|
|
||||||
|
\do_action( 'activitypub_inbox', $object, $user_id, $type, $activity );
|
||||||
|
\do_action( "activitypub_inbox_{$type}", $object, $user_id, $activity );
|
||||||
|
}
|
||||||
|
}
|
@ -2,6 +2,7 @@
|
|||||||
namespace Activitypub\Handler;
|
namespace Activitypub\Handler;
|
||||||
|
|
||||||
use Activitypub\Http;
|
use Activitypub\Http;
|
||||||
|
use Activitypub\Notification;
|
||||||
use Activitypub\Activity\Activity;
|
use Activitypub\Activity\Activity;
|
||||||
use Activitypub\Collection\Users;
|
use Activitypub\Collection\Users;
|
||||||
use Activitypub\Collection\Followers;
|
use Activitypub\Collection\Followers;
|
||||||
@ -57,6 +58,15 @@ class Follow {
|
|||||||
$user_id,
|
$user_id,
|
||||||
$follower
|
$follower
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// send notification
|
||||||
|
$notification = new Notification(
|
||||||
|
'follow',
|
||||||
|
$activity['actor'],
|
||||||
|
$activity,
|
||||||
|
$user_id
|
||||||
|
);
|
||||||
|
$notification->send();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -1,79 +0,0 @@
|
|||||||
<?php
|
|
||||||
namespace Activitypub\Model;
|
|
||||||
|
|
||||||
use WP_Query;
|
|
||||||
use Activitypub\Signature;
|
|
||||||
use Activitypub\Collection\Users;
|
|
||||||
|
|
||||||
use function Activitypub\get_rest_url_by_path;
|
|
||||||
|
|
||||||
class Application_User extends Blog_User {
|
|
||||||
/**
|
|
||||||
* The User-ID
|
|
||||||
*
|
|
||||||
* @var int
|
|
||||||
*/
|
|
||||||
protected $_id = Users::APPLICATION_USER_ID; // phpcs:ignore PSR2.Classes.PropertyDeclaration.Underscore
|
|
||||||
|
|
||||||
public function get_type() {
|
|
||||||
return 'Application';
|
|
||||||
}
|
|
||||||
|
|
||||||
public function get_discoverable() {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function get_manually_approves_followers() {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get the User-Url.
|
|
||||||
*
|
|
||||||
* @return string The User-Url.
|
|
||||||
*/
|
|
||||||
public function get_url() {
|
|
||||||
return get_rest_url_by_path( 'application' );
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the User-URL with @-Prefix for the username.
|
|
||||||
*
|
|
||||||
* @return string The User-URL with @-Prefix for the username.
|
|
||||||
*/
|
|
||||||
public function get_alternate_url() {
|
|
||||||
return \esc_url( \trailingslashit( get_home_url() ) . '@' . $this->get_preferred_username() );
|
|
||||||
}
|
|
||||||
|
|
||||||
public function get_name() {
|
|
||||||
return 'application';
|
|
||||||
}
|
|
||||||
|
|
||||||
public function get_preferred_username() {
|
|
||||||
return $this->get_name();
|
|
||||||
}
|
|
||||||
|
|
||||||
public function get_followers() {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function get_following() {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function get_attachment() {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function get_featured() {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function get_moderators() {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function get_indexable() {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
@ -0,0 +1,200 @@
|
|||||||
|
<?php
|
||||||
|
namespace Activitypub\Model;
|
||||||
|
|
||||||
|
use WP_Query;
|
||||||
|
use Activitypub\Signature;
|
||||||
|
use Activitypub\Activity\Actor;
|
||||||
|
use Activitypub\Collection\Users;
|
||||||
|
|
||||||
|
use function Activitypub\get_rest_url_by_path;
|
||||||
|
|
||||||
|
class Application extends Actor {
|
||||||
|
/**
|
||||||
|
* The User-ID
|
||||||
|
*
|
||||||
|
* @var int
|
||||||
|
*/
|
||||||
|
protected $_id = Users::APPLICATION_USER_ID; // phpcs:ignore PSR2.Classes.PropertyDeclaration.Underscore
|
||||||
|
|
||||||
|
/**
|
||||||
|
* If the User is discoverable.
|
||||||
|
*
|
||||||
|
* @see https://docs.joinmastodon.org/spec/activitypub/#discoverable
|
||||||
|
*
|
||||||
|
* @context http://joinmastodon.org/ns#discoverable
|
||||||
|
*
|
||||||
|
* @var boolean
|
||||||
|
*/
|
||||||
|
protected $discoverable = false;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* If the User is indexable.
|
||||||
|
*
|
||||||
|
* @context http://joinmastodon.org/ns#indexable
|
||||||
|
*
|
||||||
|
* @var boolean
|
||||||
|
*/
|
||||||
|
protected $indexable = false;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The WebFinger Resource.
|
||||||
|
*
|
||||||
|
* @var string<url>
|
||||||
|
*/
|
||||||
|
protected $webfinger;
|
||||||
|
|
||||||
|
public function get_type() {
|
||||||
|
return 'Application';
|
||||||
|
}
|
||||||
|
|
||||||
|
public function get_manually_approves_followers() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function get_id() {
|
||||||
|
return get_rest_url_by_path( 'application' );
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the User-Url.
|
||||||
|
*
|
||||||
|
* @return string The User-Url.
|
||||||
|
*/
|
||||||
|
public function get_url() {
|
||||||
|
return $this->get_id();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the User-URL with @-Prefix for the username.
|
||||||
|
*
|
||||||
|
* @return string The User-URL with @-Prefix for the username.
|
||||||
|
*/
|
||||||
|
public function get_alternate_url() {
|
||||||
|
return $this->get_url();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function get_name() {
|
||||||
|
return 'application';
|
||||||
|
}
|
||||||
|
|
||||||
|
public function get_preferred_username() {
|
||||||
|
return $this->get_name();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the User-Icon.
|
||||||
|
*
|
||||||
|
* @return array The User-Icon.
|
||||||
|
*/
|
||||||
|
public function get_icon() {
|
||||||
|
// try site icon first
|
||||||
|
$icon_id = get_option( 'site_icon' );
|
||||||
|
|
||||||
|
// try custom logo second
|
||||||
|
if ( ! $icon_id ) {
|
||||||
|
$icon_id = get_theme_mod( 'custom_logo' );
|
||||||
|
}
|
||||||
|
|
||||||
|
$icon_url = false;
|
||||||
|
|
||||||
|
if ( $icon_id ) {
|
||||||
|
$icon = wp_get_attachment_image_src( $icon_id, 'full' );
|
||||||
|
if ( $icon ) {
|
||||||
|
$icon_url = $icon[0];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( ! $icon_url ) {
|
||||||
|
// fallback to default icon
|
||||||
|
$icon_url = plugins_url( '/assets/img/wp-logo.png', ACTIVITYPUB_PLUGIN_FILE );
|
||||||
|
}
|
||||||
|
|
||||||
|
return array(
|
||||||
|
'type' => 'Image',
|
||||||
|
'url' => esc_url( $icon_url ),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the User-Header-Image.
|
||||||
|
*
|
||||||
|
* @return array|null The User-Header-Image.
|
||||||
|
*/
|
||||||
|
public function get_header_image() {
|
||||||
|
if ( \has_header_image() ) {
|
||||||
|
return array(
|
||||||
|
'type' => 'Image',
|
||||||
|
'url' => esc_url( \get_header_image() ),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function get_published() {
|
||||||
|
$first_post = new WP_Query(
|
||||||
|
array(
|
||||||
|
'orderby' => 'date',
|
||||||
|
'order' => 'ASC',
|
||||||
|
'number' => 1,
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
if ( ! empty( $first_post->posts[0] ) ) {
|
||||||
|
$time = \strtotime( $first_post->posts[0]->post_date_gmt );
|
||||||
|
} else {
|
||||||
|
$time = \time();
|
||||||
|
}
|
||||||
|
|
||||||
|
return \gmdate( 'Y-m-d\TH:i:s\Z', $time );
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the Inbox-API-Endpoint.
|
||||||
|
*
|
||||||
|
* @return string The Inbox-Endpoint.
|
||||||
|
*/
|
||||||
|
public function get_inbox() {
|
||||||
|
return get_rest_url_by_path( sprintf( 'actors/%d/inbox', $this->get__id() ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the Outbox-API-Endpoint.
|
||||||
|
*
|
||||||
|
* @return string The Outbox-Endpoint.
|
||||||
|
*/
|
||||||
|
public function get_outbox() {
|
||||||
|
return get_rest_url_by_path( sprintf( 'actors/%d/outbox', $this->get__id() ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a user@domain type of identifier for the user.
|
||||||
|
*
|
||||||
|
* @return string The Webfinger-Identifier.
|
||||||
|
*/
|
||||||
|
public function get_webfinger() {
|
||||||
|
return $this->get_preferred_username() . '@' . \wp_parse_url( \home_url(), \PHP_URL_HOST );
|
||||||
|
}
|
||||||
|
|
||||||
|
public function get_public_key() {
|
||||||
|
return array(
|
||||||
|
'id' => $this->get_id() . '#main-key',
|
||||||
|
'owner' => $this->get_id(),
|
||||||
|
'publicKeyPem' => Signature::get_public_key_for( Users::APPLICATION_USER_ID ),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the User-Description.
|
||||||
|
*
|
||||||
|
* @return string The User-Description.
|
||||||
|
*/
|
||||||
|
public function get_summary() {
|
||||||
|
return \wpautop(
|
||||||
|
\wp_kses(
|
||||||
|
\get_bloginfo( 'description' ),
|
||||||
|
'default'
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
@ -4,13 +4,38 @@ namespace Activitypub\Model;
|
|||||||
use WP_Query;
|
use WP_Query;
|
||||||
use WP_Error;
|
use WP_Error;
|
||||||
|
|
||||||
|
use Activitypub\Signature;
|
||||||
|
use Activitypub\Activity\Actor;
|
||||||
use Activitypub\Collection\Users;
|
use Activitypub\Collection\Users;
|
||||||
|
|
||||||
use function Activitypub\is_single_user;
|
use function Activitypub\is_single_user;
|
||||||
use function Activitypub\is_user_disabled;
|
use function Activitypub\is_user_disabled;
|
||||||
use function Activitypub\get_rest_url_by_path;
|
use function Activitypub\get_rest_url_by_path;
|
||||||
|
|
||||||
class Blog_User extends User {
|
class Blog extends Actor {
|
||||||
|
/**
|
||||||
|
* The Featured-Posts.
|
||||||
|
*
|
||||||
|
* @see https://docs.joinmastodon.org/spec/activitypub/#featured
|
||||||
|
*
|
||||||
|
* @context {
|
||||||
|
* "@id": "http://joinmastodon.org/ns#featured",
|
||||||
|
* "@type": "@id"
|
||||||
|
* }
|
||||||
|
*
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
protected $featured;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Moderators endpoint.
|
||||||
|
*
|
||||||
|
* @see https://join-lemmy.org/docs/contributors/05-federation.html
|
||||||
|
*
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
protected $moderators;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The User-ID
|
* The User-ID
|
||||||
*
|
*
|
||||||
@ -18,6 +43,42 @@ class Blog_User extends User {
|
|||||||
*/
|
*/
|
||||||
protected $_id = Users::BLOG_USER_ID; // phpcs:ignore PSR2.Classes.PropertyDeclaration.Underscore
|
protected $_id = Users::BLOG_USER_ID; // phpcs:ignore PSR2.Classes.PropertyDeclaration.Underscore
|
||||||
|
|
||||||
|
/**
|
||||||
|
* If the User is indexable.
|
||||||
|
*
|
||||||
|
* @context http://joinmastodon.org/ns#indexable
|
||||||
|
*
|
||||||
|
* @var boolean
|
||||||
|
*/
|
||||||
|
protected $indexable;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The WebFinger Resource.
|
||||||
|
*
|
||||||
|
* @var string<url>
|
||||||
|
*/
|
||||||
|
protected $webfinger;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* If the User is discoverable.
|
||||||
|
*
|
||||||
|
* @see https://docs.joinmastodon.org/spec/activitypub/#discoverable
|
||||||
|
*
|
||||||
|
* @context http://joinmastodon.org/ns#discoverable
|
||||||
|
*
|
||||||
|
* @var boolean
|
||||||
|
*/
|
||||||
|
protected $discoverable;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Restrict posting to mods
|
||||||
|
*
|
||||||
|
* @see https://join-lemmy.org/docs/contributors/05-federation.html
|
||||||
|
*
|
||||||
|
* @var boolean
|
||||||
|
*/
|
||||||
|
protected $posting_restricted_to_mods;
|
||||||
|
|
||||||
public function get_manually_approves_followers() {
|
public function get_manually_approves_followers() {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -26,19 +87,13 @@ class Blog_User extends User {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static function from_wp_user( $user_id ) {
|
/**
|
||||||
if ( is_user_disabled( $user_id ) ) {
|
* Get the User-ID.
|
||||||
return new WP_Error(
|
*
|
||||||
'activitypub_user_not_found',
|
* @return string The User-ID.
|
||||||
\__( 'User not found', 'activitypub' ),
|
*/
|
||||||
array( 'status' => 404 )
|
public function get_id() {
|
||||||
);
|
return $this->get_url();
|
||||||
}
|
|
||||||
|
|
||||||
$object = new static();
|
|
||||||
$object->_id = $user_id;
|
|
||||||
|
|
||||||
return $object;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -204,10 +259,6 @@ class Blog_User extends User {
|
|||||||
return \gmdate( 'Y-m-d\TH:i:s\Z', $time );
|
return \gmdate( 'Y-m-d\TH:i:s\Z', $time );
|
||||||
}
|
}
|
||||||
|
|
||||||
public function get_attachment() {
|
|
||||||
return array();
|
|
||||||
}
|
|
||||||
|
|
||||||
public function get_canonical_url() {
|
public function get_canonical_url() {
|
||||||
return \home_url();
|
return \home_url();
|
||||||
}
|
}
|
||||||
@ -228,6 +279,14 @@ class Blog_User extends User {
|
|||||||
return get_rest_url_by_path( 'collections/moderators' );
|
return get_rest_url_by_path( 'collections/moderators' );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function get_public_key() {
|
||||||
|
return array(
|
||||||
|
'id' => $this->get_id() . '#main-key',
|
||||||
|
'owner' => $this->get_id(),
|
||||||
|
'publicKeyPem' => Signature::get_public_key_for( $this->get__id() ),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
public function get_posting_restricted_to_mods() {
|
public function get_posting_restricted_to_mods() {
|
||||||
if ( 'Group' === $this->get_type() ) {
|
if ( 'Group' === $this->get_type() ) {
|
||||||
return true;
|
return true;
|
||||||
@ -235,4 +294,78 @@ class Blog_User extends User {
|
|||||||
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the Inbox-API-Endpoint.
|
||||||
|
*
|
||||||
|
* @return string The Inbox-Endpoint.
|
||||||
|
*/
|
||||||
|
public function get_inbox() {
|
||||||
|
return get_rest_url_by_path( sprintf( 'actors/%d/inbox', $this->get__id() ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the Outbox-API-Endpoint.
|
||||||
|
*
|
||||||
|
* @return string The Outbox-Endpoint.
|
||||||
|
*/
|
||||||
|
public function get_outbox() {
|
||||||
|
return get_rest_url_by_path( sprintf( 'actors/%d/outbox', $this->get__id() ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the Followers-API-Endpoint.
|
||||||
|
*
|
||||||
|
* @return string The Followers-Endpoint.
|
||||||
|
*/
|
||||||
|
public function get_followers() {
|
||||||
|
return get_rest_url_by_path( sprintf( 'actors/%d/followers', $this->get__id() ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the Following-API-Endpoint.
|
||||||
|
*
|
||||||
|
* @return string The Following-Endpoint.
|
||||||
|
*/
|
||||||
|
public function get_following() {
|
||||||
|
return get_rest_url_by_path( sprintf( 'actors/%d/following', $this->get__id() ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
public function get_endpoints() {
|
||||||
|
$endpoints = null;
|
||||||
|
|
||||||
|
if ( ACTIVITYPUB_SHARED_INBOX_FEATURE ) {
|
||||||
|
$endpoints = array(
|
||||||
|
'sharedInbox' => get_rest_url_by_path( 'inbox' ),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $endpoints;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a user@domain type of identifier for the user.
|
||||||
|
*
|
||||||
|
* @return string The Webfinger-Identifier.
|
||||||
|
*/
|
||||||
|
public function get_webfinger() {
|
||||||
|
return $this->get_preferred_username() . '@' . \wp_parse_url( \home_url(), \PHP_URL_HOST );
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the Featured-API-Endpoint.
|
||||||
|
*
|
||||||
|
* @return string The Featured-Endpoint.
|
||||||
|
*/
|
||||||
|
public function get_featured() {
|
||||||
|
return get_rest_url_by_path( sprintf( 'actors/%d/collections/featured', $this->get__id() ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
public function get_indexable() {
|
||||||
|
if ( \get_option( 'blog_public', 1 ) ) {
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
@ -2,7 +2,7 @@
|
|||||||
namespace Activitypub\Model;
|
namespace Activitypub\Model;
|
||||||
|
|
||||||
use Activitypub\Collection\Users;
|
use Activitypub\Collection\Users;
|
||||||
use Activitypub\Transformer\Post as Post_Transformer;
|
use Activitypub\Transformer\Factory;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* ActivityPub Post Class
|
* ActivityPub Post Class
|
||||||
@ -32,10 +32,14 @@ class Post {
|
|||||||
*/
|
*/
|
||||||
// phpcs:ignore Generic.CodeAnalysis.UnusedFunctionParameter.FoundAfterLastUsed, VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable
|
// phpcs:ignore Generic.CodeAnalysis.UnusedFunctionParameter.FoundAfterLastUsed, VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable
|
||||||
public function __construct( $post, $post_author = null ) {
|
public function __construct( $post, $post_author = null ) {
|
||||||
_deprecated_function( __CLASS__, '1.0.0', '\Activitypub\Transformer\Post' );
|
_deprecated_function( __METHOD__, '1.0.0', '\Activitypub\Transformer\Factory::get_transformer' );
|
||||||
|
|
||||||
|
$transformer = Factory::get_transformer( $post );
|
||||||
|
|
||||||
|
if ( ! \is_wp_error( $transformer ) ) {
|
||||||
$this->post = $post;
|
$this->post = $post;
|
||||||
$this->object = Post_Transformer::transform( $post )->to_object();
|
$this->object = $transformer->to_object();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -4,8 +4,9 @@ namespace Activitypub\Model;
|
|||||||
use WP_Query;
|
use WP_Query;
|
||||||
use WP_Error;
|
use WP_Error;
|
||||||
use Activitypub\Signature;
|
use Activitypub\Signature;
|
||||||
use Activitypub\Collection\Users;
|
use Activitypub\Model\Blog;
|
||||||
use Activitypub\Activity\Actor;
|
use Activitypub\Activity\Actor;
|
||||||
|
use Activitypub\Collection\Users;
|
||||||
|
|
||||||
use function Activitypub\is_user_disabled;
|
use function Activitypub\is_user_disabled;
|
||||||
use function Activitypub\get_rest_url_by_path;
|
use function Activitypub\get_rest_url_by_path;
|
||||||
@ -32,19 +33,6 @@ class User extends Actor {
|
|||||||
*/
|
*/
|
||||||
protected $featured;
|
protected $featured;
|
||||||
|
|
||||||
/**
|
|
||||||
* Moderators endpoint.
|
|
||||||
*
|
|
||||||
* @see https://join-lemmy.org/docs/contributors/05-federation.html
|
|
||||||
*
|
|
||||||
* @var string
|
|
||||||
*/
|
|
||||||
protected $moderators;
|
|
||||||
|
|
||||||
public function get_type() {
|
|
||||||
return 'Person';
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* If the User is discoverable.
|
* If the User is discoverable.
|
||||||
*
|
*
|
||||||
@ -72,14 +60,9 @@ class User extends Actor {
|
|||||||
*/
|
*/
|
||||||
protected $webfinger;
|
protected $webfinger;
|
||||||
|
|
||||||
/**
|
public function get_type() {
|
||||||
* Restrict posting to mods
|
return 'Person';
|
||||||
*
|
}
|
||||||
* @see https://join-lemmy.org/docs/contributors/05-federation.html
|
|
||||||
*
|
|
||||||
* @var boolean
|
|
||||||
*/
|
|
||||||
protected $posting_restricted_to_mods = null;
|
|
||||||
|
|
||||||
public static function from_wp_user( $user_id ) {
|
public static function from_wp_user( $user_id ) {
|
||||||
if ( is_user_disabled( $user_id ) ) {
|
if ( is_user_disabled( $user_id ) ) {
|
||||||
@ -193,7 +176,7 @@ class User extends Actor {
|
|||||||
* @return string The Inbox-Endpoint.
|
* @return string The Inbox-Endpoint.
|
||||||
*/
|
*/
|
||||||
public function get_inbox() {
|
public function get_inbox() {
|
||||||
return get_rest_url_by_path( sprintf( 'users/%d/inbox', $this->get__id() ) );
|
return get_rest_url_by_path( sprintf( 'actors/%d/inbox', $this->get__id() ) );
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -202,7 +185,7 @@ class User extends Actor {
|
|||||||
* @return string The Outbox-Endpoint.
|
* @return string The Outbox-Endpoint.
|
||||||
*/
|
*/
|
||||||
public function get_outbox() {
|
public function get_outbox() {
|
||||||
return get_rest_url_by_path( sprintf( 'users/%d/outbox', $this->get__id() ) );
|
return get_rest_url_by_path( sprintf( 'actors/%d/outbox', $this->get__id() ) );
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -211,7 +194,7 @@ class User extends Actor {
|
|||||||
* @return string The Followers-Endpoint.
|
* @return string The Followers-Endpoint.
|
||||||
*/
|
*/
|
||||||
public function get_followers() {
|
public function get_followers() {
|
||||||
return get_rest_url_by_path( sprintf( 'users/%d/followers', $this->get__id() ) );
|
return get_rest_url_by_path( sprintf( 'actors/%d/followers', $this->get__id() ) );
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -220,7 +203,7 @@ class User extends Actor {
|
|||||||
* @return string The Following-Endpoint.
|
* @return string The Following-Endpoint.
|
||||||
*/
|
*/
|
||||||
public function get_following() {
|
public function get_following() {
|
||||||
return get_rest_url_by_path( sprintf( 'users/%d/following', $this->get__id() ) );
|
return get_rest_url_by_path( sprintf( 'actors/%d/following', $this->get__id() ) );
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -229,7 +212,7 @@ class User extends Actor {
|
|||||||
* @return string The Featured-Endpoint.
|
* @return string The Featured-Endpoint.
|
||||||
*/
|
*/
|
||||||
public function get_featured() {
|
public function get_featured() {
|
||||||
return get_rest_url_by_path( sprintf( 'users/%d/collections/featured', $this->get__id() ) );
|
return get_rest_url_by_path( sprintf( 'actors/%d/collections/featured', $this->get__id() ) );
|
||||||
}
|
}
|
||||||
|
|
||||||
public function get_endpoints() {
|
public function get_endpoints() {
|
||||||
@ -296,10 +279,6 @@ class User extends Actor {
|
|||||||
return $this->get_preferred_username() . '@' . \wp_parse_url( \home_url(), \PHP_URL_HOST );
|
return $this->get_preferred_username() . '@' . \wp_parse_url( \home_url(), \PHP_URL_HOST );
|
||||||
}
|
}
|
||||||
|
|
||||||
public function get_resource() {
|
|
||||||
return $this->get_webfinger();
|
|
||||||
}
|
|
||||||
|
|
||||||
public function get_canonical_url() {
|
public function get_canonical_url() {
|
||||||
return $this->get_url();
|
return $this->get_url();
|
||||||
}
|
}
|
||||||
|
@ -12,13 +12,13 @@ use Activitypub\Collection\Users as User_Collection;
|
|||||||
use function Activitypub\is_activitypub_request;
|
use function Activitypub\is_activitypub_request;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* ActivityPub Followers REST-Class
|
* ActivityPub Actors REST-Class
|
||||||
*
|
*
|
||||||
* @author Matthias Pfefferle
|
* @author Matthias Pfefferle
|
||||||
*
|
*
|
||||||
* @see https://www.w3.org/TR/activitypub/#followers
|
* @see https://www.w3.org/TR/activitypub/#followers
|
||||||
*/
|
*/
|
||||||
class Users {
|
class Actors {
|
||||||
/**
|
/**
|
||||||
* Initialize the class, registering WordPress hooks
|
* Initialize the class, registering WordPress hooks
|
||||||
*/
|
*/
|
||||||
@ -32,7 +32,7 @@ class Users {
|
|||||||
public static function register_routes() {
|
public static function register_routes() {
|
||||||
\register_rest_route(
|
\register_rest_route(
|
||||||
ACTIVITYPUB_REST_NAMESPACE,
|
ACTIVITYPUB_REST_NAMESPACE,
|
||||||
'/users/(?P<user_id>[\w\-\.]+)',
|
'/(users|actors)/(?P<user_id>[\w\-\.]+)',
|
||||||
array(
|
array(
|
||||||
array(
|
array(
|
||||||
'methods' => WP_REST_Server::READABLE,
|
'methods' => WP_REST_Server::READABLE,
|
||||||
@ -45,7 +45,7 @@ class Users {
|
|||||||
|
|
||||||
\register_rest_route(
|
\register_rest_route(
|
||||||
ACTIVITYPUB_REST_NAMESPACE,
|
ACTIVITYPUB_REST_NAMESPACE,
|
||||||
'/users/(?P<user_id>[\w\-\.]+)/remote-follow',
|
'/(users|actors)/(?P<user_id>[\w\-\.]+)/remote-follow',
|
||||||
array(
|
array(
|
||||||
array(
|
array(
|
||||||
'methods' => WP_REST_Server::READABLE,
|
'methods' => WP_REST_Server::READABLE,
|
@ -1,13 +1,12 @@
|
|||||||
<?php
|
<?php
|
||||||
namespace Activitypub\Rest;
|
namespace Activitypub\Rest;
|
||||||
|
|
||||||
use WP_Error;
|
|
||||||
use WP_REST_Server;
|
use WP_REST_Server;
|
||||||
use WP_REST_Response;
|
use WP_REST_Response;
|
||||||
use Activitypub\Transformer\Post;
|
|
||||||
use Activitypub\Activity\Actor;
|
use Activitypub\Activity\Actor;
|
||||||
use Activitypub\Activity\Base_Object;
|
use Activitypub\Activity\Base_Object;
|
||||||
use Activitypub\Collection\Users as User_Collection;
|
use Activitypub\Collection\Users as User_Collection;
|
||||||
|
use Activitypub\Transformer\Factory;
|
||||||
|
|
||||||
use function Activitypub\esc_hashtag;
|
use function Activitypub\esc_hashtag;
|
||||||
use function Activitypub\is_single_user;
|
use function Activitypub\is_single_user;
|
||||||
@ -35,7 +34,7 @@ class Collection {
|
|||||||
public static function register_routes() {
|
public static function register_routes() {
|
||||||
\register_rest_route(
|
\register_rest_route(
|
||||||
ACTIVITYPUB_REST_NAMESPACE,
|
ACTIVITYPUB_REST_NAMESPACE,
|
||||||
'/users/(?P<user_id>[\w\-\.]+)/collections/tags',
|
'/(users|actors)/(?P<user_id>[\w\-\.]+)/collections/tags',
|
||||||
array(
|
array(
|
||||||
array(
|
array(
|
||||||
'methods' => WP_REST_Server::READABLE,
|
'methods' => WP_REST_Server::READABLE,
|
||||||
@ -48,7 +47,7 @@ class Collection {
|
|||||||
|
|
||||||
\register_rest_route(
|
\register_rest_route(
|
||||||
ACTIVITYPUB_REST_NAMESPACE,
|
ACTIVITYPUB_REST_NAMESPACE,
|
||||||
'/users/(?P<user_id>[\w\-\.]+)/collections/featured',
|
'/(users|actors)/(?P<user_id>[\w\-\.]+)/collections/featured',
|
||||||
array(
|
array(
|
||||||
array(
|
array(
|
||||||
'methods' => WP_REST_Server::READABLE,
|
'methods' => WP_REST_Server::READABLE,
|
||||||
@ -104,7 +103,7 @@ class Collection {
|
|||||||
|
|
||||||
$response = array(
|
$response = array(
|
||||||
'@context' => Base_Object::JSON_LD_CONTEXT,
|
'@context' => Base_Object::JSON_LD_CONTEXT,
|
||||||
'id' => get_rest_url_by_path( sprintf( 'users/%d/collections/tags', $user->get__id() ) ),
|
'id' => get_rest_url_by_path( sprintf( 'actors/%d/collections/tags', $user->get__id() ) ),
|
||||||
'type' => 'Collection',
|
'type' => 'Collection',
|
||||||
'totalItems' => is_countable( $tags ) ? count( $tags ) : 0,
|
'totalItems' => is_countable( $tags ) ? count( $tags ) : 0,
|
||||||
'items' => array(),
|
'items' => array(),
|
||||||
@ -162,14 +161,20 @@ class Collection {
|
|||||||
|
|
||||||
$response = array(
|
$response = array(
|
||||||
'@context' => Base_Object::JSON_LD_CONTEXT,
|
'@context' => Base_Object::JSON_LD_CONTEXT,
|
||||||
'id' => get_rest_url_by_path( sprintf( 'users/%d/collections/featured', $user_id ) ),
|
'id' => get_rest_url_by_path( sprintf( 'actors/%d/collections/featured', $user_id ) ),
|
||||||
'type' => 'OrderedCollection',
|
'type' => 'OrderedCollection',
|
||||||
'totalItems' => is_countable( $posts ) ? count( $posts ) : 0,
|
'totalItems' => is_countable( $posts ) ? count( $posts ) : 0,
|
||||||
'orderedItems' => array(),
|
'orderedItems' => array(),
|
||||||
);
|
);
|
||||||
|
|
||||||
foreach ( $posts as $post ) {
|
foreach ( $posts as $post ) {
|
||||||
$response['orderedItems'][] = Post::transform( $post )->to_object()->to_array( false );
|
$transformer = Factory::get_transformer( $post );
|
||||||
|
|
||||||
|
if ( \is_wp_error( $transformer ) ) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
$response['orderedItems'][] = $transformer->to_object()->to_array( false );
|
||||||
}
|
}
|
||||||
|
|
||||||
$rest_response = new WP_REST_Response( $response, 200 );
|
$rest_response = new WP_REST_Response( $response, 200 );
|
||||||
|
@ -29,7 +29,7 @@ class Comment {
|
|||||||
public static function register_routes() {
|
public static function register_routes() {
|
||||||
\register_rest_route(
|
\register_rest_route(
|
||||||
ACTIVITYPUB_REST_NAMESPACE,
|
ACTIVITYPUB_REST_NAMESPACE,
|
||||||
'/comments/(?P<comment_id>\d+)/remote-reply',
|
'/(users|actors)/(?P<comment_id>\d+)/remote-reply',
|
||||||
array(
|
array(
|
||||||
array(
|
array(
|
||||||
'methods' => WP_REST_Server::READABLE,
|
'methods' => WP_REST_Server::READABLE,
|
||||||
|
@ -32,7 +32,7 @@ class Followers {
|
|||||||
public static function register_routes() {
|
public static function register_routes() {
|
||||||
\register_rest_route(
|
\register_rest_route(
|
||||||
ACTIVITYPUB_REST_NAMESPACE,
|
ACTIVITYPUB_REST_NAMESPACE,
|
||||||
'/users/(?P<user_id>[\w\-\.]+)/followers',
|
'/(users|actors)/(?P<user_id>[\w\-\.]+)/followers',
|
||||||
array(
|
array(
|
||||||
array(
|
array(
|
||||||
'methods' => WP_REST_Server::READABLE,
|
'methods' => WP_REST_Server::READABLE,
|
||||||
@ -74,13 +74,13 @@ class Followers {
|
|||||||
|
|
||||||
$json->{'@context'} = \Activitypub\get_context();
|
$json->{'@context'} = \Activitypub\get_context();
|
||||||
|
|
||||||
$json->id = get_rest_url_by_path( sprintf( 'users/%d/followers', $user->get__id() ) );
|
$json->id = get_rest_url_by_path( sprintf( 'actors/%d/followers', $user->get__id() ) );
|
||||||
$json->generator = 'http://wordpress.org/?v=' . get_masked_wp_version();
|
$json->generator = 'http://wordpress.org/?v=' . get_masked_wp_version();
|
||||||
$json->actor = $user->get_id();
|
$json->actor = $user->get_id();
|
||||||
$json->type = 'OrderedCollectionPage';
|
$json->type = 'OrderedCollectionPage';
|
||||||
|
|
||||||
$json->totalItems = $data['total']; // phpcs:ignore
|
$json->totalItems = $data['total']; // phpcs:ignore
|
||||||
$json->partOf = get_rest_url_by_path( sprintf( 'users/%d/followers', $user->get__id() ) ); // phpcs:ignore
|
$json->partOf = get_rest_url_by_path( sprintf( 'actors/%d/followers', $user->get__id() ) ); // phpcs:ignore
|
||||||
|
|
||||||
$json->first = \add_query_arg( 'page', 1, $json->partOf ); // phpcs:ignore
|
$json->first = \add_query_arg( 'page', 1, $json->partOf ); // phpcs:ignore
|
||||||
$json->last = \add_query_arg( 'page', \ceil ( $json->totalItems / $per_page ), $json->partOf ); // phpcs:ignore
|
$json->last = \add_query_arg( 'page', \ceil ( $json->totalItems / $per_page ), $json->partOf ); // phpcs:ignore
|
||||||
|
@ -31,7 +31,7 @@ class Following {
|
|||||||
public static function register_routes() {
|
public static function register_routes() {
|
||||||
\register_rest_route(
|
\register_rest_route(
|
||||||
ACTIVITYPUB_REST_NAMESPACE,
|
ACTIVITYPUB_REST_NAMESPACE,
|
||||||
'/users/(?P<user_id>[\w\-\.]+)/following',
|
'/(users|actors)/(?P<user_id>[\w\-\.]+)/following',
|
||||||
array(
|
array(
|
||||||
array(
|
array(
|
||||||
'methods' => \WP_REST_Server::READABLE,
|
'methods' => \WP_REST_Server::READABLE,
|
||||||
@ -67,12 +67,12 @@ class Following {
|
|||||||
|
|
||||||
$json->{'@context'} = \Activitypub\get_context();
|
$json->{'@context'} = \Activitypub\get_context();
|
||||||
|
|
||||||
$json->id = get_rest_url_by_path( sprintf( 'users/%d/following', $user->get__id() ) );
|
$json->id = get_rest_url_by_path( sprintf( 'actors/%d/following', $user->get__id() ) );
|
||||||
$json->generator = 'http://wordpress.org/?v=' . get_masked_wp_version();
|
$json->generator = 'http://wordpress.org/?v=' . get_masked_wp_version();
|
||||||
$json->actor = $user->get_id();
|
$json->actor = $user->get_id();
|
||||||
$json->type = 'OrderedCollectionPage';
|
$json->type = 'OrderedCollectionPage';
|
||||||
|
|
||||||
$json->partOf = get_rest_url_by_path( sprintf( 'users/%d/following', $user->get__id() ) ); // phpcs:ignore
|
$json->partOf = get_rest_url_by_path( sprintf( 'actors/%d/following', $user->get__id() ) ); // phpcs:ignore
|
||||||
|
|
||||||
$items = apply_filters( 'activitypub_rest_following', array(), $user ); // phpcs:ignore
|
$items = apply_filters( 'activitypub_rest_following', array(), $user ); // phpcs:ignore
|
||||||
|
|
||||||
|
@ -48,7 +48,7 @@ class Inbox {
|
|||||||
|
|
||||||
\register_rest_route(
|
\register_rest_route(
|
||||||
ACTIVITYPUB_REST_NAMESPACE,
|
ACTIVITYPUB_REST_NAMESPACE,
|
||||||
'/users/(?P<user_id>[\w\-\.]+)/inbox',
|
'/(users|actors)/(?P<user_id>[\w\-\.]+)/inbox',
|
||||||
array(
|
array(
|
||||||
array(
|
array(
|
||||||
'methods' => WP_REST_Server::CREATABLE,
|
'methods' => WP_REST_Server::CREATABLE,
|
||||||
@ -90,10 +90,10 @@ class Inbox {
|
|||||||
$json = new \stdClass();
|
$json = new \stdClass();
|
||||||
|
|
||||||
$json->{'@context'} = get_context();
|
$json->{'@context'} = get_context();
|
||||||
$json->id = get_rest_url_by_path( sprintf( 'users/%d/inbox', $user->get__id() ) );
|
$json->id = get_rest_url_by_path( sprintf( 'actors/%d/inbox', $user->get__id() ) );
|
||||||
$json->generator = 'http://wordpress.org/?v=' . get_masked_wp_version();
|
$json->generator = 'http://wordpress.org/?v=' . get_masked_wp_version();
|
||||||
$json->type = 'OrderedCollectionPage';
|
$json->type = 'OrderedCollectionPage';
|
||||||
$json->partOf = get_rest_url_by_path( sprintf( 'users/%d/inbox', $user->get__id() ) ); // phpcs:ignore
|
$json->partOf = get_rest_url_by_path( sprintf( 'actors/%d/inbox', $user->get__id() ) ); // phpcs:ignore
|
||||||
$json->totalItems = 0; // phpcs:ignore
|
$json->totalItems = 0; // phpcs:ignore
|
||||||
$json->orderedItems = array(); // phpcs:ignore
|
$json->orderedItems = array(); // phpcs:ignore
|
||||||
$json->first = $json->partOf; // phpcs:ignore
|
$json->first = $json->partOf; // phpcs:ignore
|
||||||
|
@ -5,9 +5,9 @@ use stdClass;
|
|||||||
use WP_Error;
|
use WP_Error;
|
||||||
use WP_REST_Server;
|
use WP_REST_Server;
|
||||||
use WP_REST_Response;
|
use WP_REST_Response;
|
||||||
use Activitypub\Transformer\Post;
|
|
||||||
use Activitypub\Activity\Activity;
|
use Activitypub\Activity\Activity;
|
||||||
use Activitypub\Collection\Users as User_Collection;
|
use Activitypub\Collection\Users as User_Collection;
|
||||||
|
use Activitypub\Transformer\Factory;
|
||||||
|
|
||||||
use function Activitypub\get_context;
|
use function Activitypub\get_context;
|
||||||
use function Activitypub\get_rest_url_by_path;
|
use function Activitypub\get_rest_url_by_path;
|
||||||
@ -34,7 +34,7 @@ class Outbox {
|
|||||||
public static function register_routes() {
|
public static function register_routes() {
|
||||||
\register_rest_route(
|
\register_rest_route(
|
||||||
ACTIVITYPUB_REST_NAMESPACE,
|
ACTIVITYPUB_REST_NAMESPACE,
|
||||||
'/users/(?P<user_id>[\w\-\.]+)/outbox',
|
'/(users|actors)/(?P<user_id>[\w\-\.]+)/outbox',
|
||||||
array(
|
array(
|
||||||
array(
|
array(
|
||||||
'methods' => WP_REST_Server::READABLE,
|
'methods' => WP_REST_Server::READABLE,
|
||||||
@ -72,11 +72,11 @@ class Outbox {
|
|||||||
$json = new stdClass();
|
$json = new stdClass();
|
||||||
|
|
||||||
$json->{'@context'} = get_context();
|
$json->{'@context'} = get_context();
|
||||||
$json->id = get_rest_url_by_path( sprintf( 'users/%d/outbox', $user_id ) );
|
$json->id = get_rest_url_by_path( sprintf( 'actors/%d/outbox', $user_id ) );
|
||||||
$json->generator = 'http://wordpress.org/?v=' . get_masked_wp_version();
|
$json->generator = 'http://wordpress.org/?v=' . get_masked_wp_version();
|
||||||
$json->actor = $user->get_id();
|
$json->actor = $user->get_id();
|
||||||
$json->type = 'OrderedCollectionPage';
|
$json->type = 'OrderedCollectionPage';
|
||||||
$json->partOf = get_rest_url_by_path( sprintf( 'users/%d/outbox', $user_id ) ); // phpcs:ignore
|
$json->partOf = get_rest_url_by_path( sprintf( 'actors/%d/outbox', $user_id ) ); // phpcs:ignore
|
||||||
$json->totalItems = 0; // phpcs:ignore
|
$json->totalItems = 0; // phpcs:ignore
|
||||||
|
|
||||||
if ( $user_id > 0 ) {
|
if ( $user_id > 0 ) {
|
||||||
@ -111,7 +111,13 @@ class Outbox {
|
|||||||
);
|
);
|
||||||
|
|
||||||
foreach ( $posts as $post ) {
|
foreach ( $posts as $post ) {
|
||||||
$post = Post::transform( $post )->to_object();
|
$transformer = Factory::get_transformer( $post );
|
||||||
|
|
||||||
|
if ( \is_wp_error( $transformer ) ) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
$post = $transformer->to_object();
|
||||||
$activity = new Activity();
|
$activity = new Activity();
|
||||||
$activity->set_type( 'Create' );
|
$activity->set_type( 'Create' );
|
||||||
$activity->set_object( $post );
|
$activity->set_object( $post );
|
||||||
|
@ -5,7 +5,7 @@ use stdClass;
|
|||||||
use WP_Error;
|
use WP_Error;
|
||||||
use WP_REST_Response;
|
use WP_REST_Response;
|
||||||
use Activitypub\Signature;
|
use Activitypub\Signature;
|
||||||
use Activitypub\Model\Application_User;
|
use Activitypub\Model\Application;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* ActivityPub Server REST-Class
|
* ActivityPub Server REST-Class
|
||||||
@ -47,7 +47,7 @@ class Server {
|
|||||||
* @return WP_REST_Response The JSON profile of the Application Actor.
|
* @return WP_REST_Response The JSON profile of the Application Actor.
|
||||||
*/
|
*/
|
||||||
public static function application_actor() {
|
public static function application_actor() {
|
||||||
$user = new Application_User();
|
$user = new Application();
|
||||||
|
|
||||||
$json = $user->to_array();
|
$json = $user->to_array();
|
||||||
|
|
||||||
@ -62,6 +62,9 @@ class Server {
|
|||||||
*
|
*
|
||||||
* @see WP_REST_Request
|
* @see WP_REST_Request
|
||||||
*
|
*
|
||||||
|
* @see https://www.w3.org/wiki/SocialCG/ActivityPub/Primer/Authentication_Authorization#Authorized_fetch
|
||||||
|
* @see https://swicg.github.io/activitypub-http-signature/#authorized-fetch
|
||||||
|
*
|
||||||
* @param WP_REST_Response|WP_HTTP_Response|WP_Error|mixed $response Result to send to the client.
|
* @param WP_REST_Response|WP_HTTP_Response|WP_Error|mixed $response Result to send to the client.
|
||||||
* Usually a WP_REST_Response or WP_Error.
|
* Usually a WP_REST_Response or WP_Error.
|
||||||
* @param array $handler Route handler used for the request.
|
* @param array $handler Route handler used for the request.
|
||||||
@ -80,7 +83,8 @@ class Server {
|
|||||||
if (
|
if (
|
||||||
! \str_starts_with( $route, '/' . ACTIVITYPUB_REST_NAMESPACE ) ||
|
! \str_starts_with( $route, '/' . ACTIVITYPUB_REST_NAMESPACE ) ||
|
||||||
\str_starts_with( $route, '/' . \trailingslashit( ACTIVITYPUB_REST_NAMESPACE ) . 'webfinger' ) ||
|
\str_starts_with( $route, '/' . \trailingslashit( ACTIVITYPUB_REST_NAMESPACE ) . 'webfinger' ) ||
|
||||||
\str_starts_with( $route, '/' . \trailingslashit( ACTIVITYPUB_REST_NAMESPACE ) . 'nodeinfo' )
|
\str_starts_with( $route, '/' . \trailingslashit( ACTIVITYPUB_REST_NAMESPACE ) . 'nodeinfo' ) ||
|
||||||
|
\str_starts_with( $route, '/' . \trailingslashit( ACTIVITYPUB_REST_NAMESPACE ) . 'application' )
|
||||||
) {
|
) {
|
||||||
return $response;
|
return $response;
|
||||||
}
|
}
|
||||||
@ -102,17 +106,12 @@ class Server {
|
|||||||
return $response;
|
return $response;
|
||||||
}
|
}
|
||||||
|
|
||||||
// POST-Requets are always signed
|
if (
|
||||||
if ( 'GET' !== $request->get_method() ) {
|
// POST-Requests are always signed
|
||||||
$verified_request = Signature::verify_http_signature( $request );
|
'GET' !== $request->get_method() ||
|
||||||
if ( \is_wp_error( $verified_request ) ) {
|
// GET-Requests only require a signature in secure mode
|
||||||
return new WP_Error(
|
( 'GET' === $request->get_method() && ACTIVITYPUB_AUTHORIZED_FETCH )
|
||||||
'activitypub_signature_verification',
|
) {
|
||||||
$verified_request->get_error_message(),
|
|
||||||
array( 'status' => 401 )
|
|
||||||
);
|
|
||||||
}
|
|
||||||
} elseif ( 'GET' === $request->get_method() && ACTIVITYPUB_AUTHORIZED_FETCH ) { // GET-Requests are only signed in secure mode
|
|
||||||
$verified_request = Signature::verify_http_signature( $request );
|
$verified_request = Signature::verify_http_signature( $request );
|
||||||
if ( \is_wp_error( $verified_request ) ) {
|
if ( \is_wp_error( $verified_request ) ) {
|
||||||
return new WP_Error(
|
return new WP_Error(
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
<?php
|
<?php
|
||||||
namespace Activitypub\Transformer;
|
namespace Activitypub\Transformer;
|
||||||
|
|
||||||
|
use WP_Error;
|
||||||
use WP_Post;
|
use WP_Post;
|
||||||
use WP_Comment;
|
use WP_Comment;
|
||||||
|
|
||||||
@ -30,9 +31,9 @@ abstract class Base {
|
|||||||
*
|
*
|
||||||
* @param WP_Post|WP_Comment $wp_object The WordPress object
|
* @param WP_Post|WP_Comment $wp_object The WordPress object
|
||||||
*
|
*
|
||||||
* @return Base_Object
|
* @return Base
|
||||||
*/
|
*/
|
||||||
public static function transform( $object ) {
|
public static function transform( $object ) { // phpcs:ignore Universal.NamingConventions.NoReservedKeywordParameterNames.objectFound
|
||||||
return new static( $object );
|
return new static( $object );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -6,12 +6,13 @@ use WP_Comment_Query;
|
|||||||
|
|
||||||
use Activitypub\Webfinger;
|
use Activitypub\Webfinger;
|
||||||
use Activitypub\Comment as Comment_Utils;
|
use Activitypub\Comment as Comment_Utils;
|
||||||
use Activitypub\Model\Blog_User;
|
use Activitypub\Model\Blog;
|
||||||
use Activitypub\Collection\Users;
|
use Activitypub\Collection\Users;
|
||||||
use Activitypub\Transformer\Base;
|
use Activitypub\Transformer\Base;
|
||||||
|
|
||||||
use function Activitypub\is_single_user;
|
use function Activitypub\is_single_user;
|
||||||
use function Activitypub\get_rest_url_by_path;
|
use function Activitypub\get_rest_url_by_path;
|
||||||
|
use function Activitypub\get_comment_ancestors;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* WordPress Comment Transformer
|
* WordPress Comment Transformer
|
||||||
@ -69,7 +70,7 @@ class Comment extends Base {
|
|||||||
$this->get_locale() => $this->get_content(),
|
$this->get_locale() => $this->get_content(),
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
$path = sprintf( 'users/%d/followers', intval( $comment->comment_author ) );
|
$path = sprintf( 'actors/%d/followers', intval( $comment->comment_author ) );
|
||||||
|
|
||||||
$object->set_to(
|
$object->set_to(
|
||||||
array(
|
array(
|
||||||
@ -90,7 +91,7 @@ class Comment extends Base {
|
|||||||
*/
|
*/
|
||||||
protected function get_attributed_to() {
|
protected function get_attributed_to() {
|
||||||
if ( is_single_user() ) {
|
if ( is_single_user() ) {
|
||||||
$user = new Blog_User();
|
$user = new Blog();
|
||||||
return $user->get_url();
|
return $user->get_url();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -218,6 +219,23 @@ class Comment extends Base {
|
|||||||
return apply_filters( 'activitypub_extract_mentions', array(), $this->wp_object->comment_content, $this->wp_object );
|
return apply_filters( 'activitypub_extract_mentions', array(), $this->wp_object->comment_content, $this->wp_object );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the ancestors of the comment, but only the ones that are ActivityPub comments.
|
||||||
|
*
|
||||||
|
* @return array The list of ancestors.
|
||||||
|
*/
|
||||||
|
protected function get_comment_ancestors() {
|
||||||
|
$ancestors = get_comment_ancestors( $this->wp_object );
|
||||||
|
|
||||||
|
// Now that we have the full tree of ancestors, only return the ones received from the fediverse
|
||||||
|
return array_filter(
|
||||||
|
$ancestors,
|
||||||
|
function ( $comment_id ) {
|
||||||
|
return \get_comment_meta( $comment_id, 'protocol', true ) === 'activitypub';
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Collect all other Users that participated in this comment-thread
|
* Collect all other Users that participated in this comment-thread
|
||||||
* to send them a notification about the new reply.
|
* to send them a notification about the new reply.
|
||||||
@ -232,22 +250,14 @@ class Comment extends Base {
|
|||||||
return $mentions;
|
return $mentions;
|
||||||
}
|
}
|
||||||
|
|
||||||
$comment_query = new WP_Comment_Query(
|
$ancestors = $this->get_comment_ancestors();
|
||||||
array(
|
if ( ! $ancestors ) {
|
||||||
'post_id' => $this->wp_object->comment_post_ID,
|
return $mentions;
|
||||||
// phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_query
|
}
|
||||||
'meta_query' => array(
|
|
||||||
array(
|
|
||||||
'key' => 'protocol',
|
|
||||||
'value' => 'activitypub',
|
|
||||||
),
|
|
||||||
),
|
|
||||||
)
|
|
||||||
);
|
|
||||||
|
|
||||||
if ( $comment_query->comments ) {
|
foreach ( $ancestors as $comment_id ) {
|
||||||
foreach ( $comment_query->comments as $comment ) {
|
$comment = \get_comment( $comment_id );
|
||||||
if ( ! empty( $comment->comment_author_url ) ) {
|
if ( $comment && ! empty( $comment->comment_author_url ) ) {
|
||||||
$acct = Webfinger::uri_to_acct( $comment->comment_author_url );
|
$acct = Webfinger::uri_to_acct( $comment->comment_author_url );
|
||||||
if ( $acct && ! is_wp_error( $acct ) ) {
|
if ( $acct && ! is_wp_error( $acct ) ) {
|
||||||
$acct = str_replace( 'acct:', '@', $acct );
|
$acct = str_replace( 'acct:', '@', $acct );
|
||||||
@ -255,7 +265,6 @@ class Comment extends Base {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
return $mentions;
|
return $mentions;
|
||||||
}
|
}
|
||||||
|
@ -11,7 +11,11 @@ use Activitypub\Transformer\Attachment;
|
|||||||
* Transformer Factory
|
* Transformer Factory
|
||||||
*/
|
*/
|
||||||
class Factory {
|
class Factory {
|
||||||
public static function get_transformer( $object ) {
|
/**
|
||||||
|
* @param mixed $object The object to transform
|
||||||
|
* @return \Activitypub\Transformer|\WP_Error The transformer to use, or an error.
|
||||||
|
*/
|
||||||
|
public static function get_transformer( $object ) { // phpcs:ignore Universal.NamingConventions.NoReservedKeywordParameterNames.objectFound
|
||||||
if ( ! \is_object( $object ) ) {
|
if ( ! \is_object( $object ) ) {
|
||||||
return new WP_Error( 'invalid_object', __( 'Invalid object', 'activitypub' ) );
|
return new WP_Error( 'invalid_object', __( 'Invalid object', 'activitypub' ) );
|
||||||
}
|
}
|
||||||
@ -41,7 +45,7 @@ class Factory {
|
|||||||
* return $transformer;
|
* return $transformer;
|
||||||
* }, 10, 3 );
|
* }, 10, 3 );
|
||||||
*
|
*
|
||||||
* @param Activitypub\Transformer\Base $transformer The transformer to use.
|
* @param Base $transformer The transformer to use.
|
||||||
* @param mixed $object The object to transform.
|
* @param mixed $object The object to transform.
|
||||||
* @param string $object_class The class of the object to transform.
|
* @param string $object_class The class of the object to transform.
|
||||||
*
|
*
|
||||||
|
@ -3,13 +3,13 @@ namespace Activitypub\Transformer;
|
|||||||
|
|
||||||
use WP_Post;
|
use WP_Post;
|
||||||
use Activitypub\Shortcodes;
|
use Activitypub\Shortcodes;
|
||||||
use Activitypub\Model\Blog_User;
|
use Activitypub\Model\Blog;
|
||||||
use Activitypub\Transformer\Base;
|
use Activitypub\Transformer\Base;
|
||||||
use Activitypub\Collection\Users;
|
use Activitypub\Collection\Users;
|
||||||
use Activitypub\Activity\Base_Object;
|
|
||||||
|
|
||||||
use function Activitypub\esc_hashtag;
|
use function Activitypub\esc_hashtag;
|
||||||
use function Activitypub\is_single_user;
|
use function Activitypub\is_single_user;
|
||||||
|
use function Activitypub\get_enclosures;
|
||||||
use function Activitypub\get_rest_url_by_path;
|
use function Activitypub\get_rest_url_by_path;
|
||||||
use function Activitypub\site_supports_blocks;
|
use function Activitypub\site_supports_blocks;
|
||||||
|
|
||||||
@ -70,7 +70,7 @@ class Post extends Base {
|
|||||||
$this->get_locale() => $this->get_content(),
|
$this->get_locale() => $this->get_content(),
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
$path = sprintf( 'users/%d/followers', intval( $post->post_author ) );
|
$path = sprintf( 'actors/%d/followers', intval( $post->post_author ) );
|
||||||
|
|
||||||
$object->set_to(
|
$object->set_to(
|
||||||
array(
|
array(
|
||||||
@ -116,7 +116,7 @@ class Post extends Base {
|
|||||||
* @return string The User-URL.
|
* @return string The User-URL.
|
||||||
*/
|
*/
|
||||||
protected function get_attributed_to() {
|
protected function get_attributed_to() {
|
||||||
$blog_user = new Blog_User();
|
$blog_user = new Blog();
|
||||||
|
|
||||||
if ( is_single_user() ) {
|
if ( is_single_user() ) {
|
||||||
return $blog_user->get_url();
|
return $blog_user->get_url();
|
||||||
@ -139,14 +139,34 @@ class Post extends Base {
|
|||||||
protected function get_attachment() {
|
protected function get_attachment() {
|
||||||
// Once upon a time we only supported images, but we now support audio/video as well.
|
// 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.
|
// 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 ) ) );
|
$max_media = \intval(
|
||||||
|
\apply_filters(
|
||||||
|
'activitypub_max_image_attachments',
|
||||||
|
\get_option( 'activitypub_max_image_attachments', ACTIVITYPUB_MAX_IMAGE_ATTACHMENTS )
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
if ( site_supports_blocks() && \has_blocks( $this->wp_object->post_content ) ) {
|
$media = array(
|
||||||
$media = $this->get_block_attachments( $max_media );
|
'audio' => array(),
|
||||||
} else {
|
'video' => array(),
|
||||||
$media = $this->get_classic_editor_images( $max_media );
|
'image' => array(),
|
||||||
|
);
|
||||||
|
$id = $this->wp_object->ID;
|
||||||
|
|
||||||
|
// list post thumbnail first if this post has one
|
||||||
|
if ( \function_exists( 'has_post_thumbnail' ) && \has_post_thumbnail( $id ) ) {
|
||||||
|
$media['image'][] = array( 'id' => \get_post_thumbnail_id( $id ) );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$media = $this->get_enclosures( $media );
|
||||||
|
|
||||||
|
if ( site_supports_blocks() && \has_blocks( $this->wp_object->post_content ) ) {
|
||||||
|
$media = $this->get_block_attachments( $media, $max_media );
|
||||||
|
} else {
|
||||||
|
$media = $this->get_classic_editor_images( $media, $max_media );
|
||||||
|
}
|
||||||
|
|
||||||
|
$media = self::filter_media_by_object_type( $media, \get_post_format( $this->wp_object ), $this->wp_object );
|
||||||
$unique_ids = \array_unique( \array_column( $media, 'id' ) );
|
$unique_ids = \array_unique( \array_column( $media, 'id' ) );
|
||||||
$media = \array_intersect_key( $media, $unique_ids );
|
$media = \array_intersect_key( $media, $unique_ids );
|
||||||
$media = \array_slice( $media, 0, $max_media );
|
$media = \array_slice( $media, 0, $max_media );
|
||||||
@ -157,35 +177,21 @@ class Post extends Base {
|
|||||||
/**
|
/**
|
||||||
* Get media attachments from blocks. They will be formatted as ActivityPub attachments, not as WP attachments.
|
* Get media attachments from blocks. They will be formatted as ActivityPub attachments, not as WP attachments.
|
||||||
*
|
*
|
||||||
|
* @param array $media The media array grouped by type.
|
||||||
* @param int $max_media The maximum number of attachments to return.
|
* @param int $max_media The maximum number of attachments to return.
|
||||||
*
|
*
|
||||||
* @return array The attachments.
|
* @return array The attachments.
|
||||||
*/
|
*/
|
||||||
protected function get_block_attachments( $max_media ) {
|
protected function get_block_attachments( $media, $max_media ) {
|
||||||
// max media can't be negative or zero
|
// max media can't be negative or zero
|
||||||
if ( $max_media <= 0 ) {
|
if ( $max_media <= 0 ) {
|
||||||
return array();
|
return array();
|
||||||
}
|
}
|
||||||
|
|
||||||
$id = $this->wp_object->ID;
|
|
||||||
|
|
||||||
$media = array(
|
|
||||||
'image' => array(),
|
|
||||||
'audio' => array(),
|
|
||||||
'video' => array(),
|
|
||||||
);
|
|
||||||
|
|
||||||
// list post thumbnail first if this post has one
|
|
||||||
if ( \function_exists( 'has_post_thumbnail' ) && \has_post_thumbnail( $id ) ) {
|
|
||||||
$media['image'][] = array( 'id' => \get_post_thumbnail_id( $id ) );
|
|
||||||
}
|
|
||||||
|
|
||||||
if ( $max_media > 0 ) {
|
|
||||||
$blocks = \parse_blocks( $this->wp_object->post_content );
|
$blocks = \parse_blocks( $this->wp_object->post_content );
|
||||||
$media = self::get_media_from_blocks( $blocks, $media );
|
$media = self::get_media_from_blocks( $blocks, $media );
|
||||||
}
|
|
||||||
|
|
||||||
return self::filter_media_by_object_type( $media, \get_post_format( $this->wp_object ) );
|
return $media;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -202,6 +208,7 @@ class Post extends Base {
|
|||||||
if ( $max_images <= 0 ) {
|
if ( $max_images <= 0 ) {
|
||||||
return array();
|
return array();
|
||||||
}
|
}
|
||||||
|
|
||||||
$images = array();
|
$images = array();
|
||||||
$query = new \WP_Query(
|
$query = new \WP_Query(
|
||||||
array(
|
array(
|
||||||
@ -214,6 +221,7 @@ class Post extends Base {
|
|||||||
'posts_per_page' => $max_images,
|
'posts_per_page' => $max_images,
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
|
||||||
foreach ( $query->get_posts() as $attachment ) {
|
foreach ( $query->get_posts() as $attachment ) {
|
||||||
if ( ! \in_array( $attachment->ID, $images, true ) ) {
|
if ( ! \in_array( $attachment->ID, $images, true ) ) {
|
||||||
$images[] = array( 'id' => $attachment->ID );
|
$images[] = array( 'id' => $attachment->ID );
|
||||||
@ -249,7 +257,7 @@ class Post extends Base {
|
|||||||
// This linter warning is a false positive - we have to
|
// This linter warning is a false positive - we have to
|
||||||
// re-count each time here as we modify $images.
|
// re-count each time here as we modify $images.
|
||||||
// phpcs:ignore Squiz.PHP.DisallowSizeFunctionsInLoops.Found
|
// phpcs:ignore Squiz.PHP.DisallowSizeFunctionsInLoops.Found
|
||||||
while ( $tags->next_tag( 'img' ) && ( \count( $images ) < $max_images ) ) {
|
while ( $tags->next_tag( 'img' ) && ( \count( $images ) <= $max_images ) ) {
|
||||||
$src = $tags->get_attribute( 'src' );
|
$src = $tags->get_attribute( 'src' );
|
||||||
|
|
||||||
// If the img source is in our uploads dir, get the
|
// If the img source is in our uploads dir, get the
|
||||||
@ -292,34 +300,68 @@ class Post extends Base {
|
|||||||
* Get post images from the classic editor.
|
* Get post images from the classic editor.
|
||||||
* Note that audio/video attachments are only supported in the block editor.
|
* Note that audio/video attachments are only supported in the block editor.
|
||||||
*
|
*
|
||||||
|
* @param array $media The media array grouped by type.
|
||||||
* @param int $max_images The maximum number of images to return.
|
* @param int $max_images The maximum number of images to return.
|
||||||
*
|
*
|
||||||
* @return array The attachments.
|
* @return array The attachments.
|
||||||
*/
|
*/
|
||||||
protected function get_classic_editor_images( $max_images ) {
|
protected function get_classic_editor_images( $media, $max_images ) {
|
||||||
// max images can't be negative or zero
|
// max images can't be negative or zero
|
||||||
if ( $max_images <= 0 ) {
|
if ( $max_images <= 0 ) {
|
||||||
return array();
|
return array();
|
||||||
}
|
}
|
||||||
|
|
||||||
$id = $this->wp_object->ID;
|
if ( \count( $media['image'] ) <= $max_images ) {
|
||||||
|
|
||||||
$images = array();
|
|
||||||
|
|
||||||
// list post thumbnail first if this post has one
|
|
||||||
if ( \function_exists( 'has_post_thumbnail' ) && \has_post_thumbnail( $id ) ) {
|
|
||||||
$images[] = \get_post_thumbnail_id( $id );
|
|
||||||
}
|
|
||||||
|
|
||||||
if ( \count( $images ) < $max_images ) {
|
|
||||||
if ( \class_exists( '\WP_HTML_Tag_Processor' ) ) {
|
if ( \class_exists( '\WP_HTML_Tag_Processor' ) ) {
|
||||||
$images = \array_merge( $images, $this->get_classic_editor_image_embeds( $max_images ) );
|
$media['image'] = \array_merge( $media['image'], $this->get_classic_editor_image_embeds( $max_images ) );
|
||||||
} else {
|
} else {
|
||||||
$images = \array_merge( $images, $this->get_classic_editor_image_attachments( $max_images ) );
|
$media['image'] = \array_merge( $media['image'], $this->get_classic_editor_image_attachments( $max_images ) );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return $images;
|
return $media;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get enclosures for a post.
|
||||||
|
*
|
||||||
|
* @param array $media The media array grouped by type.
|
||||||
|
*
|
||||||
|
* @return array The media array extended with enclosures.
|
||||||
|
*/
|
||||||
|
public function get_enclosures( $media ) {
|
||||||
|
$enclosures = get_enclosures( $this->wp_object->ID );
|
||||||
|
|
||||||
|
if ( ! $enclosures ) {
|
||||||
|
return $media;
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach ( $enclosures as $enclosure ) {
|
||||||
|
// check if URL is an attachment
|
||||||
|
$attachment_id = \attachment_url_to_postid( $enclosure['url'] );
|
||||||
|
if ( $attachment_id ) {
|
||||||
|
$enclosure['id'] = $attachment_id;
|
||||||
|
$enclosure['url'] = \wp_get_attachment_url( $attachment_id );
|
||||||
|
$enclosure['mediaType'] = \get_post_mime_type( $attachment_id );
|
||||||
|
}
|
||||||
|
|
||||||
|
$mime_type = $enclosure['mediaType'];
|
||||||
|
$mime_type_parts = \explode( '/', $mime_type );
|
||||||
|
|
||||||
|
switch ( $mime_type_parts[0] ) {
|
||||||
|
case 'image':
|
||||||
|
$media['image'][] = $enclosure;
|
||||||
|
break;
|
||||||
|
case 'audio':
|
||||||
|
$media['audio'][] = $enclosure;
|
||||||
|
break;
|
||||||
|
case 'video':
|
||||||
|
$media['video'][] = $enclosure;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $media;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -331,7 +373,6 @@ class Post extends Base {
|
|||||||
* @return array The image IDs.
|
* @return array The image IDs.
|
||||||
*/
|
*/
|
||||||
protected static function get_media_from_blocks( $blocks, $media ) {
|
protected static function get_media_from_blocks( $blocks, $media ) {
|
||||||
|
|
||||||
foreach ( $blocks as $block ) {
|
foreach ( $blocks as $block ) {
|
||||||
// recurse into inner blocks
|
// recurse into inner blocks
|
||||||
if ( ! empty( $block['innerBlocks'] ) ) {
|
if ( ! empty( $block['innerBlocks'] ) ) {
|
||||||
@ -398,18 +439,18 @@ class Post extends Base {
|
|||||||
* Filter media IDs by object type.
|
* Filter media IDs by object type.
|
||||||
*
|
*
|
||||||
* @param array $media The media array grouped by type.
|
* @param array $media The media array grouped by type.
|
||||||
* @param array $type The object type.
|
* @param string $type The object type.
|
||||||
*
|
*
|
||||||
* @return array The filtered media IDs.
|
* @return array The filtered media IDs.
|
||||||
*/
|
*/
|
||||||
protected static function filter_media_by_object_type( $media, $type ) {
|
protected static function filter_media_by_object_type( $media, $type, $wp_object ) {
|
||||||
$type = \apply_filters( 'filter_media_by_object_type', \strtolower( $type ) );
|
$type = \apply_filters( 'filter_media_by_object_type', \strtolower( $type ), $wp_object );
|
||||||
|
|
||||||
if ( ! empty( $media[ $type ] ) ) {
|
if ( ! empty( $media[ $type ] ) ) {
|
||||||
return $media[ $type ];
|
return $media[ $type ];
|
||||||
}
|
}
|
||||||
|
|
||||||
return array_merge( array(), ...array_values( $media ) );
|
return array_filter( array_merge( array(), ...array_values( $media ) ) );
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -420,6 +461,10 @@ class Post extends Base {
|
|||||||
* @return array The ActivityPub Attachment.
|
* @return array The ActivityPub Attachment.
|
||||||
*/
|
*/
|
||||||
public static function wp_attachment_to_activity_attachment( $media ) {
|
public static function wp_attachment_to_activity_attachment( $media ) {
|
||||||
|
if ( ! isset( $media['id'] ) ) {
|
||||||
|
return $media;
|
||||||
|
}
|
||||||
|
|
||||||
$id = $media['id'];
|
$id = $media['id'];
|
||||||
$attachment = array();
|
$attachment = array();
|
||||||
$mime_type = \get_post_mime_type( $id );
|
$mime_type = \get_post_mime_type( $id );
|
||||||
@ -427,14 +472,14 @@ class Post extends Base {
|
|||||||
// switching on image/audio/video
|
// switching on image/audio/video
|
||||||
switch ( $mime_type_parts[0] ) {
|
switch ( $mime_type_parts[0] ) {
|
||||||
case 'image':
|
case 'image':
|
||||||
$image_size = 'full';
|
$image_size = 'large';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Filter the image URL returned for each post.
|
* Filter the image URL returned for each post.
|
||||||
*
|
*
|
||||||
* @param array|false $thumbnail The image URL, or false if no image is available.
|
* @param array|false $thumbnail The image URL, or false if no image is available.
|
||||||
* @param int $id The attachment ID.
|
* @param int $id The attachment ID.
|
||||||
* @param string $image_size The image size to retrieve. Set to 'full' by default.
|
* @param string $image_size The image size to retrieve. Set to 'large' by default.
|
||||||
*/
|
*/
|
||||||
$thumbnail = apply_filters(
|
$thumbnail = apply_filters(
|
||||||
'activitypub_get_image',
|
'activitypub_get_image',
|
||||||
@ -488,16 +533,16 @@ class Post extends Base {
|
|||||||
* Return details about an image attachment.
|
* Return details about an image attachment.
|
||||||
*
|
*
|
||||||
* @param int $id The attachment ID.
|
* @param int $id The attachment ID.
|
||||||
* @param string $image_size The image size to retrieve. Set to 'full' by default.
|
* @param string $image_size The image size to retrieve. Set to 'large' by default.
|
||||||
*
|
*
|
||||||
* @return array|false Array of image data, or boolean false if no image is available.
|
* @return array|false Array of image data, or boolean false if no image is available.
|
||||||
*/
|
*/
|
||||||
protected static function get_wordpress_attachment( $id, $image_size = 'full' ) {
|
protected static function get_wordpress_attachment( $id, $image_size = 'large' ) {
|
||||||
/**
|
/**
|
||||||
* Hook into the image retrieval process. Before image retrieval.
|
* Hook into the image retrieval process. Before image retrieval.
|
||||||
*
|
*
|
||||||
* @param int $id The attachment ID.
|
* @param int $id The attachment ID.
|
||||||
* @param string $image_size The image size to retrieve. Set to 'full' by default.
|
* @param string $image_size The image size to retrieve. Set to 'large' by default.
|
||||||
*/
|
*/
|
||||||
do_action( 'activitypub_get_image_pre', $id, $image_size );
|
do_action( 'activitypub_get_image_pre', $id, $image_size );
|
||||||
|
|
||||||
@ -507,7 +552,7 @@ class Post extends Base {
|
|||||||
* Hook into the image retrieval process. After image retrieval.
|
* Hook into the image retrieval process. After image retrieval.
|
||||||
*
|
*
|
||||||
* @param int $id The attachment ID.
|
* @param int $id The attachment ID.
|
||||||
* @param string $image_size The image size to retrieve. Set to 'full' by default.
|
* @param string $image_size The image size to retrieve. Set to 'large' by default.
|
||||||
*/
|
*/
|
||||||
do_action( 'activitypub_get_image_post', $id, $image_size );
|
do_action( 'activitypub_get_image_post', $id, $image_size );
|
||||||
|
|
||||||
@ -536,7 +581,7 @@ class Post extends Base {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Default to Article.
|
// Default to Article.
|
||||||
$object_type = 'Article';
|
$object_type = 'Note';
|
||||||
$post_format = 'standard';
|
$post_format = 'standard';
|
||||||
|
|
||||||
if ( \get_theme_support( 'post-formats' ) ) {
|
if ( \get_theme_support( 'post-formats' ) ) {
|
||||||
@ -547,18 +592,12 @@ class Post extends Base {
|
|||||||
switch ( $post_type ) {
|
switch ( $post_type ) {
|
||||||
case 'post':
|
case 'post':
|
||||||
switch ( $post_format ) {
|
switch ( $post_format ) {
|
||||||
case 'aside':
|
case 'standard':
|
||||||
case 'status':
|
case '':
|
||||||
case 'quote':
|
$object_type = 'Article';
|
||||||
case 'note':
|
|
||||||
case 'gallery':
|
|
||||||
case 'image':
|
|
||||||
case 'video':
|
|
||||||
case 'audio':
|
|
||||||
$object_type = 'Note';
|
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
$object_type = 'Article';
|
$object_type = 'Note';
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
@ -566,7 +605,7 @@ class Post extends Base {
|
|||||||
$object_type = 'Page';
|
$object_type = 'Page';
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
$object_type = 'Article';
|
$object_type = 'Note';
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -593,6 +632,16 @@ class Post extends Base {
|
|||||||
return $cc;
|
return $cc;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public function get_audience() {
|
||||||
|
if ( is_single_user() ) {
|
||||||
|
return null;
|
||||||
|
} else {
|
||||||
|
$blog = new Blog();
|
||||||
|
return $blog->get_id();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns a list of Tags, used in the Post.
|
* Returns a list of Tags, used in the Post.
|
||||||
*
|
*
|
||||||
@ -708,6 +757,8 @@ class Post extends Base {
|
|||||||
*/
|
*/
|
||||||
do_action( 'activitypub_before_get_content', $post );
|
do_action( 'activitypub_before_get_content', $post );
|
||||||
|
|
||||||
|
add_filter( 'render_block_core/embed', array( self::class, 'revert_embed_links' ), 10, 2 );
|
||||||
|
|
||||||
// phpcs:ignore WordPress.WP.GlobalVariablesOverride.Prohibited
|
// phpcs:ignore WordPress.WP.GlobalVariablesOverride.Prohibited
|
||||||
$post = $this->wp_object;
|
$post = $this->wp_object;
|
||||||
$content = $this->get_post_content_template();
|
$content = $this->get_post_content_template();
|
||||||
@ -792,4 +843,21 @@ class Post extends Base {
|
|||||||
*/
|
*/
|
||||||
return apply_filters( 'activitypub_post_locale', $lang, $post_id, $this->wp_object );
|
return apply_filters( 'activitypub_post_locale', $lang, $post_id, $this->wp_object );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Transform Embed blocks to block level link.
|
||||||
|
*
|
||||||
|
* Remote servers will simply drop iframe elements, rendering incomplete content.
|
||||||
|
*
|
||||||
|
* @see https://www.w3.org/TR/activitypub/#security-sanitizing-content
|
||||||
|
* @see https://www.w3.org/wiki/ActivityPub/Primer/HTML
|
||||||
|
*
|
||||||
|
* @param string $block_content The block content (html)
|
||||||
|
* @param object $block The block object
|
||||||
|
*
|
||||||
|
* @return string A block level link
|
||||||
|
*/
|
||||||
|
public static function revert_embed_links( $block_content, $block ) {
|
||||||
|
return '<p><a href="' . esc_url( $block['attrs']['url'] ) . '">' . $block['attrs']['url'] . '</a></p>';
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -28,10 +28,12 @@ class Enable_Mastodon_Apps {
|
|||||||
public static function init() {
|
public static function init() {
|
||||||
\add_filter( 'mastodon_api_account_followers', array( self::class, 'api_account_followers' ), 10, 2 );
|
\add_filter( 'mastodon_api_account_followers', array( self::class, 'api_account_followers' ), 10, 2 );
|
||||||
\add_filter( 'mastodon_api_account', array( self::class, 'api_account_add_followers' ), 20, 2 );
|
\add_filter( 'mastodon_api_account', array( self::class, 'api_account_add_followers' ), 20, 2 );
|
||||||
\add_filter( 'mastodon_api_account', array( self::class, 'api_account_external' ), 10, 2 );
|
\add_filter( 'mastodon_api_account', array( self::class, 'api_account_external' ), 15, 2 );
|
||||||
\add_filter( 'mastodon_api_search', array( self::class, 'api_search' ), 40, 2 );
|
\add_filter( 'mastodon_api_search', array( self::class, 'api_search' ), 40, 2 );
|
||||||
|
\add_filter( 'mastodon_api_search', array( self::class, 'api_search_by_url' ), 40, 2 );
|
||||||
\add_filter( 'mastodon_api_get_posts_query_args', array( self::class, 'api_get_posts_query_args' ) );
|
\add_filter( 'mastodon_api_get_posts_query_args', array( self::class, 'api_get_posts_query_args' ) );
|
||||||
\add_filter( 'mastodon_api_statuses', array( self::class, 'api_statuses_external' ), 10, 2 );
|
\add_filter( 'mastodon_api_statuses', array( self::class, 'api_statuses_external' ), 10, 2 );
|
||||||
|
\add_filter( 'mastodon_api_status_context', array( self::class, 'api_get_replies' ), 10, 23 );
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -102,11 +104,11 @@ class Enable_Mastodon_Apps {
|
|||||||
* @return Enable_Mastodon_Apps\Entity\Account The filtered Account
|
* @return Enable_Mastodon_Apps\Entity\Account The filtered Account
|
||||||
*/
|
*/
|
||||||
public static function api_account_add_followers( $account, $user_id ) {
|
public static function api_account_add_followers( $account, $user_id ) {
|
||||||
if ( ! $account instanceof Account || ! is_numeric( $user_id ) ) {
|
if ( ! $account instanceof Account ) {
|
||||||
return $account;
|
return $account;
|
||||||
}
|
}
|
||||||
|
|
||||||
$user = Users::get_by_id( $user_id );
|
$user = Users::get_by_various( $user_id );
|
||||||
|
|
||||||
if ( ! $user || is_wp_error( $user ) ) {
|
if ( ! $user || is_wp_error( $user ) ) {
|
||||||
return $account;
|
return $account;
|
||||||
@ -130,7 +132,7 @@ class Enable_Mastodon_Apps {
|
|||||||
$account->acct = $user->get_preferred_username();
|
$account->acct = $user->get_preferred_username();
|
||||||
$account->note = $user->get_summary();
|
$account->note = $user->get_summary();
|
||||||
|
|
||||||
$account->followers_count = Followers::count_followers( $user_id );
|
$account->followers_count = Followers::count_followers( $user->get__id() );
|
||||||
return $account;
|
return $account;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -143,7 +145,7 @@ class Enable_Mastodon_Apps {
|
|||||||
* @return Enable_Mastodon_Apps\Entity\Account The filtered Account
|
* @return Enable_Mastodon_Apps\Entity\Account The filtered Account
|
||||||
*/
|
*/
|
||||||
public static function api_account_external( $user_data, $user_id ) {
|
public static function api_account_external( $user_data, $user_id ) {
|
||||||
if ( $user_data || is_numeric( $user_id ) ) {
|
if ( $user_data || ( is_numeric( $user_id ) && $user_id ) ) {
|
||||||
// Only augment.
|
// Only augment.
|
||||||
return $user_data;
|
return $user_data;
|
||||||
}
|
}
|
||||||
@ -201,12 +203,38 @@ class Enable_Mastodon_Apps {
|
|||||||
$account->header = $data['image']['url'];
|
$account->header = $data['image']['url'];
|
||||||
$account->header_static = $data['image']['url'];
|
$account->header_static = $data['image']['url'];
|
||||||
}
|
}
|
||||||
|
if ( ! isset( $data['published'] ) ) {
|
||||||
|
$data['published'] = 'now';
|
||||||
|
}
|
||||||
$account->created_at = new DateTime( $data['published'] );
|
$account->created_at = new DateTime( $data['published'] );
|
||||||
|
|
||||||
return $account;
|
return $account;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static function api_search_by_url( $search_data, $request ) {
|
||||||
|
$p = \wp_parse_url( $request->get_param( 'q' ) );
|
||||||
|
if ( ! $p || ! isset( $p['host'] ) ) {
|
||||||
|
return $search_data;
|
||||||
|
}
|
||||||
|
|
||||||
|
$object = Http::get_remote_object( $request->get_param( 'q' ), true );
|
||||||
|
if ( is_wp_error( $object ) || ! isset( $object['attributedTo'] ) ) {
|
||||||
|
return $search_data;
|
||||||
|
}
|
||||||
|
|
||||||
|
$account = self::get_account_for_actor( $object['attributedTo'] );
|
||||||
|
if ( ! $account ) {
|
||||||
|
return $search_data;
|
||||||
|
}
|
||||||
|
|
||||||
|
$status = self::activity_to_status( $object, $account );
|
||||||
|
if ( $status ) {
|
||||||
|
$search_data['statuses'][] = $status;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $search_data;
|
||||||
|
}
|
||||||
|
|
||||||
public static function api_search( $search_data, $request ) {
|
public static function api_search( $search_data, $request ) {
|
||||||
$user_id = \get_current_user_id();
|
$user_id = \get_current_user_id();
|
||||||
if ( ! $user_id ) {
|
if ( ! $user_id ) {
|
||||||
@ -254,7 +282,7 @@ class Enable_Mastodon_Apps {
|
|||||||
return $search_data;
|
return $search_data;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function api_get_posts_query_args( $args ) {
|
public static function api_get_posts_query_args( $args ) {
|
||||||
if ( isset( $args['author'] ) && is_string( $args['author'] ) ) {
|
if ( isset( $args['author'] ) && is_string( $args['author'] ) ) {
|
||||||
$uri = Webfinger_Util::resolve( $args['author'] );
|
$uri = Webfinger_Util::resolve( $args['author'] );
|
||||||
if ( $uri && ! is_wp_error( $uri ) ) {
|
if ( $uri && ! is_wp_error( $uri ) ) {
|
||||||
@ -266,47 +294,19 @@ class Enable_Mastodon_Apps {
|
|||||||
return $args;
|
return $args;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static function api_statuses_external( $statuses, $args ) {
|
private static function activity_to_status( $item, $account ) {
|
||||||
if ( ! isset( $args['activitypub'] ) ) {
|
if ( isset( $item['object'] ) ) {
|
||||||
return $statuses;
|
|
||||||
}
|
|
||||||
|
|
||||||
$data = get_remote_metadata_by_actor( $args['activitypub'] );
|
|
||||||
|
|
||||||
if ( ! $data || is_wp_error( $data ) || ! isset( $data['outbox'] ) ) {
|
|
||||||
return $statuses;
|
|
||||||
}
|
|
||||||
|
|
||||||
$response = Http::get( $data['outbox'], true );
|
|
||||||
if ( is_wp_error( $response ) || wp_remote_retrieve_response_code( $response ) !== 200 ) {
|
|
||||||
return $statuses;
|
|
||||||
}
|
|
||||||
|
|
||||||
$outbox = json_decode( wp_remote_retrieve_body( $response ), true );
|
|
||||||
if ( ! $outbox || is_wp_error( $outbox ) || ! isset( $outbox['first'] ) ) {
|
|
||||||
return $statuses;
|
|
||||||
}
|
|
||||||
|
|
||||||
$account = self::get_account_for_actor( $args['activitypub'] );
|
|
||||||
if ( ! $account ) {
|
|
||||||
return $statuses;
|
|
||||||
}
|
|
||||||
|
|
||||||
$response = Http::get( $outbox['first'], true );
|
|
||||||
if ( is_wp_error( $response ) || wp_remote_retrieve_response_code( $response ) !== 200 ) {
|
|
||||||
return $statuses;
|
|
||||||
}
|
|
||||||
$posts = json_decode( wp_remote_retrieve_body( $response ), true );
|
|
||||||
|
|
||||||
$activitypub_statuses = array_map(
|
|
||||||
function ( $item ) use ( $account ) {
|
|
||||||
$object = $item['object'];
|
$object = $item['object'];
|
||||||
|
} else {
|
||||||
|
$object = $item;
|
||||||
|
}
|
||||||
|
|
||||||
if ( ! isset( $object['type'] ) || 'Note' !== $object['type'] ) {
|
if ( ! isset( $object['type'] ) || 'Note' !== $object['type'] ) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
$status = new Status();
|
$status = new Status();
|
||||||
$status->id = Mastodon_API::remap_url( $object['id'] );
|
$status->id = $object['id'];
|
||||||
$status->created_at = new DateTime( $object['published'] );
|
$status->created_at = new DateTime( $object['published'] );
|
||||||
$status->content = $object['content'];
|
$status->content = $object['content'];
|
||||||
$status->account = $account;
|
$status->account = $account;
|
||||||
@ -318,8 +318,12 @@ class Enable_Mastodon_Apps {
|
|||||||
if ( ! empty( $object['visibility'] ) ) {
|
if ( ! empty( $object['visibility'] ) ) {
|
||||||
$status->visibility = $object['visibility'];
|
$status->visibility = $object['visibility'];
|
||||||
}
|
}
|
||||||
|
if ( ! empty( $object['url'] ) ) {
|
||||||
|
$status->url = $object['url'];
|
||||||
$status->uri = $object['url'];
|
$status->uri = $object['url'];
|
||||||
|
} else {
|
||||||
|
$status->uri = $object['id'];
|
||||||
|
}
|
||||||
|
|
||||||
if ( ! empty( $object['attachment'] ) ) {
|
if ( ! empty( $object['attachment'] ) ) {
|
||||||
$status->media_attachments = array_map(
|
$status->media_attachments = array_map(
|
||||||
@ -336,12 +340,15 @@ class Enable_Mastodon_Apps {
|
|||||||
$attachment = array_merge( $default_attachment, $attachment );
|
$attachment = array_merge( $default_attachment, $attachment );
|
||||||
|
|
||||||
$media_attachment = new Media_Attachment();
|
$media_attachment = new Media_Attachment();
|
||||||
$media_attachment->id = Mastodon_API::remap_url( $attachment['url'], $attachment );
|
$media_attachment->id = $attachment['url'];
|
||||||
$media_attachment->type = strtok( $attachment['mediaType'], '/' );
|
$media_attachment->type = strtok( $attachment['mediaType'], '/' );
|
||||||
$media_attachment->url = $attachment['url'];
|
$media_attachment->url = $attachment['url'];
|
||||||
$media_attachment->preview_url = $attachment['url'];
|
$media_attachment->preview_url = $attachment['url'];
|
||||||
$media_attachment->description = $attachment['name'];
|
$media_attachment->description = $attachment['name'];
|
||||||
|
if ( $attachment['blurhash'] ) {
|
||||||
$media_attachment->blurhash = $attachment['blurhash'];
|
$media_attachment->blurhash = $attachment['blurhash'];
|
||||||
|
}
|
||||||
|
if ( $attachment['width'] > 0 && $attachment['height'] > 0 ) {
|
||||||
$media_attachment->meta = array(
|
$media_attachment->meta = array(
|
||||||
'original' => array(
|
'original' => array(
|
||||||
'width' => $attachment['width'],
|
'width' => $attachment['width'],
|
||||||
@ -349,7 +356,7 @@ class Enable_Mastodon_Apps {
|
|||||||
'size' => $attachment['width'] . 'x' . $attachment['height'],
|
'size' => $attachment['width'] . 'x' . $attachment['height'],
|
||||||
'aspect' => $attachment['width'] / $attachment['height'],
|
'aspect' => $attachment['width'] / $attachment['height'],
|
||||||
),
|
),
|
||||||
);
|
);}
|
||||||
return $media_attachment;
|
return $media_attachment;
|
||||||
},
|
},
|
||||||
$object['attachment']
|
$object['attachment']
|
||||||
@ -357,10 +364,99 @@ class Enable_Mastodon_Apps {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return $status;
|
return $status;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function api_statuses_external( $statuses, $args ) {
|
||||||
|
if ( ! isset( $args['activitypub'] ) ) {
|
||||||
|
return $statuses;
|
||||||
|
}
|
||||||
|
|
||||||
|
$data = get_remote_metadata_by_actor( $args['activitypub'] );
|
||||||
|
|
||||||
|
if ( ! $data || is_wp_error( $data ) || ! isset( $data['outbox'] ) ) {
|
||||||
|
return $statuses;
|
||||||
|
}
|
||||||
|
|
||||||
|
$outbox = Http::get_remote_object( $data['outbox'], true );
|
||||||
|
if ( is_wp_error( $outbox ) || ! isset( $outbox['first'] ) ) {
|
||||||
|
return $statuses;
|
||||||
|
}
|
||||||
|
|
||||||
|
$account = self::get_account_for_actor( $args['activitypub'] );
|
||||||
|
if ( ! $account ) {
|
||||||
|
return $statuses;
|
||||||
|
}
|
||||||
|
$limit = 10;
|
||||||
|
if ( isset( $args['posts_per_page'] ) ) {
|
||||||
|
$limit = $args['posts_per_page'];
|
||||||
|
}
|
||||||
|
if ( $limit > 40 ) {
|
||||||
|
$limit = 40;
|
||||||
|
}
|
||||||
|
$activitypub_statuses = array();
|
||||||
|
$url = $outbox['first'];
|
||||||
|
$tries = 0;
|
||||||
|
while ( $url ) {
|
||||||
|
if ( ++$tries > 3 ) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
$posts = Http::get_remote_object( $url, true );
|
||||||
|
if ( is_wp_error( $posts ) ) {
|
||||||
|
return $statuses;
|
||||||
|
}
|
||||||
|
|
||||||
|
$new_statuses = array_map(
|
||||||
|
function ( $item ) use ( $account, $args ) {
|
||||||
|
if ( $args['exclude_replies'] ) {
|
||||||
|
if ( isset( $item['object']['inReplyTo'] ) && $item['object']['inReplyTo'] ) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return self::activity_to_status( $item, $account );
|
||||||
},
|
},
|
||||||
$posts['orderedItems']
|
$posts['orderedItems']
|
||||||
);
|
);
|
||||||
|
$activitypub_statuses = array_merge( $activitypub_statuses, array_filter( $new_statuses ) );
|
||||||
|
$url = $posts['next'];
|
||||||
|
|
||||||
return $activitypub_statuses;
|
if ( count( $activitypub_statuses ) >= $limit ) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return array_slice( $activitypub_statuses, 0, $limit );
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function api_get_replies( $context, $post_id, $url ) {
|
||||||
|
$meta = Http::get_remote_object( $url, true );
|
||||||
|
if ( is_wp_error( $meta ) || ! isset( $meta['replies']['first']['next'] ) ) {
|
||||||
|
return $context;
|
||||||
|
}
|
||||||
|
|
||||||
|
$replies_url = $meta['replies']['first']['next'];
|
||||||
|
$replies = Http::get_remote_object( $replies_url, true );
|
||||||
|
if ( is_wp_error( $replies ) || ! isset( $replies['items'] ) ) {
|
||||||
|
return $context;
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach ( $replies['items'] as $url ) {
|
||||||
|
$response = Http::get( $url, true );
|
||||||
|
if ( is_wp_error( $response ) || wp_remote_retrieve_response_code( $response ) !== 200 ) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
$status = json_decode( wp_remote_retrieve_body( $response ), true );
|
||||||
|
if ( ! $status || is_wp_error( $status ) ) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
$account = self::get_account_for_actor( $status['attributedTo'] );
|
||||||
|
$status = self::activity_to_status( $status, $account );
|
||||||
|
if ( $status ) {
|
||||||
|
$context['descendants'][ $status->id ] = $status;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $context;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
21
wp-content/plugins/activitypub/integration/class-jetpack.php
Normal file
21
wp-content/plugins/activitypub/integration/class-jetpack.php
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
<?php
|
||||||
|
namespace Activitypub\Integration;
|
||||||
|
|
||||||
|
class Jetpack {
|
||||||
|
|
||||||
|
public static function init() {
|
||||||
|
\add_filter( 'jetpack_sync_post_meta_whitelist', [ __CLASS__, 'add_sync_meta' ] );
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function add_sync_meta( $whitelist ) {
|
||||||
|
if ( ! is_array( $whitelist ) ) {
|
||||||
|
return $whitelist;
|
||||||
|
}
|
||||||
|
$activitypub_meta_keys = [
|
||||||
|
'activitypub_user_id',
|
||||||
|
'activitypub_inbox',
|
||||||
|
'activitypub_actor_json',
|
||||||
|
];
|
||||||
|
return \array_merge( $whitelist, $activitypub_meta_keys );
|
||||||
|
}
|
||||||
|
}
|
@ -3,7 +3,7 @@ Contributors: automattic, pfefferle, mediaformat, mattwiebe, akirk, jeherve, nur
|
|||||||
Tags: OStatus, fediverse, activitypub, activitystream
|
Tags: OStatus, fediverse, activitypub, activitystream
|
||||||
Requires at least: 5.5
|
Requires at least: 5.5
|
||||||
Tested up to: 6.5
|
Tested up to: 6.5
|
||||||
Stable tag: 2.3.1
|
Stable tag: 2.4.0
|
||||||
Requires PHP: 5.6
|
Requires PHP: 5.6
|
||||||
License: MIT
|
License: MIT
|
||||||
License URI: http://opensource.org/licenses/MIT
|
License URI: http://opensource.org/licenses/MIT
|
||||||
@ -133,6 +133,32 @@ For reasons of data protection, it is not possible to see the followers of other
|
|||||||
|
|
||||||
== Changelog ==
|
== Changelog ==
|
||||||
|
|
||||||
|
= 2.4.0 =
|
||||||
|
|
||||||
|
* Added: A core/embed block filter to transform iframes to links
|
||||||
|
* Added: Basic support of incoming `Announce`s
|
||||||
|
* Added: Improve attachment handling
|
||||||
|
* Added: Notifications: Introduce general class and use it for new follows
|
||||||
|
* Added: Always fall back to `get_by_username` if one of the above fail
|
||||||
|
* Added: Notification support for Jetpack
|
||||||
|
* Added: EMA: Support for fetching external statuses without replies
|
||||||
|
* Added: EMA: Remote context
|
||||||
|
* Added: EMA: Allow searching for URLs
|
||||||
|
* Added: EMA: Ensuring numeric ids is now done in EMA directly
|
||||||
|
* Added: Podcast support
|
||||||
|
* Added: Follower count to "At a Glance" dashboard widget
|
||||||
|
* Improved: Use `Note` as default Object-Type, instead of `Article`
|
||||||
|
* Improved: Improve `AUTHORIZED_FETCH`
|
||||||
|
* Improved: Only send Mentions to comments in the direct hierarchy
|
||||||
|
* Improved: Improve transformer
|
||||||
|
* Improved: Improve Lemmy compatibility
|
||||||
|
* Improved: Updated JS dependencies
|
||||||
|
* Fixed: EMA: Add missing static keyword and try to lookup if the id is 0
|
||||||
|
* Fixed: Blog-wide account when WordPress is in subdirectory
|
||||||
|
* Fixed: Funkwhale URLs
|
||||||
|
* Fixed: Prevent infinite loops in `get_comment_ancestors`
|
||||||
|
* Fixed: Better Content-Negotiation handling
|
||||||
|
|
||||||
= 2.3.1 =
|
= 2.3.1 =
|
||||||
|
|
||||||
* Added: Enable Mastodon Apps: Add remote outbox fetching
|
* Added: Enable Mastodon Apps: Add remote outbox fetching
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
<?php
|
<?php
|
||||||
$user = new \Activitypub\Model\Blog_User();
|
$user = new \Activitypub\Model\Blog();
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Action triggerd prior to the ActivityPub profile being created and sent to the client
|
* Action triggerd prior to the ActivityPub profile being created and sent to the client
|
||||||
|
@ -1,36 +1,23 @@
|
|||||||
<?php
|
<?php
|
||||||
$comment = \get_comment( \get_query_var( 'c', null ) ); // phpcs:ignore
|
$comment = \get_comment( \get_query_var( 'c', null ) ); // phpcs:ignore
|
||||||
|
$transformer = \Activitypub\Transformer\Factory::get_transformer( $comment );
|
||||||
|
|
||||||
$object = \Activitypub\Transformer\Factory::get_transformer( $comment );
|
if ( \is_wp_error( $transformer ) ) {
|
||||||
$json = \array_merge( array( '@context' => \Activitypub\get_context() ), $object->to_object()->to_array() );
|
\wp_die(
|
||||||
|
\esc_html( $transformer->get_error_message() ),
|
||||||
// filter output
|
404
|
||||||
$json = \apply_filters( 'activitypub_json_comment_array', $json );
|
);
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Action triggerd prior to the ActivityPub profile being created and sent to the client
|
* Action triggerd prior to the ActivityPub profile being created and sent to the client
|
||||||
*/
|
*/
|
||||||
\do_action( 'activitypub_json_comment_pre' );
|
\do_action( 'activitypub_json_comment_pre' );
|
||||||
|
|
||||||
$options = 0;
|
|
||||||
// JSON_PRETTY_PRINT added in PHP 5.4
|
|
||||||
if ( \get_query_var( 'pretty' ) ) {
|
|
||||||
$options |= \JSON_PRETTY_PRINT; // phpcs:ignore
|
|
||||||
}
|
|
||||||
|
|
||||||
$options |= \JSON_HEX_TAG | \JSON_HEX_AMP | \JSON_HEX_QUOT;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Options to be passed to json_encode()
|
|
||||||
*
|
|
||||||
* @param int $options The current options flags
|
|
||||||
*/
|
|
||||||
$options = \apply_filters( 'activitypub_json_comment_options', $options );
|
|
||||||
|
|
||||||
\header( 'Content-Type: application/activity+json' );
|
\header( 'Content-Type: application/activity+json' );
|
||||||
echo \wp_json_encode( $json, $options );
|
echo $transformer->to_object()->to_json(); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Action triggerd after the ActivityPub profile has been created and sent to the client
|
* Action triggerd after the ActivityPub profile has been created and sent to the client
|
||||||
*/
|
*/
|
||||||
\do_action( 'activitypub_json_comment_comment' );
|
\do_action( 'activitypub_json_comment_post' );
|
||||||
|
@ -1,8 +1,15 @@
|
|||||||
<?php
|
<?php
|
||||||
// phpcs:ignore WordPress.WP.GlobalVariablesOverride.Prohibited
|
// phpcs:ignore WordPress.WP.GlobalVariablesOverride.Prohibited
|
||||||
$post = \get_post();
|
$post = \get_post();
|
||||||
|
$transformer = \Activitypub\Transformer\Factory::get_transformer( $post );
|
||||||
|
|
||||||
|
if ( \is_wp_error( $transformer ) ) {
|
||||||
|
\wp_die(
|
||||||
|
esc_html( $transformer->get_error_message() ),
|
||||||
|
404
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
$post_object = \Activitypub\Transformer\Factory::get_transformer( $post )->to_object();
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Action triggerd prior to the ActivityPub profile being created and sent to the client
|
* Action triggerd prior to the ActivityPub profile being created and sent to the client
|
||||||
@ -10,7 +17,7 @@ $post_object = \Activitypub\Transformer\Factory::get_transformer( $post )->to_ob
|
|||||||
\do_action( 'activitypub_json_post_pre' );
|
\do_action( 'activitypub_json_post_pre' );
|
||||||
|
|
||||||
\header( 'Content-Type: application/activity+json' );
|
\header( 'Content-Type: application/activity+json' );
|
||||||
echo $post_object->to_json(); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
|
echo $transformer->to_object()->to_json(); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Action triggerd after the ActivityPub profile has been created and sent to the client
|
* Action triggerd after the ActivityPub profile has been created and sent to the client
|
||||||
|
@ -52,7 +52,7 @@
|
|||||||
</th>
|
</th>
|
||||||
<td>
|
<td>
|
||||||
<label for="activitypub_blog_user_identifier">
|
<label for="activitypub_blog_user_identifier">
|
||||||
<input class="blog-user-identifier" name="activitypub_blog_user_identifier" id="activitypub_blog_user_identifier" type="text" value="<?php echo esc_attr( \get_option( 'activitypub_blog_user_identifier', \Activitypub\Model\Blog_User::get_default_username() ) ); ?>" />
|
<input class="blog-user-identifier" name="activitypub_blog_user_identifier" id="activitypub_blog_user_identifier" type="text" value="<?php echo esc_attr( \get_option( 'activitypub_blog_user_identifier', \Activitypub\Model\Blog::get_default_username() ) ); ?>" />
|
||||||
@<?php echo esc_html( \wp_parse_url( \home_url(), PHP_URL_HOST ) ); ?>
|
@<?php echo esc_html( \wp_parse_url( \home_url(), PHP_URL_HOST ) ); ?>
|
||||||
</label>
|
</label>
|
||||||
<p class="description">
|
<p class="description">
|
||||||
|
@ -19,7 +19,7 @@
|
|||||||
|
|
||||||
<?php
|
<?php
|
||||||
if ( ! \Activitypub\is_user_disabled( \Activitypub\Collection\Users::BLOG_USER_ID ) ) :
|
if ( ! \Activitypub\is_user_disabled( \Activitypub\Collection\Users::BLOG_USER_ID ) ) :
|
||||||
$blog_user = new \Activitypub\Model\Blog_User();
|
$blog_user = new \Activitypub\Model\Blog();
|
||||||
?>
|
?>
|
||||||
<div class="box">
|
<div class="box">
|
||||||
<h3><?php \esc_html_e( 'Blog profile', 'activitypub' ); ?></h3>
|
<h3><?php \esc_html_e( 'Blog profile', 'activitypub' ); ?></h3>
|
||||||
|
Loading…
Reference in New Issue
Block a user