updated plugin ActivityPub
version 2.2.0
This commit is contained in:
parent
fd53533f59
commit
00c5db12cc
@ -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.0.1
|
* Version: 2.2.0
|
||||||
* Author: Matthias Pfefferle & Automattic
|
* Author: Matthias Pfefferle & Automattic
|
||||||
* Author URI: https://automattic.com/
|
* Author URI: https://automattic.com/
|
||||||
* License: MIT
|
* License: MIT
|
||||||
@ -33,7 +33,10 @@ require_once __DIR__ . '/includes/functions.php';
|
|||||||
\defined( 'ACTIVITYPUB_CUSTOM_POST_CONTENT' ) || \define( 'ACTIVITYPUB_CUSTOM_POST_CONTENT', "<strong>[ap_title]</strong>\n\n[ap_content]\n\n[ap_hashtags]\n\n[ap_shortlink]" );
|
\defined( 'ACTIVITYPUB_CUSTOM_POST_CONTENT' ) || \define( 'ACTIVITYPUB_CUSTOM_POST_CONTENT', "<strong>[ap_title]</strong>\n\n[ap_content]\n\n[ap_hashtags]\n\n[ap_shortlink]" );
|
||||||
\defined( 'ACTIVITYPUB_AUTHORIZED_FETCH' ) || \define( 'ACTIVITYPUB_AUTHORIZED_FETCH', false );
|
\defined( 'ACTIVITYPUB_AUTHORIZED_FETCH' ) || \define( 'ACTIVITYPUB_AUTHORIZED_FETCH', false );
|
||||||
\defined( 'ACTIVITYPUB_DISABLE_REWRITES' ) || \define( 'ACTIVITYPUB_DISABLE_REWRITES', false );
|
\defined( 'ACTIVITYPUB_DISABLE_REWRITES' ) || \define( 'ACTIVITYPUB_DISABLE_REWRITES', false );
|
||||||
|
\defined( 'ACTIVITYPUB_DISABLE_INCOMING_INTERACTIONS' ) || \define( 'ACTIVITYPUB_DISABLE_INCOMING_INTERACTIONS', false );
|
||||||
|
\defined( 'ACTIVITYPUB_DISABLE_OUTGOING_INTERACTIONS' ) || \define( 'ACTIVITYPUB_DISABLE_OUTGOING_INTERACTIONS', false );
|
||||||
\defined( 'ACTIVITYPUB_SHARED_INBOX_FEATURE' ) || \define( 'ACTIVITYPUB_SHARED_INBOX_FEATURE', false );
|
\defined( 'ACTIVITYPUB_SHARED_INBOX_FEATURE' ) || \define( 'ACTIVITYPUB_SHARED_INBOX_FEATURE', false );
|
||||||
|
\defined( 'ACTIVITYPUB_SEND_VARY_HEADER' ) || \define( 'ACTIVITYPUB_SEND_VARY_HEADER', false );
|
||||||
|
|
||||||
\define( 'ACTIVITYPUB_PLUGIN_DIR', plugin_dir_path( __FILE__ ) );
|
\define( 'ACTIVITYPUB_PLUGIN_DIR', plugin_dir_path( __FILE__ ) );
|
||||||
\define( 'ACTIVITYPUB_PLUGIN_BASENAME', plugin_basename( __FILE__ ) );
|
\define( 'ACTIVITYPUB_PLUGIN_BASENAME', plugin_basename( __FILE__ ) );
|
||||||
@ -50,6 +53,7 @@ function rest_init() {
|
|||||||
Rest\Followers::init();
|
Rest\Followers::init();
|
||||||
Rest\Following::init();
|
Rest\Following::init();
|
||||||
Rest\Webfinger::init();
|
Rest\Webfinger::init();
|
||||||
|
Rest\Comment::init();
|
||||||
Rest\Server::init();
|
Rest\Server::init();
|
||||||
Rest\Collection::init();
|
Rest\Collection::init();
|
||||||
|
|
||||||
@ -73,6 +77,7 @@ function plugin_init() {
|
|||||||
\add_action( 'init', array( __NAMESPACE__ . '\Mention', 'init' ) );
|
\add_action( 'init', array( __NAMESPACE__ . '\Mention', 'init' ) );
|
||||||
\add_action( 'init', array( __NAMESPACE__ . '\Health_Check', 'init' ) );
|
\add_action( 'init', array( __NAMESPACE__ . '\Health_Check', 'init' ) );
|
||||||
\add_action( 'init', array( __NAMESPACE__ . '\Scheduler', 'init' ) );
|
\add_action( 'init', array( __NAMESPACE__ . '\Scheduler', 'init' ) );
|
||||||
|
\add_action( 'init', array( __NAMESPACE__ . '\Comment', 'init' ) );
|
||||||
|
|
||||||
if ( site_supports_blocks() ) {
|
if ( site_supports_blocks() ) {
|
||||||
\add_action( 'init', array( __NAMESPACE__ . '\Blocks', 'init' ) );
|
\add_action( 'init', array( __NAMESPACE__ . '\Blocks', 'init' ) );
|
||||||
@ -89,6 +94,9 @@ function plugin_init() {
|
|||||||
|
|
||||||
require_once __DIR__ . '/integration/class-nodeinfo.php';
|
require_once __DIR__ . '/integration/class-nodeinfo.php';
|
||||||
Integration\Nodeinfo::init();
|
Integration\Nodeinfo::init();
|
||||||
|
|
||||||
|
require_once __DIR__ . '/integration/class-enable-mastodon-apps.php';
|
||||||
|
Integration\Enable_Mastodon_Apps::init();
|
||||||
}
|
}
|
||||||
\add_action( 'plugins_loaded', __NAMESPACE__ . '\plugin_init' );
|
\add_action( 'plugins_loaded', __NAMESPACE__ . '\plugin_init' );
|
||||||
|
|
||||||
@ -111,7 +119,7 @@ function plugin_init() {
|
|||||||
if ( false !== strpos( $class, '\\' ) ) {
|
if ( false !== strpos( $class, '\\' ) ) {
|
||||||
$parts = explode( '\\', $class );
|
$parts = explode( '\\', $class );
|
||||||
$class = array_pop( $parts );
|
$class = array_pop( $parts );
|
||||||
$sub_dir = implode( '/', $parts );
|
$sub_dir = strtr( implode( '/', $parts ), '_', '-' );
|
||||||
$base_dir = $base_dir . $sub_dir . '/';
|
$base_dir = $base_dir . $sub_dir . '/';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1 +1 @@
|
|||||||
<?php return array( 'dependencies' => array( 'wp-api-fetch', 'wp-block-editor', 'wp-blocks', 'wp-components', 'wp-compose', 'wp-data', 'wp-element', 'wp-i18n', 'wp-primitives' ), 'version' => '3ffce3edc6fed284bfbc' );
|
<?php return array('dependencies' => array('wp-api-fetch', 'wp-block-editor', 'wp-blocks', 'wp-components', 'wp-compose', 'wp-data', 'wp-element', 'wp-i18n', 'wp-primitives'), 'version' => 'c7c2319cdfac52a18445');
|
||||||
|
File diff suppressed because one or more lines are too long
@ -1 +1 @@
|
|||||||
.activitypub-follow-me-block-wrapper{width:100%}.activitypub-follow-me-block-wrapper.has-background .activitypub-profile,.activitypub-follow-me-block-wrapper.has-border-color .activitypub-profile{padding-left:1rem;padding-right:1rem}.activitypub-follow-me-block-wrapper .activitypub-profile{align-items:center;display:flex;padding:1rem 0}.activitypub-follow-me-block-wrapper .activitypub-profile .activitypub-profile__avatar{border-radius:50%;height:75px;margin-right:1rem;width:75px}.activitypub-follow-me-block-wrapper .activitypub-profile .activitypub-profile__content{flex:1;min-width:0}.activitypub-follow-me-block-wrapper .activitypub-profile .activitypub-profile__handle,.activitypub-follow-me-block-wrapper .activitypub-profile .activitypub-profile__name{line-height:1.2;margin:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.activitypub-follow-me-block-wrapper .activitypub-profile .activitypub-profile__name{font-size:1.25em}.activitypub-follow-me-block-wrapper .activitypub-profile .activitypub-profile__follow{align-self:center;background-color:var(--wp--preset--color--black);color:var(--wp--preset--color--white);margin-left:1rem}.activitypub-profile__confirm.components-modal__frame{background-color:#f7f7f7;color:#333}.activitypub-profile__confirm.components-modal__frame .components-modal__header-heading,.activitypub-profile__confirm.components-modal__frame h4{color:#333;letter-spacing:inherit;word-spacing:inherit}.activitypub-follow-me__dialog{max-width:30em}.activitypub-follow-me__dialog h4{line-height:1;margin:0}.activitypub-follow-me__dialog .apmfd__section{margin-bottom:2em}.activitypub-follow-me__dialog .apfmd-description{font-size:var(--wp--preset--font-size--normal,.75rem);margin:.33em 0 1em}.activitypub-follow-me__dialog .apfmd__button-group{align-items:flex-end;display:flex;justify-content:flex-end}.activitypub-follow-me__dialog .apfmd__button-group svg{height:21px;margin-right:.5em;width:21px}.activitypub-follow-me__dialog .apfmd__button-group input{background-color:var(--wp--preset--color--white);border:1px solid var(--wp--preset--color--black);color:var(--wp--preset--color--black);flex:1;padding:6px 12px}
|
.activitypub-follow-me-block-wrapper{width:100%}.activitypub-follow-me-block-wrapper.has-background .activitypub-profile,.activitypub-follow-me-block-wrapper.has-border-color .activitypub-profile{padding-left:1rem;padding-right:1rem}.activitypub-follow-me-block-wrapper .activitypub-profile{align-items:center;display:flex;padding:1rem 0}.activitypub-follow-me-block-wrapper .activitypub-profile .activitypub-profile__avatar{border-radius:50%;height:75px;margin-right:1rem;width:75px}.activitypub-follow-me-block-wrapper .activitypub-profile .activitypub-profile__content{flex:1;min-width:0}.activitypub-follow-me-block-wrapper .activitypub-profile .activitypub-profile__handle,.activitypub-follow-me-block-wrapper .activitypub-profile .activitypub-profile__name{line-height:1.2;margin:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.activitypub-follow-me-block-wrapper .activitypub-profile .activitypub-profile__name{font-size:1.25em}.activitypub-follow-me-block-wrapper .activitypub-profile .activitypub-profile__follow{align-self:center;background-color:var(--wp--preset--color--black);color:var(--wp--preset--color--white);margin-left:1rem}.activitypub__modal.components-modal__frame{background-color:#f7f7f7;color:#333}.activitypub__modal.components-modal__frame .components-modal__header-heading,.activitypub__modal.components-modal__frame h4{color:#333;letter-spacing:inherit;word-spacing:inherit}.activitypub__modal.components-modal__frame .components-modal__header .components-button:hover{color:var(--wp--preset--color--white)}.activitypub__dialog{max-width:40em}.activitypub__dialog h4{line-height:1;margin:0}.activitypub__dialog .activitypub-dialog__section{margin-bottom:2em}.activitypub__dialog .activitypub-dialog__description{font-size:var(--wp--preset--font-size--normal,.75rem);margin:.33em 0 1em}.activitypub__dialog .activitypub-dialog__button-group{align-items:flex-end;display:flex;justify-content:flex-end}.activitypub__dialog .activitypub-dialog__button-group svg{height:21px;margin-right:.5em;width:21px}.activitypub__dialog .activitypub-dialog__button-group input{background-color:var(--wp--preset--color--white);border:1px solid var(--wp--preset--color--black);border-radius:inherit 0;color:var(--wp--preset--color--black);flex:1;padding:6px 12px}.activitypub__dialog .activitypub-dialog__button-group button{align-self:center;background-color:var(--wp--preset--color--black);color:var(--wp--preset--color--white);margin-left:0;text-decoration:none}
|
||||||
|
@ -1 +1 @@
|
|||||||
<?php return array( 'dependencies' => array( 'wp-api-fetch', 'wp-components', 'wp-compose', 'wp-dom-ready', 'wp-element', 'wp-i18n', 'wp-primitives' ), 'version' => '40f3434fe6f953826373' );
|
<?php return array('dependencies' => array('wp-api-fetch', 'wp-components', 'wp-compose', 'wp-dom-ready', 'wp-element', 'wp-i18n', 'wp-primitives'), 'version' => '1349fc90a3f33dde3595');
|
||||||
|
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' => 'c338a0364a63e21934ae' );
|
<?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' => 'c338a0364a63e21934ae');
|
||||||
|
@ -1 +1 @@
|
|||||||
<?php return array( 'dependencies' => array( 'react', 'wp-api-fetch', 'wp-components', 'wp-dom-ready', 'wp-element', 'wp-i18n', 'wp-url' ), 'version' => 'ed5a13e66f8b10323435' );
|
<?php return array('dependencies' => array('react', 'wp-api-fetch', 'wp-components', 'wp-dom-ready', 'wp-element', 'wp-i18n', 'wp-url'), 'version' => 'ed5a13e66f8b10323435');
|
||||||
|
11
wp-content/plugins/activitypub/build/remote-reply/block.json
Normal file
11
wp-content/plugins/activitypub/build/remote-reply/block.json
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
{
|
||||||
|
"$schema": "https://schemas.wp.org/trunk/block.json",
|
||||||
|
"name": "activitypub/remote-reply",
|
||||||
|
"apiVersion": 3,
|
||||||
|
"version": "1.0.0",
|
||||||
|
"title": "Reply on the Fediverse",
|
||||||
|
"category": "widgets",
|
||||||
|
"description": "",
|
||||||
|
"textdomain": "activitypub",
|
||||||
|
"viewScript": "file:./index.js"
|
||||||
|
}
|
@ -0,0 +1 @@
|
|||||||
|
<?php return array('dependencies' => array('wp-api-fetch', 'wp-components', 'wp-compose', 'wp-dom-ready', 'wp-element', 'wp-i18n', 'wp-primitives'), 'version' => '0235743f089d22122d70');
|
File diff suppressed because one or more lines are too long
@ -0,0 +1 @@
|
|||||||
|
.activitypub__modal.components-modal__frame{background-color:#f7f7f7;color:#333}.activitypub__modal.components-modal__frame .components-modal__header-heading,.activitypub__modal.components-modal__frame h4{color:#333;letter-spacing:inherit;word-spacing:inherit}.activitypub__modal.components-modal__frame .components-modal__header .components-button:hover{color:var(--wp--preset--color--white)}.activitypub__dialog{max-width:40em}.activitypub__dialog h4{line-height:1;margin:0}.activitypub__dialog .activitypub-dialog__section{margin-bottom:2em}.activitypub__dialog .activitypub-dialog__description{font-size:var(--wp--preset--font-size--normal,.75rem);margin:.33em 0 1em}.activitypub__dialog .activitypub-dialog__button-group{align-items:flex-end;display:flex;justify-content:flex-end}.activitypub__dialog .activitypub-dialog__button-group svg{height:21px;margin-right:.5em;width:21px}.activitypub__dialog .activitypub-dialog__button-group input{background-color:var(--wp--preset--color--white);border:1px solid var(--wp--preset--color--black);border-radius:inherit 0;color:var(--wp--preset--color--black);flex:1;padding:6px 12px}.activitypub__dialog .activitypub-dialog__button-group button{align-self:center;background-color:var(--wp--preset--color--black);color:var(--wp--preset--color--white);margin-left:0;text-decoration:none}
|
@ -17,75 +17,15 @@ use Activitypub\Activity\Base_Object;
|
|||||||
* @see https://www.w3.org/TR/activitystreams-core/#intransitiveactivities
|
* @see https://www.w3.org/TR/activitystreams-core/#intransitiveactivities
|
||||||
*/
|
*/
|
||||||
class Activity extends Base_Object {
|
class Activity extends Base_Object {
|
||||||
const CONTEXT = array(
|
const JSON_LD_CONTEXT = array(
|
||||||
'https://www.w3.org/ns/activitystreams',
|
'https://www.w3.org/ns/activitystreams',
|
||||||
'https://w3id.org/security/v1',
|
|
||||||
'https://purl.archive.org/socialweb/webfinger',
|
|
||||||
array(
|
|
||||||
'manuallyApprovesFollowers' => 'as:manuallyApprovesFollowers',
|
|
||||||
'PropertyValue' => 'schema:PropertyValue',
|
|
||||||
'schema' => 'http://schema.org#',
|
|
||||||
'pt' => 'https://joinpeertube.org/ns#',
|
|
||||||
'toot' => 'http://joinmastodon.org/ns#',
|
|
||||||
'litepub' => 'http://litepub.social/ns#',
|
|
||||||
'lemmy' => 'https://join-lemmy.org/ns#',
|
|
||||||
'value' => 'schema:value',
|
|
||||||
'Hashtag' => 'as:Hashtag',
|
|
||||||
'featured' => array(
|
|
||||||
'@id' => 'toot:featured',
|
|
||||||
'@type' => '@id',
|
|
||||||
),
|
|
||||||
'featuredTags' => array(
|
|
||||||
'@id' => 'toot:featuredTags',
|
|
||||||
'@type' => '@id',
|
|
||||||
),
|
|
||||||
'alsoKnownAs' => array(
|
|
||||||
'@id' => 'as:alsoKnownAs',
|
|
||||||
'@type' => '@id',
|
|
||||||
),
|
|
||||||
'moderators' => array(
|
|
||||||
'@id' => 'lemmy:moderators',
|
|
||||||
'@type' => '@id',
|
|
||||||
),
|
|
||||||
'postingRestrictedToMods' => 'lemmy:postingRestrictedToMods',
|
|
||||||
'discoverable' => 'toot:discoverable',
|
|
||||||
'indexable' => 'toot:indexable',
|
|
||||||
'sensitive' => 'as:sensitive',
|
|
||||||
),
|
|
||||||
);
|
);
|
||||||
|
|
||||||
/**
|
|
||||||
* The object's unique global identifier
|
|
||||||
*
|
|
||||||
* @see https://www.w3.org/TR/activitypub/#obj-id
|
|
||||||
*
|
|
||||||
* @var string
|
|
||||||
*/
|
|
||||||
protected $id;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @var string
|
* @var string
|
||||||
*/
|
*/
|
||||||
protected $type = 'Activity';
|
protected $type = 'Activity';
|
||||||
|
|
||||||
/**
|
|
||||||
* The context within which the object exists or an activity was
|
|
||||||
* performed.
|
|
||||||
* The notion of "context" used is intentionally vague.
|
|
||||||
* The intended function is to serve as a means of grouping objects
|
|
||||||
* and activities that share a common originating context or
|
|
||||||
* purpose. An example could be all activities relating to a common
|
|
||||||
* project or event.
|
|
||||||
*
|
|
||||||
* @see https://www.w3.org/TR/activitystreams-vocabulary/#dfn-context
|
|
||||||
*
|
|
||||||
* @var string
|
|
||||||
* | ObjectType
|
|
||||||
* | Link
|
|
||||||
* | null
|
|
||||||
*/
|
|
||||||
protected $context = self::CONTEXT;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Describes the direct object of the activity.
|
* Describes the direct object of the activity.
|
||||||
* For instance, in the activity "John added a movie to his
|
* For instance, in the activity "John added a movie to his
|
||||||
@ -94,7 +34,7 @@ class Activity extends Base_Object {
|
|||||||
* @see https://www.w3.org/TR/activitystreams-vocabulary/#dfn-object-term
|
* @see https://www.w3.org/TR/activitystreams-vocabulary/#dfn-object-term
|
||||||
*
|
*
|
||||||
* @var string
|
* @var string
|
||||||
* | Base_Objectr
|
* | Base_Object
|
||||||
* | Link
|
* | Link
|
||||||
* | null
|
* | null
|
||||||
*/
|
*/
|
||||||
@ -225,4 +165,21 @@ class Activity extends Base_Object {
|
|||||||
$this->set( 'id', $object->get_id() . '#activity' );
|
$this->set( 'id', $object->get_id() . '#activity' );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The context of an Activity is usually just the context of the object it contains.
|
||||||
|
*
|
||||||
|
* @return array $context A compacted JSON-LD context.
|
||||||
|
*/
|
||||||
|
public function get_json_ld_context() {
|
||||||
|
if ( $this->object instanceof Base_Object ) {
|
||||||
|
$class = get_class( $this->object );
|
||||||
|
if ( $class && $class::JSON_LD_CONTEXT ) {
|
||||||
|
// Without php 5.6 support this could be just: 'return $this->object::JSON_LD_CONTEXT;'
|
||||||
|
return $class::JSON_LD_CONTEXT;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return static::JSON_LD_CONTEXT;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -16,6 +16,39 @@ namespace Activitypub\Activity;
|
|||||||
* @see https://www.w3.org/TR/activitystreams-vocabulary/#actor-types
|
* @see https://www.w3.org/TR/activitystreams-vocabulary/#actor-types
|
||||||
*/
|
*/
|
||||||
class Actor extends Base_Object {
|
class Actor extends Base_Object {
|
||||||
|
// Reduced context for actors. TODO: still unused.
|
||||||
|
const JSON_LD_CONTEXT = array(
|
||||||
|
'https://www.w3.org/ns/activitystreams',
|
||||||
|
'https://w3id.org/security/v1',
|
||||||
|
'https://purl.archive.org/socialweb/webfinger',
|
||||||
|
array(
|
||||||
|
'schema' => 'http://schema.org#',
|
||||||
|
'toot' => 'http://joinmastodon.org/ns#',
|
||||||
|
'webfinger' => 'https://webfinger.net/#',
|
||||||
|
'lemmy' => 'https://join-lemmy.org/ns#',
|
||||||
|
'manuallyApprovesFollowers' => 'as:manuallyApprovesFollowers',
|
||||||
|
'PropertyValue' => 'schema:PropertyValue',
|
||||||
|
'value' => 'schema:value',
|
||||||
|
'Hashtag' => 'as:Hashtag',
|
||||||
|
'featured' => array(
|
||||||
|
'@id' => 'toot:featured',
|
||||||
|
'@type' => '@id',
|
||||||
|
),
|
||||||
|
'featuredTags' => array(
|
||||||
|
'@id' => 'toot:featuredTags',
|
||||||
|
'@type' => '@id',
|
||||||
|
),
|
||||||
|
'moderators' => array(
|
||||||
|
'@id' => 'lemmy:moderators',
|
||||||
|
'@type' => '@id',
|
||||||
|
),
|
||||||
|
'postingRestrictedToMods' => 'lemmy:postingRestrictedToMods',
|
||||||
|
'discoverable' => 'toot:discoverable',
|
||||||
|
'indexable' => 'toot:indexable',
|
||||||
|
'resource' => 'webfinger:resource',
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @var string
|
* @var string
|
||||||
*/
|
*/
|
||||||
@ -133,6 +166,8 @@ class Actor extends Base_Object {
|
|||||||
*
|
*
|
||||||
* @see https://docs.joinmastodon.org/spec/activitypub/#as
|
* @see https://docs.joinmastodon.org/spec/activitypub/#as
|
||||||
*
|
*
|
||||||
|
* @context as:manuallyApprovesFollowers
|
||||||
|
*
|
||||||
* @var boolean
|
* @var boolean
|
||||||
*/
|
*/
|
||||||
protected $manually_approves_followers = false;
|
protected $manually_approves_followers = false;
|
||||||
|
@ -9,6 +9,7 @@ namespace Activitypub\Activity;
|
|||||||
|
|
||||||
use WP_Error;
|
use WP_Error;
|
||||||
use ReflectionClass;
|
use ReflectionClass;
|
||||||
|
use DateTime;
|
||||||
|
|
||||||
use function Activitypub\camel_to_snake_case;
|
use function Activitypub\camel_to_snake_case;
|
||||||
use function Activitypub\snake_to_camel_case;
|
use function Activitypub\snake_to_camel_case;
|
||||||
@ -26,6 +27,13 @@ use function Activitypub\snake_to_camel_case;
|
|||||||
* @see https://www.w3.org/TR/activitystreams-core/#object
|
* @see https://www.w3.org/TR/activitystreams-core/#object
|
||||||
*/
|
*/
|
||||||
class Base_Object {
|
class Base_Object {
|
||||||
|
const JSON_LD_CONTEXT = array(
|
||||||
|
'https://www.w3.org/ns/activitystreams',
|
||||||
|
array(
|
||||||
|
'Hashtag' => 'as:Hashtag',
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The object's unique global identifier
|
* The object's unique global identifier
|
||||||
*
|
*
|
||||||
@ -457,7 +465,7 @@ class Base_Object {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if ( \strncasecmp( $method, 'set', 3 ) === 0 ) {
|
if ( \strncasecmp( $method, 'set', 3 ) === 0 ) {
|
||||||
$this->set( $var, $params[0] );
|
return $this->set( $var, $params[0] );
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( \strncasecmp( $method, 'add', 3 ) === 0 ) {
|
if ( \strncasecmp( $method, 'add', 3 ) === 0 ) {
|
||||||
@ -524,7 +532,7 @@ class Base_Object {
|
|||||||
|
|
||||||
$this->$key = $value;
|
$this->$key = $value;
|
||||||
|
|
||||||
return $this->$key;
|
return $this;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -622,9 +630,11 @@ class Base_Object {
|
|||||||
* It tries to get the object attributes if they exist
|
* It tries to get the object attributes if they exist
|
||||||
* and falls back to the getters. Empty values are ignored.
|
* and falls back to the getters. Empty values are ignored.
|
||||||
*
|
*
|
||||||
|
* @param bool $include_json_ld_context Whether to include the JSON-LD context. Default true.
|
||||||
|
*
|
||||||
* @return array An array built from the Object.
|
* @return array An array built from the Object.
|
||||||
*/
|
*/
|
||||||
public function to_array() {
|
public function to_array( $include_json_ld_context = true ) {
|
||||||
$array = array();
|
$array = array();
|
||||||
$vars = get_object_vars( $this );
|
$vars = get_object_vars( $this );
|
||||||
|
|
||||||
@ -640,7 +650,7 @@ class Base_Object {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if ( is_object( $value ) ) {
|
if ( is_object( $value ) ) {
|
||||||
$value = $value->to_array();
|
$value = $value->to_array( false );
|
||||||
}
|
}
|
||||||
|
|
||||||
// if value is still empty, ignore it for the array and continue.
|
// if value is still empty, ignore it for the array and continue.
|
||||||
@ -649,11 +659,9 @@ class Base_Object {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// replace 'context' key with '@context' and move it to the top.
|
if ( $include_json_ld_context ) {
|
||||||
if ( array_key_exists( 'context', $array ) ) {
|
// Get JsonLD context and move it to '@context' at the top.
|
||||||
$context = $array['context'];
|
$array = array_merge( array( '@context' => $this->get_json_ld_context() ), $array );
|
||||||
unset( $array['context'] );
|
|
||||||
$array = array_merge( array( '@context' => $context ), $array );
|
|
||||||
}
|
}
|
||||||
|
|
||||||
$class = new ReflectionClass( $this );
|
$class = new ReflectionClass( $this );
|
||||||
@ -668,10 +676,12 @@ class Base_Object {
|
|||||||
/**
|
/**
|
||||||
* Convert Object to JSON.
|
* Convert Object to JSON.
|
||||||
*
|
*
|
||||||
|
* @param bool $include_json_ld_context Whether to include the JSON-LD context. Default true.
|
||||||
|
*
|
||||||
* @return string The JSON string.
|
* @return string The JSON string.
|
||||||
*/
|
*/
|
||||||
public function to_json() {
|
public function to_json( $include_json_ld_context = true ) {
|
||||||
$array = $this->to_array();
|
$array = $this->to_array( $include_json_ld_context );
|
||||||
$options = \JSON_HEX_TAG | \JSON_HEX_AMP | \JSON_HEX_QUOT;
|
$options = \JSON_HEX_TAG | \JSON_HEX_AMP | \JSON_HEX_QUOT;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -692,4 +702,13 @@ class Base_Object {
|
|||||||
public function get_object_var_keys() {
|
public function get_object_var_keys() {
|
||||||
return \array_keys( \get_object_vars( $this ) );
|
return \array_keys( \get_object_vars( $this ) );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the JSON-LD context of this object.
|
||||||
|
*
|
||||||
|
* @return array $context A compacted JSON-LD context for the ActivityPub object.
|
||||||
|
*/
|
||||||
|
public function get_json_ld_context() {
|
||||||
|
return static::JSON_LD_CONTEXT;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,340 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* ActivityPub Object of type Event.
|
||||||
|
*
|
||||||
|
* @package activity-event-transformers
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Activitypub\Activity\Extended_Object;
|
||||||
|
|
||||||
|
use Activitypub\Activity\Base_Object;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Event is an implementation of one of the Activity Streams Event object type.
|
||||||
|
*
|
||||||
|
* This class contains extra keys as used by Mobilizon to ensure compatibility.
|
||||||
|
*
|
||||||
|
* @see https://www.w3.org/TR/activitystreams-vocabulary/#dfn-event
|
||||||
|
*/
|
||||||
|
class Event extends Base_Object {
|
||||||
|
// Human friendly minimal context for full Mobilizon compatible ActivityPub events.
|
||||||
|
const JSON_LD_CONTEXT = array(
|
||||||
|
'https://schema.org/', // The base context is schema.org, cause it is used a lot.
|
||||||
|
'https://www.w3.org/ns/activitystreams', // The ActivityStreams context overrides everyting also defined in schema.org.
|
||||||
|
array( // The keys here override/extend the context even more.
|
||||||
|
'pt' => 'https://joinpeertube.org/ns#',
|
||||||
|
'mz' => 'https://joinmobilizon.org/ns#',
|
||||||
|
'status' => 'http://www.w3.org/2002/12/cal/ical#status',
|
||||||
|
'commentsEnabled' => 'pt:commentsEnabled',
|
||||||
|
'isOnline' => 'mz:isOnline',
|
||||||
|
'timezone' => 'mz:timezone',
|
||||||
|
'participantCount' => 'mz:participantCount',
|
||||||
|
'anonymousParticipationEnabled' => 'mz:anonymousParticipationEnabled',
|
||||||
|
'joinMode' => array(
|
||||||
|
'@id' => 'mz:joinMode',
|
||||||
|
'@type' => 'mz:joinModeType',
|
||||||
|
),
|
||||||
|
'externalParticipationUrl' => array(
|
||||||
|
'@id' => 'mz:externalParticipationUrl',
|
||||||
|
'@type' => 'schema:URL',
|
||||||
|
),
|
||||||
|
'repliesModerationOption' => array(
|
||||||
|
'@id' => 'mz:repliesModerationOption',
|
||||||
|
'@type' => '@vocab',
|
||||||
|
),
|
||||||
|
'contacts' => array(
|
||||||
|
'@id' => 'mz:contacts',
|
||||||
|
'@type' => '@id',
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Mobilizon compatible values for repliesModertaionOption.
|
||||||
|
* @var array
|
||||||
|
*/
|
||||||
|
const REPLIES_MODERATION_OPTION_TYPES = array( 'allow_all', 'closed' );
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Mobilizon compatible values for joinModeTypes.
|
||||||
|
*/
|
||||||
|
const JOIN_MODE_TYPES = array( 'free', 'restricted', 'external' ); // and 'invite', but not used by mobilizon atm
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Allowed values for ical VEVENT STATUS.
|
||||||
|
* @var array
|
||||||
|
*/
|
||||||
|
const ICAL_EVENT_STATUS_TYPES = array( 'TENTATIVE', 'CONFIRMED', 'CANCELLED' );
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Default event categories.
|
||||||
|
*
|
||||||
|
* These values currently reflect the default set as proposed by Mobilizon to maximize interoperability.
|
||||||
|
* @var array
|
||||||
|
*/
|
||||||
|
const DEFAULT_EVENT_CATEGORIES = array(
|
||||||
|
'ARTS',
|
||||||
|
'BOOK_CLUBS',
|
||||||
|
'BUSINESS',
|
||||||
|
'CAUSES',
|
||||||
|
'COMEDY',
|
||||||
|
'CRAFTS',
|
||||||
|
'FOOD_DRINK',
|
||||||
|
'HEALTH',
|
||||||
|
'MUSIC',
|
||||||
|
'AUTO_BOAT_AIR',
|
||||||
|
'COMMUNITY',
|
||||||
|
'FAMILY_EDUCATION',
|
||||||
|
'FASHION_BEAUTY',
|
||||||
|
'FILM_MEDIA',
|
||||||
|
'GAMES',
|
||||||
|
'LANGUAGE_CULTURE',
|
||||||
|
'LEARNING',
|
||||||
|
'LGBTQ',
|
||||||
|
'MOVEMENTS_POLITICS',
|
||||||
|
'NETWORKING',
|
||||||
|
'PARTY',
|
||||||
|
'PERFORMING_VISUAL_ARTS',
|
||||||
|
'PETS',
|
||||||
|
'PHOTOGRAPHY',
|
||||||
|
'OUTDOORS_ADVENTURE',
|
||||||
|
'SPIRITUALITY_RELIGION_BELIEFS',
|
||||||
|
'SCIENCE_TECH',
|
||||||
|
'SPORTS',
|
||||||
|
'THEATRE',
|
||||||
|
'MEETING', // Default value.
|
||||||
|
);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Event is an implementation of one of the
|
||||||
|
* Activity Streams
|
||||||
|
*
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
protected $type = 'Event';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The Title of the event.
|
||||||
|
*/
|
||||||
|
protected $name;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The events contacts
|
||||||
|
*
|
||||||
|
* @context {
|
||||||
|
* '@id' => 'mz:contacts',
|
||||||
|
* '@type' => '@id',
|
||||||
|
* }
|
||||||
|
*
|
||||||
|
* @var array Array of contacts (ActivityPub actor IDs).
|
||||||
|
*/
|
||||||
|
protected $contacts;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Extension invented by PeerTube whether comments/replies are <enabled>
|
||||||
|
* Mobilizon also implemented this as a fallback to their own
|
||||||
|
* repliesModerationOption.
|
||||||
|
*
|
||||||
|
* @see https://docs.joinpeertube.org/api/activitypub#video
|
||||||
|
* @see https://docs.joinmobilizon.org/contribute/activity_pub/
|
||||||
|
* @var bool|null
|
||||||
|
*/
|
||||||
|
protected $comments_enabled;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @context https://joinmobilizon.org/ns#timezone
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
protected $timezone;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @context https://joinmobilizon.org/ns#repliesModerationOption
|
||||||
|
* @see https://docs.joinmobilizon.org/contribute/activity_pub/#repliesmoderation
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
protected $replies_moderation_option;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @context https://joinmobilizon.org/ns#anonymousParticipationEnabled
|
||||||
|
* @see https://docs.joinmobilizon.org/contribute/activity_pub/#anonymousparticipationenabled
|
||||||
|
* @var bool
|
||||||
|
*/
|
||||||
|
protected $anonymous_participation_enabled;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @context https://schema.org/category
|
||||||
|
* @var enum
|
||||||
|
*/
|
||||||
|
protected $category;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @context https://schema.org/inLanguage
|
||||||
|
* @var
|
||||||
|
*/
|
||||||
|
protected $in_language;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @context https://joinmobilizon.org/ns#isOnline
|
||||||
|
* @var bool
|
||||||
|
*/
|
||||||
|
protected $is_online;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @context https://www.w3.org/2002/12/cal/ical#status
|
||||||
|
* @var enum
|
||||||
|
*/
|
||||||
|
protected $status;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Which actor created the event.
|
||||||
|
*
|
||||||
|
* This field is needed due to the current group structure of Mobilizon.
|
||||||
|
*
|
||||||
|
* @todo this seems to not be a default property of an Object but needed by mobilizon.
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
protected $actor;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @context https://joinmobilizon.org/ns#externalParticipationUrl
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
protected $external_participation_url;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @context https://joinmobilizon.org/ns#joinMode
|
||||||
|
* @see https://docs.joinmobilizon.org/contribute/activity_pub/#joinmode
|
||||||
|
* @var
|
||||||
|
*/
|
||||||
|
protected $join_mode;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @context https://joinmobilizon.org/ns#participantCount
|
||||||
|
* @var int
|
||||||
|
*/
|
||||||
|
protected $participant_count;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @context https://schema.org/maximumAttendeeCapacity
|
||||||
|
* @see https://docs.joinmobilizon.org/contribute/activity_pub/#maximumattendeecapacity
|
||||||
|
* @var int
|
||||||
|
*/
|
||||||
|
protected $maximum_attendee_capacity;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @context https://schema.org/remainingAttendeeCapacity
|
||||||
|
* @see https://docs.joinmobilizon.org/contribute/activity_pub/#remainignattendeecapacity
|
||||||
|
* @var int
|
||||||
|
*/
|
||||||
|
protected $remaining_attendee_capacity;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Setter for the timezone.
|
||||||
|
*
|
||||||
|
* The passed timezone is only set when it is a valid one, otherwise the site's timezone is used.
|
||||||
|
*
|
||||||
|
* @param string $timezone The timezone string to be set, e.g. 'Europe/Berlin'.
|
||||||
|
*/
|
||||||
|
public function set_timezone( $timezone ) {
|
||||||
|
if ( in_array( $timezone, timezone_identifiers_list(), true ) ) {
|
||||||
|
$this->timezone = $timezone;
|
||||||
|
} else {
|
||||||
|
$this->timezone = wp_timezone_string();
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Custom setter for repliesModerationOption which also directy sets commentsEnabled accordingly.
|
||||||
|
*
|
||||||
|
* @param string $type
|
||||||
|
*/
|
||||||
|
public function set_replies_moderation_option( $type ) {
|
||||||
|
if ( in_array( $type, self::REPLIES_MODERATION_OPTION_TYPES, true ) ) {
|
||||||
|
$this->replies_moderation_option = $type;
|
||||||
|
$this->comments_enabled = ( 'allow_all' === $type ) ? true : false;
|
||||||
|
} else {
|
||||||
|
_doing_it_wrong(
|
||||||
|
__METHOD__,
|
||||||
|
'The replies moderation option must be either allow_all or closed.',
|
||||||
|
'<version_placeholder>'
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Custom setter for commentsEnabled which also directly sets repliesModerationOption accordingly.
|
||||||
|
*
|
||||||
|
* @param bool $comments_enabled
|
||||||
|
*/
|
||||||
|
public function set_comments_enabled( $comments_enabled ) {
|
||||||
|
if ( is_bool( $comments_enabled ) ) {
|
||||||
|
$this->comments_enabled = $comments_enabled;
|
||||||
|
$this->replies_moderation_option = $comments_enabled ? 'allow_all' : 'closed';
|
||||||
|
} else {
|
||||||
|
_doing_it_wrong(
|
||||||
|
__METHOD__,
|
||||||
|
'The commentsEnabled must be boolean.',
|
||||||
|
'<version_placeholder>'
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Custom setter for the ical status that checks whether the status is an ical event status.
|
||||||
|
*
|
||||||
|
* @param string $status
|
||||||
|
*/
|
||||||
|
public function set_status( $status ) {
|
||||||
|
if ( in_array( $status, self::ICAL_EVENT_STATUS_TYPES, true ) ) {
|
||||||
|
$this->status = $status;
|
||||||
|
} else {
|
||||||
|
_doing_it_wrong(
|
||||||
|
__METHOD__,
|
||||||
|
'The status of the event must be a VEVENT iCal status.',
|
||||||
|
'<version_placeholder>'
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Custom setter for the event category.
|
||||||
|
*
|
||||||
|
* Falls back to Mobilizons default category.
|
||||||
|
*
|
||||||
|
* @param string $category
|
||||||
|
* @param bool $mobilizon_compatibilty Whether the category must be compatibly with Mobilizon.
|
||||||
|
*/
|
||||||
|
public function set_category( $category, $mobilizon_compatibilty = true ) {
|
||||||
|
if ( $mobilizon_compatibilty ) {
|
||||||
|
$this->category = in_array( $category, self::DEFAULT_EVENT_CATEGORIES, true ) ? $category : 'MEETING';
|
||||||
|
} else {
|
||||||
|
$this->category = $category;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Custom setter for an external participation url.
|
||||||
|
*
|
||||||
|
* Automatically sets the joinMode to true if called.
|
||||||
|
*
|
||||||
|
* @param string $url
|
||||||
|
*/
|
||||||
|
public function set_external_participation_url( $url ) {
|
||||||
|
if ( preg_match( '/^https?:\/\/.*/i', $url ) ) {
|
||||||
|
$this->external_participation_url = $url;
|
||||||
|
$this->join_mode = 'external';
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,93 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* Event is an implementation of one of the
|
||||||
|
* Activity Streams Event object type
|
||||||
|
*
|
||||||
|
* @package activity-event-transformers
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Activitypub\Activity\Extended_Object;
|
||||||
|
|
||||||
|
use Activitypub\Activity\Base_Object;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Event is an implementation of one of the
|
||||||
|
* Activity Streams Event object type
|
||||||
|
*
|
||||||
|
* The Object is the primary base type for the Activity Streams
|
||||||
|
* vocabulary.
|
||||||
|
*
|
||||||
|
* @see https://www.w3.org/TR/activitystreams-vocabulary/#dfn-event
|
||||||
|
*/
|
||||||
|
class Place extends Base_Object {
|
||||||
|
/**
|
||||||
|
* Place is an implementation of one of the
|
||||||
|
* Activity Streams
|
||||||
|
*
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
protected $type = 'Place';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Indicates the accuracy of position coordinates on a Place objects.
|
||||||
|
* Expressed in properties of percentage. e.g. "94.0" means "94.0% accurate".
|
||||||
|
*
|
||||||
|
* @see https://www.w3.org/TR/activitystreams-vocabulary/#dfn-accuracy
|
||||||
|
* @var float xsd:float [>= 0.0f, <= 100.0f]
|
||||||
|
*/
|
||||||
|
protected $accuracy;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Indicates the altitude of a place. The measurement units is indicated using the units property.
|
||||||
|
* If units is not specified, the default is assumed to be "m" indicating meters.
|
||||||
|
*
|
||||||
|
* @see https://www.w3.org/TR/activitystreams-vocabulary/#dfn-altitude
|
||||||
|
* @var float xsd:float
|
||||||
|
*/
|
||||||
|
protected $altitude;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The latitude of a place.
|
||||||
|
*
|
||||||
|
* @see https://www.w3.org/TR/activitystreams-vocabulary/#dfn-latitude
|
||||||
|
* @var float xsd:float
|
||||||
|
*/
|
||||||
|
protected $latitude;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The longitude of a place.
|
||||||
|
*
|
||||||
|
* @see https://www.w3.org/TR/activitystreams-vocabulary/#dfn-longitude
|
||||||
|
* @var float xsd:float
|
||||||
|
*/
|
||||||
|
protected $longitude;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @see https://www.w3.org/TR/activitystreams-vocabulary/#dfn-radius
|
||||||
|
* @var float
|
||||||
|
*/
|
||||||
|
protected $radius;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @see https://www.w3.org/TR/activitystreams-vocabulary/#dfn-units
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
protected $units;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var Postal_Address|string
|
||||||
|
*/
|
||||||
|
protected $address;
|
||||||
|
|
||||||
|
public function set_address( $address ) {
|
||||||
|
if ( is_string( $address ) || is_array( $address ) ) {
|
||||||
|
$this->address = $address;
|
||||||
|
} else {
|
||||||
|
_doing_it_wrong(
|
||||||
|
__METHOD__,
|
||||||
|
'The address must be either a string or an array like schema.org/PostalAddress.',
|
||||||
|
'<version_placeholder>'
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -13,6 +13,7 @@ use Activitypub\Transformer\Comment;
|
|||||||
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\safe_remote_post;
|
use function Activitypub\safe_remote_post;
|
||||||
|
use function Activitypub\set_wp_object_state;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* ActivityPub Activity_Dispatcher Class
|
* ActivityPub Activity_Dispatcher Class
|
||||||
@ -26,6 +27,9 @@ class Activity_Dispatcher {
|
|||||||
* Initialize the class, registering WordPress hooks.
|
* Initialize the class, registering WordPress hooks.
|
||||||
*/
|
*/
|
||||||
public static function init() {
|
public static function init() {
|
||||||
|
\add_action( 'activitypub_send_post', array( self::class, 'send_post' ), 10, 2 );
|
||||||
|
\add_action( 'activitypub_send_comment', array( self::class, 'send_comment' ), 10, 2 );
|
||||||
|
|
||||||
\add_action( 'activitypub_send_activity', array( self::class, 'send_activity' ), 10, 2 );
|
\add_action( 'activitypub_send_activity', array( self::class, 'send_activity' ), 10, 2 );
|
||||||
\add_action( 'activitypub_send_activity', array( self::class, 'send_activity_or_announce' ), 10, 2 );
|
\add_action( 'activitypub_send_activity', array( self::class, 'send_activity_or_announce' ), 10, 2 );
|
||||||
\add_action( 'activitypub_send_update_profile_activity', array( self::class, 'send_profile_update' ), 10, 1 );
|
\add_action( 'activitypub_send_update_profile_activity', array( self::class, 'send_profile_update' ), 10, 1 );
|
||||||
@ -77,7 +81,7 @@ class Activity_Dispatcher {
|
|||||||
|
|
||||||
$activity = $transformer->to_activity( $type );
|
$activity = $transformer->to_activity( $type );
|
||||||
|
|
||||||
self::send_activity_to_inboxes( $activity, $user_id );
|
self::send_activity_to_followers( $activity, $user_id, $wp_object );
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -103,7 +107,7 @@ class Activity_Dispatcher {
|
|||||||
$user_id = $transformer->get_wp_user_id();
|
$user_id = $transformer->get_wp_user_id();
|
||||||
$activity = $transformer->to_activity( 'Announce' );
|
$activity = $transformer->to_activity( 'Announce' );
|
||||||
|
|
||||||
self::send_activity_to_inboxes( $activity, $user_id );
|
self::send_activity_to_followers( $activity, $user_id, $wp_object );
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -130,18 +134,24 @@ class Activity_Dispatcher {
|
|||||||
$activity->set_to( 'https://www.w3.org/ns/activitystreams#Public' );
|
$activity->set_to( 'https://www.w3.org/ns/activitystreams#Public' );
|
||||||
|
|
||||||
// send the update
|
// send the update
|
||||||
self::send_activity_to_inboxes( $activity, $user_id );
|
self::send_activity_to_followers( $activity, $user_id, $user );
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Send an Activity to all followers and mentioned users.
|
* Send an Activity to all followers and mentioned users.
|
||||||
*
|
*
|
||||||
* @param Activity $activity The ActivityPub Activity.
|
* @param Activity $activity The ActivityPub Activity.
|
||||||
* @param int $user_id The user ID.
|
* @param int $user_id The user ID.
|
||||||
|
* @param WP_User|WP_Post|WP_Comment $wp_object The WordPress object.
|
||||||
*
|
*
|
||||||
* @return void
|
* @return void
|
||||||
*/
|
*/
|
||||||
private static function send_activity_to_inboxes( $activity, $user_id ) {
|
private static function send_activity_to_followers( $activity, $user_id, $wp_object ) {
|
||||||
|
// check if the Activity should be send to the followers
|
||||||
|
if ( ! apply_filters( 'activitypub_send_activity_to_followers', true, $activity, $user_id, $wp_object ) ) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
$follower_inboxes = Followers::get_inboxes( $user_id );
|
$follower_inboxes = Followers::get_inboxes( $user_id );
|
||||||
|
|
||||||
$mentioned_inboxes = array();
|
$mentioned_inboxes = array();
|
||||||
@ -162,5 +172,57 @@ class Activity_Dispatcher {
|
|||||||
foreach ( $inboxes as $inbox ) {
|
foreach ( $inboxes as $inbox ) {
|
||||||
safe_remote_post( $inbox, $json, $user_id );
|
safe_remote_post( $inbox, $json, $user_id );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
set_wp_object_state( $wp_object, 'federated' );
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Send a "Create" or "Update" Activity for a WordPress Post.
|
||||||
|
*
|
||||||
|
* @param int $id The WordPress Post ID.
|
||||||
|
* @param string $type The Activity-Type.
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public static function send_post( $id, $type ) {
|
||||||
|
$post = get_post( $id );
|
||||||
|
|
||||||
|
if ( ! $post ) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
do_action( 'activitypub_send_activity', $post, $type );
|
||||||
|
do_action(
|
||||||
|
sprintf(
|
||||||
|
'activitypub_send_%s_activity',
|
||||||
|
\strtolower( $type )
|
||||||
|
),
|
||||||
|
$post
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Send a "Create" or "Update" Activity for a WordPress Comment.
|
||||||
|
*
|
||||||
|
* @param int $id The WordPress Comment ID.
|
||||||
|
* @param string $type The Activity-Type.
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public static function send_comment( $id, $type ) {
|
||||||
|
$comment = get_comment( $id );
|
||||||
|
|
||||||
|
if ( ! $comment ) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
do_action( 'activitypub_send_activity', $comment, $type );
|
||||||
|
do_action(
|
||||||
|
sprintf(
|
||||||
|
'activitypub_send_%s_activity',
|
||||||
|
\strtolower( $type )
|
||||||
|
),
|
||||||
|
$comment
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -6,9 +6,11 @@ use Activitypub\Signature;
|
|||||||
use Activitypub\Collection\Users;
|
use Activitypub\Collection\Users;
|
||||||
use Activitypub\Collection\Followers;
|
use Activitypub\Collection\Followers;
|
||||||
|
|
||||||
use function Activitypub\sanitize_url;
|
|
||||||
use function Activitypub\is_comment;
|
use function Activitypub\is_comment;
|
||||||
|
use function Activitypub\sanitize_url;
|
||||||
|
use function Activitypub\is_local_comment;
|
||||||
use function Activitypub\is_activitypub_request;
|
use function Activitypub\is_activitypub_request;
|
||||||
|
use function Activitypub\should_comment_be_federated;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* ActivityPub Class
|
* ActivityPub Class
|
||||||
@ -24,10 +26,9 @@ class Activitypub {
|
|||||||
\add_action( 'template_redirect', array( self::class, 'template_redirect' ) );
|
\add_action( 'template_redirect', array( self::class, 'template_redirect' ) );
|
||||||
\add_filter( 'query_vars', array( self::class, 'add_query_vars' ) );
|
\add_filter( 'query_vars', array( self::class, 'add_query_vars' ) );
|
||||||
\add_filter( 'pre_get_avatar_data', array( self::class, 'pre_get_avatar_data' ), 11, 2 );
|
\add_filter( 'pre_get_avatar_data', array( self::class, 'pre_get_avatar_data' ), 11, 2 );
|
||||||
\add_filter( 'get_comment_link', array( self::class, 'remote_comment_link' ), 11, 3 );
|
|
||||||
|
|
||||||
// Add support for ActivityPub to custom post types
|
// Add support for ActivityPub to custom post types
|
||||||
$post_types = \get_option( 'activitypub_support_post_types', array( 'post', 'page' ) ) ? \get_option( 'activitypub_support_post_types', array( 'post', 'page' ) ) : array();
|
$post_types = \get_option( 'activitypub_support_post_types', array( 'post' ) ) ? \get_option( 'activitypub_support_post_types', array( 'post' ) ) : array();
|
||||||
|
|
||||||
foreach ( $post_types as $post_type ) {
|
foreach ( $post_types as $post_type ) {
|
||||||
\add_post_type_support( $post_type, 'activitypub' );
|
\add_post_type_support( $post_type, 'activitypub' );
|
||||||
@ -42,8 +43,6 @@ class Activitypub {
|
|||||||
|
|
||||||
\add_action( 'in_plugin_update_message-' . ACTIVITYPUB_PLUGIN_BASENAME, array( self::class, 'plugin_update_message' ) );
|
\add_action( 'in_plugin_update_message-' . ACTIVITYPUB_PLUGIN_BASENAME, array( self::class, 'plugin_update_message' ) );
|
||||||
|
|
||||||
\add_filter( 'comment_class', array( self::class, 'comment_class' ), 10, 3 );
|
|
||||||
|
|
||||||
// register several post_types
|
// register several post_types
|
||||||
self::register_post_types();
|
self::register_post_types();
|
||||||
}
|
}
|
||||||
@ -100,6 +99,11 @@ class Activitypub {
|
|||||||
return $template;
|
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() ) {
|
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() ) {
|
||||||
@ -144,7 +148,7 @@ class Activitypub {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// stop if it's not an ActivityPub comment
|
// stop if it's not an ActivityPub comment
|
||||||
if ( is_activitypub_request() && $comment->user_id ) {
|
if ( is_activitypub_request() && ! is_local_comment( $comment ) ) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -225,30 +229,6 @@ class Activitypub {
|
|||||||
return \get_comment_meta( $comment->comment_ID, 'avatar_url', true );
|
return \get_comment_meta( $comment->comment_ID, 'avatar_url', true );
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Link remote comments to source url.
|
|
||||||
*
|
|
||||||
* @param string $comment_link
|
|
||||||
* @param object|WP_Comment $comment
|
|
||||||
*
|
|
||||||
* @return string $url
|
|
||||||
*/
|
|
||||||
public static function remote_comment_link( $comment_link, $comment ) {
|
|
||||||
if ( ! $comment || is_admin() ) {
|
|
||||||
return $comment_link;
|
|
||||||
}
|
|
||||||
|
|
||||||
$comment_meta = \get_comment_meta( $comment->comment_ID );
|
|
||||||
|
|
||||||
if ( ! empty( $comment_meta['source_url'][0] ) ) {
|
|
||||||
return $comment_meta['source_url'][0];
|
|
||||||
} elseif ( ! empty( $comment_meta['source_id'][0] ) ) {
|
|
||||||
return $comment_meta['source_id'][0];
|
|
||||||
}
|
|
||||||
|
|
||||||
return $comment_link;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Store permalink in meta, to send delete Activity.
|
* Store permalink in meta, to send delete Activity.
|
||||||
*
|
*
|
||||||
@ -457,22 +437,4 @@ class Activitypub {
|
|||||||
|
|
||||||
\do_action( 'activitypub_after_register_post_type' );
|
\do_action( 'activitypub_after_register_post_type' );
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Filters the CSS classes to add an ActivityPub class.
|
|
||||||
*
|
|
||||||
* @param string[] $classes An array of comment classes.
|
|
||||||
* @param string[] $css_class An array of additional classes added to the list.
|
|
||||||
* @param string $comment_id The comment ID as a numeric string.
|
|
||||||
*
|
|
||||||
* @return string[] An array of classes.
|
|
||||||
*/
|
|
||||||
public static function comment_class( $classes, $css_class, $comment_id ) {
|
|
||||||
// check if ActivityPub comment
|
|
||||||
if ( 'activitypub' === get_comment_meta( $comment_id, 'protocol', true ) ) {
|
|
||||||
$classes[] = 'activitypub-comment';
|
|
||||||
}
|
|
||||||
|
|
||||||
return $classes;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -4,6 +4,10 @@ namespace Activitypub;
|
|||||||
use WP_User_Query;
|
use WP_User_Query;
|
||||||
use Activitypub\Model\Blog_User;
|
use Activitypub\Model\Blog_User;
|
||||||
|
|
||||||
|
use function Activitypub\is_user_disabled;
|
||||||
|
use function Activitypub\was_comment_received;
|
||||||
|
use function Activitypub\is_comment_federatable;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* ActivityPub Admin Class
|
* ActivityPub Admin Class
|
||||||
*
|
*
|
||||||
@ -16,9 +20,11 @@ class Admin {
|
|||||||
public static function init() {
|
public static function init() {
|
||||||
\add_action( 'admin_menu', array( self::class, 'admin_menu' ) );
|
\add_action( 'admin_menu', array( self::class, 'admin_menu' ) );
|
||||||
\add_action( 'admin_init', array( self::class, 'register_settings' ) );
|
\add_action( 'admin_init', array( self::class, 'register_settings' ) );
|
||||||
|
\add_action( 'load-comment.php', array( self::class, 'edit_comment' ) );
|
||||||
\add_action( 'personal_options_update', array( self::class, 'save_user_description' ) );
|
\add_action( 'personal_options_update', array( self::class, 'save_user_description' ) );
|
||||||
\add_action( 'admin_enqueue_scripts', array( self::class, 'enqueue_scripts' ) );
|
\add_action( 'admin_enqueue_scripts', array( self::class, 'enqueue_scripts' ) );
|
||||||
\add_action( 'admin_notices', array( self::class, 'admin_notices' ) );
|
\add_action( 'admin_notices', array( self::class, 'admin_notices' ) );
|
||||||
|
\add_filter( 'comment_row_actions', array( self::class, 'comment_row_actions' ), 10, 2 );
|
||||||
|
|
||||||
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' ) );
|
||||||
@ -193,7 +199,7 @@ class Admin {
|
|||||||
'type' => 'string',
|
'type' => 'string',
|
||||||
'description' => \esc_html__( 'Enable ActivityPub support for post types', 'activitypub' ),
|
'description' => \esc_html__( 'Enable ActivityPub support for post types', 'activitypub' ),
|
||||||
'show_in_rest' => true,
|
'show_in_rest' => true,
|
||||||
'default' => array( 'post', 'pages' ),
|
'default' => array( 'post' ),
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
\register_setting(
|
\register_setting(
|
||||||
@ -304,4 +310,40 @@ class Admin {
|
|||||||
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-styles', plugins_url( 'assets/js/activitypub-admin.js', ACTIVITYPUB_PLUGIN_FILE ), array( 'jquery' ), '1.0.0', false );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Hook into the edit_comment functionality
|
||||||
|
*
|
||||||
|
* * Disable the edit_comment capability for federated comments.
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public static function edit_comment() {
|
||||||
|
// Disable the edit_comment capability for federated comments.
|
||||||
|
\add_filter(
|
||||||
|
'user_has_cap',
|
||||||
|
function ( $allcaps, $caps, $arg ) {
|
||||||
|
if ( 'edit_comment' !== $arg[0] ) {
|
||||||
|
return $allcaps;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( was_comment_received( $arg[2] ) ) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $allcaps;
|
||||||
|
},
|
||||||
|
1,
|
||||||
|
3
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function comment_row_actions( $actions, $comment ) {
|
||||||
|
if ( was_comment_received( $comment ) ) {
|
||||||
|
unset( $actions['edit'] );
|
||||||
|
unset( $actions['quickedit'] );
|
||||||
|
}
|
||||||
|
|
||||||
|
return $actions;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -78,6 +78,12 @@ class Blocks {
|
|||||||
array( 'icon', 'name', 'webfinger' )
|
array( 'icon', 'name', 'webfinger' )
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// add `@` prefix if it's missing
|
||||||
|
if ( '@' !== substr( $attrs['profileData']['webfinger'], 0, 1 ) ) {
|
||||||
|
$attrs['profileData']['webfinger'] = '@' . $attrs['profileData']['webfinger'];
|
||||||
|
}
|
||||||
|
|
||||||
$wrapper_attributes = get_block_wrapper_attributes(
|
$wrapper_attributes = get_block_wrapper_attributes(
|
||||||
array(
|
array(
|
||||||
'aria-label' => __( 'Follow me on the Fediverse', 'activitypub' ),
|
'aria-label' => __( 'Follow me on the Fediverse', 'activitypub' ),
|
||||||
|
384
wp-content/plugins/activitypub/includes/class-comment.php
Normal file
384
wp-content/plugins/activitypub/includes/class-comment.php
Normal file
@ -0,0 +1,384 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Activitypub;
|
||||||
|
|
||||||
|
use WP_Comment_Query;
|
||||||
|
|
||||||
|
use function Activitypub\is_user_disabled;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ActivityPub Comment Class
|
||||||
|
*
|
||||||
|
* This class is a helper/utils class that provides a collection of static
|
||||||
|
* methods that are used to handle comments.
|
||||||
|
*/
|
||||||
|
class Comment {
|
||||||
|
/**
|
||||||
|
* Initialize the class, registering WordPress hooks
|
||||||
|
*/
|
||||||
|
public static function init() {
|
||||||
|
\add_filter( 'comment_reply_link', array( self::class, 'comment_reply_link' ), 10, 3 );
|
||||||
|
\add_filter( 'comment_class', array( self::class, 'comment_class' ), 10, 3 );
|
||||||
|
\add_filter( 'get_comment_link', array( self::class, 'remote_comment_link' ), 11, 3 );
|
||||||
|
\add_action( 'wp_enqueue_scripts', array( self::class, 'enqueue_scripts' ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Filter the comment reply link.
|
||||||
|
*
|
||||||
|
* We don't want to show the comment reply link for federated comments
|
||||||
|
* if the user is disabled for federation.
|
||||||
|
*
|
||||||
|
* @param string $link The HTML markup for the comment reply link.
|
||||||
|
* @param array $args An array of arguments overriding the defaults.
|
||||||
|
* @param WP_Comment $comment The object of the comment being replied.
|
||||||
|
*
|
||||||
|
* @return string The filtered HTML markup for the comment reply link.
|
||||||
|
*/
|
||||||
|
public static function comment_reply_link( $link, $args, $comment ) {
|
||||||
|
if ( self::are_comments_allowed( $comment ) ) {
|
||||||
|
return $link;
|
||||||
|
}
|
||||||
|
|
||||||
|
$attrs = array(
|
||||||
|
'selectedComment' => self::generate_id( $comment ),
|
||||||
|
'commentId' => $comment->comment_ID,
|
||||||
|
);
|
||||||
|
|
||||||
|
$div = sprintf(
|
||||||
|
'<div class="activitypub-remote-reply" data-attrs="%s"></div>',
|
||||||
|
esc_attr( wp_json_encode( $attrs ) )
|
||||||
|
);
|
||||||
|
|
||||||
|
return apply_filters( 'activitypub_comment_reply_link', $div );
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if it is allowed to comment to a comment.
|
||||||
|
*
|
||||||
|
* Checks if the comment is local only or if the user can comment federated comments.
|
||||||
|
*
|
||||||
|
* @param mixed $comment Comment object or ID.
|
||||||
|
*
|
||||||
|
* @return boolean True if the user can comment, false otherwise.
|
||||||
|
*/
|
||||||
|
public static function are_comments_allowed( $comment ) {
|
||||||
|
$comment = \get_comment( $comment );
|
||||||
|
|
||||||
|
if ( ! self::was_received( $comment ) ) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
$current_user = get_current_user_id();
|
||||||
|
|
||||||
|
if ( ! $current_user ) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
$is_user_disabled = is_user_disabled( $current_user );
|
||||||
|
|
||||||
|
if ( $is_user_disabled ) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if a comment is federated.
|
||||||
|
*
|
||||||
|
* We consider a comment federated if comment was received via ActivityPub.
|
||||||
|
*
|
||||||
|
* Use this function to check if it is comment that was received via ActivityPub.
|
||||||
|
*
|
||||||
|
* @param mixed $comment Comment object or ID.
|
||||||
|
*
|
||||||
|
* @return boolean True if the comment is federated, false otherwise.
|
||||||
|
*/
|
||||||
|
public static function was_received( $comment ) {
|
||||||
|
$comment = \get_comment( $comment );
|
||||||
|
|
||||||
|
if ( ! $comment ) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
$protocol = \get_comment_meta( $comment->comment_ID, 'protocol', true );
|
||||||
|
|
||||||
|
if ( 'activitypub' === $protocol ) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if a comment was federated.
|
||||||
|
*
|
||||||
|
* This function checks if a comment was federated via ActivityPub.
|
||||||
|
*
|
||||||
|
* @param mixed $comment Comment object or ID.
|
||||||
|
*
|
||||||
|
* @return boolean True if the comment was federated, false otherwise.
|
||||||
|
*/
|
||||||
|
public static function was_sent( $comment ) {
|
||||||
|
$comment = \get_comment( $comment );
|
||||||
|
|
||||||
|
if ( ! $comment ) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
$status = \get_comment_meta( $comment->comment_ID, 'activitypub_status', true );
|
||||||
|
|
||||||
|
if ( $status ) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if a comment is local only.
|
||||||
|
*
|
||||||
|
* This function checks if a comment is local only and was not sent or received via ActivityPub.
|
||||||
|
*
|
||||||
|
* @param mixed $comment Comment object or ID.
|
||||||
|
*
|
||||||
|
* @return boolean True if the comment is local only, false otherwise.
|
||||||
|
*/
|
||||||
|
public static function is_local( $comment ) {
|
||||||
|
if ( self::was_sent( $comment ) || self::was_received( $comment ) ) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if a comment should be federated.
|
||||||
|
*
|
||||||
|
* We consider a comment should be federated if it is authored by a user that is
|
||||||
|
* not disabled for federation and if it is a reply directly to the post or to a
|
||||||
|
* federated comment.
|
||||||
|
*
|
||||||
|
* Use this function to check if a comment should be federated.
|
||||||
|
*
|
||||||
|
* @param mixed $comment Comment object or ID.
|
||||||
|
*
|
||||||
|
* @return boolean True if the comment should be federated, false otherwise.
|
||||||
|
*/
|
||||||
|
public static function should_be_federated( $comment ) {
|
||||||
|
// we should not federate federated comments
|
||||||
|
if ( self::was_received( $comment ) ) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
$comment = \get_comment( $comment );
|
||||||
|
$user_id = $comment->user_id;
|
||||||
|
|
||||||
|
// comments without user can't be federated
|
||||||
|
if ( ! $user_id ) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
$is_user_disabled = is_user_disabled( $user_id );
|
||||||
|
|
||||||
|
// user is disabled for federation
|
||||||
|
if ( $is_user_disabled ) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// it is a comment to the post and can be federated
|
||||||
|
if ( empty( $comment->comment_parent ) ) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// check if parent comment is federated
|
||||||
|
$parent_comment = \get_comment( $comment->comment_parent );
|
||||||
|
|
||||||
|
return ! self::is_local( $parent_comment );
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Examine a comment ID and look up an existing comment it represents.
|
||||||
|
*
|
||||||
|
* @param string $id ActivityPub object ID (usually a URL) to check.
|
||||||
|
*
|
||||||
|
* @return int|boolean Comment ID, or false on failure.
|
||||||
|
*/
|
||||||
|
public static function object_id_to_comment( $id ) {
|
||||||
|
$comment_query = new WP_Comment_Query(
|
||||||
|
array(
|
||||||
|
'meta_key' => 'source_id', // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_key
|
||||||
|
'meta_value' => $id, // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_value
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
if ( ! $comment_query->comments ) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( count( $comment_query->comments ) > 1 ) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $comment_query->comments[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Verify if URL is a local comment, or if it is a previously received
|
||||||
|
* remote comment (For threading comments locally)
|
||||||
|
*
|
||||||
|
* @param string $url The URL to check.
|
||||||
|
*
|
||||||
|
* @return int comment_ID or null if not found
|
||||||
|
*/
|
||||||
|
public static function url_to_commentid( $url ) {
|
||||||
|
if ( ! $url || ! filter_var( $url, \FILTER_VALIDATE_URL ) ) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// check for local comment
|
||||||
|
if ( \wp_parse_url( \site_url(), \PHP_URL_HOST ) === \wp_parse_url( $url, \PHP_URL_HOST ) ) {
|
||||||
|
$query = \wp_parse_url( $url, \PHP_URL_QUERY );
|
||||||
|
|
||||||
|
if ( $query ) {
|
||||||
|
parse_str( $query, $params );
|
||||||
|
|
||||||
|
if ( ! empty( $params['c'] ) ) {
|
||||||
|
$comment = \get_comment( $params['c'] );
|
||||||
|
|
||||||
|
if ( $comment ) {
|
||||||
|
return $comment->comment_ID;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$args = array(
|
||||||
|
// phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_query
|
||||||
|
'meta_query' => array(
|
||||||
|
'relation' => 'OR',
|
||||||
|
array(
|
||||||
|
'key' => 'source_url',
|
||||||
|
'value' => $url,
|
||||||
|
),
|
||||||
|
array(
|
||||||
|
'key' => 'source_id',
|
||||||
|
'value' => $url,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
$query = new WP_Comment_Query();
|
||||||
|
$comments = $query->query( $args );
|
||||||
|
|
||||||
|
if ( $comments && is_array( $comments ) ) {
|
||||||
|
return $comments[0]->comment_ID;
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Filters the CSS classes to add an ActivityPub class.
|
||||||
|
*
|
||||||
|
* @param string[] $classes An array of comment classes.
|
||||||
|
* @param string[] $css_class An array of additional classes added to the list.
|
||||||
|
* @param string $comment_id The comment ID as a numeric string.
|
||||||
|
*
|
||||||
|
* @return string[] An array of classes.
|
||||||
|
*/
|
||||||
|
public static function comment_class( $classes, $css_class, $comment_id ) {
|
||||||
|
// check if ActivityPub comment
|
||||||
|
if ( 'activitypub' === get_comment_meta( $comment_id, 'protocol', true ) ) {
|
||||||
|
$classes[] = 'activitypub-comment';
|
||||||
|
}
|
||||||
|
|
||||||
|
return $classes;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Link remote comments to source url.
|
||||||
|
*
|
||||||
|
* @param string $comment_link
|
||||||
|
* @param object|WP_Comment $comment
|
||||||
|
*
|
||||||
|
* @return string $url
|
||||||
|
*/
|
||||||
|
public static function remote_comment_link( $comment_link, $comment ) {
|
||||||
|
if ( ! $comment || is_admin() ) {
|
||||||
|
return $comment_link;
|
||||||
|
}
|
||||||
|
|
||||||
|
$comment_meta = \get_comment_meta( $comment->comment_ID );
|
||||||
|
|
||||||
|
if ( ! empty( $comment_meta['source_url'][0] ) ) {
|
||||||
|
return $comment_meta['source_url'][0];
|
||||||
|
} elseif ( ! empty( $comment_meta['source_id'][0] ) ) {
|
||||||
|
return $comment_meta['source_id'][0];
|
||||||
|
}
|
||||||
|
|
||||||
|
return $comment_link;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generates an ActivityPub URI for a comment
|
||||||
|
*
|
||||||
|
* @param WP_Comment|int $comment A comment object or comment ID
|
||||||
|
*
|
||||||
|
* @return string ActivityPub URI for comment
|
||||||
|
*/
|
||||||
|
public static function generate_id( $comment ) {
|
||||||
|
$comment = get_comment( $comment );
|
||||||
|
|
||||||
|
// show external comment ID if it exists
|
||||||
|
$source_id = get_comment_meta( $comment->comment_ID, 'source_id', true );
|
||||||
|
if ( ! empty( $source_id ) ) {
|
||||||
|
return $source_id;
|
||||||
|
}
|
||||||
|
|
||||||
|
// generate URI based on comment ID
|
||||||
|
return \add_query_arg(
|
||||||
|
array(
|
||||||
|
'c' => $comment->comment_ID,
|
||||||
|
),
|
||||||
|
\trailingslashit( site_url() )
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Enqueue scripts for remote comments
|
||||||
|
*/
|
||||||
|
public static function enqueue_scripts() {
|
||||||
|
if ( ! is_singular() ) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
$handle = 'activitypub-remote-reply';
|
||||||
|
$data = array(
|
||||||
|
'namespace' => ACTIVITYPUB_REST_NAMESPACE,
|
||||||
|
);
|
||||||
|
$js = sprintf( 'var _activityPubOptions = %s;', wp_json_encode( $data ) );
|
||||||
|
$asset_file = ACTIVITYPUB_PLUGIN_DIR . 'build/remote-reply/index.asset.php';
|
||||||
|
|
||||||
|
if ( \file_exists( $asset_file ) ) {
|
||||||
|
$assets = require_once $asset_file;
|
||||||
|
|
||||||
|
\wp_enqueue_script(
|
||||||
|
$handle,
|
||||||
|
\plugins_url( 'build/remote-reply/index.js', __DIR__ ),
|
||||||
|
$assets['dependencies'],
|
||||||
|
$assets['version'],
|
||||||
|
true
|
||||||
|
);
|
||||||
|
\wp_add_inline_script( $handle, $js, 'before' );
|
||||||
|
|
||||||
|
\wp_enqueue_style(
|
||||||
|
$handle,
|
||||||
|
\plugins_url( 'build/remote-reply/style-index.css', __DIR__ ),
|
||||||
|
[ 'wp-components' ],
|
||||||
|
$assets['version']
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -14,11 +14,12 @@ class Debug {
|
|||||||
* Initialize the class, registering WordPress hooks
|
* Initialize the class, registering WordPress hooks
|
||||||
*/
|
*/
|
||||||
public static function init() {
|
public static function init() {
|
||||||
if ( WP_DEBUG && WP_DEBUG_LOG ) {
|
if ( WP_DEBUG_LOG ) {
|
||||||
\add_action( 'activitypub_safe_remote_post_response', array( self::class, 'log_remote_post_responses' ), 10, 4 );
|
\add_action( 'activitypub_safe_remote_post_response', array( self::class, 'log_remote_post_responses' ), 10, 4 );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// phpcs:ignore Generic.CodeAnalysis.UnusedFunctionParameter.FoundAfterLastUsed, VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable
|
||||||
public static function log_remote_post_responses( $response, $url, $body, $user_id ) {
|
public static function log_remote_post_responses( $response, $url, $body, $user_id ) {
|
||||||
// phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_error_log, WordPress.PHP.DevelopmentFunctions.error_log_print_r
|
// phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_error_log, WordPress.PHP.DevelopmentFunctions.error_log_print_r
|
||||||
\error_log( "Request to: {$url} with response: " . \print_r( $response, true ) );
|
\error_log( "Request to: {$url} with response: " . \print_r( $response, true ) );
|
||||||
|
@ -102,7 +102,7 @@ class Mention {
|
|||||||
$username = $metadata['preferredUsername'];
|
$username = $metadata['preferredUsername'];
|
||||||
}
|
}
|
||||||
|
|
||||||
$url = isset( $metadata['url'] ) ? $metadata['url'] : $metadata['url'];
|
$url = isset( $metadata['url'] ) ? $metadata['url'] : $metadata['id'];
|
||||||
|
|
||||||
if ( \is_array( $url ) ) {
|
if ( \is_array( $url ) ) {
|
||||||
$url = $url[0];
|
$url = $url[0];
|
||||||
|
@ -117,6 +117,9 @@ class Migration {
|
|||||||
if ( version_compare( $version_from_db, '1.3.0', '<' ) ) {
|
if ( version_compare( $version_from_db, '1.3.0', '<' ) ) {
|
||||||
self::migrate_from_1_2_0();
|
self::migrate_from_1_2_0();
|
||||||
}
|
}
|
||||||
|
if ( version_compare( $version_from_db, '2.1.0', '<' ) ) {
|
||||||
|
self::migrate_from_2_0_0();
|
||||||
|
}
|
||||||
|
|
||||||
update_option( 'activitypub_db_version', self::get_target_version() );
|
update_option( 'activitypub_db_version', self::get_target_version() );
|
||||||
|
|
||||||
@ -197,4 +200,19 @@ class Migration {
|
|||||||
wp_cache_delete( sprintf( Followers::CACHE_KEY_INBOXES, $user_id ), 'activitypub' );
|
wp_cache_delete( sprintf( Followers::CACHE_KEY_INBOXES, $user_id ), 'activitypub' );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Unschedule Hooks after updating to 2.0.0
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
private static function migrate_from_2_0_0() {
|
||||||
|
wp_clear_scheduled_hook( 'activitypub_send_post_activity' );
|
||||||
|
wp_clear_scheduled_hook( 'activitypub_send_update_activity' );
|
||||||
|
wp_clear_scheduled_hook( 'activitypub_send_delete_activity' );
|
||||||
|
|
||||||
|
wp_unschedule_hook( 'activitypub_send_post_activity' );
|
||||||
|
wp_unschedule_hook( 'activitypub_send_update_activity' );
|
||||||
|
wp_unschedule_hook( 'activitypub_send_delete_activity' );
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -2,11 +2,14 @@
|
|||||||
|
|
||||||
namespace Activitypub;
|
namespace Activitypub;
|
||||||
|
|
||||||
|
use Activitypub\Transformer\Post;
|
||||||
use Activitypub\Collection\Users;
|
use Activitypub\Collection\Users;
|
||||||
use Activitypub\Collection\Followers;
|
use Activitypub\Collection\Followers;
|
||||||
use Activitypub\Transformer\Post;
|
|
||||||
|
|
||||||
|
use function Activitypub\was_comment_sent;
|
||||||
use function Activitypub\is_user_type_disabled;
|
use function Activitypub\is_user_type_disabled;
|
||||||
|
use function Activitypub\should_comment_be_federated;
|
||||||
|
use function Activitypub\get_remote_metadata_by_actor;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* ActivityPub Scheduler Class
|
* ActivityPub Scheduler Class
|
||||||
@ -40,20 +43,22 @@ class Scheduler {
|
|||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
// Comment transitions
|
if ( ! ACTIVITYPUB_DISABLE_OUTGOING_INTERACTIONS ) {
|
||||||
\add_action( 'transition_comment_status', array( self::class, 'schedule_comment_activity' ), 20, 3 );
|
// Comment transitions
|
||||||
\add_action(
|
\add_action( 'transition_comment_status', array( self::class, 'schedule_comment_activity' ), 20, 3 );
|
||||||
'edit_comment',
|
\add_action(
|
||||||
function ( $comment_id ) {
|
'edit_comment',
|
||||||
self::schedule_comment_activity( 'approved', 'approved', $comment_id );
|
function ( $comment_id ) {
|
||||||
}
|
self::schedule_comment_activity( 'approved', 'approved', $comment_id );
|
||||||
);
|
}
|
||||||
\add_action(
|
);
|
||||||
'wp_insert_comment',
|
\add_action(
|
||||||
function ( $comment_id ) {
|
'wp_insert_comment',
|
||||||
self::schedule_comment_activity( 'approved', '', $comment_id );
|
function ( $comment_id ) {
|
||||||
}
|
self::schedule_comment_activity( 'approved', '', $comment_id );
|
||||||
);
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
// Follower Cleanups
|
// Follower Cleanups
|
||||||
\add_action( 'activitypub_update_followers', array( self::class, 'update_followers' ) );
|
\add_action( 'activitypub_update_followers', array( self::class, 'update_followers' ) );
|
||||||
@ -136,24 +141,17 @@ class Scheduler {
|
|||||||
$type = 'Delete';
|
$type = 'Delete';
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( ! $type ) {
|
if ( empty( $type ) ) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
\wp_schedule_single_event(
|
$hook = 'activitypub_send_post';
|
||||||
\time(),
|
$args = array( $post->ID, $type );
|
||||||
'activitypub_send_activity',
|
|
||||||
array( $post, $type )
|
|
||||||
);
|
|
||||||
|
|
||||||
\wp_schedule_single_event(
|
if ( false === wp_next_scheduled( $hook, $args ) ) {
|
||||||
\time(),
|
set_wp_object_state( $post, 'federate' );
|
||||||
sprintf(
|
\wp_schedule_single_event( \time(), $hook, $args );
|
||||||
'activitypub_send_%s_activity',
|
}
|
||||||
\strtolower( $type )
|
|
||||||
),
|
|
||||||
array( $post )
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -168,11 +166,13 @@ class Scheduler {
|
|||||||
public static function schedule_comment_activity( $new_status, $old_status, $comment ) {
|
public static function schedule_comment_activity( $new_status, $old_status, $comment ) {
|
||||||
$comment = get_comment( $comment );
|
$comment = get_comment( $comment );
|
||||||
|
|
||||||
// Federate only approved comments.
|
// federate only comments that are written by a registered user.
|
||||||
if ( ! $comment->user_id ) {
|
if ( ! $comment->user_id ) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$type = false;
|
||||||
|
|
||||||
if (
|
if (
|
||||||
'approved' === $new_status &&
|
'approved' === $new_status &&
|
||||||
'approved' !== $old_status
|
'approved' !== $old_status
|
||||||
@ -188,24 +188,22 @@ class Scheduler {
|
|||||||
$type = 'Delete';
|
$type = 'Delete';
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( ! $type ) {
|
if ( empty( $type ) ) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
\wp_schedule_single_event(
|
// check if comment should be federated or not
|
||||||
\time(),
|
if ( ! should_comment_be_federated( $comment ) ) {
|
||||||
'activitypub_send_activity',
|
return;
|
||||||
array( $comment, $type )
|
}
|
||||||
);
|
|
||||||
|
|
||||||
\wp_schedule_single_event(
|
$hook = 'activitypub_send_comment';
|
||||||
\time(),
|
$args = array( $comment->comment_ID, $type );
|
||||||
sprintf(
|
|
||||||
'activitypub_send_%s_activity',
|
if ( false === wp_next_scheduled( $hook, $args ) ) {
|
||||||
\strtolower( $type )
|
set_wp_object_state( $comment, 'federate' );
|
||||||
),
|
\wp_schedule_single_event( \time(), $hook, $args );
|
||||||
array( $comment )
|
}
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -220,6 +218,7 @@ class Scheduler {
|
|||||||
$number = 50;
|
$number = 50;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$number = apply_filters( 'activitypub_update_followers_number', $number );
|
||||||
$followers = Followers::get_outdated_followers( $number );
|
$followers = Followers::get_outdated_followers( $number );
|
||||||
|
|
||||||
foreach ( $followers as $follower ) {
|
foreach ( $followers as $follower ) {
|
||||||
@ -246,6 +245,7 @@ class Scheduler {
|
|||||||
$number = 50;
|
$number = 50;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$number = apply_filters( 'activitypub_update_followers_number', $number );
|
||||||
$followers = Followers::get_faulty_followers( $number );
|
$followers = Followers::get_faulty_followers( $number );
|
||||||
|
|
||||||
foreach ( $followers as $follower ) {
|
foreach ( $followers as $follower ) {
|
||||||
|
@ -42,16 +42,31 @@ class Webfinger {
|
|||||||
return $data;
|
return $data;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if ( ! is_array( $data ) || empty( $data['links'] ) ) {
|
||||||
|
return new WP_Error(
|
||||||
|
'webfinger_missing_links',
|
||||||
|
__( 'No valid Link elements found.', 'activitypub' ),
|
||||||
|
$data
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
foreach ( $data['links'] as $link ) {
|
foreach ( $data['links'] as $link ) {
|
||||||
if (
|
if (
|
||||||
'self' === $link['rel'] &&
|
'self' === $link['rel'] &&
|
||||||
'application/activity+json' === $link['type']
|
(
|
||||||
|
'application/activity+json' === $link['type'] ||
|
||||||
|
'application/ld+json; profile="https://www.w3.org/ns/activitystreams"' === $link['type']
|
||||||
|
)
|
||||||
) {
|
) {
|
||||||
return $link['href'];
|
return $link['href'];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return new WP_Error( 'webfinger_url_no_activitypub', null, $data );
|
return new WP_Error(
|
||||||
|
'webfinger_url_no_activitypub',
|
||||||
|
__( 'The Site supports WebFinger but not ActivityPub', 'activitypub' ),
|
||||||
|
$data
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -2,8 +2,8 @@
|
|||||||
namespace Activitypub;
|
namespace Activitypub;
|
||||||
|
|
||||||
use WP_Error;
|
use WP_Error;
|
||||||
use WP_Comment_Query;
|
|
||||||
use Activitypub\Http;
|
use Activitypub\Http;
|
||||||
|
use Activitypub\Comment;
|
||||||
use Activitypub\Webfinger;
|
use Activitypub\Webfinger;
|
||||||
use Activitypub\Activity\Activity;
|
use Activitypub\Activity\Activity;
|
||||||
use Activitypub\Collection\Followers;
|
use Activitypub\Collection\Followers;
|
||||||
@ -15,7 +15,7 @@ use Activitypub\Collection\Users;
|
|||||||
* @return array the activitypub context
|
* @return array the activitypub context
|
||||||
*/
|
*/
|
||||||
function get_context() {
|
function get_context() {
|
||||||
$context = Activity::CONTEXT;
|
$context = Activity::JSON_LD_CONTEXT;
|
||||||
|
|
||||||
return \apply_filters( 'activitypub_json_context', $context );
|
return \apply_filters( 'activitypub_json_context', $context );
|
||||||
}
|
}
|
||||||
@ -312,6 +312,12 @@ function is_activitypub_request() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Check if header already sent.
|
||||||
|
if ( ! \headers_sent() && ACTIVITYPUB_SEND_VARY_HEADER ) {
|
||||||
|
// Send Vary header for Accept header.
|
||||||
|
\header( 'Vary: Accept' );
|
||||||
|
}
|
||||||
|
|
||||||
// One can trigger an ActivityPub request by adding ?activitypub to the URL.
|
// One can trigger an ActivityPub request by adding ?activitypub to the URL.
|
||||||
// phpcs:ignore VariableAnalysis.CodeAnalysis.VariableAnalysis.VariableRedeclaration
|
// phpcs:ignore VariableAnalysis.CodeAnalysis.VariableAnalysis.VariableRedeclaration
|
||||||
global $wp_query;
|
global $wp_query;
|
||||||
@ -664,22 +670,7 @@ function get_total_users() {
|
|||||||
* @return int|boolean Comment ID, or false on failure.
|
* @return int|boolean Comment ID, or false on failure.
|
||||||
*/
|
*/
|
||||||
function object_id_to_comment( $id ) {
|
function object_id_to_comment( $id ) {
|
||||||
$comment_query = new WP_Comment_Query(
|
return Comment::object_id_to_comment( $id );
|
||||||
array(
|
|
||||||
'meta_key' => 'source_id', // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_key
|
|
||||||
'meta_value' => $id, // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_value
|
|
||||||
)
|
|
||||||
);
|
|
||||||
|
|
||||||
if ( ! $comment_query->comments ) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ( count( $comment_query->comments ) > 1 ) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return $comment_query->comments[0];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -692,50 +683,7 @@ function object_id_to_comment( $id ) {
|
|||||||
* @return int comment_ID or null if not found
|
* @return int comment_ID or null if not found
|
||||||
*/
|
*/
|
||||||
function url_to_commentid( $url ) {
|
function url_to_commentid( $url ) {
|
||||||
if ( ! $url || ! filter_var( $url, FILTER_VALIDATE_URL ) ) {
|
return Comment::url_to_commentid( $url );
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
// check for local comment
|
|
||||||
if ( \wp_parse_url( \site_url(), \PHP_URL_HOST ) === \wp_parse_url( $url, \PHP_URL_HOST ) ) {
|
|
||||||
$query = \wp_parse_url( $url, PHP_URL_QUERY );
|
|
||||||
|
|
||||||
if ( $query ) {
|
|
||||||
parse_str( $query, $params );
|
|
||||||
|
|
||||||
if ( ! empty( $params['c'] ) ) {
|
|
||||||
$comment = \get_comment( $params['c'] );
|
|
||||||
|
|
||||||
if ( $comment ) {
|
|
||||||
return $comment->comment_ID;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
$args = array(
|
|
||||||
// phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_query
|
|
||||||
'meta_query' => array(
|
|
||||||
'relation' => 'OR',
|
|
||||||
array(
|
|
||||||
'key' => 'source_url',
|
|
||||||
'value' => $url,
|
|
||||||
),
|
|
||||||
array(
|
|
||||||
'key' => 'source_id',
|
|
||||||
'value' => $url,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
|
|
||||||
$query = new \WP_Comment_Query();
|
|
||||||
$comments = $query->query( $args );
|
|
||||||
|
|
||||||
if ( $comments && is_array( $comments ) ) {
|
|
||||||
return $comments[0]->comment_ID;
|
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -774,3 +722,108 @@ function object_to_uri( $object ) {
|
|||||||
|
|
||||||
return $object;
|
return $object;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if a comment should be federated.
|
||||||
|
*
|
||||||
|
* We consider a comment should be federated if it is authored by a user that is
|
||||||
|
* not disabled for federation and if it is a reply directly to the post or to a
|
||||||
|
* federated comment.
|
||||||
|
*
|
||||||
|
* @param mixed $comment Comment object or ID.
|
||||||
|
*
|
||||||
|
* @return boolean True if the comment should be federated, false otherwise.
|
||||||
|
*/
|
||||||
|
function should_comment_be_federated( $comment ) {
|
||||||
|
return Comment::should_be_federated( $comment );
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if a comment was federated.
|
||||||
|
*
|
||||||
|
* This function checks if a comment was federated via ActivityPub.
|
||||||
|
*
|
||||||
|
* @param mixed $comment Comment object or ID.
|
||||||
|
*
|
||||||
|
* @return boolean True if the comment was federated, false otherwise.
|
||||||
|
*/
|
||||||
|
function was_comment_sent( $comment ) {
|
||||||
|
return Comment::was_sent( $comment );
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if a comment is federated.
|
||||||
|
*
|
||||||
|
* We consider a comment federated if comment was received via ActivityPub.
|
||||||
|
*
|
||||||
|
* Use this function to check if it is comment that was received via ActivityPub.
|
||||||
|
*
|
||||||
|
* @param mixed $comment Comment object or ID.
|
||||||
|
*
|
||||||
|
* @return boolean True if the comment is federated, false otherwise.
|
||||||
|
*/
|
||||||
|
function was_comment_received( $comment ) {
|
||||||
|
return Comment::was_received( $comment );
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if a comment is local only.
|
||||||
|
*
|
||||||
|
* This function checks if a comment is local only and was not sent or received via ActivityPub.
|
||||||
|
*
|
||||||
|
* @param mixed $comment Comment object or ID.
|
||||||
|
*
|
||||||
|
* @return boolean True if the comment is local only, false otherwise.
|
||||||
|
*/
|
||||||
|
function is_local_comment( $comment ) {
|
||||||
|
return Comment::is_local( $comment );
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Mark a WordPress object as federated.
|
||||||
|
*
|
||||||
|
* @param WP_Comment|WP_Post|mixed $wp_object
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
function set_wp_object_state( $wp_object, $state ) {
|
||||||
|
$meta_key = 'activitypub_status';
|
||||||
|
|
||||||
|
if ( $wp_object instanceof \WP_Post ) {
|
||||||
|
\update_post_meta( $wp_object->ID, $meta_key, $state );
|
||||||
|
} elseif ( $wp_object instanceof \WP_Comment ) {
|
||||||
|
\update_comment_meta( $wp_object->comment_ID, $meta_key, $state );
|
||||||
|
} else {
|
||||||
|
\apply_filters( 'activitypub_mark_wp_object_as_federated', $wp_object );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the description of a post type.
|
||||||
|
*
|
||||||
|
* Set some default descriptions for the default post types.
|
||||||
|
*
|
||||||
|
* @param WP_Post_Type $post_type The post type object.
|
||||||
|
*
|
||||||
|
* @return string The description of the post type.
|
||||||
|
*/
|
||||||
|
function get_post_type_description( $post_type ) {
|
||||||
|
$description = '';
|
||||||
|
|
||||||
|
switch ( $post_type->name ) {
|
||||||
|
case 'post':
|
||||||
|
$description = '';
|
||||||
|
break;
|
||||||
|
case 'page':
|
||||||
|
$description = '';
|
||||||
|
break;
|
||||||
|
case 'attachment':
|
||||||
|
$description = ' - ' . __( 'The attachments that you have uploaded to a post (images, videos, documents or other files).', 'activitypub' );
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
if ( ! empty( $post_type->description ) ) {
|
||||||
|
$description = ' - ' . $post_type->description;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return apply_filters( 'activitypub_post_type_description', $description, $post_type->name, $post_type );
|
||||||
|
}
|
||||||
|
@ -33,6 +33,10 @@ class Create {
|
|||||||
* @return void
|
* @return void
|
||||||
*/
|
*/
|
||||||
public static function handle_create( $array, $user_id, $object = null ) {
|
public static function handle_create( $array, $user_id, $object = null ) {
|
||||||
|
if ( ACTIVITYPUB_DISABLE_INCOMING_INTERACTIONS ) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (
|
if (
|
||||||
! isset( $array['object'] ) ||
|
! isset( $array['object'] ) ||
|
||||||
! isset( $array['object']['id'] )
|
! isset( $array['object']['id'] )
|
||||||
|
@ -81,7 +81,6 @@ class Delete {
|
|||||||
// check if Object is an Actor.
|
// check if Object is an Actor.
|
||||||
if ( $activity['actor'] === $activity['object'] ) {
|
if ( $activity['actor'] === $activity['object'] ) {
|
||||||
self::maybe_delete_follower( $activity );
|
self::maybe_delete_follower( $activity );
|
||||||
self::maybe_delete_interactions( $activity );
|
|
||||||
} else { // assume a interaction otherwise.
|
} else { // assume a interaction otherwise.
|
||||||
self::maybe_delete_interaction( $activity );
|
self::maybe_delete_interaction( $activity );
|
||||||
}
|
}
|
||||||
@ -101,6 +100,7 @@ class Delete {
|
|||||||
// verify if Actor is deleted.
|
// verify if Actor is deleted.
|
||||||
if ( $follower && Http::is_tombstone( $activity['actor'] ) ) {
|
if ( $follower && Http::is_tombstone( $activity['actor'] ) ) {
|
||||||
$follower->delete();
|
$follower->delete();
|
||||||
|
self::maybe_delete_interactions( $activity );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -15,19 +15,17 @@ class Application_User extends Blog_User {
|
|||||||
*/
|
*/
|
||||||
protected $_id = Users::APPLICATION_USER_ID; // phpcs:ignore PSR2.Classes.PropertyDeclaration.Underscore
|
protected $_id = Users::APPLICATION_USER_ID; // phpcs:ignore PSR2.Classes.PropertyDeclaration.Underscore
|
||||||
|
|
||||||
/**
|
public function get_type() {
|
||||||
* The User-Type
|
return 'Application';
|
||||||
*
|
}
|
||||||
* @var string
|
|
||||||
*/
|
|
||||||
protected $type = 'Application';
|
|
||||||
|
|
||||||
/**
|
public function get_discoverable() {
|
||||||
* If the User is discoverable.
|
return false;
|
||||||
*
|
}
|
||||||
* @var boolean
|
|
||||||
*/
|
public function get_manually_approves_followers() {
|
||||||
protected $discoverable = false;
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the User-Url.
|
* Get the User-Url.
|
||||||
@ -52,7 +50,7 @@ class Application_User extends Blog_User {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public function get_preferred_username() {
|
public function get_preferred_username() {
|
||||||
return $this::get_name();
|
return $this->get_name();
|
||||||
}
|
}
|
||||||
|
|
||||||
public function get_followers() {
|
public function get_followers() {
|
||||||
@ -78,8 +76,4 @@ class Application_User extends Blog_User {
|
|||||||
public function get_indexable() {
|
public function get_indexable() {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function get_type() {
|
|
||||||
return $this->type;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -2,7 +2,8 @@
|
|||||||
namespace Activitypub\Model;
|
namespace Activitypub\Model;
|
||||||
|
|
||||||
use WP_Query;
|
use WP_Query;
|
||||||
use Activitypub\Signature;
|
use WP_Error;
|
||||||
|
|
||||||
use Activitypub\Collection\Users;
|
use Activitypub\Collection\Users;
|
||||||
|
|
||||||
use function Activitypub\is_single_user;
|
use function Activitypub\is_single_user;
|
||||||
@ -17,19 +18,13 @@ 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
|
||||||
|
|
||||||
/**
|
public function get_manually_approves_followers() {
|
||||||
* The User-Type
|
return false;
|
||||||
*
|
}
|
||||||
* @var string
|
|
||||||
*/
|
|
||||||
protected $type = null;
|
|
||||||
|
|
||||||
/**
|
public function get_discoverable() {
|
||||||
* Is Account discoverable?
|
return true;
|
||||||
*
|
}
|
||||||
* @var boolean
|
|
||||||
*/
|
|
||||||
protected $discoverable = true;
|
|
||||||
|
|
||||||
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 ) ) {
|
||||||
|
@ -162,8 +162,10 @@ class Follower extends Actor {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$post_id = $this->get__id();
|
||||||
|
|
||||||
$args = array(
|
$args = array(
|
||||||
'ID' => $this->get__id(),
|
'ID' => $post_id,
|
||||||
'guid' => esc_url_raw( $this->get_id() ),
|
'guid' => esc_url_raw( $this->get_id() ),
|
||||||
'post_title' => wp_strip_all_tags( sanitize_text_field( $this->get_name() ) ),
|
'post_title' => wp_strip_all_tags( sanitize_text_field( $this->get_name() ) ),
|
||||||
'post_author' => 0,
|
'post_author' => 0,
|
||||||
@ -174,6 +176,14 @@ class Follower extends Actor {
|
|||||||
'meta_input' => $this->get_post_meta_input(),
|
'meta_input' => $this->get_post_meta_input(),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
if ( ! empty( $post_id ) ) {
|
||||||
|
// If this is an update, prevent the "followed" date from being
|
||||||
|
// overwritten by the current date.
|
||||||
|
$post = get_post( $post_id );
|
||||||
|
$args['post_date'] = $post->post_date;
|
||||||
|
$args['post_date_gmt'] = $post->post_date_gmt;
|
||||||
|
}
|
||||||
|
|
||||||
$post_id = wp_insert_post( $args );
|
$post_id = wp_insert_post( $args );
|
||||||
$this->_id = $post_id;
|
$this->_id = $post_id;
|
||||||
|
|
||||||
@ -286,6 +296,25 @@ class Follower extends Actor {
|
|||||||
return $icon;
|
return $icon;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the Icon URL (Avatar)
|
||||||
|
*
|
||||||
|
* @return string The URL to the Avatar.
|
||||||
|
*/
|
||||||
|
public function get_image_url() {
|
||||||
|
$image = $this->get_image();
|
||||||
|
|
||||||
|
if ( ! $image ) {
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( is_array( $image ) ) {
|
||||||
|
return $image['url'];
|
||||||
|
}
|
||||||
|
|
||||||
|
return $image;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the shared inbox, with a fallback to the inbox.
|
* Get the shared inbox, with a fallback to the inbox.
|
||||||
*
|
*
|
||||||
|
@ -23,6 +23,11 @@ class User extends Actor {
|
|||||||
*
|
*
|
||||||
* @see https://docs.joinmastodon.org/spec/activitypub/#featured
|
* @see https://docs.joinmastodon.org/spec/activitypub/#featured
|
||||||
*
|
*
|
||||||
|
* @context {
|
||||||
|
* "@id": "http://joinmastodon.org/ns#featured",
|
||||||
|
* "@type": "@id"
|
||||||
|
* }
|
||||||
|
*
|
||||||
* @var string
|
* @var string
|
||||||
*/
|
*/
|
||||||
protected $featured;
|
protected $featured;
|
||||||
@ -36,18 +41,17 @@ class User extends Actor {
|
|||||||
*/
|
*/
|
||||||
protected $moderators;
|
protected $moderators;
|
||||||
|
|
||||||
/**
|
public function get_type() {
|
||||||
* The User-Type
|
return 'Person';
|
||||||
*
|
}
|
||||||
* @var string
|
|
||||||
*/
|
|
||||||
protected $type = 'Person';
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* If the User is discoverable.
|
* If the User is discoverable.
|
||||||
*
|
*
|
||||||
* @see https://docs.joinmastodon.org/spec/activitypub/#discoverable
|
* @see https://docs.joinmastodon.org/spec/activitypub/#discoverable
|
||||||
*
|
*
|
||||||
|
* @context http://joinmastodon.org/ns#discoverable
|
||||||
|
*
|
||||||
* @var boolean
|
* @var boolean
|
||||||
*/
|
*/
|
||||||
protected $discoverable = true;
|
protected $discoverable = true;
|
||||||
@ -55,6 +59,8 @@ class User extends Actor {
|
|||||||
/**
|
/**
|
||||||
* If the User is indexable.
|
* If the User is indexable.
|
||||||
*
|
*
|
||||||
|
* @context http://joinmastodon.org/ns#indexable
|
||||||
|
*
|
||||||
* @var boolean
|
* @var boolean
|
||||||
*/
|
*/
|
||||||
protected $indexable;
|
protected $indexable;
|
||||||
|
@ -5,7 +5,8 @@ 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\Transformer\Post;
|
||||||
use Activitypub\Activity\Activity;
|
use Activitypub\Activity\Actor;
|
||||||
|
use Activitypub\Activity\Base_Object;
|
||||||
use Activitypub\Collection\Users as User_Collection;
|
use Activitypub\Collection\Users as User_Collection;
|
||||||
|
|
||||||
use function Activitypub\esc_hashtag;
|
use function Activitypub\esc_hashtag;
|
||||||
@ -102,7 +103,7 @@ class Collection {
|
|||||||
}
|
}
|
||||||
|
|
||||||
$response = array(
|
$response = array(
|
||||||
'@context' => Activity::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( 'users/%d/collections/tags', $user->get__id() ) ),
|
||||||
'type' => 'Collection',
|
'type' => 'Collection',
|
||||||
'totalItems' => is_countable( $tags ) ? count( $tags ) : 0,
|
'totalItems' => is_countable( $tags ) ? count( $tags ) : 0,
|
||||||
@ -160,7 +161,7 @@ class Collection {
|
|||||||
}
|
}
|
||||||
|
|
||||||
$response = array(
|
$response = array(
|
||||||
'@context' => Activity::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( 'users/%d/collections/featured', $user_id ) ),
|
||||||
'type' => 'OrderedCollection',
|
'type' => 'OrderedCollection',
|
||||||
'totalItems' => is_countable( $posts ) ? count( $posts ) : 0,
|
'totalItems' => is_countable( $posts ) ? count( $posts ) : 0,
|
||||||
@ -168,7 +169,7 @@ class Collection {
|
|||||||
);
|
);
|
||||||
|
|
||||||
foreach ( $posts as $post ) {
|
foreach ( $posts as $post ) {
|
||||||
$response['orderedItems'][] = Post::transform( $post )->to_object()->to_array();
|
$response['orderedItems'][] = Post::transform( $post )->to_object()->to_array( false );
|
||||||
}
|
}
|
||||||
|
|
||||||
$rest_response = new WP_REST_Response( $response, 200 );
|
$rest_response = new WP_REST_Response( $response, 200 );
|
||||||
@ -186,7 +187,7 @@ class Collection {
|
|||||||
*/
|
*/
|
||||||
public static function moderators_get( $request ) {
|
public static function moderators_get( $request ) {
|
||||||
$response = array(
|
$response = array(
|
||||||
'@context' => Activity::CONTEXT,
|
'@context' => Actor::JSON_LD_CONTEXT,
|
||||||
'id' => get_rest_url_by_path( 'collections/moderators' ),
|
'id' => get_rest_url_by_path( 'collections/moderators' ),
|
||||||
'type' => 'OrderedCollection',
|
'type' => 'OrderedCollection',
|
||||||
'orderedItems' => array(),
|
'orderedItems' => array(),
|
||||||
|
@ -0,0 +1,91 @@
|
|||||||
|
<?php
|
||||||
|
namespace Activitypub\Rest;
|
||||||
|
|
||||||
|
use WP_Error;
|
||||||
|
use WP_REST_Server;
|
||||||
|
use WP_REST_Request;
|
||||||
|
use WP_REST_Response;
|
||||||
|
use Activitypub\Comment as Comment_Utils;
|
||||||
|
use Activitypub\Webfinger as Webfinger_Utils;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ActivityPub Followers REST-Class
|
||||||
|
*
|
||||||
|
* @author Matthias Pfefferle
|
||||||
|
*
|
||||||
|
* @see https://www.w3.org/TR/activitypub/#followers
|
||||||
|
*/
|
||||||
|
class Comment {
|
||||||
|
/**
|
||||||
|
* Initialize the class, registering WordPress hooks
|
||||||
|
*/
|
||||||
|
public static function init() {
|
||||||
|
self::register_routes();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Register routes
|
||||||
|
*/
|
||||||
|
public static function register_routes() {
|
||||||
|
\register_rest_route(
|
||||||
|
ACTIVITYPUB_REST_NAMESPACE,
|
||||||
|
'/comments/(?P<comment_id>\d+)/remote-reply',
|
||||||
|
array(
|
||||||
|
array(
|
||||||
|
'methods' => WP_REST_Server::READABLE,
|
||||||
|
'callback' => array( self::class, 'remote_reply_get' ),
|
||||||
|
'permission_callback' => '__return_true',
|
||||||
|
'args' => array(
|
||||||
|
'resource' => array(
|
||||||
|
'required' => true,
|
||||||
|
'sanitize_callback' => 'sanitize_text_field',
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Endpoint for remote follow UI/Block
|
||||||
|
*
|
||||||
|
* @param WP_REST_Request $request The request object.
|
||||||
|
*
|
||||||
|
* @return void|string The URL to the remote follow page
|
||||||
|
*/
|
||||||
|
public static function remote_reply_get( WP_REST_Request $request ) {
|
||||||
|
$resource = $request->get_param( 'resource' );
|
||||||
|
$comment_id = $request->get_param( 'comment_id' );
|
||||||
|
|
||||||
|
$comment = get_comment( $comment_id );
|
||||||
|
|
||||||
|
if ( ! $comment ) {
|
||||||
|
return new WP_Error( 'activitypub_comment_not_found', __( 'Comment not found', 'activitypub' ), array( 'status' => 404 ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
$is_local = Comment_Utils::is_local( $comment );
|
||||||
|
|
||||||
|
if ( $is_local ) {
|
||||||
|
return new WP_Error( 'activitypub_local_only_comment', __( 'Comment is local only', 'activitypub' ), array( 'status' => 403 ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
$template = Webfinger_Utils::get_remote_follow_endpoint( $resource );
|
||||||
|
|
||||||
|
if ( is_wp_error( $template ) ) {
|
||||||
|
return $template;
|
||||||
|
}
|
||||||
|
|
||||||
|
$resource = \get_comment_meta( $comment_id, 'source_id', true );
|
||||||
|
|
||||||
|
if ( ! $resource ) {
|
||||||
|
$resource = Comment_Utils::generate_id( $comment );
|
||||||
|
}
|
||||||
|
|
||||||
|
$url = str_replace( '{uri}', $resource, $template );
|
||||||
|
|
||||||
|
return new WP_REST_Response(
|
||||||
|
array( 'url' => $url ),
|
||||||
|
200
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
@ -96,7 +96,7 @@ class Followers {
|
|||||||
$json->orderedItems = array_map(
|
$json->orderedItems = array_map(
|
||||||
function ( $item ) use ( $context ) {
|
function ( $item ) use ( $context ) {
|
||||||
if ( 'full' === $context ) {
|
if ( 'full' === $context ) {
|
||||||
return $item->to_array();
|
return $item->to_array( false );
|
||||||
}
|
}
|
||||||
return $item->get_url();
|
return $item->get_url();
|
||||||
},
|
},
|
||||||
|
@ -11,7 +11,6 @@ use function Activitypub\get_context;
|
|||||||
use function Activitypub\object_to_uri;
|
use function Activitypub\object_to_uri;
|
||||||
use function Activitypub\url_to_authorid;
|
use function Activitypub\url_to_authorid;
|
||||||
use function Activitypub\get_rest_url_by_path;
|
use function Activitypub\get_rest_url_by_path;
|
||||||
use function Activitypub\get_remote_metadata_by_actor;
|
|
||||||
use function Activitypub\extract_recipients_from_activity;
|
use function Activitypub\extract_recipients_from_activity;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -129,7 +129,7 @@ class Nodeinfo {
|
|||||||
|
|
||||||
$nodeinfo = array();
|
$nodeinfo = array();
|
||||||
|
|
||||||
$nodeinfo['version'] = '1.0';
|
$nodeinfo['version'] = '2.0';
|
||||||
$nodeinfo['server'] = array(
|
$nodeinfo['server'] = array(
|
||||||
'baseUrl' => \home_url( '/' ),
|
'baseUrl' => \home_url( '/' ),
|
||||||
'name' => \get_bloginfo( 'name' ),
|
'name' => \get_bloginfo( 'name' ),
|
||||||
|
@ -59,7 +59,7 @@ class Outbox {
|
|||||||
return $user;
|
return $user;
|
||||||
}
|
}
|
||||||
|
|
||||||
$post_types = \get_option( 'activitypub_support_post_types', array( 'post', 'page' ) );
|
$post_types = \get_option( 'activitypub_support_post_types', array( 'post' ) );
|
||||||
|
|
||||||
$page = $request->get_param( 'page', 1 );
|
$page = $request->get_param( 'page', 1 );
|
||||||
|
|
||||||
@ -108,10 +108,8 @@ class Outbox {
|
|||||||
$post = Post::transform( $post )->to_object();
|
$post = Post::transform( $post )->to_object();
|
||||||
$activity = new Activity();
|
$activity = new Activity();
|
||||||
$activity->set_type( 'Create' );
|
$activity->set_type( 'Create' );
|
||||||
$activity->set_context( null );
|
|
||||||
$activity->set_object( $post );
|
$activity->set_object( $post );
|
||||||
|
$json->orderedItems[] = $activity->to_array( false ); // phpcs:ignore
|
||||||
$json->orderedItems[] = $activity->to_array(); // phpcs:ignore
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -49,10 +49,6 @@ class Server {
|
|||||||
public static function application_actor() {
|
public static function application_actor() {
|
||||||
$user = new Application_User();
|
$user = new Application_User();
|
||||||
|
|
||||||
$user->set_context(
|
|
||||||
\Activitypub\Activity\Activity::CONTEXT
|
|
||||||
);
|
|
||||||
|
|
||||||
$json = $user->to_array();
|
$json = $user->to_array();
|
||||||
|
|
||||||
$rest_response = new WP_REST_Response( $json, 200 );
|
$rest_response = new WP_REST_Response( $json, 200 );
|
||||||
|
@ -88,10 +88,6 @@ class Users {
|
|||||||
*/
|
*/
|
||||||
\do_action( 'activitypub_rest_users_pre' );
|
\do_action( 'activitypub_rest_users_pre' );
|
||||||
|
|
||||||
$user->set_context(
|
|
||||||
Activity::CONTEXT
|
|
||||||
);
|
|
||||||
|
|
||||||
$json = $user->to_array();
|
$json = $user->to_array();
|
||||||
|
|
||||||
$rest_response = new WP_REST_Response( $json, 200 );
|
$rest_response = new WP_REST_Response( $json, 200 );
|
||||||
|
@ -30,7 +30,7 @@ abstract class Base {
|
|||||||
*
|
*
|
||||||
* @param WP_Post|WP_Comment $wp_object The WordPress object
|
* @param WP_Post|WP_Comment $wp_object The WordPress object
|
||||||
*
|
*
|
||||||
* @return void
|
* @return Base_Object
|
||||||
*/
|
*/
|
||||||
public static function transform( $object ) {
|
public static function transform( $object ) {
|
||||||
return new static( $object );
|
return new static( $object );
|
||||||
@ -46,13 +46,13 @@ abstract class Base {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Transform the WordPress Object into an ActivityPub Object.
|
* Transform all properties with available get(ter) functions.
|
||||||
*
|
*
|
||||||
* @return Activitypub\Activity\Base_Object
|
* @param Base_Object|object $object
|
||||||
|
*
|
||||||
|
* @return Base_Object|object $object
|
||||||
*/
|
*/
|
||||||
public function to_object() {
|
protected function transform_object_properties( $activitypub_object ) {
|
||||||
$activitypub_object = new Base_Object();
|
|
||||||
|
|
||||||
$vars = $activitypub_object->get_object_var_keys();
|
$vars = $activitypub_object->get_object_var_keys();
|
||||||
|
|
||||||
foreach ( $vars as $var ) {
|
foreach ( $vars as $var ) {
|
||||||
@ -68,6 +68,17 @@ abstract class Base {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return $activitypub_object;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Transform the WordPress Object into an ActivityPub Object.
|
||||||
|
*
|
||||||
|
* @return Activitypub\Activity\Base_Object
|
||||||
|
*/
|
||||||
|
public function to_object() {
|
||||||
|
$activitypub_object = new Base_Object();
|
||||||
|
$activitypub_object = $this->transform_object_properties( $activitypub_object );
|
||||||
|
|
||||||
return $activitypub_object;
|
return $activitypub_object;
|
||||||
}
|
}
|
||||||
@ -84,6 +95,8 @@ abstract class Base {
|
|||||||
|
|
||||||
$activity = new Activity();
|
$activity = new Activity();
|
||||||
$activity->set_type( $type );
|
$activity->set_type( $type );
|
||||||
|
|
||||||
|
// Pre-fill the Activity with data (for example cc and to).
|
||||||
$activity->set_object( $object );
|
$activity->set_object( $object );
|
||||||
|
|
||||||
// Use simple Object (only ID-URI) for Like and Announce
|
// Use simple Object (only ID-URI) for Like and Announce
|
||||||
|
@ -4,6 +4,8 @@ namespace Activitypub\Transformer;
|
|||||||
use WP_Comment;
|
use WP_Comment;
|
||||||
use WP_Comment_Query;
|
use WP_Comment_Query;
|
||||||
|
|
||||||
|
use Activitypub\Webfinger;
|
||||||
|
use Activitypub\Comment as Comment_Utils;
|
||||||
use Activitypub\Model\Blog_User;
|
use Activitypub\Model\Blog_User;
|
||||||
use Activitypub\Collection\Users;
|
use Activitypub\Collection\Users;
|
||||||
use Activitypub\Transformer\Base;
|
use Activitypub\Transformer\Base;
|
||||||
@ -51,7 +53,7 @@ class Comment extends Base {
|
|||||||
$comment = $this->wp_object;
|
$comment = $this->wp_object;
|
||||||
$object = parent::to_object();
|
$object = parent::to_object();
|
||||||
|
|
||||||
$object->set_url( \get_comment_link( $comment->comment_ID ) );
|
$object->set_url( $this->get_id() );
|
||||||
$object->set_type( 'Note' );
|
$object->set_type( 'Note' );
|
||||||
|
|
||||||
$published = \strtotime( $comment->comment_date_gmt );
|
$published = \strtotime( $comment->comment_date_gmt );
|
||||||
@ -107,10 +109,10 @@ class Comment extends Base {
|
|||||||
$comment = $this->wp_object;
|
$comment = $this->wp_object;
|
||||||
$content = $comment->comment_content;
|
$content = $comment->comment_content;
|
||||||
|
|
||||||
$content = \wpautop( $content );
|
$content = \apply_filters( 'comment_text', $content, $comment, array() );
|
||||||
$content = \preg_replace( '/[\n\r\t]/', '', $content );
|
$content = \preg_replace( '/[\n\r\t]/', '', $content );
|
||||||
$content = \trim( $content );
|
$content = \trim( $content );
|
||||||
$content = \apply_filters( 'the_content', $content, $comment );
|
$content = \apply_filters( 'activitypub_the_content', $content, $comment );
|
||||||
|
|
||||||
return $content;
|
return $content;
|
||||||
}
|
}
|
||||||
@ -123,7 +125,12 @@ class Comment extends Base {
|
|||||||
protected function get_in_reply_to() {
|
protected function get_in_reply_to() {
|
||||||
$comment = $this->wp_object;
|
$comment = $this->wp_object;
|
||||||
|
|
||||||
$parent_comment = \get_comment( $comment->comment_parent );
|
$parent_comment = null;
|
||||||
|
$in_reply_to = null;
|
||||||
|
|
||||||
|
if ( $comment->comment_parent ) {
|
||||||
|
$parent_comment = \get_comment( $comment->comment_parent );
|
||||||
|
}
|
||||||
|
|
||||||
if ( $parent_comment ) {
|
if ( $parent_comment ) {
|
||||||
$comment_meta = \get_comment_meta( $parent_comment->comment_ID );
|
$comment_meta = \get_comment_meta( $parent_comment->comment_ID );
|
||||||
@ -132,8 +139,8 @@ class Comment extends Base {
|
|||||||
$in_reply_to = $comment_meta['source_id'][0];
|
$in_reply_to = $comment_meta['source_id'][0];
|
||||||
} elseif ( ! empty( $comment_meta['source_url'][0] ) ) {
|
} elseif ( ! empty( $comment_meta['source_url'][0] ) ) {
|
||||||
$in_reply_to = $comment_meta['source_url'][0];
|
$in_reply_to = $comment_meta['source_url'][0];
|
||||||
} else {
|
} elseif ( ! empty( $parent_comment->user_id ) ) {
|
||||||
$in_reply_to = $this->generate_id( $parent_comment );
|
$in_reply_to = Comment_Utils::generate_id( $parent_comment );
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
$in_reply_to = \get_permalink( $comment->comment_post_ID );
|
$in_reply_to = \get_permalink( $comment->comment_post_ID );
|
||||||
@ -152,25 +159,7 @@ class Comment extends Base {
|
|||||||
*/
|
*/
|
||||||
protected function get_id() {
|
protected function get_id() {
|
||||||
$comment = $this->wp_object;
|
$comment = $this->wp_object;
|
||||||
return $this->generate_id( $comment );
|
return Comment_Utils::generate_id( $comment );
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Generates an ActivityPub URI for a comment
|
|
||||||
*
|
|
||||||
* @param WP_Comment|int $comment A comment object or comment ID
|
|
||||||
*
|
|
||||||
* @return string ActivityPub URI for comment
|
|
||||||
*/
|
|
||||||
protected function generate_id( $comment ) {
|
|
||||||
$comment = get_comment( $comment );
|
|
||||||
|
|
||||||
return \add_query_arg(
|
|
||||||
array(
|
|
||||||
'c' => $comment->comment_ID,
|
|
||||||
),
|
|
||||||
\trailingslashit( site_url() )
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -190,31 +179,7 @@ class Comment extends Base {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
$comment_query = new WP_Comment_Query(
|
return array_unique( $cc );
|
||||||
array(
|
|
||||||
'post_id' => $this->wp_object->comment_post_ID,
|
|
||||||
// phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_query
|
|
||||||
'meta_query' => array(
|
|
||||||
array(
|
|
||||||
'key' => 'source_id',
|
|
||||||
'compare' => 'EXISTS',
|
|
||||||
),
|
|
||||||
),
|
|
||||||
)
|
|
||||||
);
|
|
||||||
|
|
||||||
if ( $comment_query->comments ) {
|
|
||||||
foreach ( $comment_query->comments as $comment ) {
|
|
||||||
if ( empty( $comment->comment_author_url ) ) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
$cc[] = \esc_url( $comment->comment_author_url );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
$cc = \array_unique( $cc );
|
|
||||||
|
|
||||||
return $cc;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -248,9 +213,53 @@ class Comment extends Base {
|
|||||||
* @return array The list of @-Mentions.
|
* @return array The list of @-Mentions.
|
||||||
*/
|
*/
|
||||||
protected function get_mentions() {
|
protected function get_mentions() {
|
||||||
|
\add_filter( 'activitypub_extract_mentions', array( $this, 'extract_reply_context' ) );
|
||||||
|
|
||||||
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 );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Collect all other Users that participated in this comment-thread
|
||||||
|
* to send them a notification about the new reply.
|
||||||
|
*
|
||||||
|
* @param array $mentions The already mentioned ActivityPub users
|
||||||
|
*
|
||||||
|
* @return array The list of all Repliers.
|
||||||
|
*/
|
||||||
|
public function extract_reply_context( $mentions ) {
|
||||||
|
// Check if `$this->wp_object` is a WP_Comment
|
||||||
|
if ( 'WP_Comment' !== get_class( $this->wp_object ) ) {
|
||||||
|
return $mentions;
|
||||||
|
}
|
||||||
|
|
||||||
|
$comment_query = new WP_Comment_Query(
|
||||||
|
array(
|
||||||
|
'post_id' => $this->wp_object->comment_post_ID,
|
||||||
|
// phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_query
|
||||||
|
'meta_query' => array(
|
||||||
|
array(
|
||||||
|
'key' => 'protocol',
|
||||||
|
'value' => 'activitypub',
|
||||||
|
),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
if ( $comment_query->comments ) {
|
||||||
|
foreach ( $comment_query->comments as $comment ) {
|
||||||
|
if ( ! empty( $comment->comment_author_url ) ) {
|
||||||
|
$acct = Webfinger::uri_to_acct( $comment->comment_author_url );
|
||||||
|
if ( $acct && ! is_wp_error( $acct ) ) {
|
||||||
|
$acct = str_replace( 'acct:', '@', $acct );
|
||||||
|
$mentions[ $acct ] = $comment->comment_author_url;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $mentions;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the locale of the post.
|
* Returns the locale of the post.
|
||||||
*
|
*
|
||||||
|
@ -0,0 +1,172 @@
|
|||||||
|
<?php
|
||||||
|
namespace Activitypub\Integration;
|
||||||
|
|
||||||
|
use DateTime;
|
||||||
|
use Activitypub\Webfinger as Webfinger_Util;
|
||||||
|
use Activitypub\Collection\Users;
|
||||||
|
use Activitypub\Collection\Followers;
|
||||||
|
use Activitypub\Integration\Nodeinfo;
|
||||||
|
use Enable_Mastodon_Apps\Entity\Account;
|
||||||
|
|
||||||
|
use function Activitypub\get_remote_metadata_by_actor;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Class Enable_Mastodon_Apps
|
||||||
|
*
|
||||||
|
* This class is used to enable Mastodon Apps to work with ActivityPub
|
||||||
|
*
|
||||||
|
* @see https://github.com/akirk/enable-mastodon-apps
|
||||||
|
*/
|
||||||
|
class Enable_Mastodon_Apps {
|
||||||
|
/**
|
||||||
|
* Initialize the class, registering WordPress hooks
|
||||||
|
*/
|
||||||
|
public static function init() {
|
||||||
|
\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_external' ), 10, 2 );
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add followers to Mastodon API
|
||||||
|
*
|
||||||
|
* @param array $followers An array of followers
|
||||||
|
* @param string $user_id The user id
|
||||||
|
* @param WP_REST_Request $request The request object
|
||||||
|
*
|
||||||
|
* @return array The filtered followers
|
||||||
|
*/
|
||||||
|
public static function api_account_followers( $followers, $user_id ) {
|
||||||
|
$activitypub_followers = Followers::get_followers( $user_id, 40 );
|
||||||
|
$mastodon_followers = array_map(
|
||||||
|
function ( $item ) {
|
||||||
|
$acct = Webfinger_Util::uri_to_acct( $item->get_id() );
|
||||||
|
|
||||||
|
if ( $acct && ! is_wp_error( $acct ) ) {
|
||||||
|
$acct = \str_replace( 'acct:', '', $acct );
|
||||||
|
} else {
|
||||||
|
$acct = $item->get_url();
|
||||||
|
}
|
||||||
|
|
||||||
|
$activitypub_follower = array(
|
||||||
|
'id' => \strval( $item->get__id() ),
|
||||||
|
'username' => $item->get_preferred_username(),
|
||||||
|
'acct' => $acct,
|
||||||
|
'display_name' => $item->get_name(),
|
||||||
|
'url' => $item->get_url(),
|
||||||
|
'uri' => $item->get_id(),
|
||||||
|
'avatar' => $item->get_icon_url(),
|
||||||
|
'avatar_static' => $item->get_icon_url(),
|
||||||
|
'created_at' => gmdate( DATE_W3C, strtotime( $item->get_published() ) ),
|
||||||
|
'last_status_at' => gmdate( DATE_W3C, strtotime( $item->get_published() ) ),
|
||||||
|
'note' => $item->get_summary(),
|
||||||
|
'header' => $item->get_image_url(),
|
||||||
|
'header_static' => $item->get_image_url(),
|
||||||
|
'followers_count' => 0,
|
||||||
|
'following_count' => 0,
|
||||||
|
'statuses_count' => 0,
|
||||||
|
'bot' => false,
|
||||||
|
'locked' => false,
|
||||||
|
'group' => false,
|
||||||
|
'discoversable' => false,
|
||||||
|
'indexable' => false,
|
||||||
|
'hide_collections' => false,
|
||||||
|
'noindex' => false,
|
||||||
|
'fields' => array(),
|
||||||
|
'emojis' => array(),
|
||||||
|
'roles' => array(),
|
||||||
|
);
|
||||||
|
|
||||||
|
return $activitypub_follower;
|
||||||
|
},
|
||||||
|
$activitypub_followers
|
||||||
|
);
|
||||||
|
|
||||||
|
$followers = array_merge( $mastodon_followers, $followers );
|
||||||
|
|
||||||
|
return $followers;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add followers count to Mastodon API
|
||||||
|
*
|
||||||
|
* @param Enable_Mastodon_Apps\Entity\Account $account The account
|
||||||
|
* @param int $user_id The user id
|
||||||
|
*
|
||||||
|
* @return Enable_Mastodon_Apps\Entity\Account The filtered Account
|
||||||
|
*/
|
||||||
|
public static function api_account_add_followers( $account, $user_id ) {
|
||||||
|
if ( ! $account instanceof Account ) {
|
||||||
|
return $account;
|
||||||
|
}
|
||||||
|
|
||||||
|
$user = Users::get_by_id( $user_id );
|
||||||
|
|
||||||
|
if ( ! $user || is_wp_error( $user ) ) {
|
||||||
|
return $account;
|
||||||
|
}
|
||||||
|
|
||||||
|
$account->followers_count = Followers::count_followers( $user_id );
|
||||||
|
return $account;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Resolve external accounts for Mastodon API
|
||||||
|
*
|
||||||
|
* @param Enable_Mastodon_Apps\Entity\Account $user_data The user data
|
||||||
|
* @param string $user_id The user id
|
||||||
|
*
|
||||||
|
* @return Enable_Mastodon_Apps\Entity\Account The filtered Account
|
||||||
|
*/
|
||||||
|
public static function api_account_external( $user_data, $user_id ) {
|
||||||
|
if ( ! preg_match( '/^' . ACTIVITYPUB_USERNAME_REGEXP . '$/', $user_id ) ) {
|
||||||
|
return $user_data;
|
||||||
|
}
|
||||||
|
|
||||||
|
$uri = Webfinger_Util::resolve( $user_id );
|
||||||
|
|
||||||
|
if ( ! $uri ) {
|
||||||
|
return $user_data;
|
||||||
|
}
|
||||||
|
|
||||||
|
$acct = Webfinger_Util::uri_to_acct( $uri );
|
||||||
|
$data = get_remote_metadata_by_actor( $uri );
|
||||||
|
|
||||||
|
if ( ! $data || is_wp_error( $data ) ) {
|
||||||
|
return $user_data;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( $user_data instanceof Account ) {
|
||||||
|
$account = $user_data;
|
||||||
|
} else {
|
||||||
|
$account = new Account();
|
||||||
|
}
|
||||||
|
|
||||||
|
$account->id = strval( $user_id );
|
||||||
|
$account->username = $acct;
|
||||||
|
$account->acct = $acct;
|
||||||
|
$account->display_name = $data['name'];
|
||||||
|
$account->url = $uri;
|
||||||
|
if ( ! empty( $data['summary'] ) ) {
|
||||||
|
$account->note = $data['summary'];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (
|
||||||
|
isset( $data['icon']['type'] ) &&
|
||||||
|
isset( $data['icon']['url'] ) &&
|
||||||
|
'Image' === $data['icon']['type']
|
||||||
|
) {
|
||||||
|
$account->avatar = $data['icon']['url'];
|
||||||
|
$account->avatar_static = $data['icon']['url'];
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( isset( $data['image'] ) ) {
|
||||||
|
$account->header = $data['image'];
|
||||||
|
$account->header_static = $data['image'];
|
||||||
|
}
|
||||||
|
|
||||||
|
$account->created_at = new DateTime( $data['published'] );
|
||||||
|
|
||||||
|
return $account;
|
||||||
|
}
|
||||||
|
}
|
@ -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.4
|
Tested up to: 6.4
|
||||||
Stable tag: 2.0.1
|
Stable tag: 2.2.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
|
||||||
@ -105,10 +105,56 @@ Where 'blog' is the path to the subdirectory at which your blog resides.
|
|||||||
|
|
||||||
In that case you don't need the redirect, because the index.php will take care of that.
|
In that case you don't need the redirect, because the index.php will take care of that.
|
||||||
|
|
||||||
|
= Constants =
|
||||||
|
|
||||||
|
The plugin uses PHP Constants to enable, disable or change its default behaviour. Please use them with caution and only if you know what you are doing.
|
||||||
|
|
||||||
|
* `ACTIVITYPUB_REST_NAMESPACE` - Change the default Namespace of the REST endpoint. Default: `activitypub/1.0`.
|
||||||
|
* `ACTIVITYPUB_EXCERPT_LENGTH` - Change the length of the Excerpt. Default: `400`.
|
||||||
|
* `ACTIVITYPUB_SHOW_PLUGIN_RECOMMENDATIONS` - show plugin recommendations in the ActivityPub settings. Default: `true`.
|
||||||
|
* `ACTIVITYPUB_MAX_IMAGE_ATTACHMENTS` - Change the number of attachments, that should be federated. Default: `3`.
|
||||||
|
* `ACTIVITYPUB_HASHTAGS_REGEXP` - Change the default regex to detect hashtext in a text. Default: `(?:(?<=\s)|(?<=<p>)|(?<=<br>)|^)#([A-Za-z0-9_]+)(?:(?=\s|[[:punct:]]|$))`.
|
||||||
|
* `ACTIVITYPUB_USERNAME_REGEXP` - Change the default regex to detect @-replies in a text. Default: `(?:([A-Za-z0-9\._-]+)@((?:[A-Za-z0-9_-]+\.)+[A-Za-z]+))`.
|
||||||
|
* `ACTIVITYPUB_CUSTOM_POST_CONTENT` - Change the default template for Activities. Default: `<strong>[ap_title]</strong>\n\n[ap_content]\n\n[ap_hashtags]\n\n[ap_shortlink]`.
|
||||||
|
* `ACTIVITYPUB_AUTHORIZED_FETCH` - Enable AUTHORIZED_FETCH. Default: `false`.
|
||||||
|
* `ACTIVITYPUB_DISABLE_REWRITES` - Disable auto generation of `mod_rewrite` rules. Default: `false`.
|
||||||
|
* `ACTIVITYPUB_DISABLE_INCOMING_INTERACTIONS` - Block incoming replies/comments/likes. Default: `false`.
|
||||||
|
* `ACTIVITYPUB_DISABLE_OUTGOING_INTERACTIONS` - Disable outgoing replies/comments/likes. Default: `false`.
|
||||||
|
* `ACTIVITYPUB_SHARED_INBOX_FEATURE` - Enable the shared inbox. Default: `false`.
|
||||||
|
* `ACTIVITYPUB_SEND_VARY_HEADER` - Enable to send the `Vary: Accept` header. Default: `false`.
|
||||||
|
|
||||||
|
= Where can you manage your followers? =
|
||||||
|
|
||||||
|
If you have activated the blog user, you will find the list of his followers in the settings under `/wp-admin/options-general.php?page=activitypub&tab=followers`.
|
||||||
|
|
||||||
|
The followers of a user can be found in the menu under "Users" -> "Followers" or under `wp-admin/users.php?page=activitypub-followers-list`.
|
||||||
|
|
||||||
|
For reasons of data protection, it is not possible to see the followers of other users.
|
||||||
|
|
||||||
== Changelog ==
|
== Changelog ==
|
||||||
|
|
||||||
Project maintained on GitHub at [automattic/wordpress-activitypub](https://github.com/automattic/wordpress-activitypub).
|
Project maintained on GitHub at [automattic/wordpress-activitypub](https://github.com/automattic/wordpress-activitypub).
|
||||||
|
|
||||||
|
= 2.2.0 =
|
||||||
|
|
||||||
|
* Added: Remote-Reply lightbox
|
||||||
|
* Added: Support `application/ld+json` mime-type with AP profile in WebFinger
|
||||||
|
* Fixed: Prevent scheduler overload
|
||||||
|
|
||||||
|
= 2.1.1 =
|
||||||
|
|
||||||
|
* Added: Add `@` prefix to Follow-Block
|
||||||
|
* Added: Apply `comment_text` filter to Activity
|
||||||
|
|
||||||
|
= 2.1.0 =
|
||||||
|
|
||||||
|
* Fixed: Some Federated Comment improvements
|
||||||
|
* Fixed: Remove old/abandoned Crons
|
||||||
|
* Added: Various endpoints for the "Enable Mastodon Apps" plugin
|
||||||
|
* Added: Event Objects
|
||||||
|
* Added: Send notification to all Repliers if a new Comment is added
|
||||||
|
* Added: Vary-Header support behind feature flag
|
||||||
|
|
||||||
= 2.0.1 =
|
= 2.0.1 =
|
||||||
|
|
||||||
* Fixed: Comment `Update` Federation
|
* Fixed: Comment `Update` Federation
|
||||||
|
@ -1,10 +1,6 @@
|
|||||||
<?php
|
<?php
|
||||||
$user = \Activitypub\Collection\Users::get_by_id( \get_the_author_meta( 'ID' ) );
|
$user = \Activitypub\Collection\Users::get_by_id( \get_the_author_meta( 'ID' ) );
|
||||||
|
|
||||||
$user->set_context(
|
|
||||||
\Activitypub\Activity\Activity::CONTEXT
|
|
||||||
);
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* 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,10 +1,6 @@
|
|||||||
<?php
|
<?php
|
||||||
$user = new \Activitypub\Model\Blog_User();
|
$user = new \Activitypub\Model\Blog_User();
|
||||||
|
|
||||||
$user->set_context(
|
|
||||||
\Activitypub\Activity\Activity::CONTEXT
|
|
||||||
);
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* 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
|
||||||
*/
|
*/
|
||||||
|
@ -3,7 +3,6 @@
|
|||||||
$post = \get_post();
|
$post = \get_post();
|
||||||
|
|
||||||
$post_object = \Activitypub\Transformer\Factory::get_transformer( $post )->to_object();
|
$post_object = \Activitypub\Transformer\Factory::get_transformer( $post )->to_object();
|
||||||
$post_object->set_context( \Activitypub\get_context() );
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Action triggerd prior to the ActivityPub profile being created and sent to the client
|
* Action triggerd prior to the ActivityPub profile being created and sent to the client
|
||||||
|
@ -118,7 +118,7 @@
|
|||||||
</label>
|
</label>
|
||||||
</p>
|
</p>
|
||||||
<p>
|
<p>
|
||||||
<textarea name="activitypub_custom_post_content" id="activitypub_custom_post_content" rows="10" cols="50" class="large-text" placeholder="<?php echo wp_kses( ACTIVITYPUB_CUSTOM_POST_CONTENT, 'post' ); ?>"><?php echo wp_kses( \get_option( 'activitypub_custom_post_content', ACTIVITYPUB_CUSTOM_POST_CONTENT ), 'post' ); ?></textarea>
|
<textarea name="activitypub_custom_post_content" id="activitypub_custom_post_content" rows="10" cols="50" class="large-text" placeholder="<?php echo wp_kses( ACTIVITYPUB_CUSTOM_POST_CONTENT, 'post' ); ?>"><?php echo esc_textarea( wp_kses( \get_option( 'activitypub_custom_post_content', ACTIVITYPUB_CUSTOM_POST_CONTENT ), 'post' ) ); ?></textarea>
|
||||||
<details>
|
<details>
|
||||||
<summary><?php esc_html_e( 'See a list of ActivityPub Template Tags.', 'activitypub' ); ?></summary>
|
<summary><?php esc_html_e( 'See a list of ActivityPub Template Tags.', 'activitypub' ); ?></summary>
|
||||||
<div class="description">
|
<div class="description">
|
||||||
@ -205,16 +205,19 @@
|
|||||||
<th scope="row"><?php \esc_html_e( 'Supported post types', 'activitypub' ); ?></th>
|
<th scope="row"><?php \esc_html_e( 'Supported post types', 'activitypub' ); ?></th>
|
||||||
<td>
|
<td>
|
||||||
<fieldset>
|
<fieldset>
|
||||||
<?php \esc_html_e( 'Enable ActivityPub support for the following post types:', 'activitypub' ); ?>
|
<?php \esc_html_e( 'Automatically publish items of the selected post types to the fediverse:', 'activitypub' ); ?>
|
||||||
|
|
||||||
<?php $post_types = \get_post_types( array( 'public' => true ), 'objects' ); ?>
|
<?php $post_types = \get_post_types( array( 'public' => true ), 'objects' ); ?>
|
||||||
<?php $support_post_types = \get_option( 'activitypub_support_post_types', array( 'post', 'page' ) ) ? \get_option( 'activitypub_support_post_types', array( 'post', 'page' ) ) : array(); ?>
|
<?php $support_post_types = \get_option( 'activitypub_support_post_types', array( 'post' ) ) ? \get_option( 'activitypub_support_post_types', array( 'post' ) ) : array(); ?>
|
||||||
<ul>
|
<ul>
|
||||||
<?php // phpcs:ignore WordPress.WP.GlobalVariablesOverride.Prohibited ?>
|
<?php // phpcs:ignore WordPress.WP.GlobalVariablesOverride.Prohibited ?>
|
||||||
<?php foreach ( $post_types as $post_type ) { ?>
|
<?php foreach ( $post_types as $post_type ) { ?>
|
||||||
<li>
|
<li>
|
||||||
<input type="checkbox" id="activitypub_support_post_type_<?php echo \esc_attr( $post_type->name ); ?>" name="activitypub_support_post_types[]" value="<?php echo \esc_attr( $post_type->name ); ?>" <?php echo \checked( \in_array( $post_type->name, $support_post_types, true ) ); ?> />
|
<input type="checkbox" id="activitypub_support_post_type_<?php echo \esc_attr( $post_type->name ); ?>" name="activitypub_support_post_types[]" value="<?php echo \esc_attr( $post_type->name ); ?>" <?php echo \checked( \in_array( $post_type->name, $support_post_types, true ) ); ?> />
|
||||||
<label for="activitypub_support_post_type_<?php echo \esc_attr( $post_type->name ); ?>"><?php echo \esc_html( $post_type->label ); ?></label>
|
<label for="activitypub_support_post_type_<?php echo \esc_attr( $post_type->name ); ?>"><?php echo \esc_html( $post_type->label ); ?></label>
|
||||||
|
<span class="description">
|
||||||
|
<?php echo \esc_html( \Activitypub\get_post_type_description( $post_type ) ); ?>
|
||||||
|
</span>
|
||||||
</li>
|
</li>
|
||||||
<?php } ?>
|
<?php } ?>
|
||||||
</ul>
|
</ul>
|
||||||
|
Loading…
Reference in New Issue
Block a user