WIP: Add Event Sources Logic (ActivityPub follows) #86

Draft
linos wants to merge 36 commits from event_sources into main
9 changed files with 138 additions and 74 deletions
Showing only changes of commit 33f1ccbc86 - Show all commits

View file

@ -67,14 +67,13 @@ class Create {
return;
}
$transmogrifier_class = Setup::get_transmogrifier();
$transmogrifier = Setup::get_transmogrifier();
if ( ! $transmogrifier_class ) {
if ( ! $transmogrifier ) {
return;
}
$transmogrifier = new $transmogrifier_class( $activity['object'] );
$transmogrifier->save();
$transmogrifier->save( $activity['object'] );
}
/**

View file

@ -47,13 +47,12 @@ class Delete {
return;
}
$transmogrifier_class = Setup::get_transmogrifier();
$transmogrifier = Setup::get_transmogrifier();
if ( ! $transmogrifier_class ) {
if ( ! $transmogrifier ) {
return;
}
$transmogrifier = new $transmogrifier_class( $activity['object'] );
$transmogrifier->delete();
$transmogrifier->delete( $activity['object'] );
}
}

View file

@ -34,6 +34,8 @@ abstract class Base {
/**
* Internal function to actually save the event.
*
* @return false|int Post-ID on success, false on failure.
*/
abstract protected function save_event();
@ -50,7 +52,13 @@ abstract class Base {
}
$this->activitypub_event = $activitypub_event;
$this->save_event();
$post_id = $this->save_event();
if ( $post_id ) {
update_post_meta( $post_id, 'event_bridge_for_activitypub_is_cached', 'yes' );
update_post_meta( $post_id, 'activitypub_content_visibility', constant( 'ACTIVITYPUB_CONTENT_VISIBILITY_LOCAL' ) ?? '' );
}
}
/**
@ -287,9 +295,19 @@ abstract class Base {
}
/**
* Save the ActivityPub event object as GatherPress event.
* Delete a local event in WordPress that is a cached remote one.
*
* @param array $activitypub_event The ActivityPub event as associative array.
*/
public function delete() {
public function delete( $activitypub_event ) {
$activitypub_event = Event::init_from_array( $activitypub_event );
if ( is_wp_error( $activitypub_event ) ) {
return;
}
$this->activitypub_event = $activitypub_event;
$post_id = $this->get_post_id_from_activitypub_id();
if ( ! $post_id ) {

View file

@ -120,9 +120,9 @@ class GatherPress extends Base {
/**
* Save the ActivityPub event object as GatherPress Event.
*
* @return void
* @return false|int
*/
protected function save_event(): void {
protected function save_event() {
// Limit this as a safety measure.
add_filter( 'wp_revisions_to_keep', array( self::class, 'revisions_to_keep' ) );
@ -135,10 +135,6 @@ class GatherPress extends Base {
'post_excerpt' => wp_kses_post( $this->activitypub_event->get_summary() ),
'post_status' => 'publish',
'guid' => sanitize_url( $this->activitypub_event->get_id() ),
'meta_input' => array(
'event_bridge_for_activitypub_is_cached' => 'GatherPress',
'activitypub_content_visibility' => ACTIVITYPUB_CONTENT_VISIBILITY_LOCAL,
),
);
if ( $post_id ) {
@ -151,7 +147,7 @@ class GatherPress extends Base {
}
if ( ! $post_id || is_wp_error( $post_id ) ) {
return;
return false;
}
// Insert the dates.
@ -182,5 +178,7 @@ class GatherPress extends Base {
// Limit this as a safety measure.
remove_filter( 'wp_revisions_to_keep', array( self::class, 'revisions_to_keep' ) );
return $post_id;
}
}

View file

@ -28,15 +28,7 @@ class User_Interface {
public static function init() {
\add_filter( 'page_row_actions', array( self::class, 'row_actions' ), 10, 2 );
\add_filter( 'post_row_actions', array( self::class, 'row_actions' ), 10, 2 );
\add_action(
'admin_init',
\add_filter(
'map_meta_cap',
array( self::class, 'disable_editing_for_external_events' ),
10,
4
)
);
\add_filter( 'map_meta_cap', array( self::class, 'disable_editing_for_external_events' ), 10, 4 );
}
/**

View file

@ -14,7 +14,8 @@ use Event_Bridge_For_ActivityPub\ActivityPub\Collection\Event_Sources as Event_S
use Event_Bridge_For_ActivityPub\Activitypub\Transmogrifier\GatherPress;
use Event_Bridge_For_ActivityPub\Activitypub\Handler;
use Event_Bridge_For_ActivityPub\Admin\User_Interface;
use Event_Bridge_For_ActivityPub\Integrations\Event_Plugin_Integration;
use Event_Bridge_For_ActivityPub\Integrations\Feature_Event_Sources;
use function Activitypub\get_remote_metadata_by_actor;
use function Activitypub\is_activitypub_request;
@ -31,7 +32,8 @@ class Event_Sources {
public static function init() {
\add_action( 'init', array( Event_Sources_Collection::class, 'init' ) );
\add_action( 'activitypub_register_handlers', array( Handler::class, 'register_handlers' ) );
\add_action( 'admin_init', array( User_Interface::class, 'init' ) );
\add_action( 'init', array( User_Interface::class, 'init' ) );
\add_action( 'init', array( self::class, 'register_post_meta' ) );
\add_filter( 'activitypub_is_post_disabled', array( self::class, 'is_cached_external_post' ), 10, 2 );
if ( ! \wp_next_scheduled( 'event_bridge_for_activitypub_event_sources_clear_cache' ) ) {
\wp_schedule_event( time(), 'daily', 'event_bridge_for_activitypub_event_sources_clear_cache' );
@ -41,6 +43,31 @@ class Event_Sources {
\add_filter( 'template_include', array( self::class, 'redirect_activitypub_requests_for_cached_external_events' ), 100 );
}
/**
* Register post meta.
*/
public static function register_post_meta() {
$setup = Setup::get_instance();
foreach ( $setup->get_active_event_plugins() as $event_plugin_integration ) {
if ( ! $event_plugin_integration instanceof Feature_Event_Sources && $event_plugin_integration instanceof Event_Plugin_Integration ) {
continue;
}
\register_post_meta(
$event_plugin_integration::get_post_type(),
'event_bridge_for_activitypub_is_cached',
array(
'type' => 'string',
'single' => false,
'sanitize_callback' => function ( $value ) {
return esc_sql( $value );
},
)
);
}
}
/**
* Get metadata of ActivityPub Actor by ID/URL.
*
@ -117,7 +144,7 @@ class Event_Sources {
* @return bool
*/
public static function is_cached_external_event_post( $post ): bool {
if ( get_post_meta( $post->id, 'event_bridge_for_activitypub_is_cached', true ) ) {
if ( get_post_meta( $post->ID, 'event_bridge_for_activitypub_is_cached', true ) ) {
return true;
}
@ -157,9 +184,18 @@ class Event_Sources {
* Delete old cached events that took place in the past.
*/
public static function clear_cache() {
// Get the event plugin integration that is used.
$event_plugin_integration = Setup::get_event_plugin_integration_used_for_event_sources_feature();
if ( ! $event_plugin_integration ) {
return;
}
$cache_retention_period = get_option( 'event_bridge_for_activitypub_event_source_cache_retention', WEEK_IN_SECONDS );
$past_event_ids = GatherPress::get_past_events( $cache_retention_period );
$ended_before_time = gmdate( 'Y-m-d H:i:s', time() - $cache_retention_period );
$past_event_ids = $event_plugin_integration::get_cached_remote_events( $ended_before_time );
foreach ( $past_event_ids as $post_id ) {
if ( has_post_thumbnail( $post_id ) ) {

View file

@ -120,12 +120,12 @@ class Settings {
\register_setting(
'event-bridge-for-activitypub',
'event_bridge_for_activitypub_plugin_used_for_event_source_feature',
'event_bridge_for_activitypub_integration_used_for_event_sources_feature',
array(
'type' => 'array',
'description' => \__( 'Define which plugin/integration is used for the event sources feature', 'event-bridge-for-activitypub' ),
'default' => array(),
'sanitize_callback' => array( self::class, 'sanitize_plugin_used_for_event_sources' ),
'sanitize_callback' => array( self::class, 'sanitize_event_plugin_integration_used_for_event_sources' ),
)
);
@ -144,11 +144,11 @@ class Settings {
/**
* Sanitize the option which event plugin.
*
* @param string $plugin The setting.
* @param string $event_plugin_integration The setting.
* @return string
*/
public static function sanitize_plugin_used_for_event_sources( $plugin ) {
if ( ! is_string( $plugin ) ) {
public static function sanitize_event_plugin_integration_used_for_event_sources( $event_plugin_integration ) {
if ( ! is_string( $event_plugin_integration ) ) {
return '';
}
$setup = Setup::get_instance();
@ -157,14 +157,13 @@ class Settings {
$valid_options = array();
foreach ( $active_event_plugins as $active_event_plugin ) {
if ( $active_event_plugin instanceof Feature_Event_Sources ) {
$full_class = $active_event_plugin::class;
$valid_options[] = substr( $full_class, strrpos( $full_class, '\\' ) + 1 );
$valid_options[] = $active_event_plugin::class;
}
}
if ( in_array( $plugin, $valid_options, true ) ) {
return $plugin;
if ( in_array( $event_plugin_integration, $valid_options, true ) ) {
return $event_plugin_integration;
}
return '';
return Setup::get_default_integration_class_name_used_for_event_sources_feature();
}
/**

View file

@ -15,12 +15,11 @@ namespace Event_Bridge_For_ActivityPub;
// Exit if accessed directly.
defined( 'ABSPATH' ) || exit; // @codeCoverageIgnore
use Event_Bridge_For_ActivityPub\ActivityPub\Transmogrifier\Base as Transmogrifier_Base;
use Event_Bridge_For_ActivityPub\ActivityPub\Transmogrifier\Base as Transmogrifier;
use Event_Bridge_For_ActivityPub\Admin\Event_Plugin_Admin_Notices;
use Event_Bridge_For_ActivityPub\Admin\General_Admin_Notices;
use Event_Bridge_For_ActivityPub\Admin\Health_Check;
use Event_Bridge_For_ActivityPub\Admin\Settings_Page;
use Event_Bridge_For_ActivityPub\Admin\User_Interface;
use Event_Bridge_For_ActivityPub\Integrations\Event_Plugin;
use Event_Bridge_For_ActivityPub\Integrations\Feature_Event_Sources;
@ -381,42 +380,66 @@ class Setup {
self::activate_activitypub_support_for_active_event_plugins();
}
/**
* Get the event plugin integration class name used for the event sources feature.
*
* @return string The class name of the event plugin integration class.
*/
public static function get_event_plugin_integration_used_for_event_sources_feature() {
// Get plugin option.
$event_plugin_integration = get_option( 'event_bridge_for_activitypub_integration_used_for_event_sources_feature', '' );
// Exit if event sources are not active or no plugin is specified.
if ( empty( $event_plugin_integration ) ) {
return null;
}
// Validate if setting is actual existing class.
if ( ! class_exists( $event_plugin_integration ) ) {
return null;
}
return $event_plugin_integration;
}
/**
* Get the transmogrifier class.
*
* Retrieves the appropriate transmogrifier class based on the active event plugins and settings.
*
* @return Transmogrifier_Base|null The transmogrifier class name or null if not available.
* @return ?Transmogrifier The transmogrifier class name or null if not available.
*/
public static function get_transmogrifier() {
// Retrieve singleton instance.
$setup = self::get_instance();
public static function get_transmogrifier(): ?Transmogrifier {
$event_plugin_integration = self::get_event_plugin_integration_used_for_event_sources_feature();
// Get plugin options.
$event_sources_active = (bool) get_option( 'event_bridge_for_activitypub_event_sources_active', false );
$event_plugin = get_option( 'event_bridge_for_activitypub_plugin_used_for_event_source_feature', '' );
// Exit if event sources are not active or no plugin is specified.
if ( ! $event_sources_active || empty( $event_plugin ) ) {
if ( ! $event_plugin_integration ) {
return null;
}
// Get the list of active event plugins.
$active_event_plugins = $setup->get_active_event_plugins();
// Loop through active plugins to find a match.
foreach ( $active_event_plugins as $active_event_plugin ) {
// Retrieve the class name of the active plugin.
$active_plugin_class_name = get_class( $active_event_plugin );
// Check if the active plugin class name contains the specified event plugin name.
if ( false !== strpos( $active_plugin_class_name, $event_plugin ) ) {
// Return the transmogrifier class provided by the plugin.
return $active_event_plugin->get_transmogrifier();
}
// Validate if get_transformer method exists in event plugin integration.
if ( ! method_exists( $event_plugin_integration, 'get_transmogrifier' ) ) {
return null;
}
// Return null if no matching plugin is found.
return null;
$transmogrifier = $event_plugin_integration::get_transmogrifier();
return $transmogrifier;
}
/**
* Get the full class name of the first event plugin integration that is active and supports the event source feature.
*
* @return string The full class name of the event plugin integration.
*/
public static function get_default_integration_class_name_used_for_event_sources_feature(): string {
$setup = self::get_instance();
$event_plugin_integrations = $setup->get_active_event_plugins();
foreach ( $event_plugin_integrations as $event_plugin_integration ) {
if ( $event_plugin_integration instanceof Feature_Event_Sources ) {
return $event_plugin_integration::class;
}
}
return '';
}
}

View file

@ -38,7 +38,7 @@ if ( ! isset( $args ) || ! array_key_exists( 'supports_event_sources', $args ) )
$event_plugins_supporting_event_sources = $args['supports_event_sources'];
$selected_plugin = \get_option( 'event_bridge_for_activitypub_plugin_used_for_event_source_feature', '' );
$selected_plugin = \get_option( 'event_bridge_for_activitypub_integration_used_for_event_sources_feature', '' );
$event_sources_active = \get_option( 'event_bridge_for_activitypub_event_sources_active', false );
$cache_retention_period = \get_option( 'event_bridge_for_activitypub_event_source_cache_retention', DAY_IN_SECONDS );
@ -128,18 +128,18 @@ $current_category_mapping = \get_option( 'event_bridge_for_activitypub_ev
?>
<tr>
<th scope="row">
<label for="event_bridge_for_activitypub_plugin_used_for_event_source_feature"><?php \esc_html_e( 'Event Plugin', 'event-bridge-for-activitypub' ); ?></label>
<label for="event_bridge_for_activitypub_integration_used_for_event_sources_feature"><?php \esc_html_e( 'Event Plugin', 'event-bridge-for-activitypub' ); ?></label>
</th>
<td>
<select
name="event_bridge_for_activitypub_plugin_used_for_event_source_feature"
id="event_bridge_for_activitypub_plugin_used_for_event_source_feature"
name="event_bridge_for_activitypub_integration_used_for_event_sources_feature"
id="event_bridge_for_activitypub_integration_used_for_event_sources_feature"
value="gatherpress"
aria-describedby="event-sources-used-plugin-description"
>
<?php
foreach ( $event_plugins_supporting_event_sources as $event_plugin ) {
echo '<option value="' . esc_attr( $event_plugin ) . '" ' . selected( $selected_plugin, $event_plugin, true ) . '>' . esc_attr( $event_plugin ) . '</option>';
foreach ( $event_plugins_supporting_event_sources as $event_plugin_class_name => $event_plugin_name ) {
echo '<option value="' . esc_attr( $event_plugin_class_name ) . '" ' . selected( $event_plugin_class_name, $event_plugin, true ) . '>' . esc_attr( $event_plugin_name ) . '</option>';
}
?>
</select>