Compare commits
167 commits
main
...
add/commen
Author | SHA1 | Date | |
---|---|---|---|
|
e9ea9db6a3 | ||
|
7d525a8efe | ||
|
8a5e83b619 | ||
|
1e5c7675cd | ||
|
5b440b3819 | ||
|
84e394f9df | ||
|
ebe9af4497 | ||
|
be2d6683bb | ||
|
24315a41b6 | ||
|
d12708e746 | ||
|
1353c89b38 | ||
|
2d09cf5b45 | ||
|
15555bdd31 | ||
|
3c089a6549 | ||
|
406d03345e | ||
|
921829f1b4 | ||
|
9da43a9d71 | ||
|
2335fb1b47 | ||
|
e04816a12b | ||
|
06be55b61a | ||
|
9d1ba79788 | ||
|
5301cdaed7 | ||
|
e6473db4f8 | ||
|
375886b3de | ||
|
9af54707b9 | ||
|
8a0e02b39f | ||
|
e7c065d173 | ||
|
c7da88a35c | ||
|
588c5698de | ||
|
e8736c15a9 | ||
|
8f03fdeaf8 | ||
|
a18740c1ef | ||
|
69a0a90b10 | ||
|
1a58b69b80 | ||
|
f92524b384 | ||
|
77e611cf2c | ||
|
e57932b099 | ||
|
0185e38013 | ||
|
b9e49d42c3 | ||
|
69195f8b3a | ||
|
50f7ae2a04 | ||
|
a9c1862369 | ||
|
b07e756927 | ||
|
63a8502205 | ||
|
110eb409d4 | ||
|
92fd280133 | ||
|
a78a581b41 | ||
|
f018432033 | ||
|
734235b500 | ||
|
bf5cccf685 | ||
|
f44298f503 | ||
|
641d250a06 | ||
|
7339a6419d | ||
|
d67556ca33 | ||
|
8495fda263 | ||
|
133c16dc3e | ||
|
f808ff33cb | ||
|
da2495e88f | ||
|
43452e4443 | ||
|
c9c155a858 | ||
|
af6dd43241 | ||
|
e4c991393d | ||
|
1927587e95 | ||
|
3017e10e06 | ||
|
86c8148cde | ||
|
9f1b65b7ef | ||
|
0ab95add02 | ||
|
6f55010df2 | ||
|
1fe5959960 | ||
|
d07bb1f808 | ||
|
a058ec0569 | ||
|
1ac6882f3a | ||
|
116070e23c | ||
|
30129adac5 | ||
|
e46137a970 | ||
|
0525645f7c | ||
|
33429cfb83 | ||
|
7e6c112679 | ||
|
2e72e93cbd | ||
|
627e5a9c0c | ||
|
9cab6248e9 | ||
|
f7fad1b118 | ||
|
44d05351eb | ||
|
f943de1e79 | ||
|
aca5e273fe | ||
|
c653575594 | ||
|
a159d70658 | ||
|
80901b5b65 | ||
|
f9c0edc681 | ||
|
22e0ddc134 | ||
|
a9a5b112b0 | ||
|
85ca37aa1d | ||
|
7fa58cf26c | ||
|
8db9be5c2e | ||
|
6a906e5fe2 | ||
|
f1f3c3165d | ||
|
45b6e63f32 | ||
|
6b15d8d158 | ||
|
733bc88faf | ||
|
8f6ad230ce | ||
|
ae54805cf0 | ||
|
2ca6bdc487 | ||
|
f1dfd52329 | ||
|
093bc3c88b | ||
|
b04538a2ab | ||
|
e48986dd99 | ||
|
a6657edd4a | ||
|
0f356acee8 | ||
|
0b881bb9fd | ||
|
3f92bcc41b | ||
|
fe7def2c84 | ||
|
5dbf365c58 | ||
|
b1f64f6828 | ||
|
d7c9e10c21 | ||
|
18a8752c6a | ||
|
8cce944edd | ||
|
1ef6fc21c1 | ||
|
f87dbd87be | ||
|
de74f1d70b | ||
|
c801d072c8 | ||
|
852634360b | ||
|
055c22e9c3 | ||
|
8707387ab8 | ||
|
ecbe724b82 | ||
|
2d0e962263 | ||
|
6a69a40295 | ||
|
63bf62402d | ||
|
aa1b54462e | ||
|
be9b74478d | ||
4e3069d931 | |||
308df84c28 | |||
a5a6967b48 | |||
6fe621bf50 | |||
30a37dab6e | |||
7c24e994ec | |||
cbc989fe83 | |||
db84a5257a | |||
b4e79e1988 | |||
|
a4e8cc7e14 | ||
|
6af56cf82e | ||
|
36bef694b6 | ||
|
3db5fb2626 | ||
|
5d8e3a9ba9 | ||
|
e5080f8693 | ||
2d50b44d13 | |||
|
7fe1ce4888 | ||
|
8e1c9ff6bb | ||
|
51643142aa | ||
|
73e6be9782 | ||
|
8938c67073 | ||
|
163f2ed013 | ||
6772d300b4 | |||
a88eea6c1c | |||
|
395187a717 | ||
|
a41f84e88f | ||
|
dc5ef86887 | ||
|
0462e9d7b0 | ||
|
4c723d1e28 | ||
|
de98e07019 | ||
|
4a4589ae1f | ||
e0730a62be | |||
8733957963 | |||
338200b386 | |||
d452ba8585 | |||
8e98fffab6 | |||
|
146321102d | ||
|
17c386cb9d |
43 changed files with 1558 additions and 1476 deletions
17
README.md
17
README.md
|
@ -2,8 +2,8 @@
|
||||||
**Contributors:** [automattic](https://profiles.wordpress.org/automattic/), [pfefferle](https://profiles.wordpress.org/pfefferle/), [mediaformat](https://profiles.wordpress.org/mediaformat/), [mattwiebe](https://profiles.wordpress.org/mattwiebe/), [akirk](https://profiles.wordpress.org/akirk/), [jeherve](https://profiles.wordpress.org/jeherve/), [nuriapena](https://profiles.wordpress.org/nuriapena/), [cavalierlife](https://profiles.wordpress.org/cavalierlife/)
|
**Contributors:** [automattic](https://profiles.wordpress.org/automattic/), [pfefferle](https://profiles.wordpress.org/pfefferle/), [mediaformat](https://profiles.wordpress.org/mediaformat/), [mattwiebe](https://profiles.wordpress.org/mattwiebe/), [akirk](https://profiles.wordpress.org/akirk/), [jeherve](https://profiles.wordpress.org/jeherve/), [nuriapena](https://profiles.wordpress.org/nuriapena/), [cavalierlife](https://profiles.wordpress.org/cavalierlife/)
|
||||||
**Tags:** OStatus, fediverse, activitypub, activitystream
|
**Tags:** OStatus, fediverse, activitypub, activitystream
|
||||||
**Requires at least:** 4.7
|
**Requires at least:** 4.7
|
||||||
**Tested up to:** 6.4
|
**Tested up to:** 6.3
|
||||||
**Stable tag:** 1.2.0
|
**Stable tag:** 1.0.10
|
||||||
**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,24 +105,11 @@ Where 'blog' is the path to the subdirectory at which your blog resides.
|
||||||
|
|
||||||
Project maintained on GitHub at [automattic/wordpress-activitypub](https://github.com/automattic/wordpress-activitypub).
|
Project maintained on GitHub at [automattic/wordpress-activitypub](https://github.com/automattic/wordpress-activitypub).
|
||||||
|
|
||||||
### 1.2.0 ###
|
|
||||||
|
|
||||||
* Add: Search and order followerer lists
|
|
||||||
* Add: Have a filter to defer signature verification
|
|
||||||
* Improved: "Follow Me" styles for dark themes
|
|
||||||
* Improved: Allow `p` and `br` tags only for AP comments
|
|
||||||
* Fixed: Deduplicate attachments earlier to prevent incorrect max_media
|
|
||||||
|
|
||||||
|
|
||||||
### 1.1.0 ###
|
### 1.1.0 ###
|
||||||
|
|
||||||
* Improved: audio and video attachments are now supported!
|
* Improved: audio and video attachments are now supported!
|
||||||
* Improved: better error messages if remote profile is not accessible
|
* Improved: better error messages if remote profile is not accessible
|
||||||
* Improved: PHP 8.1 compatibility
|
* Improved: PHP 8.1 compatibility
|
||||||
* Fixed: don't try to parse mentions or hashtags for very large (>1MB) posts to prevent timeouts
|
|
||||||
* Fixed: better handling of ISO-639-1 locale codes
|
|
||||||
* Improved: more reliable [ap_author], props @uk3
|
|
||||||
* Improved: NodeInfo statistics
|
|
||||||
|
|
||||||
### 1.0.10 ###
|
### 1.0.10 ###
|
||||||
|
|
||||||
|
|
|
@ -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: 1.2.0
|
* Version: 1.0.10
|
||||||
* Author: Matthias Pfefferle & Automattic
|
* Author: Matthias Pfefferle & Automattic
|
||||||
* Author URI: https://automattic.com/
|
* Author URI: https://automattic.com/
|
||||||
* License: MIT
|
* License: MIT
|
||||||
|
@ -33,15 +33,12 @@ 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_DEFAULT_TRANSFORMER' ) || \define( 'ACTIVITYPUB_DEFAULT_TRANSFORMER', 'activitypub/default' );
|
|
||||||
|
|
||||||
|
|
||||||
\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__ ) );
|
||||||
\define( 'ACTIVITYPUB_PLUGIN_FILE', plugin_dir_path( __FILE__ ) . '/' . basename( __FILE__ ) );
|
\define( 'ACTIVITYPUB_PLUGIN_FILE', plugin_dir_path( __FILE__ ) . '/' . basename( __FILE__ ) );
|
||||||
\define( 'ACTIVITYPUB_PLUGIN_URL', plugin_dir_url( __FILE__ ) );
|
\define( 'ACTIVITYPUB_PLUGIN_URL', plugin_dir_url( __FILE__ ) );
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Initialize REST routes.
|
* Initialize REST routes.
|
||||||
*/
|
*/
|
||||||
|
@ -75,6 +72,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__ . '\Comments', 'init' ) );
|
||||||
|
|
||||||
if ( site_supports_blocks() ) {
|
if ( site_supports_blocks() ) {
|
||||||
\add_action( 'init', array( __NAMESPACE__ . '\Blocks', 'init' ) );
|
\add_action( 'init', array( __NAMESPACE__ . '\Blocks', 'init' ) );
|
||||||
|
|
|
@ -11,10 +11,11 @@ jQuery( function( $ ) {
|
||||||
$( '#' + $( this ).attr( 'aria-controls' ) ).attr( 'hidden', false );
|
$( '#' + $( this ).attr( 'aria-controls' ) ).attr( 'hidden', false );
|
||||||
}
|
}
|
||||||
} );
|
} );
|
||||||
|
|
||||||
$(document).on( 'wp-plugin-install-success', function( event, response ) {
|
$(document).on( 'wp-plugin-install-success', function( event, response ) {
|
||||||
setTimeout( function() {
|
setTimeout( function() {
|
||||||
$( '.activate-now' ).removeClass( 'thickbox open-plugin-details-modal' );
|
$( '.activate-now' ).removeClass( 'thickbox open-plugin-details-modal' );
|
||||||
}, 1200 );
|
}, 1200 );
|
||||||
} );
|
} );
|
||||||
|
|
||||||
} );
|
} );
|
||||||
|
|
|
@ -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-profile__confirm.components-modal__frame{background-color:#f7f7f7;color:#333}.activitypub-profile__confirm.components-modal__frame .components-modal__header-heading,.activitypub-profile__confirm.components-modal__frame h4{color:#333;letter-spacing:inherit;word-spacing:inherit}.activitypub-follow-me__dialog{max-width:30em}.activitypub-follow-me__dialog h4{line-height:1;margin:0}.activitypub-follow-me__dialog .apmfd__section{margin-bottom:2em}.activitypub-follow-me__dialog .apfmd-description{font-size:var(--wp--preset--font-size--normal,.75rem);margin:.33em 0 1em}.activitypub-follow-me__dialog .apfmd__button-group{display:flex;justify-content:flex-end}.activitypub-follow-me__dialog .apfmd__button-group svg{height:21px;margin-right:.5em;width:21px}.activitypub-follow-me__dialog .apfmd__button-group input{flex:1;padding-left:1em;padding-right:1em}
|
||||||
|
|
|
@ -1,23 +0,0 @@
|
||||||
<?php
|
|
||||||
/**
|
|
||||||
* Inspired by the PHP ActivityPub Library by @Landrok
|
|
||||||
*
|
|
||||||
* @link https://github.com/landrok/activitypub
|
|
||||||
*/
|
|
||||||
|
|
||||||
namespace Activitypub\Activity;
|
|
||||||
|
|
||||||
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 Note extends Base_Object {
|
|
||||||
protected $type = 'Event';
|
|
||||||
}
|
|
|
@ -1,23 +0,0 @@
|
||||||
<?php
|
|
||||||
/**
|
|
||||||
* Inspired by the PHP ActivityPub Library by @Landrok
|
|
||||||
*
|
|
||||||
* @link https://github.com/landrok/activitypub
|
|
||||||
*/
|
|
||||||
|
|
||||||
namespace Activitypub\Activity;
|
|
||||||
|
|
||||||
use Activitypub\Activity\Base_Object;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Note is an implementation of one of the
|
|
||||||
* Activity Streams Note object type
|
|
||||||
*
|
|
||||||
* The Object is the primary base type for the Activity Streams
|
|
||||||
* vocabulary.
|
|
||||||
*
|
|
||||||
* @see https://www.w3.org/TR/activitystreams-vocabulary/#dfn-note
|
|
||||||
*/
|
|
||||||
class Note extends Base_Object {
|
|
||||||
protected $type = 'Note';
|
|
||||||
}
|
|
|
@ -2,10 +2,13 @@
|
||||||
namespace Activitypub;
|
namespace Activitypub;
|
||||||
|
|
||||||
use WP_Post;
|
use WP_Post;
|
||||||
|
use WP_Comment;
|
||||||
use Activitypub\Activity\Activity;
|
use Activitypub\Activity\Activity;
|
||||||
use Activitypub\Collection\Users;
|
use Activitypub\Collection\Users;
|
||||||
use Activitypub\Collection\Followers;
|
use Activitypub\Collection\Followers;
|
||||||
use Activitypub\Transformer\Transformers_Manager;
|
use Activitypub\Transformer\Factory;
|
||||||
|
use Activitypub\Transformer\Post;
|
||||||
|
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;
|
||||||
|
@ -30,12 +33,12 @@ class Activity_Dispatcher {
|
||||||
/**
|
/**
|
||||||
* Send Activities to followers and mentioned users or `Announce` (boost) a blog post.
|
* Send Activities to followers and mentioned users or `Announce` (boost) a blog post.
|
||||||
*
|
*
|
||||||
* @param WP_Post $wp_post The ActivityPub Post.
|
* @param mixed $wp_object The ActivityPub Post.
|
||||||
* @param string $type The Activity-Type.
|
* @param string $type The Activity-Type.
|
||||||
*
|
*
|
||||||
* @return void
|
* @return void
|
||||||
*/
|
*/
|
||||||
public static function send_activity_or_announce( WP_Post $wp_post, $type ) {
|
public static function send_activity_or_announce( $wp_object, $type ) {
|
||||||
// check if a migration is needed before sending new posts
|
// check if a migration is needed before sending new posts
|
||||||
Migration::maybe_migrate();
|
Migration::maybe_migrate();
|
||||||
|
|
||||||
|
@ -43,36 +46,35 @@ class Activity_Dispatcher {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
$wp_post->post_author = Users::BLOG_USER_ID;
|
$wp_object->post_author = Users::BLOG_USER_ID;
|
||||||
|
|
||||||
if ( is_single_user() ) {
|
if ( is_single_user() ) {
|
||||||
self::send_activity( $wp_post, $type );
|
self::send_activity( $wp_object, $type );
|
||||||
} else {
|
} else {
|
||||||
self::send_announce( $wp_post, $type );
|
self::send_announce( $wp_object, $type );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Send Activities to followers and mentioned users.
|
* Send Activities to followers and mentioned users.
|
||||||
*
|
*
|
||||||
* @param WP_Post $wp_post The ActivityPub Post.
|
* @param mixed $wp_object The ActivityPub Post.
|
||||||
* @param string $type The Activity-Type.
|
* @param string $type The Activity-Type.
|
||||||
*
|
*
|
||||||
* @return void
|
* @return void
|
||||||
*/
|
*/
|
||||||
public static function send_activity( WP_Post $wp_post, $type ) {
|
public static function send_activity( $wp_object, $type ) {
|
||||||
if ( is_user_disabled( $wp_post->post_author ) ) {
|
if ( is_user_disabled( $wp_object->post_author ) ) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
$transformer = Transformers_Manager::instance()->get_transformer( $wp_post );
|
$object = Factory::get_transformer( $wp_object )->to_object();
|
||||||
$object = $transformer->to_object();
|
|
||||||
|
|
||||||
$activity = new Activity();
|
$activity = new Activity();
|
||||||
$activity->set_type( $type );
|
$activity->set_type( $type );
|
||||||
$activity->set_object( $object );
|
$activity->set_object( $object );
|
||||||
|
|
||||||
$follower_inboxes = Followers::get_inboxes( $wp_post->post_author );
|
$follower_inboxes = Followers::get_inboxes( $wp_object->post_author );
|
||||||
$mentioned_inboxes = Mention::get_inboxes( $activity->get_cc() );
|
$mentioned_inboxes = Mention::get_inboxes( $activity->get_cc() );
|
||||||
|
|
||||||
$inboxes = array_merge( $follower_inboxes, $mentioned_inboxes );
|
$inboxes = array_merge( $follower_inboxes, $mentioned_inboxes );
|
||||||
|
@ -81,19 +83,19 @@ class Activity_Dispatcher {
|
||||||
$json = $activity->to_json();
|
$json = $activity->to_json();
|
||||||
|
|
||||||
foreach ( $inboxes as $inbox ) {
|
foreach ( $inboxes as $inbox ) {
|
||||||
safe_remote_post( $inbox, $json, $wp_post->post_author );
|
safe_remote_post( $inbox, $json, $wp_object->post_author );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Send Announces to followers and mentioned users.
|
* Send Announces to followers and mentioned users.
|
||||||
*
|
*
|
||||||
* @param WP_Post $wp_post The ActivityPub Post.
|
* @param mixed $wp_object The ActivityPub Post.
|
||||||
* @param string $type The Activity-Type.
|
* @param string $type The Activity-Type.
|
||||||
*
|
*
|
||||||
* @return void
|
* @return void
|
||||||
*/
|
*/
|
||||||
public static function send_announce( WP_Post $wp_post, $type ) {
|
public static function send_announce( $wp_object, $type ) {
|
||||||
if ( ! in_array( $type, array( 'Create', 'Update' ), true ) ) {
|
if ( ! in_array( $type, array( 'Create', 'Update' ), true ) ) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -102,8 +104,7 @@ class Activity_Dispatcher {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
$transformer = Transformers_Manager::instance()->get_transformer( $wp_post );
|
$object = Factory::get_transformer( $wp_object )->to_object();
|
||||||
$object = $transformer->to_object();
|
|
||||||
|
|
||||||
$activity = new Activity();
|
$activity = new Activity();
|
||||||
$activity->set_type( 'Announce' );
|
$activity->set_type( 'Announce' );
|
||||||
|
@ -112,7 +113,7 @@ class Activity_Dispatcher {
|
||||||
// send only the id
|
// send only the id
|
||||||
$activity->set_object( $object->get_id() );
|
$activity->set_object( $object->get_id() );
|
||||||
|
|
||||||
$follower_inboxes = Followers::get_inboxes( $wp_post->post_author );
|
$follower_inboxes = Followers::get_inboxes( $wp_object->post_author );
|
||||||
$mentioned_inboxes = Mention::get_inboxes( $activity->get_cc() );
|
$mentioned_inboxes = Mention::get_inboxes( $activity->get_cc() );
|
||||||
|
|
||||||
$inboxes = array_merge( $follower_inboxes, $mentioned_inboxes );
|
$inboxes = array_merge( $follower_inboxes, $mentioned_inboxes );
|
||||||
|
@ -121,7 +122,7 @@ class Activity_Dispatcher {
|
||||||
$json = $activity->to_json();
|
$json = $activity->to_json();
|
||||||
|
|
||||||
foreach ( $inboxes as $inbox ) {
|
foreach ( $inboxes as $inbox ) {
|
||||||
safe_remote_post( $inbox, $json, $wp_post->post_author );
|
safe_remote_post( $inbox, $json, $wp_object->post_author );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,6 +4,8 @@ namespace Activitypub;
|
||||||
use Activitypub\Signature;
|
use Activitypub\Signature;
|
||||||
use Activitypub\Collection\Users;
|
use Activitypub\Collection\Users;
|
||||||
|
|
||||||
|
use function Activitypub\is_comment;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* ActivityPub Class
|
* ActivityPub Class
|
||||||
*
|
*
|
||||||
|
@ -20,9 +22,9 @@ class Activitypub {
|
||||||
\add_filter( 'get_comment_link', array( self::class, 'remote_comment_link' ), 11, 3 );
|
\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
|
||||||
$transformer_mapping = \get_option( 'activitypub_transformer_mapping', array( 'post' => 'activitypub/default', 'page' => 'activitypub/default' ) ) ? \get_option( 'activitypub_transformer_mapping', array( 'post' => 'activitypub/default', 'page' => 'activitypub/default' ) ) : array();
|
$post_types = \get_option( 'activitypub_support_post_types', array( 'post', 'page' ) ) ? \get_option( 'activitypub_support_post_types', array( 'post', 'page' ) ) : array();
|
||||||
|
|
||||||
foreach ( array_keys( $transformer_mapping ) as $post_type ) {
|
foreach ( $post_types as $post_type ) {
|
||||||
\add_post_type_support( $post_type, 'activitypub' );
|
\add_post_type_support( $post_type, 'activitypub' );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -92,6 +94,8 @@ class Activitypub {
|
||||||
|
|
||||||
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() ) {
|
||||||
|
$json_template = ACTIVITYPUB_PLUGIN_DIR . '/templates/comment-json.php';
|
||||||
} elseif ( \is_singular() ) {
|
} elseif ( \is_singular() ) {
|
||||||
$json_template = ACTIVITYPUB_PLUGIN_DIR . '/templates/post-json.php';
|
$json_template = ACTIVITYPUB_PLUGIN_DIR . '/templates/post-json.php';
|
||||||
} elseif ( \is_home() ) {
|
} elseif ( \is_home() ) {
|
||||||
|
@ -114,6 +118,7 @@ class Activitypub {
|
||||||
*/
|
*/
|
||||||
public static function add_query_vars( $vars ) {
|
public static function add_query_vars( $vars ) {
|
||||||
$vars[] = 'activitypub';
|
$vars[] = 'activitypub';
|
||||||
|
$vars[] = 'replytocom';
|
||||||
|
|
||||||
return $vars;
|
return $vars;
|
||||||
}
|
}
|
||||||
|
@ -189,9 +194,11 @@ class Activitypub {
|
||||||
* @return string $url
|
* @return string $url
|
||||||
*/
|
*/
|
||||||
public static function remote_comment_link( $comment_link, $comment ) {
|
public static function remote_comment_link( $comment_link, $comment ) {
|
||||||
$remote_comment_link = get_comment_meta( $comment->comment_ID, 'source_url', true );
|
if ( ! is_admin() ) {
|
||||||
if ( $remote_comment_link ) {
|
$remote_comment_link = get_comment_meta( $comment->comment_ID, 'source_url', true );
|
||||||
$comment_link = esc_url( $remote_comment_link );
|
if ( $remote_comment_link ) {
|
||||||
|
$comment_link = esc_url( $remote_comment_link );
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return $comment_link;
|
return $comment_link;
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,7 +3,6 @@ namespace Activitypub;
|
||||||
|
|
||||||
use WP_User_Query;
|
use WP_User_Query;
|
||||||
use Activitypub\Model\Blog_User;
|
use Activitypub\Model\Blog_User;
|
||||||
use Activitypub\Base\Transformer\Base as Transformer_Base;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* ActivityPub Admin Class
|
* ActivityPub Admin Class
|
||||||
|
@ -23,15 +22,6 @@ class Admin {
|
||||||
if ( ! is_user_disabled( get_current_user_id() ) ) {
|
if ( ! is_user_disabled( get_current_user_id() ) ) {
|
||||||
\add_action( 'show_user_profile', array( self::class, 'add_profile' ) );
|
\add_action( 'show_user_profile', array( self::class, 'add_profile' ) );
|
||||||
}
|
}
|
||||||
|
|
||||||
add_filter(
|
|
||||||
'activitypub/transformers/is_transformer_enabled',
|
|
||||||
function( $should_register, Transformer_Base $transformer_instance ) {
|
|
||||||
return ! Options::is_transformer_disabled( $transformer_instance->get_name() );
|
|
||||||
},
|
|
||||||
10,
|
|
||||||
2
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -164,47 +154,14 @@ class Admin {
|
||||||
'default' => '0',
|
'default' => '0',
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
\register_setting(
|
||||||
/**
|
|
||||||
* Flexible activation of post_types together with mapping ActivityPub transformers.
|
|
||||||
*
|
|
||||||
* If a post-type is not mapped to any ActivtiyPub transformer it means it is not activated
|
|
||||||
* for ActivityPub federation.
|
|
||||||
*
|
|
||||||
* @since version_number_transformer_management_placeholder
|
|
||||||
*/
|
|
||||||
register_setting(
|
|
||||||
'activitypub',
|
'activitypub',
|
||||||
'activitypub_transformer_mapping',
|
'activitypub_support_post_types',
|
||||||
array(
|
array(
|
||||||
'type' => 'array',
|
'type' => 'string',
|
||||||
'default' => array(
|
'description' => \esc_html__( 'Enable ActivityPub support for post types', 'activitypub' ),
|
||||||
'post' => 'note',
|
'show_in_rest' => true,
|
||||||
),
|
'default' => array( 'post', 'pages' ),
|
||||||
'show_in_rest' => array(
|
|
||||||
'schema' => array(
|
|
||||||
'type' => 'array',
|
|
||||||
'items' => array(
|
|
||||||
'type' => 'string',
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
'sanitize_callback' => function ( $value ) {
|
|
||||||
// Check if $value is an array
|
|
||||||
if ( ! is_array( $value ) ) {
|
|
||||||
return array();
|
|
||||||
}
|
|
||||||
$value_keys = array_keys( $value );
|
|
||||||
|
|
||||||
$all_public_post_types = \get_post_types( array( 'public' => true ), 'names' );
|
|
||||||
|
|
||||||
// Unset the keys that are missing in $keysToCheck
|
|
||||||
foreach ( array_diff( $value_keys, $all_public_post_types ) as $missing_key ) {
|
|
||||||
unset( $value[ $missing_key ] );
|
|
||||||
}
|
|
||||||
// var_dump($value);
|
|
||||||
return $value;
|
|
||||||
},
|
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
\register_setting(
|
\register_setting(
|
||||||
|
|
118
includes/class-comments.php
Normal file
118
includes/class-comments.php
Normal file
|
@ -0,0 +1,118 @@
|
||||||
|
<?php
|
||||||
|
namespace Activitypub;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Comments Class
|
||||||
|
*
|
||||||
|
* @author Django Doucet
|
||||||
|
*/
|
||||||
|
class Comments {
|
||||||
|
/**
|
||||||
|
* Initialize the class, registering WordPress hooks.
|
||||||
|
*/
|
||||||
|
public static function init() {
|
||||||
|
|
||||||
|
\add_filter( 'preprocess_comment', array( self::class, 'preprocess_comment' ) );
|
||||||
|
\add_filter( 'comment_excerpt', array( self::class, 'comment_excerpt' ), 10, 3 );
|
||||||
|
\add_filter( 'comment_text', array( self::class, 'comment_content_filter' ), 10, 3 );
|
||||||
|
\add_filter( 'comment_post', array( self::class, 'postprocess_comment' ), 10, 3 );
|
||||||
|
\add_action( 'edit_comment', array( self::class, 'edit_comment' ), 20, 2 ); //schedule_admin_comment_activity
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* preprocess local comments for federated replies
|
||||||
|
*/
|
||||||
|
public static function preprocess_comment( $commentdata ) {
|
||||||
|
// only process replies from authorized local actors, for ap enabled post types
|
||||||
|
if ( 0 === $commentdata['user_id'] ) {
|
||||||
|
return $commentdata;
|
||||||
|
}
|
||||||
|
$user = \get_userdata( $commentdata['user_id'] );
|
||||||
|
$comment_parent_post = \get_post_type( $commentdata['comment_post_ID'] );
|
||||||
|
$post_types = \get_option( 'activitypub_support_post_types', array( 'post', 'page' ) );
|
||||||
|
|
||||||
|
if ( $user->has_cap( 'publish_post', $commentdata['comment_post_ID'] ) && \in_array( $comment_parent_post, $post_types, true ) ) {
|
||||||
|
$commentdata['comment_meta']['protocol'] = 'activitypub';
|
||||||
|
}
|
||||||
|
return $commentdata;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Filters the comment text to display webfinger in the Recently Published Dashboard Widget.
|
||||||
|
* comment_excerpt( $comment_excerpt, $comment_id )
|
||||||
|
*
|
||||||
|
* doesn't work on received webfinger links as get_comment_excerpt strips tags
|
||||||
|
* https://developer.wordpress.org/reference/functions/get_comment_excerpt/
|
||||||
|
* @param string $comment_text
|
||||||
|
* @param int $comment_id
|
||||||
|
* @param array $args
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public static function comment_excerpt( $comment_excerpt, $comment_id ) {
|
||||||
|
$comment = get_comment( $comment_id );
|
||||||
|
$comment_excerpt = \apply_filters( 'the_content', $comment_excerpt, $comment );
|
||||||
|
return $comment_excerpt;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* comment_text( $comment )
|
||||||
|
* Filters the comment text for display.
|
||||||
|
*
|
||||||
|
* @param string $comment_text
|
||||||
|
* @param WP_Comment $comment
|
||||||
|
* @param array $args
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public static function comment_content_filter( $comment_text, $comment, $args ) {
|
||||||
|
$comment_text = \apply_filters( 'the_content', $comment_text, $comment );
|
||||||
|
$protocol = \get_comment_meta( $comment->comment_ID, 'protocol', true );
|
||||||
|
// TODO Test if this is returned by Model/Comment
|
||||||
|
if ( 'activitypub' === $protocol ) {
|
||||||
|
$updated = \get_comment_meta( $comment->comment_ID, 'activitypub_last_modified', true );
|
||||||
|
if ( $updated ) {
|
||||||
|
$format = get_option( 'date_format' ) . ' ' . get_option( 'time_format' );
|
||||||
|
$formatted_datetime = \date_i18n( $format, \strtotime( $updated ) );
|
||||||
|
$iso_date = \wp_date( 'c', \strtotime( $updated ) );
|
||||||
|
$i18n_text = sprintf(
|
||||||
|
/* translators: %s: Displays comment last modified date and time */
|
||||||
|
__( '(Last edited on %s)', 'activitypub' ),
|
||||||
|
"<time class='modified' datetime='{$iso_date}'>$formatted_datetime</time>"
|
||||||
|
);
|
||||||
|
$comment_text .= "<small>$i18n_text<small>";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return $comment_text;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* comment_post()
|
||||||
|
* postprocess_comment for federating replies and inbox-forwarding
|
||||||
|
*/
|
||||||
|
public static function postprocess_comment( $comment_id, $comment_approved, $commentdata ) {
|
||||||
|
//Administrator role users comments bypass transition_comment_status (auto approved)
|
||||||
|
$user = \get_userdata( $commentdata['user_id'] );
|
||||||
|
if (
|
||||||
|
( 1 === $comment_approved ) &&
|
||||||
|
\in_array( 'administrator', $user->roles )
|
||||||
|
) {
|
||||||
|
// Only needed for Admins (because of transition bypass)
|
||||||
|
$wp_comment = \get_comment( $comment_id );
|
||||||
|
\wp_schedule_single_event( \time(), 'activitypub_send_activity', array( $wp_comment, 'Create' ) );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* edit_comment()
|
||||||
|
*
|
||||||
|
* Fires immediately after a comment is updated in the database.
|
||||||
|
* Fires immediately before comment status transition hooks are fired. (useful only for admin)
|
||||||
|
*/
|
||||||
|
public static function edit_comment( $comment_id, $commentdata ) {
|
||||||
|
update_comment_meta( $comment_id, 'activitypub_last_modified', \wp_date( 'Y-m-d H:i:s' ) );
|
||||||
|
$user = \get_userdata( $commentdata['user_id'] );
|
||||||
|
if ( \in_array( 'administrator', $user->roles ) ) {
|
||||||
|
$wp_comment = \get_comment( $comment_id );
|
||||||
|
\wp_schedule_single_event( \time(), 'activitypub_send_activity', array( $wp_comment, 'Update' ) );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -43,10 +43,6 @@ class Hashtag {
|
||||||
* @return string the filtered post-content
|
* @return string the filtered post-content
|
||||||
*/
|
*/
|
||||||
public static function the_content( $the_content ) {
|
public static function the_content( $the_content ) {
|
||||||
// small protection against execution timeouts: limit to 1 MB
|
|
||||||
if ( mb_strlen( $the_content ) > MB_IN_BYTES ) {
|
|
||||||
return $the_content;
|
|
||||||
}
|
|
||||||
$tag_stack = array();
|
$tag_stack = array();
|
||||||
$protected_tags = array(
|
$protected_tags = array(
|
||||||
'pre',
|
'pre',
|
||||||
|
@ -67,7 +63,7 @@ class Hashtag {
|
||||||
$tag = strtolower( $m[2] );
|
$tag = strtolower( $m[2] );
|
||||||
if ( '/' === $m[1] ) {
|
if ( '/' === $m[1] ) {
|
||||||
// Closing tag.
|
// Closing tag.
|
||||||
$i = array_search( $tag, $tag_stack );
|
$i = array_search( $tag, $tag_stack, true );
|
||||||
// We can only remove the tag from the stack if it is in the stack.
|
// We can only remove the tag from the stack if it is in the stack.
|
||||||
if ( false !== $i ) {
|
if ( false !== $i ) {
|
||||||
$tag_stack = array_slice( $tag_stack, 0, $i );
|
$tag_stack = array_slice( $tag_stack, 0, $i );
|
||||||
|
|
|
@ -26,10 +26,6 @@ class Mention {
|
||||||
* @return string the filtered post-content
|
* @return string the filtered post-content
|
||||||
*/
|
*/
|
||||||
public static function the_content( $the_content ) {
|
public static function the_content( $the_content ) {
|
||||||
// small protection against execution timeouts: limit to 1 MB
|
|
||||||
if ( mb_strlen( $the_content ) > MB_IN_BYTES ) {
|
|
||||||
return $the_content;
|
|
||||||
}
|
|
||||||
$tag_stack = array();
|
$tag_stack = array();
|
||||||
$protected_tags = array(
|
$protected_tags = array(
|
||||||
'pre',
|
'pre',
|
||||||
|
|
|
@ -4,7 +4,6 @@ namespace Activitypub;
|
||||||
use Activitypub\Activitypub;
|
use Activitypub\Activitypub;
|
||||||
use Activitypub\Model\Blog_User;
|
use Activitypub\Model\Blog_User;
|
||||||
use Activitypub\Collection\Followers;
|
use Activitypub\Collection\Followers;
|
||||||
use Activitypub\Admin;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* ActivityPub Migration Class
|
* ActivityPub Migration Class
|
||||||
|
@ -115,30 +114,12 @@ class Migration {
|
||||||
if ( version_compare( $version_from_db, '1.0.0', '<' ) ) {
|
if ( version_compare( $version_from_db, '1.0.0', '<' ) ) {
|
||||||
self::migrate_from_0_17();
|
self::migrate_from_0_17();
|
||||||
}
|
}
|
||||||
if ( version_compare( $version_from_db, 'version_number_transformer_management_placeholder', '<' ) ) {
|
|
||||||
self::migrate_from_version_number_transformer_management_placeholder();
|
|
||||||
}
|
|
||||||
|
|
||||||
update_option( 'activitypub_db_version', self::get_target_version() );
|
update_option( 'activitypub_db_version', self::get_target_version() );
|
||||||
|
|
||||||
self::unlock();
|
self::unlock();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Updates the supported post type settings to the mapped transformer setting.
|
|
||||||
* TODO: Test this
|
|
||||||
* @return void
|
|
||||||
*/
|
|
||||||
private static function migrate_from_version_number_transformer_management_placeholder() {
|
|
||||||
$supported_post_types = \get_option( 'activitypub_support_post_types', array( 'post', 'page' ) );
|
|
||||||
Admin::register_settings();
|
|
||||||
$transformer_mapping = array();
|
|
||||||
foreach ( $supported_post_types as $supported_post_type ) {
|
|
||||||
$transformer_mapping[ $supported_post_type ] = ACTIVITYPUB_DEFAULT_TRANSFORMER;
|
|
||||||
}
|
|
||||||
update_option( 'activitypub_transformer_mapping', $transformer_mapping );
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Updates the DB-schema of the followers-list
|
* Updates the DB-schema of the followers-list
|
||||||
*
|
*
|
||||||
|
|
|
@ -4,6 +4,7 @@ namespace Activitypub;
|
||||||
|
|
||||||
use Activitypub\Collection\Users;
|
use Activitypub\Collection\Users;
|
||||||
use Activitypub\Collection\Followers;
|
use Activitypub\Collection\Followers;
|
||||||
|
use Activitypub\Transformer\Post;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* ActivityPub Scheduler Class
|
* ActivityPub Scheduler Class
|
||||||
|
@ -16,6 +17,7 @@ class Scheduler {
|
||||||
*/
|
*/
|
||||||
public static function init() {
|
public static function init() {
|
||||||
\add_action( 'transition_post_status', array( self::class, 'schedule_post_activity' ), 33, 3 );
|
\add_action( 'transition_post_status', array( self::class, 'schedule_post_activity' ), 33, 3 );
|
||||||
|
\add_action( 'transition_comment_status', array( self::class, 'schedule_comment_activity' ), 20, 3 );
|
||||||
|
|
||||||
\add_action( 'activitypub_update_followers', array( self::class, 'update_followers' ) );
|
\add_action( 'activitypub_update_followers', array( self::class, 'update_followers' ) );
|
||||||
\add_action( 'activitypub_cleanup_followers', array( self::class, 'cleanup_followers' ) );
|
\add_action( 'activitypub_cleanup_followers', array( self::class, 'cleanup_followers' ) );
|
||||||
|
@ -98,6 +100,36 @@ class Scheduler {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Schedule Comment Activities
|
||||||
|
*
|
||||||
|
* transition_comment_status()
|
||||||
|
*
|
||||||
|
* @param string $new_status New comment status.
|
||||||
|
* @param string $old_status Old comment status.
|
||||||
|
* @param WP_Comment $comment Comment object.
|
||||||
|
*/
|
||||||
|
public static function schedule_comment_activity( $new_status, $old_status, $comment ) {
|
||||||
|
$comment = get_comment( $comment );
|
||||||
|
|
||||||
|
if ( ! $comment->user_id ) {
|
||||||
|
// Registered comment author
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( 'approved' === $new_status && 'approved' !== $old_status ) {
|
||||||
|
// Only federate replies from local actors
|
||||||
|
$activity_object = unserialize( \get_comment_meta( $comment->comment_ID, 'ap_object', true ) );
|
||||||
|
if ( empty( $activity_object ) ) {
|
||||||
|
\wp_schedule_single_event( \time(), 'activitypub_send_activity', array( $comment, 'Create' ) );
|
||||||
|
}
|
||||||
|
} elseif ( 'trash' === $new_status ) {
|
||||||
|
\wp_schedule_single_event( \time(), 'activitypub_send_activity', array( $comment, 'Delete' ) );
|
||||||
|
} elseif ( 'update' === $new_status ) {
|
||||||
|
\wp_schedule_single_event( \time(), 'activitypub_send_activity', array( $comment, 'Update' ) );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Update followers
|
* Update followers
|
||||||
*
|
*
|
||||||
|
|
|
@ -390,8 +390,7 @@ class Shortcodes {
|
||||||
return '';
|
return '';
|
||||||
}
|
}
|
||||||
|
|
||||||
$author_id = \get_post_field( 'post_author', $item->ID );
|
$name = \get_the_author_meta( 'display_name', $item->post_author );
|
||||||
$name = \get_the_author_meta( 'display_name', $author_id );
|
|
||||||
|
|
||||||
if ( ! $name ) {
|
if ( ! $name ) {
|
||||||
return '';
|
return '';
|
||||||
|
@ -416,8 +415,7 @@ class Shortcodes {
|
||||||
return '';
|
return '';
|
||||||
}
|
}
|
||||||
|
|
||||||
$author_id = \get_post_field( 'post_author', $item->ID );
|
$url = \get_the_author_meta( 'user_url', $item->post_author );
|
||||||
$url = \get_the_author_meta( 'user_url', $author_id );
|
|
||||||
|
|
||||||
if ( ! $url ) {
|
if ( ! $url ) {
|
||||||
return '';
|
return '';
|
||||||
|
|
|
@ -41,14 +41,9 @@ class Webfinger {
|
||||||
* @return string|WP_Error The URL or WP_Error
|
* @return string|WP_Error The URL or WP_Error
|
||||||
*/
|
*/
|
||||||
public static function resolve( $resource ) {
|
public static function resolve( $resource ) {
|
||||||
if ( ! $resource ) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ( ! preg_match( '/^@?' . ACTIVITYPUB_USERNAME_REGEXP . '$/i', $resource, $m ) ) {
|
if ( ! preg_match( '/^@?' . ACTIVITYPUB_USERNAME_REGEXP . '$/i', $resource, $m ) ) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
$transient_key = 'activitypub_resolve_' . ltrim( $resource, '@' );
|
$transient_key = 'activitypub_resolve_' . ltrim( $resource, '@' );
|
||||||
|
|
||||||
$link = \get_transient( $transient_key );
|
$link = \get_transient( $transient_key );
|
||||||
|
|
|
@ -2,10 +2,11 @@
|
||||||
namespace Activitypub;
|
namespace Activitypub;
|
||||||
|
|
||||||
use WP_Error;
|
use WP_Error;
|
||||||
|
use WP_Comment_Query;
|
||||||
use Activitypub\Http;
|
use Activitypub\Http;
|
||||||
|
use Activitypub\Webfinger;
|
||||||
use Activitypub\Activity\Activity;
|
use Activitypub\Activity\Activity;
|
||||||
use Activitypub\Collection\Followers;
|
use Activitypub\Collection\Followers;
|
||||||
use Activitypub\Collection\Users;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the ActivityPub default JSON-context
|
* Returns the ActivityPub default JSON-context
|
||||||
|
@ -167,6 +168,161 @@ function url_to_authorid( $url ) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Examine a comment ID and look up an existing comment it represents.
|
||||||
|
*
|
||||||
|
* @param string $id ActivityPub object ID (usually a URL) to check.
|
||||||
|
*
|
||||||
|
* @return WP_Comment, or undef if no comment could be found.
|
||||||
|
*/
|
||||||
|
function object_id_to_comment( $id ) {
|
||||||
|
$comment_query = new WP_Comment_Query(
|
||||||
|
array(
|
||||||
|
'meta_key' => 'source_id',
|
||||||
|
'meta_value' => $id,
|
||||||
|
)
|
||||||
|
);
|
||||||
|
if ( ! $comment_query->comments ) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if ( count( $comment_query->comments ) > 1 ) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
return $comment_query->comments[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Examine an activity object and find the post that the specified URL field refers to.
|
||||||
|
*
|
||||||
|
* @param string $field_name The name of the URL field in the object to query.
|
||||||
|
*
|
||||||
|
* @return int Post ID, or null on failure.
|
||||||
|
*/
|
||||||
|
function object_to_post_id_by_field_name( $object, $field_name ) {
|
||||||
|
if ( ! isset( $object['object'][ $field_name ] ) ) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
$result = \url_to_postid( $object['object'][ $field_name ] );
|
||||||
|
if ( $result > 0 ) {
|
||||||
|
return $result;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Verify if in_replyto_url is a local Post,
|
||||||
|
* (For backwards compatibility)
|
||||||
|
*
|
||||||
|
* @param string activitypub object id URI
|
||||||
|
* @return int post_id
|
||||||
|
*/
|
||||||
|
function url_to_postid( $in_replyto_url ) {
|
||||||
|
if ( ! empty( $in_replyto_url ) ) {
|
||||||
|
$tentative_postid = \url_to_postid( $in_replyto_url );
|
||||||
|
if ( is_null( $tentative_postid ) ) {
|
||||||
|
$post_types = \get_option( 'activitypub_support_post_types', array( 'post', 'page' ) );
|
||||||
|
$query_args = array(
|
||||||
|
'type' => $post_types,
|
||||||
|
'meta_query' => array(
|
||||||
|
array(
|
||||||
|
'key' => 'activitypub_canonical_url',
|
||||||
|
'value' => $in_replyto_url,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
$posts_query = new \WP_Query();
|
||||||
|
$posts = $posts_query->query( $query_args );
|
||||||
|
$found_post_ids = array();
|
||||||
|
if ( $posts ) {
|
||||||
|
foreach ( $posts as $post ) {
|
||||||
|
$found_post_ids[] = $post->comment_ID;
|
||||||
|
}
|
||||||
|
return $found_post_ids[0];
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return $tentative_postid;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Verify if in_replyto_url is a local comment,
|
||||||
|
* Or if it is a previously received remote comment
|
||||||
|
* (For threading comments locally)
|
||||||
|
*
|
||||||
|
* @param string activitypub object id URI
|
||||||
|
* @return int comment_id
|
||||||
|
*/
|
||||||
|
function url_to_commentid( $in_replyto_url ) {
|
||||||
|
if ( empty( $in_replyto_url ) ) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
//rewrite for activitypub object id simplification
|
||||||
|
$url_maybe_id = \wp_parse_url( $in_replyto_url );
|
||||||
|
if ( site_url() === $url_maybe_id['scheme'] . '://' . $url_maybe_id['host'] && ! empty( $url_maybe_id['query'] ) ) {
|
||||||
|
//is local post or comment
|
||||||
|
\parse_str( $url_maybe_id['query'], $reply_query );
|
||||||
|
if ( isset( $reply_query['replytocom'] ) ) {
|
||||||
|
//is local comment
|
||||||
|
return $reply_query['replytocom'];
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
//is remote url
|
||||||
|
//verify if in_replyto_url corresponds to a previously received comment
|
||||||
|
$comment_args = array(
|
||||||
|
'type' => 'activitypub',
|
||||||
|
'meta_query' => array(
|
||||||
|
array(
|
||||||
|
'key' => 'source_url', // $object['object']['id']
|
||||||
|
'value' => $in_replyto_url,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
$comments_query = new WP_Comment_Query();
|
||||||
|
$comments = $comments_query->query( $comment_args );
|
||||||
|
$found_comment_ids = array();
|
||||||
|
if ( $comments ) {
|
||||||
|
foreach ( $comments as $comment ) {
|
||||||
|
$found_comment_ids[] = $comment->comment_ID;
|
||||||
|
}
|
||||||
|
return $found_comment_ids[0];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Verify if url is a wp_ap_comment,
|
||||||
|
* Or if it is a previously received remote comment
|
||||||
|
*
|
||||||
|
* @return int comment_id
|
||||||
|
*/
|
||||||
|
function is_comment() {
|
||||||
|
$comment_id = get_query_var( 'replytocom', null );
|
||||||
|
if ( ! is_null( $comment_id ) ) {
|
||||||
|
$comment = \get_comment( $comment_id );
|
||||||
|
// Only return local origin comments
|
||||||
|
if ( $comment->user_id ) {
|
||||||
|
return $comment_id;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param string $user_url
|
||||||
|
* @return string $webfinger
|
||||||
|
*/
|
||||||
|
function url_to_webfinger( $user_url ) {
|
||||||
|
$user_url = \untrailingslashit( $user_url );
|
||||||
|
$user_url_array = \explode( '/', $user_url );
|
||||||
|
$user_name = \end( $user_url_array );
|
||||||
|
$url_host = \wp_parse_url( $user_url, PHP_URL_HOST );
|
||||||
|
$webfinger = '@' . $user_name . '@' . $url_host;
|
||||||
|
return $webfinger;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Check for Tombstone Objects
|
* Check for Tombstone Objects
|
||||||
*
|
*
|
||||||
|
@ -279,16 +435,6 @@ function is_activitypub_request() {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check if the current post type supports ActivityPub.
|
|
||||||
if ( \is_singular() ) {
|
|
||||||
$queried_object = \get_queried_object();
|
|
||||||
$post_type = \get_post_type( $queried_object );
|
|
||||||
|
|
||||||
if ( ! \post_type_supports( $post_type, 'activitypub' ) ) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// One can trigger an ActivityPub request by adding ?activitypub to the URL.
|
// 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;
|
||||||
|
@ -485,75 +631,3 @@ function is_json( $data ) {
|
||||||
function is_blog_public() {
|
function is_blog_public() {
|
||||||
return (bool) apply_filters( 'activitypub_is_blog_public', \get_option( 'blog_public', 1 ) );
|
return (bool) apply_filters( 'activitypub_is_blog_public', \get_option( 'blog_public', 1 ) );
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Get active users based on a given duration
|
|
||||||
*
|
|
||||||
* @param int $duration The duration to check in month(s)
|
|
||||||
*
|
|
||||||
* @return int The number of active users
|
|
||||||
*/
|
|
||||||
function get_active_users( $duration = 1 ) {
|
|
||||||
|
|
||||||
$duration = intval( $duration );
|
|
||||||
$transient_key = sprintf( 'monthly_active_users_%d', $duration );
|
|
||||||
$count = get_transient( $transient_key );
|
|
||||||
|
|
||||||
if ( false === $count ) {
|
|
||||||
global $wpdb;
|
|
||||||
$query = "SELECT COUNT( DISTINCT post_author ) FROM {$wpdb->posts} WHERE post_type = 'post' AND post_status = 'publish' AND post_date <= DATE_SUB( NOW(), INTERVAL %d MONTH )";
|
|
||||||
$query = $wpdb->prepare( $query, $duration );
|
|
||||||
$count = $wpdb->get_var( $query ); // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery
|
|
||||||
|
|
||||||
set_transient( $transient_key, $count, DAY_IN_SECONDS );
|
|
||||||
}
|
|
||||||
|
|
||||||
// if 0 authors where active
|
|
||||||
if ( 0 === $count ) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
// if single user mode
|
|
||||||
if ( is_single_user() ) {
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
// if blog user is disabled
|
|
||||||
if ( is_user_disabled( Users::BLOG_USER_ID ) ) {
|
|
||||||
return $count;
|
|
||||||
}
|
|
||||||
|
|
||||||
// also count blog user
|
|
||||||
return $count + 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get the total number of users
|
|
||||||
*
|
|
||||||
* @return int The total number of users
|
|
||||||
*/
|
|
||||||
function get_total_users() {
|
|
||||||
// if single user mode
|
|
||||||
if ( is_single_user() ) {
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
$users = \get_users(
|
|
||||||
array(
|
|
||||||
'capability__in' => array( 'publish_posts' ),
|
|
||||||
)
|
|
||||||
);
|
|
||||||
|
|
||||||
if ( is_array( $users ) ) {
|
|
||||||
$users = count( $users );
|
|
||||||
} else {
|
|
||||||
$users = 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
// if blog user is disabled
|
|
||||||
if ( is_user_disabled( Users::BLOG_USER_ID ) ) {
|
|
||||||
return $users;
|
|
||||||
}
|
|
||||||
|
|
||||||
return $users + 1;
|
|
||||||
}
|
|
||||||
|
|
|
@ -315,7 +315,7 @@ class Follower extends Actor {
|
||||||
$object->set_id( $post->guid );
|
$object->set_id( $post->guid );
|
||||||
$object->set_name( $post->post_title );
|
$object->set_name( $post->post_title );
|
||||||
$object->set_summary( $post->post_excerpt );
|
$object->set_summary( $post->post_excerpt );
|
||||||
$object->set_published( gmdate( 'Y-m-d H:i:s', strtotime( $post->post_date ) ) );
|
$object->set_published( gmdate( 'Y-m-d H:i:s', strtotime( $post->post_published ) ) );
|
||||||
$object->set_updated( gmdate( 'Y-m-d H:i:s', strtotime( $post->post_modified ) ) );
|
$object->set_updated( gmdate( 'Y-m-d H:i:s', strtotime( $post->post_modified ) ) );
|
||||||
|
|
||||||
return $object;
|
return $object;
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
<?php
|
<?php
|
||||||
namespace Activitypub\Model;
|
namespace Activitypub\Model;
|
||||||
|
|
||||||
use Activitypub\Transformer\Post as Transformer_Post;
|
use Activitypub\Transformer\Post as Post_Transformer;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* ActivityPub Post Class
|
* ActivityPub Post Class
|
||||||
|
@ -33,9 +33,8 @@ class Post {
|
||||||
public function __construct( $post, $post_author = null ) {
|
public function __construct( $post, $post_author = null ) {
|
||||||
_deprecated_function( __CLASS__, '1.0.0', '\Activitypub\Transformer\Post' );
|
_deprecated_function( __CLASS__, '1.0.0', '\Activitypub\Transformer\Post' );
|
||||||
|
|
||||||
$this->post = $post;
|
$this->post = $post;
|
||||||
$transformer = new Transformer_Post();
|
$this->object = Post_Transformer::transform( $post )->to_object();
|
||||||
$this->object = $transformer->set_wp_post( $post )->to_object();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -4,7 +4,7 @@ namespace Activitypub\Rest;
|
||||||
use WP_Error;
|
use WP_Error;
|
||||||
use WP_REST_Server;
|
use WP_REST_Server;
|
||||||
use WP_REST_Response;
|
use WP_REST_Response;
|
||||||
use Activitypub\Transformer\Transformers_Manager;
|
use Activitypub\Transformer\Post;
|
||||||
use Activitypub\Activity\Activity;
|
use Activitypub\Activity\Activity;
|
||||||
use Activitypub\Collection\Users as User_Collection;
|
use Activitypub\Collection\Users as User_Collection;
|
||||||
|
|
||||||
|
@ -168,7 +168,7 @@ class Collection {
|
||||||
);
|
);
|
||||||
|
|
||||||
foreach ( $posts as $post ) {
|
foreach ( $posts as $post ) {
|
||||||
$response['orderedItems'][] = Transformers_Manager::instance()->get_transformer( $post )->to_object()->to_array();
|
$response['orderedItems'][] = Post::transform( $post )->to_object()->to_array();
|
||||||
}
|
}
|
||||||
|
|
||||||
$rest_response = new WP_REST_Response( $response, 200 );
|
$rest_response = new WP_REST_Response( $response, 200 );
|
||||||
|
|
|
@ -236,12 +236,8 @@ class Inbox {
|
||||||
$params['actor'] = array(
|
$params['actor'] = array(
|
||||||
'required' => true,
|
'required' => true,
|
||||||
'sanitize_callback' => function( $param, $request, $key ) {
|
'sanitize_callback' => function( $param, $request, $key ) {
|
||||||
if ( \is_array( $param ) ) {
|
if ( ! \is_string( $param ) ) {
|
||||||
if ( isset( $param['id'] ) ) {
|
$param = $param['id'];
|
||||||
$param = $param['id'];
|
|
||||||
} else {
|
|
||||||
$param = $param['url'];
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return \esc_url_raw( $param );
|
return \esc_url_raw( $param );
|
||||||
},
|
},
|
||||||
|
@ -340,6 +336,72 @@ class Inbox {
|
||||||
return $params;
|
return $params;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Converts a new ActivityPub object to comment data suitable for creating a comment
|
||||||
|
*
|
||||||
|
* @param array $object The activity-object.
|
||||||
|
*
|
||||||
|
* @return array Comment data suitable for creating a comment.
|
||||||
|
*/
|
||||||
|
public static function convert_object_to_comment_data( $object, $user_id ) {
|
||||||
|
$object['user_id'] = $user_id;
|
||||||
|
if ( ! isset( $object['object']['inReplyTo'] ) ) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// check if Activity is public or not
|
||||||
|
if ( ! self::is_activity_public( $object ) ) {
|
||||||
|
// @todo maybe send email
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
$meta = \Activitypub\get_remote_metadata_by_actor( $object['actor'] );
|
||||||
|
|
||||||
|
$id = $object['object']['id'];
|
||||||
|
|
||||||
|
// Only handle replies
|
||||||
|
if ( ! isset( $object['object']['inReplyTo'] ) ) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
$in_reply_to = $object['object']['inReplyTo'];
|
||||||
|
|
||||||
|
// Comment already exists
|
||||||
|
if ( \Activitypub\object_id_to_comment( $id ) ) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
$parent_comment = \Activitypub\object_id_to_comment( $in_reply_to );
|
||||||
|
|
||||||
|
// save only replies and reactions
|
||||||
|
$comment_post_id = \Activitypub\object_to_post_id_by_field_name( $object, 'context' );
|
||||||
|
if ( ! $comment_post_id ) {
|
||||||
|
$comment_post_id = \Activitypub\object_to_post_id_by_field_name( $object, 'inReplyTo' );
|
||||||
|
}
|
||||||
|
if ( ! $comment_post_id ) {
|
||||||
|
$comment_post_id = $parent_comment->comment_post_ID;
|
||||||
|
}
|
||||||
|
if ( ! $comment_post_id ) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return array(
|
||||||
|
'comment_post_ID' => $comment_post_id,
|
||||||
|
'comment_author' => \esc_attr( $meta['name'] ),
|
||||||
|
'comment_author_url' => \esc_url_raw( $object['actor'] ),
|
||||||
|
'comment_content' => \wp_filter_kses( $object['object']['content'] ),
|
||||||
|
'comment_type' => 'comment',
|
||||||
|
'comment_author_email' => '',
|
||||||
|
'comment_parent' => $parent_comment ? $parent_comment->comment_ID : 0,
|
||||||
|
'comment_meta' => array(
|
||||||
|
'ap_object' => \serialize( $object ),
|
||||||
|
'source_id' => \esc_url_raw( $id ),
|
||||||
|
'source_url' => \esc_url_raw( $object['object']['url'] ),
|
||||||
|
'avatar_url' => \esc_url_raw( $meta['icon']['url'] ),
|
||||||
|
'protocol' => 'activitypub',
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Handles "Create" requests
|
* Handles "Create" requests
|
||||||
*
|
*
|
||||||
|
@ -347,40 +409,11 @@ class Inbox {
|
||||||
* @param int $user_id The id of the local blog-user
|
* @param int $user_id The id of the local blog-user
|
||||||
*/
|
*/
|
||||||
public static function handle_create( $object, $user_id ) {
|
public static function handle_create( $object, $user_id ) {
|
||||||
$meta = get_remote_metadata_by_actor( $object['actor'] );
|
$commentdata = self::convert_object_to_comment_data( $object, $user_id );
|
||||||
|
if ( ! $commentdata ) {
|
||||||
if ( ! isset( $object['object']['inReplyTo'] ) ) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// check if Activity is public or not
|
|
||||||
if ( ! self::is_activity_public( $object ) ) {
|
|
||||||
// @todo maybe send email
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
$comment_post_id = \url_to_postid( $object['object']['inReplyTo'] );
|
|
||||||
|
|
||||||
// save only replys and reactions
|
|
||||||
if ( ! $comment_post_id ) {
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
$commentdata = array(
|
|
||||||
'comment_post_ID' => $comment_post_id,
|
|
||||||
'comment_author' => \esc_attr( $meta['name'] ),
|
|
||||||
'comment_author_url' => \esc_url_raw( $object['actor'] ),
|
|
||||||
'comment_content' => addslashes( \wp_kses( $object['object']['content'], 'pre_comment_content' ) ),
|
|
||||||
'comment_type' => 'comment',
|
|
||||||
'comment_author_email' => '',
|
|
||||||
'comment_parent' => 0,
|
|
||||||
'comment_meta' => array(
|
|
||||||
'source_url' => \esc_url_raw( $object['object']['url'] ),
|
|
||||||
'avatar_url' => \esc_url_raw( $meta['icon']['url'] ),
|
|
||||||
'protocol' => 'activitypub',
|
|
||||||
),
|
|
||||||
);
|
|
||||||
|
|
||||||
// disable flood control
|
// disable flood control
|
||||||
\remove_action( 'check_comment_flood', 'check_comment_flood_db', 10 );
|
\remove_action( 'check_comment_flood', 'check_comment_flood_db', 10 );
|
||||||
|
|
||||||
|
@ -395,11 +428,8 @@ class Inbox {
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
\add_filter( 'wp_kses_allowed_html', array( self::class, 'allowed_comment_html' ), 10, 2 );
|
|
||||||
|
|
||||||
$state = \wp_new_comment( $commentdata, true );
|
$state = \wp_new_comment( $commentdata, true );
|
||||||
|
|
||||||
\remove_filter( 'wp_kses_allowed_html', array( self::class, 'allowed_comment_html' ) );
|
|
||||||
\remove_filter( 'pre_option_require_name_email', '__return_false' );
|
\remove_filter( 'pre_option_require_name_email', '__return_false' );
|
||||||
|
|
||||||
// re-add flood control
|
// re-add flood control
|
||||||
|
@ -408,6 +438,76 @@ class Inbox {
|
||||||
do_action( 'activitypub_handled_create', $object, $user_id, $state, $commentdata );
|
do_action( 'activitypub_handled_create', $object, $user_id, $state, $commentdata );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handles "Update" requests
|
||||||
|
*
|
||||||
|
* @param array $object The activity-object
|
||||||
|
* @param int $user_id The id of the local blog-user
|
||||||
|
*/
|
||||||
|
public static function handle_update( $object, $user_id ) {
|
||||||
|
$meta = \Activitypub\get_remote_metadata_by_actor( $object['actor'] );
|
||||||
|
|
||||||
|
//Determine comment_ID
|
||||||
|
$object_comment_id = \Activitypub\url_to_commentid( \esc_url_raw( $object['object']['id'] ) );
|
||||||
|
if ( ! is_null( $object_comment_id ) ) {
|
||||||
|
|
||||||
|
//found a local comment id
|
||||||
|
$commentdata = \get_comment( $object_comment_id, ARRAY_A );
|
||||||
|
$commentdata['comment_author'] = \esc_attr( $meta['name'] ? $meta['name'] : $meta['preferredUsername'] );
|
||||||
|
$commentdata['comment_content'] = \wp_filter_kses( $object['object']['content'] );
|
||||||
|
$commentdata['comment_meta']['avatar_url'] = \esc_url_raw( $meta['icon']['url'] );
|
||||||
|
$commentdata['comment_meta']['ap_published'] = \wp_date( 'Y-m-d H:i:s', strtotime( $object['object']['published'] ) );
|
||||||
|
$commentdata['comment_meta']['activitypub_last_modified'] = $object['object']['updated'];
|
||||||
|
$commentdata['comment_meta']['ap_object'] = \serialize( $object );
|
||||||
|
|
||||||
|
// disable flood control
|
||||||
|
\remove_action( 'check_comment_flood', 'check_comment_flood_db', 10 );
|
||||||
|
|
||||||
|
// do not require email for AP entries
|
||||||
|
\add_filter( 'pre_option_require_name_email', '__return_false' );
|
||||||
|
|
||||||
|
$state = \wp_update_comment( $commentdata, true );
|
||||||
|
|
||||||
|
\remove_filter( 'pre_option_require_name_email', '__return_false' );
|
||||||
|
|
||||||
|
// re-add flood control
|
||||||
|
\add_action( 'check_comment_flood', 'check_comment_flood_db', 10, 4 );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handles "Delete" requests
|
||||||
|
*
|
||||||
|
* @param array $object The activity-object
|
||||||
|
* @param int $user_id The id of the local blog-user
|
||||||
|
*/
|
||||||
|
public static function handle_delete( $object, $user_id ) {
|
||||||
|
if ( ! isset( $object['object']['id'] ) ) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
//Determine comment_ID
|
||||||
|
$object_comment_id = \Activitypub\url_to_commentid( \esc_url_raw( $object['object']['id'] ) );
|
||||||
|
if ( ! is_null( $object_comment_id ) ) {
|
||||||
|
|
||||||
|
//found a local comment id
|
||||||
|
$commentdata = \get_comment( $object_comment_id, ARRAY_A );
|
||||||
|
|
||||||
|
// disable flood control
|
||||||
|
\remove_action( 'check_comment_flood', 'check_comment_flood_db', 10 );
|
||||||
|
|
||||||
|
// do not require email for AP entries
|
||||||
|
\add_filter( 'pre_option_require_name_email', '__return_false' );
|
||||||
|
|
||||||
|
// Should we trash or send back to moderation
|
||||||
|
$state = \wp_trash_comment( $commentdata['comment_ID'], true );
|
||||||
|
|
||||||
|
\remove_filter( 'pre_option_require_name_email', '__return_false' );
|
||||||
|
|
||||||
|
// re-add flood control
|
||||||
|
\add_action( 'check_comment_flood', 'check_comment_flood_db', 10, 4 );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Extract recipient URLs from Activity object
|
* Extract recipient URLs from Activity object
|
||||||
*
|
*
|
||||||
|
@ -490,29 +590,4 @@ class Inbox {
|
||||||
|
|
||||||
return in_array( 'https://www.w3.org/ns/activitystreams#Public', $recipients, true );
|
return in_array( 'https://www.w3.org/ns/activitystreams#Public', $recipients, true );
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Adds line breaks to the list of allowed comment tags.
|
|
||||||
*
|
|
||||||
* @param array $allowedtags Allowed HTML tags.
|
|
||||||
* @param string $context Context.
|
|
||||||
* @return array Filtered tag list.
|
|
||||||
*/
|
|
||||||
public static function allowed_comment_html( $allowedtags, $context = '' ) {
|
|
||||||
if ( 'pre_comment_content' !== $context ) {
|
|
||||||
// Do nothing.
|
|
||||||
return $allowedtags;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Add `p` and `br` to the list of allowed tags.
|
|
||||||
if ( ! array_key_exists( 'br', $allowedtags ) ) {
|
|
||||||
$allowedtags['br'] = array();
|
|
||||||
}
|
|
||||||
|
|
||||||
if ( ! array_key_exists( 'p', $allowedtags ) ) {
|
|
||||||
$allowedtags['p'] = array();
|
|
||||||
}
|
|
||||||
|
|
||||||
return $allowedtags;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,8 +3,6 @@ namespace Activitypub\Rest;
|
||||||
|
|
||||||
use WP_REST_Response;
|
use WP_REST_Response;
|
||||||
|
|
||||||
use function Activitypub\get_total_users;
|
|
||||||
use function Activitypub\get_active_users;
|
|
||||||
use function Activitypub\get_rest_url_by_path;
|
use function Activitypub\get_rest_url_by_path;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -84,14 +82,24 @@ class Nodeinfo {
|
||||||
'version' => \get_bloginfo( 'version' ),
|
'version' => \get_bloginfo( 'version' ),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
$users = \get_users(
|
||||||
|
array(
|
||||||
|
'capability__in' => array( 'publish_posts' ),
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
if ( is_countable( $users ) ) {
|
||||||
|
$users = count( $users );
|
||||||
|
} else {
|
||||||
|
$users = 1;
|
||||||
|
}
|
||||||
|
|
||||||
$posts = \wp_count_posts();
|
$posts = \wp_count_posts();
|
||||||
$comments = \wp_count_comments();
|
$comments = \wp_count_comments();
|
||||||
|
|
||||||
$nodeinfo['usage'] = array(
|
$nodeinfo['usage'] = array(
|
||||||
'users' => array(
|
'users' => array(
|
||||||
'total' => get_total_users(),
|
'total' => $users,
|
||||||
'activeMonth' => get_active_users( '1 month ago' ),
|
|
||||||
'activeHalfyear' => get_active_users( '6 month ago' ),
|
|
||||||
),
|
),
|
||||||
'localPosts' => (int) $posts->publish,
|
'localPosts' => (int) $posts->publish,
|
||||||
'localComments' => (int) $comments->approved,
|
'localComments' => (int) $comments->approved,
|
||||||
|
@ -131,14 +139,24 @@ class Nodeinfo {
|
||||||
'version' => \get_bloginfo( 'version' ),
|
'version' => \get_bloginfo( 'version' ),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
$users = \get_users(
|
||||||
|
array(
|
||||||
|
'capability__in' => array( 'publish_posts' ),
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
if ( is_countable( $users ) ) {
|
||||||
|
$users = count( $users );
|
||||||
|
} else {
|
||||||
|
$users = 1;
|
||||||
|
}
|
||||||
|
|
||||||
$posts = \wp_count_posts();
|
$posts = \wp_count_posts();
|
||||||
$comments = \wp_count_comments();
|
$comments = \wp_count_comments();
|
||||||
|
|
||||||
$nodeinfo['usage'] = array(
|
$nodeinfo['usage'] = array(
|
||||||
'users' => array(
|
'users' => array(
|
||||||
'total' => get_total_users(),
|
'total' => (int) $users,
|
||||||
'activeMonth' => get_active_users( 1 ),
|
|
||||||
'activeHalfyear' => get_active_users( 6 ),
|
|
||||||
),
|
),
|
||||||
'localPosts' => (int) $posts->publish,
|
'localPosts' => (int) $posts->publish,
|
||||||
'localComments' => (int) $comments->approved,
|
'localComments' => (int) $comments->approved,
|
||||||
|
|
|
@ -5,7 +5,7 @@ use stdClass;
|
||||||
use WP_Error;
|
use WP_Error;
|
||||||
use WP_REST_Server;
|
use WP_REST_Server;
|
||||||
use WP_REST_Response;
|
use WP_REST_Response;
|
||||||
use Activitypub\Transformer\Transformers_Manager;
|
use Activitypub\Transformer\Post;
|
||||||
use Activitypub\Activity\Activity;
|
use Activitypub\Activity\Activity;
|
||||||
use Activitypub\Collection\Users as User_Collection;
|
use Activitypub\Collection\Users as User_Collection;
|
||||||
|
|
||||||
|
@ -59,7 +59,7 @@ class Outbox {
|
||||||
return $user;
|
return $user;
|
||||||
}
|
}
|
||||||
|
|
||||||
$post_types = array_keys( \get_option( 'activitypub_transformer_mapping', array( 'post' => 'activitypub/default', 'page' => 'activitypub/default' ) ) );
|
$post_types = \get_option( 'activitypub_support_post_types', array( 'post', 'page' ) );
|
||||||
|
|
||||||
$page = $request->get_param( 'page', 1 );
|
$page = $request->get_param( 'page', 1 );
|
||||||
|
|
||||||
|
@ -105,8 +105,7 @@ class Outbox {
|
||||||
);
|
);
|
||||||
|
|
||||||
foreach ( $posts as $post ) {
|
foreach ( $posts as $post ) {
|
||||||
$transformer = \Activitypub\Transformer\Transformers_Manager::instance()->get_transformer( $post );
|
$post = Post::transform( $post )->to_object();
|
||||||
$post = $transformer->to_object();
|
|
||||||
$activity = new Activity();
|
$activity = new Activity();
|
||||||
$activity->set_type( 'Create' );
|
$activity->set_type( 'Create' );
|
||||||
$activity->set_context( null );
|
$activity->set_context( null );
|
||||||
|
|
|
@ -89,23 +89,6 @@ class Server {
|
||||||
return $response;
|
return $response;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Filter to defer signature verification
|
|
||||||
*
|
|
||||||
* Skip signature verification for debugging purposes or to reduce load for
|
|
||||||
* certain Activity-Types, like "Delete".
|
|
||||||
*
|
|
||||||
* @param bool $defer Whether to defer signature verification.
|
|
||||||
* @param WP_REST_Request $request The request used to generate the response.
|
|
||||||
*
|
|
||||||
* @return bool Whether to defer signature verification.
|
|
||||||
*/
|
|
||||||
$defer = \apply_filters( 'activitypub_defer_signature_verification', false, $request );
|
|
||||||
|
|
||||||
if ( $defer ) {
|
|
||||||
return $response;
|
|
||||||
}
|
|
||||||
|
|
||||||
// POST-Requets are always signed
|
// POST-Requets are always signed
|
||||||
if ( 'GET' !== $request->get_method() ) {
|
if ( 'GET' !== $request->get_method() ) {
|
||||||
$verified_request = Signature::verify_http_signature( $request );
|
$verified_request = Signature::verify_http_signature( $request );
|
||||||
|
|
|
@ -30,24 +30,19 @@ class Followers extends WP_List_Table {
|
||||||
|
|
||||||
public function get_columns() {
|
public function get_columns() {
|
||||||
return array(
|
return array(
|
||||||
'cb' => '<input type="checkbox" />',
|
'cb' => '<input type="checkbox" />',
|
||||||
'avatar' => \__( 'Avatar', 'activitypub' ),
|
'avatar' => \__( 'Avatar', 'activitypub' ),
|
||||||
'post_title' => \__( 'Name', 'activitypub' ),
|
'name' => \__( 'Name', 'activitypub' ),
|
||||||
'username' => \__( 'Username', 'activitypub' ),
|
'username' => \__( 'Username', 'activitypub' ),
|
||||||
'url' => \__( 'URL', 'activitypub' ),
|
'url' => \__( 'URL', 'activitypub' ),
|
||||||
'published' => \__( 'Followed', 'activitypub' ),
|
'updated' => \__( 'Last updated', 'activitypub' ),
|
||||||
'modified' => \__( 'Last updated', 'activitypub' ),
|
//'errors' => \__( 'Errors', 'activitypub' ),
|
||||||
|
//'latest-error' => \__( 'Latest Error Message', 'activitypub' ),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function get_sortable_columns() {
|
public function get_sortable_columns() {
|
||||||
$sortable_columns = array(
|
return array();
|
||||||
'post_title' => array( 'post_title', true ),
|
|
||||||
'modified' => array( 'modified', false ),
|
|
||||||
'published' => array( 'published', false ),
|
|
||||||
);
|
|
||||||
|
|
||||||
return $sortable_columns;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public function prepare_items() {
|
public function prepare_items() {
|
||||||
|
@ -60,32 +55,8 @@ class Followers extends WP_List_Table {
|
||||||
$page_num = $this->get_pagenum();
|
$page_num = $this->get_pagenum();
|
||||||
$per_page = 20;
|
$per_page = 20;
|
||||||
|
|
||||||
$args = array();
|
$followers = FollowerCollection::get_followers( $this->user_id, $per_page, $page_num );
|
||||||
|
$counter = FollowerCollection::count_followers( $this->user_id );
|
||||||
// phpcs:ignore WordPress.Security.NonceVerification.Recommended
|
|
||||||
if ( isset( $_GET['orderby'] ) ) {
|
|
||||||
// phpcs:ignore WordPress.Security.NonceVerification.Recommended
|
|
||||||
$args['orderby'] = sanitize_text_field( wp_unslash( $_GET['orderby'] ) );
|
|
||||||
}
|
|
||||||
|
|
||||||
// phpcs:ignore WordPress.Security.NonceVerification.Recommended
|
|
||||||
if ( isset( $_GET['order'] ) ) {
|
|
||||||
// phpcs:ignore WordPress.Security.NonceVerification.Recommended
|
|
||||||
$args['order'] = sanitize_text_field( wp_unslash( $_GET['order'] ) );
|
|
||||||
}
|
|
||||||
|
|
||||||
// phpcs:ignore WordPress.Security.NonceVerification.Recommended
|
|
||||||
if ( isset( $_GET['s'] ) && isset( $_REQUEST['_wpnonce'] ) ) {
|
|
||||||
$nonce = sanitize_text_field( wp_unslash( $_REQUEST['_wpnonce'] ) );
|
|
||||||
if ( wp_verify_nonce( $nonce, 'bulk-' . $this->_args['plural'] ) ) {
|
|
||||||
// phpcs:ignore WordPress.Security.NonceVerification.Recommended
|
|
||||||
$args['s'] = sanitize_text_field( wp_unslash( $_GET['s'] ) );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
$followers_with_count = FollowerCollection::get_followers_with_count( $this->user_id, $per_page, $page_num, $args );
|
|
||||||
$followers = $followers_with_count['followers'];
|
|
||||||
$counter = $followers_with_count['total'];
|
|
||||||
|
|
||||||
$this->items = array();
|
$this->items = array();
|
||||||
$this->set_pagination_args(
|
$this->set_pagination_args(
|
||||||
|
@ -98,13 +69,14 @@ class Followers extends WP_List_Table {
|
||||||
|
|
||||||
foreach ( $followers as $follower ) {
|
foreach ( $followers as $follower ) {
|
||||||
$item = array(
|
$item = array(
|
||||||
'icon' => esc_attr( $follower->get_icon_url() ),
|
'icon' => esc_attr( $follower->get_icon_url() ),
|
||||||
'post_title' => esc_attr( $follower->get_name() ),
|
'name' => esc_attr( $follower->get_name() ),
|
||||||
'username' => esc_attr( $follower->get_preferred_username() ),
|
'username' => esc_attr( $follower->get_preferred_username() ),
|
||||||
'url' => esc_attr( $follower->get_url() ),
|
'url' => esc_attr( $follower->get_url() ),
|
||||||
'identifier' => esc_attr( $follower->get_id() ),
|
'identifier' => esc_attr( $follower->get_id() ),
|
||||||
'published' => esc_attr( $follower->get_published() ),
|
'updated' => esc_attr( $follower->get_updated() ),
|
||||||
'modified' => esc_attr( $follower->get_updated() ),
|
'errors' => $follower->count_errors(),
|
||||||
|
'latest-error' => $follower->get_latest_error_message(),
|
||||||
);
|
);
|
||||||
|
|
||||||
$this->items[] = $item;
|
$this->items[] = $item;
|
||||||
|
@ -144,11 +116,11 @@ class Followers extends WP_List_Table {
|
||||||
}
|
}
|
||||||
|
|
||||||
public function process_action() {
|
public function process_action() {
|
||||||
if ( ! isset( $_REQUEST['followers'] ) || ! isset( $_REQUEST['_wpnonce'] ) ) {
|
if ( ! isset( $_REQUEST['followers'] ) || ! isset( $_REQUEST['_apnonce'] ) ) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
$nonce = sanitize_text_field( wp_unslash( $_REQUEST['_wpnonce'] ) );
|
$nonce = sanitize_text_field( wp_unslash( $_REQUEST['_apnonce'] ) );
|
||||||
if ( ! wp_verify_nonce( $nonce, 'bulk-' . $this->_args['plural'] ) ) {
|
if ( ! wp_verify_nonce( $nonce, 'activitypub-followers-list' ) ) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,619 +0,0 @@
|
||||||
<?php
|
|
||||||
/**
|
|
||||||
* Inspired by the PHP ActivityPub Library by @Landrok
|
|
||||||
*
|
|
||||||
* @link https://github.com/landrok/activitypub
|
|
||||||
*/
|
|
||||||
namespace Activitypub\Transformer;
|
|
||||||
|
|
||||||
use WP_Post;
|
|
||||||
use Activitypub\Collection\Users;
|
|
||||||
use Activitypub\Model\Blog_User;
|
|
||||||
use Activitypub\Activity\Base_Object;
|
|
||||||
use Activitypub\Shortcodes;
|
|
||||||
|
|
||||||
use function Activitypub\esc_hashtag;
|
|
||||||
use function Activitypub\is_single_user;
|
|
||||||
use function Activitypub\get_rest_url_by_path;
|
|
||||||
use function Activitypub\site_supports_blocks;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Base class to implement WordPress to ActivityPub transformers.
|
|
||||||
*/
|
|
||||||
abstract class Base {
|
|
||||||
/**
|
|
||||||
* The WP_Post object.
|
|
||||||
*
|
|
||||||
* @var WP_Post
|
|
||||||
*/
|
|
||||||
protected $wp_post;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Assign WP_Post Object to a specific transformer instance.
|
|
||||||
*
|
|
||||||
* This helps to chain the output of the Transformer.
|
|
||||||
*
|
|
||||||
* @param WP_Post $wp_post The WP_Post object.
|
|
||||||
* @return void
|
|
||||||
*/
|
|
||||||
public function set_wp_post( WP_Post $wp_post ) {
|
|
||||||
$post_type = get_post_type( $wp_post );
|
|
||||||
if ( ! $this->supports_post_type( $post_type ) ) {
|
|
||||||
_doing_it_wrong(
|
|
||||||
__METHOD__,
|
|
||||||
/* translators: %s: Block name. */
|
|
||||||
sprintf( 'The Transformer "%s" does not support the post type "%s".', esc_html( $this->get_label() ), esc_html( $post_type ) ),
|
|
||||||
'version_number_transformer_management_placeholder'
|
|
||||||
);
|
|
||||||
}
|
|
||||||
$this->wp_post = $wp_post;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get the supported WP post types that the transformer can use as an input.
|
|
||||||
*
|
|
||||||
* By default all post types are supported.
|
|
||||||
* You may very likely wish to override this function.
|
|
||||||
*
|
|
||||||
* @since version_number_transformer_management_placeholder
|
|
||||||
* @return string[] An array containing all the supported post types.
|
|
||||||
*/
|
|
||||||
public function get_supported_post_types() {
|
|
||||||
return \get_post_types( array( 'public' => true ), 'names' );
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get the name of the plugin that registered the transformer.
|
|
||||||
*
|
|
||||||
* @see Forked from the WordPress elementor plugin.
|
|
||||||
* @since version_number_transformer_management_placeholder
|
|
||||||
* @return string Plugin name
|
|
||||||
*/
|
|
||||||
private function get_plugin_name_from_transformer_instance( $transformer ) {
|
|
||||||
$class_reflection = new \ReflectionClass( $transformer );
|
|
||||||
|
|
||||||
$plugin_basename = plugin_basename( $class_reflection->getFileName() );
|
|
||||||
|
|
||||||
$plugin_directory = strtok( $plugin_basename, '/' );
|
|
||||||
|
|
||||||
$plugins_data = get_plugins( '/' . $plugin_directory );
|
|
||||||
$plugin_data = array_shift( $plugins_data );
|
|
||||||
|
|
||||||
if ( isset( $plugin_data['Name'] ) ) {
|
|
||||||
return $plugin_data['Name'];
|
|
||||||
} else {
|
|
||||||
return esc_html__( 'Unknown', 'activitypub' );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Return whether the transformer supports a post type.
|
|
||||||
*
|
|
||||||
* @since version_number_transformer_management_placeholder
|
|
||||||
* @return string post_type Post type name.
|
|
||||||
*/
|
|
||||||
final public function supports_post_type( $post_type ) {
|
|
||||||
return in_array( $post_type, $this->get_supported_post_types(), true );
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get the name used for registering the transformer with the ActivityPub plugin.
|
|
||||||
*
|
|
||||||
* @since version_number_transformer_management_placeholder
|
|
||||||
* @return string name
|
|
||||||
*/
|
|
||||||
abstract public function get_name();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get the display name for the ActivityPub transformer.
|
|
||||||
*
|
|
||||||
* @since version_number_transformer_management_placeholder
|
|
||||||
* @return string display name
|
|
||||||
*/
|
|
||||||
abstract public function get_label();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the ActivityStreams 2.0 Object-Type for a Post.
|
|
||||||
*
|
|
||||||
* @see https://www.w3.org/TR/activitystreams-vocabulary/#activity-types
|
|
||||||
*
|
|
||||||
* @return string The Object-Type.
|
|
||||||
*/
|
|
||||||
abstract protected function get_object_type();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the content for the ActivityPub Item.
|
|
||||||
*
|
|
||||||
* The content will be generated based on the user settings.
|
|
||||||
*
|
|
||||||
* @return string The content.
|
|
||||||
*/
|
|
||||||
protected function get_content() {
|
|
||||||
global $post;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Provides an action hook so plugins can add their own hooks/filters before AP content is generated.
|
|
||||||
*
|
|
||||||
* Example: if a plugin adds a filter to `the_content` to add a button to the end of posts, it can also remove that filter here.
|
|
||||||
*
|
|
||||||
* @param WP_Post $post The post object.
|
|
||||||
*/
|
|
||||||
do_action( 'activitypub_before_get_content', $post );
|
|
||||||
|
|
||||||
// phpcs:ignore WordPress.WP.GlobalVariablesOverride.Prohibited
|
|
||||||
$post = $this->wp_post;
|
|
||||||
$content = $this->get_post_content_template();
|
|
||||||
|
|
||||||
// Register our shortcodes just in time.
|
|
||||||
Shortcodes::register();
|
|
||||||
// Fill in the shortcodes.
|
|
||||||
setup_postdata( $post );
|
|
||||||
$content = do_shortcode( $content );
|
|
||||||
wp_reset_postdata();
|
|
||||||
|
|
||||||
$content = \wpautop( $content );
|
|
||||||
$content = \preg_replace( '/[\n\r\t]/', '', $content );
|
|
||||||
$content = \trim( $content );
|
|
||||||
|
|
||||||
$content = \apply_filters( 'activitypub_the_content', $content, $post );
|
|
||||||
|
|
||||||
// Don't need these any more, should never appear in a post.
|
|
||||||
Shortcodes::unregister();
|
|
||||||
|
|
||||||
return $content;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets the template to use to generate the content of the activitypub item.
|
|
||||||
*
|
|
||||||
* @return string The Template.
|
|
||||||
*/
|
|
||||||
protected function get_post_content_template() {
|
|
||||||
if ( 'excerpt' === \get_option( 'activitypub_post_content_type', 'content' ) ) {
|
|
||||||
return "[ap_excerpt]\n\n[ap_permalink type=\"html\"]";
|
|
||||||
}
|
|
||||||
|
|
||||||
if ( 'title' === \get_option( 'activitypub_post_content_type', 'content' ) ) {
|
|
||||||
return "[ap_title]\n\n[ap_permalink type=\"html\"]";
|
|
||||||
}
|
|
||||||
|
|
||||||
if ( 'content' === \get_option( 'activitypub_post_content_type', 'content' ) ) {
|
|
||||||
return "[ap_content]\n\n[ap_permalink type=\"html\"]\n\n[ap_hashtags]";
|
|
||||||
}
|
|
||||||
|
|
||||||
return \get_option( 'activitypub_custom_post_content', ACTIVITYPUB_CUSTOM_POST_CONTENT );
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the ID of the Post.
|
|
||||||
*
|
|
||||||
* @return string The Posts ID.
|
|
||||||
*/
|
|
||||||
public function get_id() {
|
|
||||||
return $this->get_url();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the URL of the Post.
|
|
||||||
*
|
|
||||||
* @return string The Posts URL.
|
|
||||||
*/
|
|
||||||
public function get_url() {
|
|
||||||
$post = $this->wp_post;
|
|
||||||
|
|
||||||
if ( 'trash' === get_post_status( $post ) ) {
|
|
||||||
$permalink = \get_post_meta( $post->ID, 'activitypub_canonical_url', true );
|
|
||||||
} else {
|
|
||||||
$permalink = \get_permalink( $post );
|
|
||||||
}
|
|
||||||
|
|
||||||
return \esc_url( $permalink );
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the User-URL of the Author of the Post.
|
|
||||||
*
|
|
||||||
* If `single_user` mode is enabled, the URL of the Blog-User is returned.
|
|
||||||
*
|
|
||||||
* @return string The User-URL.
|
|
||||||
*/
|
|
||||||
protected function get_attributed_to() {
|
|
||||||
if ( is_single_user() ) {
|
|
||||||
$user = new Blog_User();
|
|
||||||
return $user->get_url();
|
|
||||||
}
|
|
||||||
|
|
||||||
return Users::get_by_id( $this->wp_post->post_author )->get_url();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Generates all Media Attachments for a Post.
|
|
||||||
*
|
|
||||||
* @return array The Attachments.
|
|
||||||
*/
|
|
||||||
protected function get_attachments() {
|
|
||||||
// Once upon a time we only supported images, but we now support audio/video as well.
|
|
||||||
// We maintain the image-centric naming for backwards compatibility.
|
|
||||||
$max_media = intval( \apply_filters( 'activitypub_max_image_attachments', \get_option( 'activitypub_max_image_attachments', ACTIVITYPUB_MAX_IMAGE_ATTACHMENTS ) ) );
|
|
||||||
|
|
||||||
if ( site_supports_blocks() && \has_blocks( $this->wp_post->post_content ) ) {
|
|
||||||
return $this->get_block_attachments( $max_media );
|
|
||||||
}
|
|
||||||
|
|
||||||
return $this->get_classic_editor_images( $max_media );
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get media attachments from blocks. They will be formatted as ActivityPub attachments, not as WP attachments.
|
|
||||||
*
|
|
||||||
* @param int $max_media The maximum number of attachments to return.
|
|
||||||
*
|
|
||||||
* @return array The attachments.
|
|
||||||
*/
|
|
||||||
protected function get_block_attachments( $max_media ) {
|
|
||||||
// max media can't be negative or zero
|
|
||||||
if ( $max_media <= 0 ) {
|
|
||||||
return array();
|
|
||||||
}
|
|
||||||
|
|
||||||
$id = $this->wp_post->ID;
|
|
||||||
|
|
||||||
$media_ids = array();
|
|
||||||
|
|
||||||
// list post thumbnail first if this post has one
|
|
||||||
if ( \function_exists( 'has_post_thumbnail' ) && \has_post_thumbnail( $id ) ) {
|
|
||||||
$media_ids[] = \get_post_thumbnail_id( $id );
|
|
||||||
}
|
|
||||||
|
|
||||||
if ( $max_media > 0 ) {
|
|
||||||
$blocks = \parse_blocks( $this->wp_post->post_content );
|
|
||||||
$media_ids = self::get_media_ids_from_blocks( $blocks, $media_ids, $max_media );
|
|
||||||
}
|
|
||||||
|
|
||||||
return \array_filter( \array_map( array( self::class, 'wp_attachment_to_activity_attachment' ), $media_ids ) );
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get image attachments from the classic editor.
|
|
||||||
* Note that audio/video attachments are only supported in the block editor.
|
|
||||||
*
|
|
||||||
* @param int $max_images The maximum number of images to return.
|
|
||||||
*
|
|
||||||
* @return array The attachments.
|
|
||||||
*/
|
|
||||||
protected function get_classic_editor_images( $max_images ) {
|
|
||||||
// max images can't be negative or zero
|
|
||||||
if ( $max_images <= 0 ) {
|
|
||||||
return array();
|
|
||||||
}
|
|
||||||
|
|
||||||
$id = $this->wp_post->ID;
|
|
||||||
|
|
||||||
$image_ids = array();
|
|
||||||
|
|
||||||
// list post thumbnail first if this post has one
|
|
||||||
if ( \function_exists( 'has_post_thumbnail' ) && \has_post_thumbnail( $id ) ) {
|
|
||||||
$image_ids[] = \get_post_thumbnail_id( $id );
|
|
||||||
--$max_images;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ( $max_images > 0 ) {
|
|
||||||
$query = new \WP_Query(
|
|
||||||
array(
|
|
||||||
'post_parent' => $id,
|
|
||||||
'post_status' => 'inherit',
|
|
||||||
'post_type' => 'attachment',
|
|
||||||
'post_mime_type' => 'image',
|
|
||||||
'order' => 'ASC',
|
|
||||||
'orderby' => 'menu_order ID',
|
|
||||||
'posts_per_page' => $max_images,
|
|
||||||
)
|
|
||||||
);
|
|
||||||
foreach ( $query->get_posts() as $attachment ) {
|
|
||||||
if ( ! \in_array( $attachment->ID, $image_ids, true ) ) {
|
|
||||||
$image_ids[] = $attachment->ID;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
$image_ids = \array_unique( $image_ids );
|
|
||||||
|
|
||||||
return \array_filter( \array_map( array( self::class, 'wp_attachment_to_activity_attachment' ), $image_ids ) );
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Recursively get media IDs from blocks.
|
|
||||||
*
|
|
||||||
* @param array $blocks The blocks to search for media IDs
|
|
||||||
* @param array $media_ids The media IDs to append new IDs to
|
|
||||||
* @param int $max_media The maximum number of media to return.
|
|
||||||
*
|
|
||||||
* @return array The image IDs.
|
|
||||||
*/
|
|
||||||
protected static function get_media_ids_from_blocks( $blocks, $media_ids, $max_media ) {
|
|
||||||
|
|
||||||
foreach ( $blocks as $block ) {
|
|
||||||
// recurse into inner blocks
|
|
||||||
if ( ! empty( $block['innerBlocks'] ) ) {
|
|
||||||
$media_ids = self::get_media_ids_from_blocks( $block['innerBlocks'], $media_ids, $max_media );
|
|
||||||
}
|
|
||||||
|
|
||||||
switch ( $block['blockName'] ) {
|
|
||||||
case 'core/image':
|
|
||||||
case 'core/cover':
|
|
||||||
case 'core/audio':
|
|
||||||
case 'core/video':
|
|
||||||
case 'videopress/video':
|
|
||||||
if ( ! empty( $block['attrs']['id'] ) ) {
|
|
||||||
$media_ids[] = $block['attrs']['id'];
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case 'jetpack/slideshow':
|
|
||||||
case 'jetpack/tiled-gallery':
|
|
||||||
if ( ! empty( $block['attrs']['ids'] ) ) {
|
|
||||||
$media_ids = array_merge( $media_ids, $block['attrs']['ids'] );
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case 'jetpack/image-compare':
|
|
||||||
if ( ! empty( $block['attrs']['beforeImageId'] ) ) {
|
|
||||||
$media_ids[] = $block['attrs']['beforeImageId'];
|
|
||||||
}
|
|
||||||
if ( ! empty( $block['attrs']['afterImageId'] ) ) {
|
|
||||||
$media_ids[] = $block['attrs']['afterImageId'];
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
// depupe
|
|
||||||
$media_ids = \array_unique( $media_ids );
|
|
||||||
|
|
||||||
// stop doing unneeded work
|
|
||||||
if ( count( $media_ids ) >= $max_media ) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// still need to slice it because one gallery could knock us over the limit
|
|
||||||
return array_slice( $media_ids, 0, $max_media );
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Converts a WordPress Attachment to an ActivityPub Attachment.
|
|
||||||
*
|
|
||||||
* @param int $id The Attachment ID.
|
|
||||||
*
|
|
||||||
* @return array The ActivityPub Attachment.
|
|
||||||
*/
|
|
||||||
public static function wp_attachment_to_activity_attachment( $id ) {
|
|
||||||
$attachment = array();
|
|
||||||
$mime_type = \get_post_mime_type( $id );
|
|
||||||
$mime_type_parts = \explode( '/', $mime_type );
|
|
||||||
// switching on image/audio/video
|
|
||||||
switch ( $mime_type_parts[0] ) {
|
|
||||||
case 'image':
|
|
||||||
$image_size = 'full';
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Filter the image URL returned for each post.
|
|
||||||
*
|
|
||||||
* @param array|false $thumbnail The image URL, or false if no image is available.
|
|
||||||
* @param int $id The attachment ID.
|
|
||||||
* @param string $image_size The image size to retrieve. Set to 'full' by default.
|
|
||||||
*/
|
|
||||||
$thumbnail = apply_filters(
|
|
||||||
'activitypub_get_image',
|
|
||||||
self::get_image( $id, $image_size ),
|
|
||||||
$id,
|
|
||||||
$image_size
|
|
||||||
);
|
|
||||||
|
|
||||||
if ( $thumbnail ) {
|
|
||||||
$alt = \get_post_meta( $id, '_wp_attachment_image_alt', true );
|
|
||||||
$image = array(
|
|
||||||
'type' => 'Image',
|
|
||||||
'url' => $thumbnail[0],
|
|
||||||
'mediaType' => $mime_type,
|
|
||||||
);
|
|
||||||
|
|
||||||
if ( $alt ) {
|
|
||||||
$image['name'] = $alt;
|
|
||||||
}
|
|
||||||
$attachment = $image;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 'audio':
|
|
||||||
case 'video':
|
|
||||||
$attachment = array(
|
|
||||||
'type' => 'Document',
|
|
||||||
'mediaType' => $mime_type,
|
|
||||||
'url' => \wp_get_attachment_url( $id ),
|
|
||||||
'name' => \get_the_title( $id ),
|
|
||||||
);
|
|
||||||
$meta = wp_get_attachment_metadata( $id );
|
|
||||||
// height and width for videos
|
|
||||||
if ( isset( $meta['width'] ) && isset( $meta['height'] ) ) {
|
|
||||||
$attachment['width'] = $meta['width'];
|
|
||||||
$attachment['height'] = $meta['height'];
|
|
||||||
}
|
|
||||||
// @todo: add `icon` support for audio/video attachments. Maybe use post thumbnail?
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
return \apply_filters( 'activitypub_attachment', $attachment, $id );
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Return details about an image attachment.
|
|
||||||
*
|
|
||||||
* @param int $id The attachment ID.
|
|
||||||
* @param string $image_size The image size to retrieve. Set to 'full' by default.
|
|
||||||
*
|
|
||||||
* @return array|false Array of image data, or boolean false if no image is available.
|
|
||||||
*/
|
|
||||||
protected static function get_image( $id, $image_size = 'full' ) {
|
|
||||||
/**
|
|
||||||
* Hook into the image retrieval process. Before image retrieval.
|
|
||||||
*
|
|
||||||
* @param int $id The attachment ID.
|
|
||||||
* @param string $image_size The image size to retrieve. Set to 'full' by default.
|
|
||||||
*/
|
|
||||||
do_action( 'activitypub_get_image_pre', $id, $image_size );
|
|
||||||
|
|
||||||
$image = \wp_get_attachment_image_src( $id, $image_size );
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Hook into the image retrieval process. After image retrieval.
|
|
||||||
*
|
|
||||||
* @param int $id The attachment ID.
|
|
||||||
* @param string $image_size The image size to retrieve. Set to 'full' by default.
|
|
||||||
*/
|
|
||||||
do_action( 'activitypub_get_image_post', $id, $image_size );
|
|
||||||
|
|
||||||
return $image;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Helper function to get the @-Mentions from the post content.
|
|
||||||
*
|
|
||||||
* @return array The list of @-Mentions.
|
|
||||||
*/
|
|
||||||
protected function get_mentions() {
|
|
||||||
return apply_filters( 'activitypub_extract_mentions', array(), $this->wp_post->post_content, $this->wp_post );
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns a list of Mentions, used in the Post.
|
|
||||||
*
|
|
||||||
* @see https://docs.joinmastodon.org/spec/activitypub/#Mention
|
|
||||||
*
|
|
||||||
* @return array The list of Mentions.
|
|
||||||
*/
|
|
||||||
protected function get_cc() {
|
|
||||||
$cc = array();
|
|
||||||
|
|
||||||
$mentions = $this->get_mentions();
|
|
||||||
if ( $mentions ) {
|
|
||||||
foreach ( $mentions as $url ) {
|
|
||||||
$cc[] = $url;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return $cc;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns a list of Tags, used in the Post.
|
|
||||||
*
|
|
||||||
* This includes Hash-Tags and Mentions.
|
|
||||||
*
|
|
||||||
* @return array The list of Tags.
|
|
||||||
*/
|
|
||||||
protected function get_tags() {
|
|
||||||
$tags = array();
|
|
||||||
|
|
||||||
$post_tags = \get_the_tags( $this->wp_post->ID );
|
|
||||||
if ( $post_tags ) {
|
|
||||||
foreach ( $post_tags as $post_tag ) {
|
|
||||||
$tag = array(
|
|
||||||
'type' => 'Hashtag',
|
|
||||||
'href' => \esc_url( \get_tag_link( $post_tag->term_id ) ),
|
|
||||||
'name' => esc_hashtag( $post_tag->name ),
|
|
||||||
);
|
|
||||||
$tags[] = $tag;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
$mentions = $this->get_mentions();
|
|
||||||
if ( $mentions ) {
|
|
||||||
foreach ( $mentions as $mention => $url ) {
|
|
||||||
$tag = array(
|
|
||||||
'type' => 'Mention',
|
|
||||||
'href' => \esc_url( $url ),
|
|
||||||
'name' => \esc_html( $mention ),
|
|
||||||
);
|
|
||||||
$tags[] = $tag;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return $tags;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the locale of the post.
|
|
||||||
*
|
|
||||||
* @return string The locale of the post.
|
|
||||||
*/
|
|
||||||
public function get_locale() {
|
|
||||||
$post_id = $this->wp_post->ID;
|
|
||||||
$lang = \strtolower( \strtok( \get_locale(), '_-' ) );
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Filter the locale of the post.
|
|
||||||
*
|
|
||||||
* @param string $lang The locale of the post.
|
|
||||||
* @param int $post_id The post ID.
|
|
||||||
* @param WP_Post $post The post object.
|
|
||||||
*
|
|
||||||
* @return string The filtered locale of the post.
|
|
||||||
*/
|
|
||||||
return apply_filters( 'activitypub_post_locale', $lang, $post_id, $this->wp_post );
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets the contentMap
|
|
||||||
*
|
|
||||||
* @see https://www.w3.org/TR/activitystreams-vocabulary/#dfn-contentmap
|
|
||||||
*
|
|
||||||
* @return array the contenmap
|
|
||||||
*/
|
|
||||||
protected function get_content_map() {
|
|
||||||
return array(
|
|
||||||
$this->get_locale() => $this->get_content(),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Transforms the WP_Post object to an ActivityPub Object
|
|
||||||
*
|
|
||||||
* @see \Activitypub\Activity\Base_Object
|
|
||||||
*
|
|
||||||
* @return \Activitypub\Activity\Base_Object The ActivityPub Object
|
|
||||||
*/
|
|
||||||
public function to_object() {
|
|
||||||
$wp_post = $this->wp_post;
|
|
||||||
$object = new Base_Object();
|
|
||||||
|
|
||||||
$object->set_id( $this->get_id() );
|
|
||||||
$object->set_url( $this->get_url() );
|
|
||||||
$object->set_type( $this->get_object_type() );
|
|
||||||
|
|
||||||
$published = \strtotime( $wp_post->post_date_gmt );
|
|
||||||
|
|
||||||
$object->set_published( \gmdate( 'Y-m-d\TH:i:s\Z', $published ) );
|
|
||||||
|
|
||||||
$updated = \strtotime( $wp_post->post_modified_gmt );
|
|
||||||
|
|
||||||
if ( $updated > $published ) {
|
|
||||||
$object->set_updated( \gmdate( 'Y-m-d\TH:i:s\Z', $updated ) );
|
|
||||||
}
|
|
||||||
|
|
||||||
$object->set_attributed_to( $this->get_attributed_to() );
|
|
||||||
$object->set_content( $this->get_content() );
|
|
||||||
$object->set_content_map( $this->get_content_map );
|
|
||||||
$path = sprintf( 'users/%d/followers', intval( $wp_post->post_author ) );
|
|
||||||
|
|
||||||
$object->set_to(
|
|
||||||
array(
|
|
||||||
'https://www.w3.org/ns/activitystreams#Public',
|
|
||||||
get_rest_url_by_path( $path ),
|
|
||||||
)
|
|
||||||
);
|
|
||||||
$object->set_cc( $this->get_cc() );
|
|
||||||
$object->set_attachment( $this->get_attachments() );
|
|
||||||
$object->set_tag( $this->get_tags() );
|
|
||||||
|
|
||||||
return $object;
|
|
||||||
}
|
|
||||||
}
|
|
299
includes/transformer/class-comment.php
Normal file
299
includes/transformer/class-comment.php
Normal file
|
@ -0,0 +1,299 @@
|
||||||
|
<?php
|
||||||
|
namespace Activitypub\Transformer;
|
||||||
|
|
||||||
|
use WP_Comment;
|
||||||
|
use Activitypub\Collection\Users;
|
||||||
|
use Activitypub\Model\Blog_User;
|
||||||
|
use Activitypub\Activity\Base_Object;
|
||||||
|
use Activitypub\Hashtag;
|
||||||
|
|
||||||
|
use function Activitypub\esc_hashtag;
|
||||||
|
use function Activitypub\is_single_user;
|
||||||
|
use function Activitypub\get_rest_url_by_path;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* WordPress Comment Transformer
|
||||||
|
*
|
||||||
|
* The Comment Transformer is responsible for transforming a WP_Comment object into different
|
||||||
|
* Object-Types.
|
||||||
|
*
|
||||||
|
* Currently supported are:
|
||||||
|
*
|
||||||
|
* - Activitypub\Activity\Base_Object
|
||||||
|
*/
|
||||||
|
class Comment {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The WP_Comment object.
|
||||||
|
*
|
||||||
|
* @var WP_Comment
|
||||||
|
*/
|
||||||
|
protected $wp_comment;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Static function to Transform a WP_Comment Object.
|
||||||
|
*
|
||||||
|
* This helps to chain the output of the Transformer.
|
||||||
|
*
|
||||||
|
* @param WP_Comment $wp_comment The WP_Comment object
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public static function transform( WP_Comment $wp_comment ) {
|
||||||
|
return new static( $wp_comment );
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* @param WP_Comment $wp_comment
|
||||||
|
*/
|
||||||
|
public function __construct( WP_Comment $wp_comment ) {
|
||||||
|
$this->wp_comment = $wp_comment;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Transforms the WP_Comment object to an ActivityPub Object
|
||||||
|
*
|
||||||
|
* @see \Activitypub\Activity\Base_Object
|
||||||
|
*
|
||||||
|
* @return \Activitypub\Activity\Base_Object The ActivityPub Object
|
||||||
|
*/
|
||||||
|
public function to_object() {
|
||||||
|
$comment = $this->wp_comment;
|
||||||
|
$object = new Base_Object();
|
||||||
|
|
||||||
|
$object->set_id( $this->generate_id( $comment ) );
|
||||||
|
$object->set_url( \get_comment_link( $comment->ID ) );
|
||||||
|
$object->set_type( 'Note' );
|
||||||
|
|
||||||
|
$published = \strtotime( $comment->comment_date_gmt );
|
||||||
|
$object->set_published( \gmdate( 'Y-m-d\TH:i:s\Z', $published ) );
|
||||||
|
|
||||||
|
$updated = \get_comment_meta( $comment->comment_ID, 'activitypub_last_modified', true );
|
||||||
|
if ( $updated > $published ) {
|
||||||
|
$object->set_updated( \gmdate( 'Y-m-d\TH:i:s\Z', \strtotime( $updated ) ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
$object->set_attributed_to( $this->get_attributed_to() );
|
||||||
|
$object->set_in_reply_to( $this->get_in_reply_to() );
|
||||||
|
$object->set_content( $this->get_content() );
|
||||||
|
$object->set_content_map(
|
||||||
|
array(
|
||||||
|
$this->get_locale() => $this->get_content(),
|
||||||
|
)
|
||||||
|
);
|
||||||
|
$path = sprintf( 'users/%d/followers', intval( $comment->comment_author ) );
|
||||||
|
|
||||||
|
$object->set_to(
|
||||||
|
array(
|
||||||
|
'https://www.w3.org/ns/activitystreams#Public',
|
||||||
|
get_rest_url_by_path( $path ),
|
||||||
|
)
|
||||||
|
);
|
||||||
|
$object->set_cc( $this->get_cc() );
|
||||||
|
$object->set_tag( $this->get_tags() );
|
||||||
|
|
||||||
|
return $object;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the User-URL of the Author of the Post.
|
||||||
|
*
|
||||||
|
* If `single_user` mode is enabled, the URL of the Blog-User is returned.
|
||||||
|
*
|
||||||
|
* @return string The User-URL.
|
||||||
|
*/
|
||||||
|
protected function get_attributed_to() {
|
||||||
|
if ( is_single_user() ) {
|
||||||
|
$user = new Blog_User();
|
||||||
|
return $user->get_url();
|
||||||
|
}
|
||||||
|
|
||||||
|
return Users::get_by_id( $this->wp_comment->user_id )->get_url();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the content for the ActivityPub Item.
|
||||||
|
*
|
||||||
|
* The content will be generated based on the user settings.
|
||||||
|
*
|
||||||
|
* @return string The content.
|
||||||
|
*/
|
||||||
|
protected function get_content() {
|
||||||
|
// phpcs:ignore WordPress.WP.GlobalVariablesOverride.Prohibited
|
||||||
|
$comment = $this->wp_comment;
|
||||||
|
$content = $comment->comment_content;
|
||||||
|
|
||||||
|
$content = \wpautop( $content );
|
||||||
|
$content = \preg_replace( '/[\n\r\t]/', '', $content );
|
||||||
|
$content = \trim( $content );
|
||||||
|
$content = \apply_filters( 'the_content', $content, $comment );
|
||||||
|
|
||||||
|
return $content;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* get_parent_uri (in reply to)
|
||||||
|
* takes a comment and returns
|
||||||
|
* @param WP_Comment activitypub object id URI
|
||||||
|
* @return int comment_id
|
||||||
|
*/
|
||||||
|
protected function get_in_reply_to() {
|
||||||
|
$comment = $this->wp_comment;
|
||||||
|
|
||||||
|
$parent_comment = \get_comment( $comment->comment_parent );
|
||||||
|
if ( $parent_comment ) {
|
||||||
|
//is parent remote?
|
||||||
|
$in_reply_to = $this->get_source_id( $parent_comment );
|
||||||
|
if ( ! $in_reply_to ) {
|
||||||
|
//local
|
||||||
|
$in_reply_to = $this->generate_id( $parent_comment );
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
$pretty_permalink = \get_post_meta( $comment->comment_post_ID, 'activitypub_canonical_url', true );
|
||||||
|
if ( $pretty_permalink ) {
|
||||||
|
$in_reply_to = $pretty_permalink;
|
||||||
|
} else {
|
||||||
|
$in_reply_to = \get_permalink( $comment->comment_post_ID );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return $in_reply_to;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param $comment or $comment_id
|
||||||
|
* @return ActivityPub URI of comment
|
||||||
|
*
|
||||||
|
* AP Object ID must be unique
|
||||||
|
*
|
||||||
|
* https://www.w3.org/TR/activitypub/#obj-id
|
||||||
|
* https://github.com/tootsuite/mastodon/issues/13879
|
||||||
|
*/
|
||||||
|
protected function generate_id( $comment ) {
|
||||||
|
|
||||||
|
$comment = \get_comment( $comment );
|
||||||
|
$ap_comment_id = \add_query_arg(
|
||||||
|
array(
|
||||||
|
'p' => $comment->comment_post_ID,
|
||||||
|
'replytocom' => $comment->comment_ID,
|
||||||
|
),
|
||||||
|
\trailingslashit( site_url() )
|
||||||
|
);
|
||||||
|
return $ap_comment_id;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks a comment ID for a source_id, or source_url
|
||||||
|
*/
|
||||||
|
protected function get_source_id( $comment ) {
|
||||||
|
if ( $comment->user_id ) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
$source_id = \get_comment_meta( $comment->comment_ID, 'source_id', true );
|
||||||
|
if ( ! $source_id ) {
|
||||||
|
$source_url = \get_comment_meta( $comment->comment_ID, 'source_url', true );
|
||||||
|
if ( ! $source_url ) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
$response = \safe_remote_get( $source_url );
|
||||||
|
$body = \wp_remote_retrieve_body( $response );
|
||||||
|
$remote_status = \json_decode( $body, true );
|
||||||
|
if ( \is_wp_error( $remote_status )
|
||||||
|
|| ! isset( $remote_status['@context'] )
|
||||||
|
|| ! isset( $remote_status['object']['id'] ) ) {
|
||||||
|
// the original post may have been deleted, before we started processing deletes.
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
$source_id = $remote_status['object']['id'];
|
||||||
|
}
|
||||||
|
return $source_id;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function get_context() {
|
||||||
|
$comment = $this->wp_comment;
|
||||||
|
$pretty_permalink = \get_post_meta( $comment->comment_post_ID, 'activitypub_canonical_url', true );
|
||||||
|
if ( $pretty_permalink ) {
|
||||||
|
$context = $pretty_permalink;
|
||||||
|
} else {
|
||||||
|
$context = \get_permalink( $comment->comment_post_ID );
|
||||||
|
}
|
||||||
|
return $context;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a list of Mentions, used in the Comment.
|
||||||
|
*
|
||||||
|
* @see https://docs.joinmastodon.org/spec/activitypub/#Mention
|
||||||
|
*
|
||||||
|
* @return array The list of Mentions.
|
||||||
|
*/
|
||||||
|
protected function get_cc() {
|
||||||
|
$cc = array();
|
||||||
|
|
||||||
|
$mentions = $this->get_mentions();
|
||||||
|
if ( $mentions ) {
|
||||||
|
foreach ( $mentions as $mention => $url ) {
|
||||||
|
$cc[] = $url;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $cc;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a list of Tags, used in the Comment.
|
||||||
|
*
|
||||||
|
* This includes Hash-Tags and Mentions.
|
||||||
|
*
|
||||||
|
* @return array The list of Tags.
|
||||||
|
*/
|
||||||
|
protected function get_tags() {
|
||||||
|
$tags = array();
|
||||||
|
|
||||||
|
$mentions = $this->get_mentions();
|
||||||
|
if ( $mentions ) {
|
||||||
|
foreach ( $mentions as $mention => $url ) {
|
||||||
|
$tag = array(
|
||||||
|
'type' => 'Mention',
|
||||||
|
'href' => \esc_url( $url ),
|
||||||
|
'name' => \esc_html( $mention ),
|
||||||
|
);
|
||||||
|
$tags[] = $tag;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $tags;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Helper function to get the @-Mentions from the comment content.
|
||||||
|
*
|
||||||
|
* @return array The list of @-Mentions.
|
||||||
|
*/
|
||||||
|
protected function get_mentions() {
|
||||||
|
return apply_filters( 'activitypub_extract_mentions', array(), $this->wp_comment->comment_content, $this->wp_comment );
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the locale of the post.
|
||||||
|
*
|
||||||
|
* @return string The locale of the post.
|
||||||
|
*/
|
||||||
|
public function get_locale() {
|
||||||
|
$comment_id = $this->wp_comment->ID;
|
||||||
|
$lang = \strtolower( \strtok( \get_locale(), '_-' ) );
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Filter the locale of the comment.
|
||||||
|
*
|
||||||
|
* @param string $lang The locale of the comment.
|
||||||
|
* @param int $comment_id The comment ID.
|
||||||
|
* @param WP_Post $post The comment object.
|
||||||
|
*
|
||||||
|
* @return string The filtered locale of the comment.
|
||||||
|
*/
|
||||||
|
return apply_filters( 'activitypub_comment_locale', $lang, $comment_id, $this->wp_comment );
|
||||||
|
}
|
||||||
|
}
|
21
includes/transformer/class-factory.php
Normal file
21
includes/transformer/class-factory.php
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
<?php
|
||||||
|
namespace Activitypub\Transformer;
|
||||||
|
|
||||||
|
use Activitypub\Transformer\Post;
|
||||||
|
use Activitypub\Transformer\Comment;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Transformer Factory
|
||||||
|
*/
|
||||||
|
class Factory {
|
||||||
|
public static function get_transformer( $object ) {
|
||||||
|
switch ( get_class( $object ) ) {
|
||||||
|
case 'WP_Post':
|
||||||
|
return new Post( $object );
|
||||||
|
case 'WP_Comment':
|
||||||
|
return new Comment( $object );
|
||||||
|
default:
|
||||||
|
return apply_filters( 'activitypub_transformer', null, $object, get_class( $object ) );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -6,7 +6,6 @@ use Activitypub\Collection\Users;
|
||||||
use Activitypub\Model\Blog_User;
|
use Activitypub\Model\Blog_User;
|
||||||
use Activitypub\Activity\Base_Object;
|
use Activitypub\Activity\Base_Object;
|
||||||
use Activitypub\Shortcodes;
|
use Activitypub\Shortcodes;
|
||||||
use Activitypub\Transformer\Base;
|
|
||||||
|
|
||||||
use function Activitypub\esc_hashtag;
|
use function Activitypub\esc_hashtag;
|
||||||
use function Activitypub\is_single_user;
|
use function Activitypub\is_single_user;
|
||||||
|
@ -15,29 +14,375 @@ use function Activitypub\site_supports_blocks;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* WordPress Post Transformer
|
* WordPress Post Transformer
|
||||||
|
*
|
||||||
* The Post Transformer is responsible for transforming a WP_Post object into different othe
|
* The Post Transformer is responsible for transforming a WP_Post object into different othe
|
||||||
* Object-Types.
|
* Object-Types.
|
||||||
*
|
*
|
||||||
* Currently supported are:
|
* Currently supported are:
|
||||||
|
*
|
||||||
* - Activitypub\Activity\Base_Object
|
* - Activitypub\Activity\Base_Object
|
||||||
*/
|
*/
|
||||||
class Post extends Base {
|
class Post {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Getter function for the name of the transformer.
|
* The WP_Post object.
|
||||||
*
|
*
|
||||||
* @return string name
|
* @var WP_Post
|
||||||
*/
|
*/
|
||||||
public function get_name() {
|
protected $wp_post;
|
||||||
return 'activitypub/default';
|
|
||||||
|
/**
|
||||||
|
* Static function to Transform a WP_Post Object.
|
||||||
|
*
|
||||||
|
* This helps to chain the output of the Transformer.
|
||||||
|
*
|
||||||
|
* @param WP_Post $wp_post The WP_Post object
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public static function transform( WP_Post $wp_post ) {
|
||||||
|
return new static( $wp_post );
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Getter function for the display name (label/title) of the transformer.
|
|
||||||
*
|
*
|
||||||
* @return string name
|
*
|
||||||
|
* @param WP_Post $wp_post
|
||||||
*/
|
*/
|
||||||
public function get_label() {
|
public function __construct( WP_Post $wp_post ) {
|
||||||
return 'Built-In';
|
$this->wp_post = $wp_post;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Transforms the WP_Post object to an ActivityPub Object
|
||||||
|
*
|
||||||
|
* @see \Activitypub\Activity\Base_Object
|
||||||
|
*
|
||||||
|
* @return \Activitypub\Activity\Base_Object The ActivityPub Object
|
||||||
|
*/
|
||||||
|
public function to_object() {
|
||||||
|
$wp_post = $this->wp_post;
|
||||||
|
$object = new Base_Object();
|
||||||
|
|
||||||
|
$object->set_id( $this->get_id() );
|
||||||
|
$object->set_url( $this->get_url() );
|
||||||
|
$object->set_type( $this->get_object_type() );
|
||||||
|
|
||||||
|
$published = \strtotime( $wp_post->post_date_gmt );
|
||||||
|
|
||||||
|
$object->set_published( \gmdate( 'Y-m-d\TH:i:s\Z', $published ) );
|
||||||
|
|
||||||
|
$updated = \strtotime( $wp_post->post_modified_gmt );
|
||||||
|
|
||||||
|
if ( $updated > $published ) {
|
||||||
|
$object->set_updated( \gmdate( 'Y-m-d\TH:i:s\Z', $updated ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
$object->set_attributed_to( $this->get_attributed_to() );
|
||||||
|
$object->set_content( $this->get_content() );
|
||||||
|
$object->set_content_map(
|
||||||
|
array(
|
||||||
|
\strstr( \get_locale(), '_', true ) => $this->get_content(),
|
||||||
|
)
|
||||||
|
);
|
||||||
|
$path = sprintf( 'users/%d/followers', intval( $wp_post->post_author ) );
|
||||||
|
|
||||||
|
$object->set_to(
|
||||||
|
array(
|
||||||
|
'https://www.w3.org/ns/activitystreams#Public',
|
||||||
|
get_rest_url_by_path( $path ),
|
||||||
|
)
|
||||||
|
);
|
||||||
|
$object->set_cc( $this->get_cc() );
|
||||||
|
$object->set_attachment( $this->get_attachments() );
|
||||||
|
$object->set_tag( $this->get_tags() );
|
||||||
|
|
||||||
|
return $object;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the ID of the Post.
|
||||||
|
*
|
||||||
|
* @return string The Posts ID.
|
||||||
|
*/
|
||||||
|
public function get_id() {
|
||||||
|
return $this->get_url();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the URL of the Post.
|
||||||
|
*
|
||||||
|
* @return string The Posts URL.
|
||||||
|
*/
|
||||||
|
public function get_url() {
|
||||||
|
$post = $this->wp_post;
|
||||||
|
|
||||||
|
if ( 'trash' === get_post_status( $post ) ) {
|
||||||
|
$permalink = \get_post_meta( $post->ID, 'activitypub_canonical_url', true );
|
||||||
|
} else {
|
||||||
|
$permalink = \get_permalink( $post );
|
||||||
|
}
|
||||||
|
|
||||||
|
return \esc_url( $permalink );
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the User-URL of the Author of the Post.
|
||||||
|
*
|
||||||
|
* If `single_user` mode is enabled, the URL of the Blog-User is returned.
|
||||||
|
*
|
||||||
|
* @return string The User-URL.
|
||||||
|
*/
|
||||||
|
protected function get_attributed_to() {
|
||||||
|
if ( is_single_user() ) {
|
||||||
|
$user = new Blog_User();
|
||||||
|
return $user->get_url();
|
||||||
|
}
|
||||||
|
|
||||||
|
return Users::get_by_id( $this->wp_post->post_author )->get_url();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generates all Media Attachments for a Post.
|
||||||
|
*
|
||||||
|
* @return array The Attachments.
|
||||||
|
*/
|
||||||
|
protected function get_attachments() {
|
||||||
|
// Once upon a time we only supported images, but we now support audio/video as well.
|
||||||
|
// We maintain the image-centric naming for backwards compatibility.
|
||||||
|
$max_media = intval( \apply_filters( 'activitypub_max_image_attachments', \get_option( 'activitypub_max_image_attachments', ACTIVITYPUB_MAX_IMAGE_ATTACHMENTS ) ) );
|
||||||
|
|
||||||
|
if ( site_supports_blocks() && \has_blocks( $this->wp_post->post_content ) ) {
|
||||||
|
return $this->get_block_attachments( $max_media );
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->get_classic_editor_images( $max_media );
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get media attachments from blocks. They will be formatted as ActivityPub attachments, not as WP attachments.
|
||||||
|
*
|
||||||
|
* @param int $max_media The maximum number of attachments to return.
|
||||||
|
*
|
||||||
|
* @return array The attachments.
|
||||||
|
*/
|
||||||
|
protected function get_block_attachments( $max_media ) {
|
||||||
|
// max media can't be negative or zero
|
||||||
|
if ( $max_media <= 0 ) {
|
||||||
|
return array();
|
||||||
|
}
|
||||||
|
|
||||||
|
$id = $this->wp_post->ID;
|
||||||
|
|
||||||
|
$media_ids = array();
|
||||||
|
|
||||||
|
// list post thumbnail first if this post has one
|
||||||
|
if ( \function_exists( 'has_post_thumbnail' ) && \has_post_thumbnail( $id ) ) {
|
||||||
|
$media_ids[] = \get_post_thumbnail_id( $id );
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( $max_media > 0 ) {
|
||||||
|
$blocks = \parse_blocks( $this->wp_post->post_content );
|
||||||
|
$media_ids = self::get_media_ids_from_blocks( $blocks, $media_ids, $max_media );
|
||||||
|
}
|
||||||
|
$media_ids = \array_unique( $media_ids );
|
||||||
|
|
||||||
|
return \array_filter( \array_map( array( self::class, 'wp_attachment_to_activity_attachment' ), $media_ids ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get image attachments from the classic editor.
|
||||||
|
* Note that audio/video attachments are only supported in the block editor.
|
||||||
|
*
|
||||||
|
* @param int $max_images The maximum number of images to return.
|
||||||
|
*
|
||||||
|
* @return array The attachments.
|
||||||
|
*/
|
||||||
|
protected function get_classic_editor_images( $max_images ) {
|
||||||
|
// max images can't be negative or zero
|
||||||
|
if ( $max_images <= 0 ) {
|
||||||
|
return array();
|
||||||
|
}
|
||||||
|
|
||||||
|
$id = $this->wp_post->ID;
|
||||||
|
|
||||||
|
$image_ids = array();
|
||||||
|
|
||||||
|
// list post thumbnail first if this post has one
|
||||||
|
if ( \function_exists( 'has_post_thumbnail' ) && \has_post_thumbnail( $id ) ) {
|
||||||
|
$image_ids[] = \get_post_thumbnail_id( $id );
|
||||||
|
--$max_images;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( $max_images > 0 ) {
|
||||||
|
$query = new \WP_Query(
|
||||||
|
array(
|
||||||
|
'post_parent' => $id,
|
||||||
|
'post_status' => 'inherit',
|
||||||
|
'post_type' => 'attachment',
|
||||||
|
'post_mime_type' => 'image',
|
||||||
|
'order' => 'ASC',
|
||||||
|
'orderby' => 'menu_order ID',
|
||||||
|
'posts_per_page' => $max_images,
|
||||||
|
)
|
||||||
|
);
|
||||||
|
foreach ( $query->get_posts() as $attachment ) {
|
||||||
|
if ( ! \in_array( $attachment->ID, $image_ids, true ) ) {
|
||||||
|
$image_ids[] = $attachment->ID;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$image_ids = \array_unique( $image_ids );
|
||||||
|
|
||||||
|
return \array_filter( \array_map( array( self::class, 'wp_attachment_to_activity_attachment' ), $image_ids ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Recursively get media IDs from blocks.
|
||||||
|
* @param array $blocks The blocks to search for media IDs
|
||||||
|
* @param array $media_ids The media IDs to append new IDs to
|
||||||
|
* @param int $max_media The maximum number of media to return.
|
||||||
|
*
|
||||||
|
* @return array The image IDs.
|
||||||
|
*/
|
||||||
|
protected static function get_media_ids_from_blocks( $blocks, $media_ids, $max_media ) {
|
||||||
|
|
||||||
|
foreach ( $blocks as $block ) {
|
||||||
|
// recurse into inner blocks
|
||||||
|
if ( ! empty( $block['innerBlocks'] ) ) {
|
||||||
|
$media_ids = self::get_media_ids_from_blocks( $block['innerBlocks'], $media_ids, $max_media );
|
||||||
|
}
|
||||||
|
|
||||||
|
switch ( $block['blockName'] ) {
|
||||||
|
case 'core/image':
|
||||||
|
case 'core/cover':
|
||||||
|
case 'core/audio':
|
||||||
|
case 'core/video':
|
||||||
|
case 'videopress/video':
|
||||||
|
if ( ! empty( $block['attrs']['id'] ) ) {
|
||||||
|
$media_ids[] = $block['attrs']['id'];
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 'jetpack/slideshow':
|
||||||
|
case 'jetpack/tiled-gallery':
|
||||||
|
if ( ! empty( $block['attrs']['ids'] ) ) {
|
||||||
|
$media_ids = array_merge( $media_ids, $block['attrs']['ids'] );
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 'jetpack/image-compare':
|
||||||
|
if ( ! empty( $block['attrs']['beforeImageId'] ) ) {
|
||||||
|
$media_ids[] = $block['attrs']['beforeImageId'];
|
||||||
|
}
|
||||||
|
if ( ! empty( $block['attrs']['afterImageId'] ) ) {
|
||||||
|
$media_ids[] = $block['attrs']['afterImageId'];
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// stop doing unneeded work
|
||||||
|
if ( count( $media_ids ) >= $max_media ) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// still need to slice it because one gallery could knock us over the limit
|
||||||
|
return array_slice( $media_ids, 0, $max_media );
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Converts a WordPress Attachment to an ActivityPub Attachment.
|
||||||
|
*
|
||||||
|
* @param int $id The Attachment ID.
|
||||||
|
*
|
||||||
|
* @return array The ActivityPub Attachment.
|
||||||
|
*/
|
||||||
|
public static function wp_attachment_to_activity_attachment( $id ) {
|
||||||
|
$attachment = array();
|
||||||
|
$mime_type = \get_post_mime_type( $id );
|
||||||
|
$mime_type_parts = \explode( '/', $mime_type );
|
||||||
|
// switching on image/audio/video
|
||||||
|
switch ( $mime_type_parts[0] ) {
|
||||||
|
case 'image':
|
||||||
|
$image_size = 'full';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Filter the image URL returned for each post.
|
||||||
|
*
|
||||||
|
* @param array|false $thumbnail The image URL, or false if no image is available.
|
||||||
|
* @param int $id The attachment ID.
|
||||||
|
* @param string $image_size The image size to retrieve. Set to 'full' by default.
|
||||||
|
*/
|
||||||
|
$thumbnail = apply_filters(
|
||||||
|
'activitypub_get_image',
|
||||||
|
self::get_image( $id, $image_size ),
|
||||||
|
$id,
|
||||||
|
$image_size
|
||||||
|
);
|
||||||
|
|
||||||
|
if ( $thumbnail ) {
|
||||||
|
$alt = \get_post_meta( $id, '_wp_attachment_image_alt', true );
|
||||||
|
$image = array(
|
||||||
|
'type' => 'Image',
|
||||||
|
'url' => $thumbnail[0],
|
||||||
|
'mediaType' => $mime_type,
|
||||||
|
);
|
||||||
|
|
||||||
|
if ( $alt ) {
|
||||||
|
$image['name'] = $alt;
|
||||||
|
}
|
||||||
|
$attachment = $image;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'audio':
|
||||||
|
case 'video':
|
||||||
|
$attachment = array(
|
||||||
|
'type' => 'Document',
|
||||||
|
'mediaType' => $mime_type,
|
||||||
|
'url' => \wp_get_attachment_url( $id ),
|
||||||
|
'name' => \get_the_title( $id ),
|
||||||
|
);
|
||||||
|
$meta = wp_get_attachment_metadata( $id );
|
||||||
|
// height and width for videos
|
||||||
|
if ( isset( $meta['width'] ) && isset( $meta['height'] ) ) {
|
||||||
|
$attachment['width'] = $meta['width'];
|
||||||
|
$attachment['height'] = $meta['height'];
|
||||||
|
}
|
||||||
|
// @todo: add `icon` support for audio/video attachments. Maybe use post thumbnail?
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return \apply_filters( 'activitypub_attachment', $attachment, $id );
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return details about an image attachment.
|
||||||
|
*
|
||||||
|
* @param int $id The attachment ID.
|
||||||
|
* @param string $image_size The image size to retrieve. Set to 'full' by default.
|
||||||
|
*
|
||||||
|
* @return array|false Array of image data, or boolean false if no image is available.
|
||||||
|
*/
|
||||||
|
protected static function get_image( $id, $image_size = 'full' ) {
|
||||||
|
/**
|
||||||
|
* Hook into the image retrieval process. Before image retrieval.
|
||||||
|
*
|
||||||
|
* @param int $id The attachment ID.
|
||||||
|
* @param string $image_size The image size to retrieve. Set to 'full' by default.
|
||||||
|
*/
|
||||||
|
do_action( 'activitypub_get_image_pre', $id, $image_size );
|
||||||
|
|
||||||
|
$image = \wp_get_attachment_image_src( $id, $image_size );
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Hook into the image retrieval process. After image retrieval.
|
||||||
|
*
|
||||||
|
* @param int $id The attachment ID.
|
||||||
|
* @param string $image_size The image size to retrieve. Set to 'full' by default.
|
||||||
|
*/
|
||||||
|
do_action( 'activitypub_get_image_post', $id, $image_size );
|
||||||
|
|
||||||
|
return $image;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -55,7 +400,7 @@ class Post extends Base {
|
||||||
|
|
||||||
// Default to Article.
|
// Default to Article.
|
||||||
$object_type = 'Article';
|
$object_type = 'Article';
|
||||||
$post_type = \get_post_type( $this->wp_post );
|
$post_type = \get_post_type( $this->wp_post );
|
||||||
switch ( $post_type ) {
|
switch ( $post_type ) {
|
||||||
case 'post':
|
case 'post':
|
||||||
$post_format = \get_post_format( $this->wp_post );
|
$post_format = \get_post_format( $this->wp_post );
|
||||||
|
@ -85,7 +430,7 @@ class Post extends Base {
|
||||||
$object_type = 'Page';
|
$object_type = 'Page';
|
||||||
break;
|
break;
|
||||||
case 'attachment':
|
case 'attachment':
|
||||||
$mime_type = \get_post_mime_type();
|
$mime_type = \get_post_mime_type();
|
||||||
$media_type = \preg_replace( '/(\/[a-zA-Z]+)/i', '', $mime_type );
|
$media_type = \preg_replace( '/(\/[a-zA-Z]+)/i', '', $mime_type );
|
||||||
switch ( $media_type ) {
|
switch ( $media_type ) {
|
||||||
case 'audio':
|
case 'audio':
|
||||||
|
@ -106,4 +451,192 @@ class Post extends Base {
|
||||||
|
|
||||||
return $object_type;
|
return $object_type;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a list of Mentions, used in the Post.
|
||||||
|
*
|
||||||
|
* @see https://docs.joinmastodon.org/spec/activitypub/#Mention
|
||||||
|
*
|
||||||
|
* @return array The list of Mentions.
|
||||||
|
*/
|
||||||
|
protected function get_cc() {
|
||||||
|
$cc = array();
|
||||||
|
|
||||||
|
$mentions = $this->get_mentions();
|
||||||
|
if ( $mentions ) {
|
||||||
|
foreach ( $mentions as $url ) {
|
||||||
|
$cc[] = $url;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $cc;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a list of Tags, used in the Post.
|
||||||
|
*
|
||||||
|
* This includes Hash-Tags and Mentions.
|
||||||
|
*
|
||||||
|
* @return array The list of Tags.
|
||||||
|
*/
|
||||||
|
protected function get_tags() {
|
||||||
|
$tags = array();
|
||||||
|
|
||||||
|
$post_tags = \get_the_tags( $this->wp_post->ID );
|
||||||
|
if ( $post_tags ) {
|
||||||
|
foreach ( $post_tags as $post_tag ) {
|
||||||
|
$tag = array(
|
||||||
|
'type' => 'Hashtag',
|
||||||
|
'href' => \esc_url( \get_tag_link( $post_tag->term_id ) ),
|
||||||
|
'name' => esc_hashtag( $post_tag->name ),
|
||||||
|
);
|
||||||
|
$tags[] = $tag;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$mentions = $this->get_mentions();
|
||||||
|
if ( $mentions ) {
|
||||||
|
foreach ( $mentions as $mention => $url ) {
|
||||||
|
$tag = array(
|
||||||
|
'type' => 'Mention',
|
||||||
|
'href' => \esc_url( $url ),
|
||||||
|
'name' => \esc_html( $mention ),
|
||||||
|
);
|
||||||
|
$tags[] = $tag;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $tags;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the content for the ActivityPub Item.
|
||||||
|
*
|
||||||
|
* The content will be generated based on the user settings.
|
||||||
|
*
|
||||||
|
* @return string The content.
|
||||||
|
*/
|
||||||
|
protected function get_content() {
|
||||||
|
global $post;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Provides an action hook so plugins can add their own hooks/filters before AP content is generated.
|
||||||
|
*
|
||||||
|
* Example: if a plugin adds a filter to `the_content` to add a button to the end of posts, it can also remove that filter here.
|
||||||
|
*
|
||||||
|
* @param WP_Post $post The post object.
|
||||||
|
*/
|
||||||
|
do_action( 'activitypub_before_get_content', $post );
|
||||||
|
|
||||||
|
// phpcs:ignore WordPress.WP.GlobalVariablesOverride.Prohibited
|
||||||
|
$post = $this->wp_post;
|
||||||
|
$content = $this->get_post_content_template();
|
||||||
|
|
||||||
|
// Register our shortcodes just in time.
|
||||||
|
Shortcodes::register();
|
||||||
|
// Fill in the shortcodes.
|
||||||
|
setup_postdata( $post );
|
||||||
|
$content = do_shortcode( $content );
|
||||||
|
wp_reset_postdata();
|
||||||
|
|
||||||
|
$content = \wpautop( $content );
|
||||||
|
$content = \preg_replace( '/[\n\r\t]/', '', $content );
|
||||||
|
$content = \trim( $content );
|
||||||
|
|
||||||
|
$content = \apply_filters( 'activitypub_the_content', $content, $post );
|
||||||
|
|
||||||
|
// Don't need these any more, should never appear in a post.
|
||||||
|
Shortcodes::unregister();
|
||||||
|
|
||||||
|
return $content;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the template to use to generate the content of the activitypub item.
|
||||||
|
*
|
||||||
|
* @return string The Template.
|
||||||
|
*/
|
||||||
|
protected function get_post_content_template() {
|
||||||
|
if ( 'excerpt' === \get_option( 'activitypub_post_content_type', 'content' ) ) {
|
||||||
|
return "[ap_excerpt]\n\n[ap_permalink type=\"html\"]";
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( 'title' === \get_option( 'activitypub_post_content_type', 'content' ) ) {
|
||||||
|
return "[ap_title]\n\n[ap_permalink type=\"html\"]";
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( 'content' === \get_option( 'activitypub_post_content_type', 'content' ) ) {
|
||||||
|
return "[ap_content]\n\n[ap_permalink type=\"html\"]\n\n[ap_hashtags]";
|
||||||
|
}
|
||||||
|
|
||||||
|
return \get_option( 'activitypub_custom_post_content', ACTIVITYPUB_CUSTOM_POST_CONTENT );
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Helper function to get the @-Mentions from the post content.
|
||||||
|
*
|
||||||
|
* @return array The list of @-Mentions.
|
||||||
|
*/
|
||||||
|
protected function get_mentions() {
|
||||||
|
return apply_filters( 'activitypub_extract_mentions', array(), $this->wp_post->post_content, $this->wp_post );
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get deleted datetime
|
||||||
|
*/
|
||||||
|
public function get_deleted() {
|
||||||
|
$post = $this->wp_post;
|
||||||
|
$deleted = null;
|
||||||
|
if ( 'trash' === $post->post_status ) {
|
||||||
|
$this->deleted = \gmdate( 'Y-m-d\TH:i:s\Z', \strtotime( $post->post_modified_gmt ) );
|
||||||
|
}
|
||||||
|
return $deleted;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function get_replies() {
|
||||||
|
$replies = null;
|
||||||
|
|
||||||
|
$post = $this->wp_post;
|
||||||
|
|
||||||
|
if ( $post->comment_count > 0 ) {
|
||||||
|
$args = array(
|
||||||
|
'post_id' => $post->ID,
|
||||||
|
'hierarchical' => false,
|
||||||
|
'status' => 'approve',
|
||||||
|
);
|
||||||
|
$comments = \get_comments( $args );
|
||||||
|
$items = array();
|
||||||
|
|
||||||
|
foreach ( $comments as $comment ) {
|
||||||
|
// include self replies
|
||||||
|
if ( $post->post_author === $comment->user_id ) {
|
||||||
|
$comment_url = \add_query_arg( //
|
||||||
|
array(
|
||||||
|
'p' => $post->ID,
|
||||||
|
'replytocom' => $comment->comment_ID,
|
||||||
|
),
|
||||||
|
trailingslashit( site_url() )
|
||||||
|
);
|
||||||
|
$items[] = $comment_url;
|
||||||
|
} else {
|
||||||
|
$comment_url = \get_comment_meta( $comment->comment_ID, 'source_url', true );
|
||||||
|
if ( ! empty( $comment_url ) ) {
|
||||||
|
$items[] = \get_comment_meta( $comment->comment_ID, 'source_url', true );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$replies = array(
|
||||||
|
'type' => 'Collection',
|
||||||
|
'id' => \add_query_arg( array( 'replies' => '' ), $this->get_id() ),
|
||||||
|
'first' => array(
|
||||||
|
'type' => 'CollectionPage',
|
||||||
|
'partOf' => \add_query_arg( array( 'replies' => '' ), $this->get_id() ),
|
||||||
|
'items' => $items,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $replies;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,292 +0,0 @@
|
||||||
<?php
|
|
||||||
/**
|
|
||||||
* Inspired by the way elementor handles addons.
|
|
||||||
*
|
|
||||||
* @link https://github.com/elementor/elementor/
|
|
||||||
* @package Activitypub
|
|
||||||
*/
|
|
||||||
|
|
||||||
namespace Activitypub\Transformer;
|
|
||||||
|
|
||||||
use WP_Post;
|
|
||||||
use WP_Comment;
|
|
||||||
|
|
||||||
use function Activitypub\camel_to_snake_case;
|
|
||||||
use function Activitypub\snake_to_camel_case;
|
|
||||||
|
|
||||||
if ( ! defined( 'ABSPATH' ) ) {
|
|
||||||
exit; // Exit if accessed directly.
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* ActivityPub transformers manager.
|
|
||||||
*
|
|
||||||
* ActivityPub transformers manager handler class is responsible for registering and
|
|
||||||
* initializing all the supported WP-Pobject to ActivityPub transformers.
|
|
||||||
*
|
|
||||||
* @since version_number_transformer_management_placeholder
|
|
||||||
*/
|
|
||||||
class Transformers_Manager {
|
|
||||||
const DEFAULT_TRANSFORMER_MAPPING = array(
|
|
||||||
'post' => ACTIVITYPUB_DEFAULT_TRANSFORMER,
|
|
||||||
'page' => ACTIVITYPUB_DEFAULT_TRANSFORMER,
|
|
||||||
);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Transformers.
|
|
||||||
*
|
|
||||||
* Holds the list of all the ActivityPub transformers. Default is `null`.
|
|
||||||
*
|
|
||||||
* @since version_number_transformer_management_placeholder
|
|
||||||
* @access private
|
|
||||||
*
|
|
||||||
* @var \ActivityPub\Transformer\Base[]
|
|
||||||
*/
|
|
||||||
private $transformers = null;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Transformer_Manager instance.
|
|
||||||
*
|
|
||||||
* Holds the transformer instance.
|
|
||||||
*
|
|
||||||
* @since version_number_transformer_management_placeholder
|
|
||||||
* @access protected
|
|
||||||
*
|
|
||||||
* @var Transformer_Manager
|
|
||||||
*/
|
|
||||||
protected static $_instances = [];
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Instance.
|
|
||||||
*
|
|
||||||
* Ensures only one instance of the transformer manager class is loaded or can be loaded.
|
|
||||||
*
|
|
||||||
* @since version_number_transformer_management_placeholder
|
|
||||||
* @access public
|
|
||||||
* @static
|
|
||||||
*
|
|
||||||
* @return Transformer_Manager An instance of the class.
|
|
||||||
*/
|
|
||||||
public static function instance() {
|
|
||||||
$class_name = static::class_name();
|
|
||||||
|
|
||||||
if ( empty( static::$_instances[ $class_name ] ) ) {
|
|
||||||
static::$_instances[ $class_name ] = new static();
|
|
||||||
}
|
|
||||||
|
|
||||||
return static::$_instances[ $class_name ];
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Class name.
|
|
||||||
*
|
|
||||||
* Retrieve the name of the class.
|
|
||||||
*
|
|
||||||
* @since version_number_transformer_management_placeholder
|
|
||||||
* @access public
|
|
||||||
* @static
|
|
||||||
*/
|
|
||||||
public static function class_name() {
|
|
||||||
return get_called_class();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Transformers manager constructor.
|
|
||||||
*
|
|
||||||
* Initializing ActivityPub transformers manager.
|
|
||||||
*
|
|
||||||
* @since version_number_transformer_management_placeholder
|
|
||||||
* @access public
|
|
||||||
*/
|
|
||||||
public function __construct() {
|
|
||||||
$this->require_files();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Require files.
|
|
||||||
*
|
|
||||||
* Require ActivityPub transformer base class.
|
|
||||||
*
|
|
||||||
* @since version_number_transformer_management_placeholder
|
|
||||||
* @access private
|
|
||||||
*/
|
|
||||||
private function require_files() {
|
|
||||||
require ACTIVITYPUB_PLUGIN_DIR . 'includes/transformer/class-base.php';
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Checks if a transformer is registered.
|
|
||||||
*
|
|
||||||
* @since version_number_transformer_management_placeholder
|
|
||||||
*
|
|
||||||
* @param string $name Transformer name including namespace.
|
|
||||||
* @return bool True if the block type is registered, false otherwise.
|
|
||||||
*/
|
|
||||||
public function is_registered( $name ) {
|
|
||||||
return isset( $this->transformers[ $name ] );
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Register a transformer.
|
|
||||||
*
|
|
||||||
* @since version_number_transformer_management_placeholder
|
|
||||||
* @access public
|
|
||||||
*
|
|
||||||
* @param \ActivityPub\Transformer\Base $transformer_instance ActivityPub Transformer.
|
|
||||||
*
|
|
||||||
* @return bool True if the ActivityPub transformer was registered.
|
|
||||||
*/
|
|
||||||
public function register( \ActivityPub\Transformer\Base $transformer_instance ) {
|
|
||||||
|
|
||||||
if ( ! $transformer_instance instanceof \ActivityPub\Transformer\Base ) {
|
|
||||||
_doing_it_wrong(
|
|
||||||
__METHOD__,
|
|
||||||
\esc_html__( 'ActivityPub transformer instance must be a of \ActivityPub\Transformer_Base class.' ),
|
|
||||||
'version_number_transformer_management_placeholder'
|
|
||||||
);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
$transformer_name = $transformer_instance->get_name();
|
|
||||||
if ( preg_match( '/[A-Z]+/', $transformer_name ) ) {
|
|
||||||
_doing_it_wrong(
|
|
||||||
__METHOD__,
|
|
||||||
\esc_html__( 'ActivityPub transformer names must not contain uppercase characters.' ),
|
|
||||||
'version_number_transformer_management_placeholder'
|
|
||||||
);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
$name_matcher = '/^[a-z0-9-]+\/[a-z0-9-]+$/';
|
|
||||||
if ( ! preg_match( $name_matcher, $transformer_name ) ) {
|
|
||||||
_doing_it_wrong(
|
|
||||||
__METHOD__,
|
|
||||||
\esc_html__( 'ActivityPub transformer names must contain a namespace prefix. Example: my-plugin/my-custom-transformer' ),
|
|
||||||
'version_number_transformer_management_placeholder'
|
|
||||||
);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ( $this->is_registered( $transformer_name ) ) {
|
|
||||||
_doing_it_wrong(
|
|
||||||
__METHOD__,
|
|
||||||
/* translators: %s: Block name. */
|
|
||||||
sprintf( 'ActivityPub transformer with name "%s" is already registered.', esc_html( $transformer_name ) ),
|
|
||||||
'version_number_transformer_management_placeholder'
|
|
||||||
);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Should the ActivityPub transformer be registered.
|
|
||||||
*
|
|
||||||
* @since version_number_transformer_management_placeholder
|
|
||||||
*
|
|
||||||
* @param bool $should_register Should the ActivityPub transformer be registered. Default is `true`.
|
|
||||||
* @param \ActivityPub\Transformer\Base $transformer_instance Widget instance.
|
|
||||||
*/
|
|
||||||
// TODO: does not implementing this slow down the website? -> compare with gutenberg block registration.
|
|
||||||
// $should_register = apply_filters( 'activitypub/transformers/is_transformer_enabled', true, $transformer_instance );
|
|
||||||
|
|
||||||
// if ( ! $should_register ) {
|
|
||||||
// return false;
|
|
||||||
// }
|
|
||||||
|
|
||||||
$this->transformers[ $transformer_name ] = $transformer_instance;
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Init transformers.
|
|
||||||
*
|
|
||||||
* Initialize ActivityPub transformer manager.
|
|
||||||
* Include the builtin transformers by default and add third party ones.
|
|
||||||
*
|
|
||||||
* @since version_number_transformer_management_placeholder
|
|
||||||
* @access private
|
|
||||||
*/
|
|
||||||
private function init_transformers() {
|
|
||||||
$builtin_transformers = [
|
|
||||||
'post',
|
|
||||||
];
|
|
||||||
|
|
||||||
$this->transformers = [];
|
|
||||||
|
|
||||||
foreach ( $builtin_transformers as $transformer_name ) {
|
|
||||||
include ACTIVITYPUB_PLUGIN_DIR . 'includes/transformer/class-' . $transformer_name . '.php';
|
|
||||||
|
|
||||||
$class_name = ucfirst( $transformer_name );
|
|
||||||
|
|
||||||
$class_name = '\Activitypub\Transformer\\' . $class_name;
|
|
||||||
|
|
||||||
$this->register( new $class_name() );
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Let other transformers register.
|
|
||||||
*
|
|
||||||
* Fires after the built-in Activitypub transformers are registered.
|
|
||||||
*
|
|
||||||
* @since version_number_transformer_management_placeholder
|
|
||||||
*
|
|
||||||
* @param Transformers_Manager $this The widgets manager.
|
|
||||||
*/
|
|
||||||
do_action( 'activitypub_transformers_register', $this );
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get available ActivityPub transformers.
|
|
||||||
*
|
|
||||||
* Retrieve the registered transformers list. If given a transformer name
|
|
||||||
* it returns the given transformer if it is registered.
|
|
||||||
*
|
|
||||||
* @since version_number_transformer_management_placeholder
|
|
||||||
* @access public
|
|
||||||
*
|
|
||||||
* @param string $transformer_name Optional. Transformer name. Default is null.
|
|
||||||
*
|
|
||||||
* @return Base|Base[]|null Registered transformers.
|
|
||||||
*/
|
|
||||||
public function get_transformers( $transformer_name = null ) {
|
|
||||||
if ( is_null( $this->transformers ) ) {
|
|
||||||
$this->init_transformers();
|
|
||||||
}
|
|
||||||
|
|
||||||
if ( null !== $transformer_name ) {
|
|
||||||
return isset( $this->transformers[ $transformer_name ] ) ? $this->transformers[ $transformer_name ] : null;
|
|
||||||
}
|
|
||||||
|
|
||||||
return $this->transformers;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get the mapped ActivityPub transformer.
|
|
||||||
*
|
|
||||||
* Returns a new instance of the needed WordPress to ActivityPub transformer.
|
|
||||||
*
|
|
||||||
* @since version_number_transformer_management_placeholder
|
|
||||||
* @access public
|
|
||||||
*
|
|
||||||
* @param WP_Post|WP_Comment $object The WordPress Post/Comment.
|
|
||||||
*
|
|
||||||
* @return \ActivityPub\Transformer\Base|null Registered transformers.
|
|
||||||
*/
|
|
||||||
public function get_transformer( $object ) {
|
|
||||||
switch ( get_class( $object ) ) {
|
|
||||||
case 'WP_Post':
|
|
||||||
$post_type = get_post_type( $object );
|
|
||||||
$transformer_mapping = \get_option( 'activitypub_transformer_mapping', self::DEFAULT_TRANSFORMER_MAPPING );
|
|
||||||
$transformer_name = $transformer_mapping[ $post_type ];
|
|
||||||
$transformer_class = $this->get_transformers( $transformer_name );
|
|
||||||
$transformer_instance = new $transformer_class();
|
|
||||||
$transformer_instance->set_wp_post( $object );
|
|
||||||
return $transformer_instance;
|
|
||||||
case 'WP_Comment':
|
|
||||||
return new Comment( $object );
|
|
||||||
default:
|
|
||||||
return apply_filters( 'activitypub_transformer', null, $object, get_class( $object ) );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,9 +1,6 @@
|
||||||
<?php
|
<?php
|
||||||
namespace Activitypub\Integration;
|
namespace Activitypub\Integration;
|
||||||
|
|
||||||
use function Activitypub\get_total_users;
|
|
||||||
use function Activitypub\get_active_users;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Compatibility with the NodeInfo plugin
|
* Compatibility with the NodeInfo plugin
|
||||||
*
|
*
|
||||||
|
@ -34,12 +31,6 @@ class Nodeinfo {
|
||||||
$nodeinfo['protocols']['outbound'][] = 'activitypub';
|
$nodeinfo['protocols']['outbound'][] = 'activitypub';
|
||||||
}
|
}
|
||||||
|
|
||||||
$nodeinfo['usage']['users'] = array(
|
|
||||||
'total' => get_total_users(),
|
|
||||||
'activeMonth' => get_active_users( '1 month ago' ),
|
|
||||||
'activeHalfyear' => get_active_users( '6 month ago' ),
|
|
||||||
);
|
|
||||||
|
|
||||||
return $nodeinfo;
|
return $nodeinfo;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -53,12 +44,6 @@ class Nodeinfo {
|
||||||
public static function add_nodeinfo2_discovery( $nodeinfo ) {
|
public static function add_nodeinfo2_discovery( $nodeinfo ) {
|
||||||
$nodeinfo['protocols'][] = 'activitypub';
|
$nodeinfo['protocols'][] = 'activitypub';
|
||||||
|
|
||||||
$nodeinfo['usage']['users'] = array(
|
|
||||||
'total' => get_total_users(),
|
|
||||||
'activeMonth' => get_active_users( '1 month ago' ),
|
|
||||||
'activeHalfyear' => get_active_users( '6 month ago' ),
|
|
||||||
);
|
|
||||||
|
|
||||||
return $nodeinfo;
|
return $nodeinfo;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -30,10 +30,6 @@ class Webfinger {
|
||||||
public static function add_user_discovery( $array, $resource, $user ) {
|
public static function add_user_discovery( $array, $resource, $user ) {
|
||||||
$user = User_Collection::get_by_id( $user->ID );
|
$user = User_Collection::get_by_id( $user->ID );
|
||||||
|
|
||||||
if ( ! $user || is_wp_error( $user ) ) {
|
|
||||||
return $array;
|
|
||||||
}
|
|
||||||
|
|
||||||
$array['links'][] = array(
|
$array['links'][] = array(
|
||||||
'rel' => 'self',
|
'rel' => 'self',
|
||||||
'type' => 'application/activity+json',
|
'type' => 'application/activity+json',
|
||||||
|
|
17
readme.txt
17
readme.txt
|
@ -2,8 +2,8 @@
|
||||||
Contributors: automattic, pfefferle, mediaformat, mattwiebe, akirk, jeherve, nuriapena, cavalierlife
|
Contributors: automattic, pfefferle, mediaformat, mattwiebe, akirk, jeherve, nuriapena, cavalierlife
|
||||||
Tags: OStatus, fediverse, activitypub, activitystream
|
Tags: OStatus, fediverse, activitypub, activitystream
|
||||||
Requires at least: 4.7
|
Requires at least: 4.7
|
||||||
Tested up to: 6.4
|
Tested up to: 6.3
|
||||||
Stable tag: 1.2.0
|
Stable tag: 1.0.10
|
||||||
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,24 +105,11 @@ Where 'blog' is the path to the subdirectory at which your blog resides.
|
||||||
|
|
||||||
Project maintained on GitHub at [automattic/wordpress-activitypub](https://github.com/automattic/wordpress-activitypub).
|
Project maintained on GitHub at [automattic/wordpress-activitypub](https://github.com/automattic/wordpress-activitypub).
|
||||||
|
|
||||||
= 1.2.0 =
|
|
||||||
|
|
||||||
* Add: Search and order followerer lists
|
|
||||||
* Add: Have a filter to defer signature verification
|
|
||||||
* Improved: "Follow Me" styles for dark themes
|
|
||||||
* Improved: Allow `p` and `br` tags only for AP comments
|
|
||||||
* Fixed: Deduplicate attachments earlier to prevent incorrect max_media
|
|
||||||
|
|
||||||
|
|
||||||
= 1.1.0 =
|
= 1.1.0 =
|
||||||
|
|
||||||
* Improved: audio and video attachments are now supported!
|
* Improved: audio and video attachments are now supported!
|
||||||
* Improved: better error messages if remote profile is not accessible
|
* Improved: better error messages if remote profile is not accessible
|
||||||
* Improved: PHP 8.1 compatibility
|
* Improved: PHP 8.1 compatibility
|
||||||
* Fixed: don't try to parse mentions or hashtags for very large (>1MB) posts to prevent timeouts
|
|
||||||
* Fixed: better handling of ISO-639-1 locale codes
|
|
||||||
* Improved: more reliable [ap_author], props @uk3
|
|
||||||
* Improved: NodeInfo statistics
|
|
||||||
|
|
||||||
= 1.0.10 =
|
= 1.0.10 =
|
||||||
|
|
||||||
|
|
|
@ -73,7 +73,6 @@
|
||||||
.apfmd__button-group {
|
.apfmd__button-group {
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: flex-end;
|
justify-content: flex-end;
|
||||||
align-items: flex-end;
|
|
||||||
|
|
||||||
svg {
|
svg {
|
||||||
margin-right: .5em;
|
margin-right: .5em;
|
||||||
|
@ -83,11 +82,10 @@
|
||||||
|
|
||||||
input {
|
input {
|
||||||
flex: 1;
|
flex: 1;
|
||||||
padding: 6px 12px;
|
padding: {
|
||||||
background-color: var( --wp--preset--color--white );
|
left: 1em;
|
||||||
color: var( --wp--preset--color--black );
|
right: 1em;
|
||||||
border: 1px solid var( --wp--preset--color--black );
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -21,8 +21,8 @@ $followers_template = _n( 'Your blog profile currently has %s follower.', 'Your
|
||||||
<input type="hidden" name="tab" value="followers" />
|
<input type="hidden" name="tab" value="followers" />
|
||||||
<?php
|
<?php
|
||||||
$table->prepare_items();
|
$table->prepare_items();
|
||||||
$table->search_box( 'Search', 'search' );
|
|
||||||
$table->display();
|
$table->display();
|
||||||
?>
|
?>
|
||||||
|
<?php wp_nonce_field( 'activitypub-followers-list', '_apnonce' ); ?>
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
|
|
36
templates/comment-json.php
Normal file
36
templates/comment-json.php
Normal file
|
@ -0,0 +1,36 @@
|
||||||
|
<?php
|
||||||
|
$comment = \get_comment( \get_query_var( 'replytocom' ) ); // phpcs:ignore
|
||||||
|
|
||||||
|
$object = new \Activitypub\Transformer\Comment( $comment );
|
||||||
|
$json = \array_merge( array( '@context' => \Activitypub\get_context() ), $object->to_object()->to_array() );
|
||||||
|
|
||||||
|
// filter output
|
||||||
|
$json = \apply_filters( 'activitypub_json_comment_array', $json );
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Action triggerd prior to the ActivityPub profile being created and sent to the client
|
||||||
|
*/
|
||||||
|
\do_action( 'activitypub_json_comment_pre' );
|
||||||
|
|
||||||
|
$options = 0;
|
||||||
|
// JSON_PRETTY_PRINT added in PHP 5.4
|
||||||
|
if ( \get_query_var( 'pretty' ) ) {
|
||||||
|
$options |= \JSON_PRETTY_PRINT; // phpcs:ignore
|
||||||
|
}
|
||||||
|
|
||||||
|
$options |= \JSON_HEX_TAG | \JSON_HEX_AMP | \JSON_HEX_QUOT;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Options to be passed to json_encode()
|
||||||
|
*
|
||||||
|
* @param int $options The current options flags
|
||||||
|
*/
|
||||||
|
$options = \apply_filters( 'activitypub_json_comment_options', $options );
|
||||||
|
|
||||||
|
\header( 'Content-Type: application/activity+json' );
|
||||||
|
echo \wp_json_encode( $json, $options );
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Action triggerd after the ActivityPub profile has been created and sent to the client
|
||||||
|
*/
|
||||||
|
\do_action( 'activitypub_json_comment_comment' );
|
|
@ -2,9 +2,8 @@
|
||||||
// phpcs:ignore WordPress.WP.GlobalVariablesOverride.Prohibited
|
// phpcs:ignore WordPress.WP.GlobalVariablesOverride.Prohibited
|
||||||
$post = \get_post();
|
$post = \get_post();
|
||||||
|
|
||||||
$transformer = \Activitypub\Transformer\Transformers_Manager::instance()->get_transformer( $post );
|
$object = new \Activitypub\Transformer\Post( $post );
|
||||||
|
$json = \array_merge( array( '@context' => \Activitypub\get_context() ), $object->to_object()->to_array() );
|
||||||
$json = \array_merge( array( '@context' => \Activitypub\get_context() ), $transformer->to_object()->to_array() );
|
|
||||||
|
|
||||||
// filter output
|
// filter output
|
||||||
$json = \apply_filters( 'activitypub_json_post_array', $json );
|
$json = \apply_filters( 'activitypub_json_post_array', $json );
|
||||||
|
|
|
@ -201,6 +201,26 @@
|
||||||
</p>
|
</p>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<th scope="row"><?php \esc_html_e( 'Supported post types', 'activitypub' ); ?></th>
|
||||||
|
<td>
|
||||||
|
<fieldset>
|
||||||
|
<?php \esc_html_e( 'Enable ActivityPub support for the following post types:', 'activitypub' ); ?>
|
||||||
|
|
||||||
|
<?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(); ?>
|
||||||
|
<ul>
|
||||||
|
<?php // phpcs:ignore WordPress.WP.GlobalVariablesOverride.Prohibited ?>
|
||||||
|
<?php foreach ( $post_types as $post_type ) { ?>
|
||||||
|
<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 ) ); ?> />
|
||||||
|
<label for="activitypub_support_post_type_<?php echo \esc_attr( $post_type->name ); ?>"><?php echo \esc_html( $post_type->label ); ?></label>
|
||||||
|
</li>
|
||||||
|
<?php } ?>
|
||||||
|
</ul>
|
||||||
|
</fieldset>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<th scope="row">
|
<th scope="row">
|
||||||
<?php \esc_html_e( 'Hashtags (beta)', 'activitypub' ); ?>
|
<?php \esc_html_e( 'Hashtags (beta)', 'activitypub' ); ?>
|
||||||
|
@ -217,93 +237,6 @@
|
||||||
<?php \do_settings_fields( 'activitypub', 'activity' ); ?>
|
<?php \do_settings_fields( 'activitypub', 'activity' ); ?>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- OUR FORK HERE -->
|
|
||||||
<div class="box">
|
|
||||||
<h3><?php \esc_html_e( 'Enable ActivityPub support for post type', 'activitypub' ); ?></h3>
|
|
||||||
|
|
||||||
<table class="form-table">
|
|
||||||
<tbody>
|
|
||||||
<tr>
|
|
||||||
<th scope="row">
|
|
||||||
<?php \esc_html_e( 'Mapping', 'activitypub' ); ?>
|
|
||||||
</th>
|
|
||||||
<td>
|
|
||||||
<?php \esc_html_e( 'Enable ActivityPub support for a certain post type by selecting one of the available ActivityPub transformers.', 'activitypub' ); ?>
|
|
||||||
|
|
||||||
<?php $all_public_post_types = \get_post_types( array( 'public' => true ), 'objects' );
|
|
||||||
$transformer_mapping = \get_option( 'activitypub_transformer_mapping', array( 'default' => 'note' ) );
|
|
||||||
|
|
||||||
$all_public_post_type_names = array_map(function ($object) {
|
|
||||||
return $object->name;
|
|
||||||
}, $all_public_post_types);
|
|
||||||
|
|
||||||
$transformer_manager = \Activitypub\Transformer\Transformers_Manager::instance();
|
|
||||||
$transformers = $transformer_manager->get_transformers();
|
|
||||||
|
|
||||||
?>
|
|
||||||
|
|
||||||
|
|
||||||
<script>
|
|
||||||
// TODO Probably we should use checkboxes and not select and make this less buggy and insert the js at the right place.
|
|
||||||
document.addEventListener('DOMContentLoaded', function () {
|
|
||||||
var radioGroups = {};
|
|
||||||
|
|
||||||
var radioButtons = document.querySelectorAll('input[type="radio"]');
|
|
||||||
|
|
||||||
radioButtons.forEach(function (radioButton) {
|
|
||||||
radioButton.addEventListener('click', function () {
|
|
||||||
var name = this.name;
|
|
||||||
|
|
||||||
if (!radioGroups[name]) {
|
|
||||||
radioGroups[name] = this;
|
|
||||||
} else {
|
|
||||||
radioGroups[name].checked = false;
|
|
||||||
radioGroups[name] = this;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<table>
|
|
||||||
<thead>
|
|
||||||
<tr>
|
|
||||||
<th></th>
|
|
||||||
<?php
|
|
||||||
// Generate column headers based on transformer objects
|
|
||||||
foreach ($transformers as $transformer) {
|
|
||||||
echo '<th>' . htmlspecialchars($transformer->get_label()) . '</th>';
|
|
||||||
}
|
|
||||||
?>
|
|
||||||
</tr>
|
|
||||||
</thead>
|
|
||||||
<tbody>
|
|
||||||
<?php
|
|
||||||
// Generate rows based on post types and transformers
|
|
||||||
foreach ($all_public_post_types as $post_type) {
|
|
||||||
echo '<tr>';
|
|
||||||
echo '<td><strong>' . htmlspecialchars($post_type->label) . '</strong></td>';
|
|
||||||
// Generate radio inputs for each transformer, considering support for the post type
|
|
||||||
foreach ($transformers as $transformer) {
|
|
||||||
$disabled_attribute = $transformer->supports_post_type( $post_type->name ) ? '' : ' disabled';
|
|
||||||
$is_selected = ( is_array( $transformer_mapping ) && isset( $transformer_mapping[ $post_type->name ] ) && $transformer_mapping[ $post_type->name ] === $transformer->get_name() ) ? ' checked ' : '';
|
|
||||||
echo '<td><input type="radio" name="activitypub_transformer_mapping[' . $post_type->name . ']" value="' . $transformer->get_name() . '"' . $is_selected . $disabled_attribute . '></td>';
|
|
||||||
}
|
|
||||||
|
|
||||||
echo '</tr>';
|
|
||||||
}
|
|
||||||
?>
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
|
|
||||||
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
</div>
|
|
||||||
<!-- OUR FORK ENDS HERE -->
|
|
||||||
|
|
||||||
<div class="box">
|
<div class="box">
|
||||||
<h3><?php \esc_html_e( 'Server', 'activitypub' ); ?></h3>
|
<h3><?php \esc_html_e( 'Server', 'activitypub' ); ?></h3>
|
||||||
<table class="form-table">
|
<table class="form-table">
|
||||||
|
|
|
@ -14,8 +14,8 @@ $followers_template = _n( 'Your author profile currently has %s follower.', 'You
|
||||||
<input type="hidden" name="page" value="activitypub-followers-list" />
|
<input type="hidden" name="page" value="activitypub-followers-list" />
|
||||||
<?php
|
<?php
|
||||||
$table->prepare_items();
|
$table->prepare_items();
|
||||||
$table->search_box( 'Search', 'search' );
|
|
||||||
$table->display();
|
$table->display();
|
||||||
?>
|
?>
|
||||||
|
<?php wp_nonce_field( 'activitypub-followers-list', '_apnonce' ); ?>
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -17,8 +17,7 @@ class Test_Activitypub_Activity extends WP_UnitTestCase {
|
||||||
10
|
10
|
||||||
);
|
);
|
||||||
|
|
||||||
$wp_post = get_post( $post );
|
$activitypub_post = \Activitypub\Transformer\Post::transform( get_post( $post ) )->to_object();
|
||||||
$activitypub_post = \Activitypub\Transformer\Transformers_Manager::instance()->get_transformer( $post )->to_object();
|
|
||||||
|
|
||||||
$activitypub_activity = new \Activitypub\Activity\Activity();
|
$activitypub_activity = new \Activitypub\Activity\Activity();
|
||||||
$activitypub_activity->set_type( 'Create' );
|
$activitypub_activity->set_type( 'Create' );
|
||||||
|
|
|
@ -10,13 +10,13 @@ class Test_Activitypub_Post extends WP_UnitTestCase {
|
||||||
|
|
||||||
$permalink = \get_permalink( $post );
|
$permalink = \get_permalink( $post );
|
||||||
|
|
||||||
$activitypub_post = \Activitypub\Transformer\Transformers_Manager::instance()->get_transformer( get_post( $post ) )->to_object();
|
$activitypub_post = \Activitypub\Transformer\Post::transform( get_post( $post ) )->to_object();
|
||||||
|
|
||||||
$this->assertEquals( $permalink, $activitypub_post->get_id() );
|
$this->assertEquals( $permalink, $activitypub_post->get_id() );
|
||||||
|
|
||||||
\wp_trash_post( $post );
|
\wp_trash_post( $post );
|
||||||
|
|
||||||
$activitypub_post = \Activitypub\Transformer\Transformers_Manager::instance()->get_transformer( get_post( $post ) )->to_object();
|
$activitypub_post = \Activitypub\Transformer\Post::transform( get_post( $post ) )->to_object();
|
||||||
|
|
||||||
$this->assertEquals( $permalink, $activitypub_post->get_id() );
|
$this->assertEquals( $permalink, $activitypub_post->get_id() );
|
||||||
|
|
||||||
|
|
|
@ -10,7 +10,7 @@ class Test_Activitypub_Signature_Verification extends WP_UnitTestCase {
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
$remote_actor = \get_author_posts_url( 2 );
|
$remote_actor = \get_author_posts_url( 2 );
|
||||||
$activitypub_post = \Activitypub\Transformer\Transformers_Manager::instance()->get_transformer( get_post( $post ) )->to_object();
|
$activitypub_post = \Activitypub\Transformer\Post::transform( get_post( $post ) )->to_object();
|
||||||
$activitypub_activity = new Activitypub\Activity\Activity( 'Create' );
|
$activitypub_activity = new Activitypub\Activity\Activity( 'Create' );
|
||||||
$activitypub_activity->set_type( 'Create' );
|
$activitypub_activity->set_type( 'Create' );
|
||||||
$activitypub_activity->set_object( $activitypub_post );
|
$activitypub_activity->set_object( $activitypub_post );
|
||||||
|
@ -82,7 +82,7 @@ class Test_Activitypub_Signature_Verification extends WP_UnitTestCase {
|
||||||
);
|
);
|
||||||
$remote_actor = \get_author_posts_url( 2 );
|
$remote_actor = \get_author_posts_url( 2 );
|
||||||
$remote_actor_inbox = Activitypub\get_rest_url_by_path( '/inbox' );
|
$remote_actor_inbox = Activitypub\get_rest_url_by_path( '/inbox' );
|
||||||
$activitypub_post = \Activitypub\Transformer\Transformers_Manager::instance()->get_transformer( get_post( $post ) )->to_object();
|
$activitypub_post = \Activitypub\Transformer\Post::transform( \get_post( $post ) )->to_object();
|
||||||
$activitypub_activity = new Activitypub\Activity\Activity();
|
$activitypub_activity = new Activitypub\Activity\Activity();
|
||||||
$activitypub_activity->set_type( 'Create' );
|
$activitypub_activity->set_type( 'Create' );
|
||||||
$activitypub_activity->set_object( $activitypub_post );
|
$activitypub_activity->set_object( $activitypub_post );
|
||||||
|
|
66
tests/test-class-inbox.php
Normal file
66
tests/test-class-inbox.php
Normal file
|
@ -0,0 +1,66 @@
|
||||||
|
<?php
|
||||||
|
class Test_Inbox extends WP_UnitTestCase {
|
||||||
|
public $post_permalink;
|
||||||
|
public $user_url;
|
||||||
|
|
||||||
|
public function set_up() {
|
||||||
|
$authordata = \get_userdata( 1 );
|
||||||
|
$this->user_url = $authordata->user_url;
|
||||||
|
|
||||||
|
$post = \wp_insert_post(
|
||||||
|
array(
|
||||||
|
'post_author' => 1,
|
||||||
|
'post_content' => 'test',
|
||||||
|
)
|
||||||
|
);
|
||||||
|
$this->post_permalink = \get_permalink( $post );
|
||||||
|
|
||||||
|
\add_filter( 'pre_get_remote_metadata_by_actor', array( '\Test_Inbox', 'get_remote_metadata_by_actor' ), 10, 2 );
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function get_remote_metadata_by_actor( $value, $actor ) {
|
||||||
|
return array(
|
||||||
|
'name' => 'Example User',
|
||||||
|
'icon' => array(
|
||||||
|
'url' => 'https://example.com/icon',
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function test_convert_object_to_comment_data_basic() {
|
||||||
|
$object = array(
|
||||||
|
'actor' => $this->user_url,
|
||||||
|
'to' => [ $this->user_url ],
|
||||||
|
'cc' => [ 'https://www.w3.org/ns/activitystreams#Public' ],
|
||||||
|
'object' => array(
|
||||||
|
'id' => '123',
|
||||||
|
'url' => 'https://example.com/example',
|
||||||
|
'inReplyTo' => $this->post_permalink,
|
||||||
|
'content' => 'example',
|
||||||
|
),
|
||||||
|
);
|
||||||
|
$converted = \Activitypub\Rest\Inbox::convert_object_to_comment_data( $object, 1 );
|
||||||
|
|
||||||
|
$this->assertGreaterThan( 1, $converted['comment_post_ID'] );
|
||||||
|
$this->assertEquals( $converted['comment_author'], 'Example User' );
|
||||||
|
$this->assertEquals( $converted['comment_author_url'], 'http://example.org' );
|
||||||
|
$this->assertEquals( $converted['comment_content'], 'example' );
|
||||||
|
$this->assertEquals( $converted['comment_type'], 'comment' );
|
||||||
|
$this->assertEquals( $converted['comment_author_email'], '' );
|
||||||
|
$this->assertEquals( $converted['comment_parent'], 0 );
|
||||||
|
$this->assertArrayHasKey( 'comment_meta', $converted );
|
||||||
|
$this->assertEquals( $converted['comment_meta']['source_id'], 'http://123' );
|
||||||
|
$this->assertEquals( $converted['comment_meta']['source_url'], 'https://example.com/example' );
|
||||||
|
$this->assertEquals( $converted['comment_meta']['avatar_url'], 'https://example.com/icon' );
|
||||||
|
$this->assertEquals( $converted['comment_meta']['protocol'], 'activitypub' );
|
||||||
|
}
|
||||||
|
|
||||||
|
public function test_convert_object_to_comment_data_non_public_rejected() {
|
||||||
|
$object = array(
|
||||||
|
'to' => array( 'https://example.com/profile/test' ),
|
||||||
|
'cc' => array(),
|
||||||
|
);
|
||||||
|
$converted = \Activitypub\Rest\Inbox::convert_object_to_comment_data( $object, 1 );
|
||||||
|
$this->assertFalse( $converted );
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in a new issue