diff --git a/activitypub.php b/activitypub.php
index 6475042d..e4ac2960 100644
--- a/activitypub.php
+++ b/activitypub.php
@@ -21,6 +21,8 @@ use function Activitypub\site_supports_blocks;
require_once __DIR__ . '/includes/compat.php';
require_once __DIR__ . '/includes/functions.php';
+\define( 'ACTIVITYPUB_PLUGIN_VERSION', '2.2.0' );
+
/**
* Initialize the plugin constants.
*/
@@ -101,6 +103,7 @@ function plugin_init() {
}
\add_action( 'plugins_loaded', __NAMESPACE__ . '\plugin_init' );
+
/**
* Class Autoloader
*/
@@ -218,6 +221,10 @@ function get_plugin_meta( $default_headers = array() ) {
* Plugin Version Number used for caching.
*/
function get_plugin_version() {
+ if ( \defined( 'ACTIVITYPUB_PLUGIN_VERSION' ) ) {
+ return ACTIVITYPUB_PLUGIN_VERSION;
+ }
+
$meta = get_plugin_meta( array( 'Version' => 'Version' ) );
return $meta['Version'];
diff --git a/includes/class-activity-dispatcher.php b/includes/class-activity-dispatcher.php
index 6278c669..16387154 100644
--- a/includes/class-activity-dispatcher.php
+++ b/includes/class-activity-dispatcher.php
@@ -44,9 +44,6 @@ class Activity_Dispatcher {
* @return void
*/
public static function send_activity_or_announce( $wp_object, $type ) {
- // check if a migration is needed before sending new posts
- Migration::maybe_migrate();
-
if ( is_user_type_disabled( 'blog' ) ) {
return;
}
diff --git a/includes/class-activitypub.php b/includes/class-activitypub.php
index 45e6503a..442cdfa5 100644
--- a/includes/class-activitypub.php
+++ b/includes/class-activitypub.php
@@ -40,6 +40,8 @@ class Activitypub {
\add_action( 'init', array( self::class, 'add_rewrite_rules' ), 11 );
\add_action( 'init', array( self::class, 'theme_compat' ), 11 );
+ \add_action( 'user_register', array( self::class, 'user_register' ) );
+
\add_action( 'in_plugin_update_message-' . ACTIVITYPUB_PLUGIN_BASENAME, array( self::class, 'plugin_update_message' ) );
// register several post_types
@@ -455,4 +457,17 @@ class Activitypub {
\do_action( 'activitypub_after_register_post_type' );
}
+
+ /**
+ * Add the 'activitypub' query variable so WordPress won't mangle it.
+ *
+ * @param int $user_id User ID.
+ * @param array $userdata The raw array of data passed to wp_insert_user().
+ */
+ public static function user_register( $user_id ) {
+ if ( \user_can( $user_id, 'publish_posts' ) ) {
+ $user = \get_user_by( 'id', $user_id );
+ $user->add_cap( 'activitypub' );
+ }
+ }
}
diff --git a/includes/class-admin.php b/includes/class-admin.php
index 32617556..62f04469 100644
--- a/includes/class-admin.php
+++ b/includes/class-admin.php
@@ -26,6 +26,11 @@ class Admin {
\add_action( 'admin_notices', array( self::class, 'admin_notices' ) );
\add_filter( 'comment_row_actions', array( self::class, 'comment_row_actions' ), 10, 2 );
+ \add_filter( 'manage_users_columns', array( self::class, 'manage_users_columns' ), 10, 1 );
+ \add_filter( 'manage_users_custom_column', array( self::class, 'manage_users_custom_column' ), 10, 3 );
+ \add_filter( 'bulk_actions-users', array( self::class, 'user_bulk_options' ) );
+ \add_filter( 'handle_bulk_actions-users', array( self::class, 'handle_bulk_request' ), 10, 3 );
+
if ( ! is_user_disabled( get_current_user_id() ) ) {
\add_action( 'show_user_profile', array( self::class, 'add_profile' ) );
}
@@ -345,4 +350,88 @@ class Admin {
return $actions;
}
+
+ /**
+ * Add a column "activitypub"
+ *
+ * This column shows if the user has the capability to use ActivityPub.
+ *
+ * @param array $columns The columns.
+ *
+ * @return array The columns extended by the activitypub.
+ */
+ public static function manage_users_columns( $columns ) {
+ $columns['activitypub'] = __( 'ActivityPub Support', 'activitypub' );
+ return $columns;
+ }
+
+ /**
+ * Return the results for the activitypub column.
+ *
+ * @param string $output Custom column output. Default empty.
+ * @param string $column_name Column name.
+ * @param int $user_id ID of the currently-listed user.
+ *
+ * @return string The column contents.
+ */
+ public static function manage_users_custom_column( $output, $column_name, $user_id ) {
+ if ( 'activitypub' !== $column_name ) {
+ return $output;
+ }
+
+ if ( \user_can( $user_id, 'activitypub' ) ) {
+ return '✓';
+ } else {
+ return '✗';
+ }
+ }
+
+ /**
+ * Add options to the Bulk dropdown on the users page
+ *
+ * @param array $actions The existing bulk options.
+ *
+ * @return array The extended bulk options.
+ */
+ public static function user_bulk_options( $actions ) {
+ $actions['add_activitypub_cap'] = __( 'Enable for ActivityPub', 'activitypub' );
+ $actions['remove_activitypub_cap'] = __( 'Disable for ActivityPub', 'activitypub' );
+
+ return $actions;
+ }
+
+ /**
+ * Handle bulk activitypub requests
+ *
+ * * `add_activitypub_cap` - Add the activitypub capability to the selected users.
+ * * `remove_activitypub_cap` - Remove the activitypub capability from the selected users.
+ *
+ * @param string $sendback The URL to send the user back to.
+ * @param string $action The requested action.
+ * @param array $users The selected users.
+ *
+ * @return string The URL to send the user back to.
+ */
+ public static function handle_bulk_request( $sendback, $action, $users ) {
+ if (
+ 'remove_activitypub_cap' !== $action &&
+ 'add_activitypub_cap' !== $action
+ ) {
+ return $sendback;
+ }
+
+ foreach ( $users as $user_id ) {
+ $user = new \WP_User( $user_id );
+ if (
+ 'add_activitypub_cap' === $action &&
+ user_can( $user_id, 'publish_posts' )
+ ) {
+ $user->add_cap( 'activitypub' );
+ } elseif ( 'remove_activitypub_cap' === $action ) {
+ $user->remove_cap( 'activitypub' );
+ }
+ }
+
+ return $sendback;
+ }
}
diff --git a/includes/class-migration.php b/includes/class-migration.php
index 219c0d83..52624c46 100644
--- a/includes/class-migration.php
+++ b/includes/class-migration.php
@@ -15,7 +15,9 @@ class Migration {
* Initialize the class, registering WordPress hooks
*/
public static function init() {
- \add_action( 'activitypub_schedule_migration', array( self::class, 'maybe_migrate' ) );
+ \add_action( 'activitypub_migrate', array( self::class, 'async_migration' ) );
+
+ self::maybe_migrate();
}
/**
@@ -108,18 +110,28 @@ class Migration {
$version_from_db = self::get_version();
+ // check for inital migration
+ if ( ! $version_from_db ) {
+ self::add_default_settings();
+ $version_from_db = self::get_target_version();
+ }
+
+ // schedule the async migration
+ if ( ! \wp_next_scheduled( 'activitypub_migrate', $version_from_db ) ) {
+ \wp_schedule_single_event( \time(), 'activitypub_migrate', $version_from_db );
+ }
if ( version_compare( $version_from_db, '0.17.0', '<' ) ) {
self::migrate_from_0_16();
}
- if ( version_compare( $version_from_db, '1.0.0', '<' ) ) {
- self::migrate_from_0_17();
- }
if ( version_compare( $version_from_db, '1.3.0', '<' ) ) {
self::migrate_from_1_2_0();
}
if ( version_compare( $version_from_db, '2.1.0', '<' ) ) {
self::migrate_from_2_0_0();
}
+ if ( version_compare( $version_from_db, '2.3.0', '<' ) ) {
+ self::migrate_from_2_1_0();
+ }
update_option( 'activitypub_db_version', self::get_target_version() );
@@ -127,23 +139,14 @@ class Migration {
}
/**
- * Updates the DB-schema of the followers-list
+ * Asynchronously migrates the database structure.
*
- * @return void
+ * @param string $version_from_db The version from which to migrate.
*/
- private static function migrate_from_0_17() {
- // migrate followers
- foreach ( get_users( array( 'fields' => 'ID' ) ) as $user_id ) {
- $followers = get_user_meta( $user_id, 'activitypub_followers', true );
-
- if ( $followers ) {
- foreach ( $followers as $actor ) {
- Followers::add_follower( $user_id, $actor );
- }
- }
+ public static function async_migration( $version_from_db ) {
+ if ( version_compare( $version_from_db, '1.0.0', '<' ) ) {
+ self::migrate_from_0_17();
}
-
- Activitypub::flush_rewrite_rules();
}
/**
@@ -183,13 +186,33 @@ class Migration {
}
}
+ /**
+ * Updates the DB-schema of the followers-list
+ *
+ * @return void
+ */
+ public static function migrate_from_0_17() {
+ // migrate followers
+ foreach ( get_users( array( 'fields' => 'ID' ) ) as $user_id ) {
+ $followers = get_user_meta( $user_id, 'activitypub_followers', true );
+
+ if ( $followers ) {
+ foreach ( $followers as $actor ) {
+ Followers::add_follower( $user_id, $actor );
+ }
+ }
+ }
+
+ Activitypub::flush_rewrite_rules();
+ }
+
/**
* Clear the cache after updating to 1.3.0
*
* @return void
*/
private static function migrate_from_1_2_0() {
- $user_ids = get_users(
+ $user_ids = \get_users(
array(
'fields' => 'ID',
'capability__in' => array( 'publish_posts' ),
@@ -220,4 +243,45 @@ class Migration {
\update_option( 'activitypub_object_type', 'wordpress-post-format' );
}
}
+
+ /**
+ * Add the ActivityPub capability to all users that can publish posts
+ * Delete old meta to store followers
+ *
+ * @return void
+ */
+ private static function migrate_from_2_1_0() {
+ // add the ActivityPub capability to all users that can publish posts
+ self::add_activitypub_capability();
+ }
+
+ /**
+ * Set the defaults needed for the plugin to work
+ *
+ * * Add the ActivityPub capability to all users that can publish posts
+ *
+ * @return void
+ */
+ public static function add_default_settings() {
+ self::add_activitypub_capability();
+ }
+
+ /**
+ * Add the ActivityPub capability to all users that can publish posts
+ *
+ * @return void
+ */
+ private static function add_activitypub_capability() {
+ // get all WP_User objects that can publish posts
+ $users = \get_users(
+ array(
+ 'capability__in' => array( 'publish_posts' ),
+ )
+ );
+
+ // add ActivityPub capability to all users that can publish posts
+ foreach ( $users as $user ) {
+ $user->add_cap( 'activitypub' );
+ }
+ }
}
diff --git a/includes/class-scheduler.php b/includes/class-scheduler.php
index 0db5fe9f..48a556bc 100644
--- a/includes/class-scheduler.php
+++ b/includes/class-scheduler.php
@@ -64,9 +64,6 @@ class Scheduler {
\add_action( 'activitypub_update_followers', array( self::class, 'update_followers' ) );
\add_action( 'activitypub_cleanup_followers', array( self::class, 'cleanup_followers' ) );
- // Migration
- \add_action( 'admin_init', array( self::class, 'schedule_migration' ) );
-
// profile updates for blog options
if ( ! is_user_type_disabled( 'blog' ) ) {
\add_action( 'update_option_site_icon', array( self::class, 'blog_user_update' ) );
@@ -265,17 +262,6 @@ class Scheduler {
}
}
- /**
- * Schedule migration if DB-Version is not up to date.
- *
- * @return void
- */
- public static function schedule_migration() {
- if ( ! \wp_next_scheduled( 'activitypub_schedule_migration' ) && ! Migration::is_latest_version() ) {
- \wp_schedule_single_event( \time(), 'activitypub_schedule_migration' );
- }
- }
-
/**
* Send a profile update when relevant user meta is updated.
*
@@ -287,7 +273,7 @@ class Scheduler {
*/
public static function user_meta_update( $meta_id, $user_id, $meta_key ) {
// don't bother if the user can't publish
- if ( ! \user_can( $user_id, 'publish_posts' ) ) {
+ if ( ! \user_can( $user_id, 'activitypub' ) ) {
return;
}
// the user meta fields that affect a profile.
@@ -311,7 +297,7 @@ class Scheduler {
*/
public static function user_update( $user_id ) {
// don't bother if the user can't publish
- if ( ! \user_can( $user_id, 'publish_posts' ) ) {
+ if ( ! \user_can( $user_id, 'activitypub' ) ) {
return;
}
diff --git a/includes/collection/class-users.php b/includes/collection/class-users.php
index ad94297c..a331c203 100644
--- a/includes/collection/class-users.php
+++ b/includes/collection/class-users.php
@@ -268,7 +268,7 @@ class Users {
public static function get_collection() {
$users = \get_users(
array(
- 'capability__in' => array( 'publish_posts' ),
+ 'capability__in' => array( 'activitypub' ),
)
);
diff --git a/includes/functions.php b/includes/functions.php
index ed516328..ad229b7a 100644
--- a/includes/functions.php
+++ b/includes/functions.php
@@ -384,7 +384,7 @@ function is_user_disabled( $user_id ) {
break;
}
- if ( ! \user_can( $user_id, 'publish_posts' ) ) {
+ if ( ! \user_can( $user_id, 'activitypub' ) ) {
$return = true;
break;
}
@@ -644,7 +644,7 @@ function get_total_users() {
$users = \get_users(
array(
- 'capability__in' => array( 'publish_posts' ),
+ 'capability__in' => array( 'activitypub' ),
)
);
diff --git a/phpcs.xml b/phpcs.xml
index 636c1052..c128c9b0 100644
--- a/phpcs.xml
+++ b/phpcs.xml
@@ -73,4 +73,11 @@
**/*.asset.php
+
+
+
+
+
+
+
diff --git a/tests/bootstrap.php b/tests/bootstrap.php
index 1aaa4f51..f4468fbc 100644
--- a/tests/bootstrap.php
+++ b/tests/bootstrap.php
@@ -29,3 +29,5 @@ function _manually_load_plugin() {
// Start up the WP testing environment.
require $_tests_dir . '/includes/bootstrap.php';
require __DIR__ . '/class-activitypub-testcase-cache-http.php';
+
+\Activitypub\Migration::add_default_settings();
diff --git a/tests/test-class-activitypub-followers.php b/tests/test-class-activitypub-followers.php
index 9a566650..f2ddfece 100644
--- a/tests/test-class-activitypub-followers.php
+++ b/tests/test-class-activitypub-followers.php
@@ -271,6 +271,8 @@ class Test_Activitypub_Followers extends WP_UnitTestCase {
}
public function test_migration() {
+ update_option( 'activitypub_db_version', '0.0.1' );
+
$followers = array(
'https://example.com/author/jon',
'https://example.og/errors',
@@ -286,6 +288,12 @@ class Test_Activitypub_Followers extends WP_UnitTestCase {
\Activitypub\Migration::maybe_migrate();
+ $schedule = \wp_next_scheduled( 'activitypub_migrate', '0.0.1' );
+
+ $this->assertNotFalse( $schedule );
+
+ do_action( 'activitypub_migrate', '0.0.1' );
+
$db_followers = \Activitypub\Collection\Followers::get_followers( 1 );
$this->assertCount( 3, $db_followers );
diff --git a/tests/test-class-activitypub-user.php b/tests/test-class-activitypub-user.php
new file mode 100644
index 00000000..1c2e4870
--- /dev/null
+++ b/tests/test-class-activitypub-user.php
@@ -0,0 +1,33 @@
+ 'subscriber@example.com',
+ 'first_name' => 'Max',
+ 'last_name' => 'Mustermann',
+ 'user_login' => 'subscriber',
+ 'user_pass' => 'subscriber',
+ 'role' => 'subscriber',
+ );
+
+ $user_id = wp_insert_user( $userdata );
+ $can = user_can( $user_id, 'activitypub' );
+
+ $this->assertFalse( $can );
+
+ $userdata = array(
+ 'user_email' => 'editor@example.com',
+ 'first_name' => 'Max',
+ 'last_name' => 'Mustermann',
+ 'user_login' => 'editor',
+ 'user_pass' => 'editor',
+ 'role' => 'editor',
+ );
+
+ $user_id = wp_insert_user( $userdata );
+ $can = user_can( $user_id, 'activitypub' );
+
+ $this->assertTrue( $can );
+ }
+}