refactoring
All checks were successful
PHP Code Checker / PHP Code Checker (pull_request) Successful in 50s
PHPUnit / PHPUnit – PHP 7.4 (pull_request) Successful in 1m4s
PHPUnit / PHPUnit – PHP 8.0 (pull_request) Successful in 1m2s
PHPUnit / PHPUnit – PHP 8.1 (pull_request) Successful in 1m2s
PHPUnit / PHPUnit – PHP 8.2 (pull_request) Successful in 1m7s
PHPUnit / PHPUnit – PHP 8.3 (pull_request) Successful in 1m6s
PHPUnit / PHPUnit – PHP 8.4 (pull_request) Successful in 1m5s

This commit is contained in:
André Menrath 2024-12-15 22:25:28 +01:00
parent 0c0bba5d15
commit 5712706457
23 changed files with 716 additions and 526 deletions

View file

@ -28,10 +28,10 @@ First you need to add some basic information about your event plugin. Just creat
final class My_Event_Plugin extends Event_Plugin { final class My_Event_Plugin extends Event_Plugin {
``` ```
Then you need to tell the Event Bridge for ActivityPub about that class by adding it to the `EVENT_PLUGIN_CLASSES` constant in the `includes/setup.php` file: Then you need to tell the Event Bridge for ActivityPub about that class by adding it to the `EVENT_PLUGIN_INTEGRATIONS` constant in the `includes/setup.php` file:
```php ```php
private const EVENT_PLUGIN_CLASSES = array( private const EVENT_PLUGIN_INTEGRATIONS = array(
... ...
'\Event_Bridge_For_ActivityPub\Integrations\My_Event_Plugin', '\Event_Bridge_For_ActivityPub\Integrations\My_Event_Plugin',
); );

View file

@ -31,6 +31,7 @@ class Event_Sources {
*/ */
public static function init() { public static function init() {
self::register_post_type(); self::register_post_type();
\add_filter( 'allowed_redirect_hosts', array( self::class, 'add_event_sources_hosts_to_allowed_redirect_hosts' ) );
\add_action( 'event_bridge_for_activitypub_follow', array( self::class, 'activitypub_follow_actor' ), 10, 1 ); \add_action( 'event_bridge_for_activitypub_follow', array( self::class, 'activitypub_follow_actor' ), 10, 1 );
\add_action( 'event_bridge_for_activitypub_unfollow', array( self::class, 'activitypub_unfollow_actor' ), 10, 1 ); \add_action( 'event_bridge_for_activitypub_unfollow', array( self::class, 'activitypub_unfollow_actor' ), 10, 1 );
} }

View file

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

View file

@ -0,0 +1,339 @@
<?php
/**
* Base class with common functions for transforming an ActivityPub Event object to a WordPress object.
*
* @package Event_Bridge_For_ActivityPub
* @since 1.0.0
* @license AGPL-3.0-or-later
*/
namespace Event_Bridge_For_ActivityPub\Activitypub\Transmogrifier;
use Activitypub\Activity\Extended_Object\Event;
use DateTime;
use Exception;
use WP_Error;
use function Activitypub\sanitize_url;
// Exit if accessed directly.
defined( 'ABSPATH' ) || exit; // @codeCoverageIgnore
/**
* Base class with common functions for transforming an ActivityPub Event object to a WordPress object.
*
* @since 1.0.0
*/
abstract class Base {
/**
* The current GatherPress Event object.
*
* @var Event
*/
protected $activitypub_event;
/**
* Internal function to actually save the event.
*/
abstract protected function save_event();
/**
* Save the ActivityPub event object as GatherPress Event.
*
* @param array $activitypub_event The ActivityPub event as associative array.
*/
public function save( $activitypub_event ) {
$activitypub_event = Event::init_from_array( $activitypub_event );
if ( is_wp_error( $activitypub_event ) ) {
return;
}
$this->activitypub_event = $activitypub_event;
$this->save_event();
}
/**
* Get post.
*/
protected function get_post_id_from_activitypub_id() {
global $wpdb;
return $wpdb->get_var(
$wpdb->prepare(
"SELECT ID FROM $wpdb->posts WHERE guid=%s",
esc_sql( $this->activitypub_event->get_id() )
)
);
}
/**
* Validate a time string if it is according to the ActivityPub specification.
*
* @param string $time_string The time string.
* @return bool
*/
public static function is_valid_activitypub_time_string( $time_string ) {
// Try to create a DateTime object from the input string.
try {
$date = new DateTime( $time_string );
} catch ( Exception $e ) {
// If parsing fails, it's not valid.
return false;
}
// Ensure the timezone is correctly formatted (e.g., 'Z' or a valid offset).
$timezone = $date->getTimezone();
$formatted_timezone = $timezone->getName();
// Return true only if the time string includes 'Z' or a valid timezone offset.
$valid = 'Z' === $formatted_timezone || preg_match( '/^[+-]\d{2}:\d{2}$/ ', $formatted_timezone );
return $valid;
}
/**
* Get the image URL and alt-text of an ActivityPub object.
*
* @param array $data The ActivityPub object as ann associative array.
* @return ?array Array containing the images URL and alt-text.
*/
private static function extract_image_alt_and_url( $data ) {
$image = array(
'url' => null,
'alt' => null,
);
// Check whether it is already simple.
if ( ! $data || is_string( $data ) ) {
$image['url'] = $data;
return $image;
}
if ( ! isset( $data['type'] ) ) {
return $image;
}
if ( ! in_array( $data['type'], array( 'Document', 'Image' ), true ) ) {
return $image;
}
if ( isset( $data['url'] ) ) {
$image['url'] = $data['url'];
} elseif ( isset( $data['id'] ) ) {
$image['id'] = $data['id'];
}
if ( isset( $data['name'] ) ) {
$image['alt'] = $data['name'];
}
return $image;
}
/**
* Returns the URL of the featured image.
*
* @return array
*/
protected function get_featured_image() {
$event = $this->activitypub_event;
$image = $event->get_image();
if ( $image ) {
return self::extract_image_alt_and_url( $image );
}
$attachment = $event->get_attachment();
if ( is_array( $attachment ) && ! empty( $attachment ) ) {
$supported_types = array( 'Image', 'Document' );
$match = null;
foreach ( $attachment as $item ) {
if ( in_array( $item['type'], $supported_types, true ) ) {
$match = $item;
break;
}
}
$attachment = $match;
}
return self::extract_image_alt_and_url( $attachment );
}
/**
* Given an image URL return an attachment ID. Image will be side-loaded into the media library if it doesn't exist.
*
* Forked from https://gist.github.com/kingkool68/a66d2df7835a8869625282faa78b489a.
*
* @param int $post_id The post ID where the image will be set as featured image.
* @param string $url The image URL to maybe sideload.
* @uses media_sideload_image
* @return string|int|WP_Error
*/
protected static function maybe_sideload_image( $post_id, $url = '' ) {
global $wpdb;
// Include necessary WordPress file for media handling.
if ( ! function_exists( 'media_sideload_image' ) ) {
require_once ABSPATH . 'wp-admin/includes/media.php';
require_once ABSPATH . 'wp-admin/includes/file.php';
require_once ABSPATH . 'wp-admin/includes/image.php';
}
// Check to see if the URL has already been fetched, if so return the attachment ID.
$attachment_id = $wpdb->get_var(
$wpdb->prepare( "SELECT `post_id` FROM {$wpdb->postmeta} WHERE `meta_key` = '_source_url' AND `meta_value` = %s", sanitize_url( $url ) )
);
if ( ! empty( $attachment_id ) ) {
return $attachment_id;
}
$attachment_id = $wpdb->get_var(
$wpdb->prepare( "SELECT `ID` FROM {$wpdb->posts} WHERE guid=%s", $url )
);
if ( ! empty( $attachment_id ) ) {
return $attachment_id;
}
// If the URL doesn't exist, sideload it to the media library.
return media_sideload_image( sanitize_url( $url ), $post_id, sanitize_url( $url ), 'id' );
}
/**
* Sideload an image_url set it as featured image and add the alt-text.
*
* @param int $post_id The post ID where the image will be set as featured image.
* @param string $image_url The image URL.
* @param string $alt_text The alt-text of the image.
* @return int The attachment ID
*/
protected static function set_featured_image_with_alt( $post_id, $image_url, $alt_text = '' ) {
// Maybe sideload the image or get the Attachment ID of an existing one.
$image_id = self::maybe_sideload_image( $post_id, $image_url );
if ( is_wp_error( $image_id ) ) {
// Handle the error.
return $image_id;
}
// Set the image as the featured image for the post.
set_post_thumbnail( $post_id, $image_id );
// Update the alt text.
if ( ! empty( $alt_text ) ) {
update_post_meta( $image_id, '_wp_attachment_image_alt', sanitize_text_field( $alt_text ) );
}
return $image_id; // Return the attachment ID for further use if needed.
}
/**
* Convert a PostalAddress to a string.
*
* @link https://schema.org/PostalAddress
*
* @param array $postal_address The PostalAddress as an associative array.
* @return string
*/
private static function postal_address_to_string( $postal_address ) {
if ( ! is_array( $postal_address ) || 'PostalAddress' !== $postal_address['type'] ) {
_doing_it_wrong(
__METHOD__,
'The parameter postal_address must be an associate array like schema.org/PostalAddress.',
esc_html( EVENT_BRIDGE_FOR_ACTIVITYPUB_PLUGIN_VERSION )
);
}
$address = array();
$known_attributes = array(
'streetAddress',
'postalCode',
'addressLocality',
'addressState',
'addressCountry',
);
foreach ( $known_attributes as $attribute ) {
if ( isset( $postal_address[ $attribute ] ) && is_string( $postal_address[ $attribute ] ) ) {
$address[] = $postal_address[ $attribute ];
}
}
$address_string = implode( ' ,', $address );
return $address_string;
}
/**
* Convert an address to a string.
*
* @param mixed $address The address as an object, string or associative array.
* @return string
*/
protected static function address_to_string( $address ) {
if ( is_string( $address ) ) {
return $address;
}
if ( is_object( $address ) ) {
$address = (array) $address;
}
if ( ! is_array( $address ) || ! isset( $address['type'] ) ) {
return '';
}
if ( 'PostalAddress' === $address['type'] ) {
return self::postal_address_to_string( $address );
}
return '';
}
/**
* Save the ActivityPub event object as GatherPress event.
*/
public function delete() {
$post_id = $this->get_post_id_from_activitypub_id();
if ( ! $post_id ) {
return new WP_Error(
'event_bridge_for_activitypub_remote_event_not_found',
\__( 'Remote event not found in cache', 'event-bridge-for-activitypub' ),
array( 'status' => 404 )
);
}
$thumbnail_id = get_post_thumbnail_id( $post_id );
if ( $thumbnail_id ) {
wp_delete_attachment( $thumbnail_id, true );
}
wp_delete_post( $post_id, true );
}
/**
* Return the number of revisions to keep.
*
* @return int The number of revisions to keep.
*/
public static function revisions_to_keep() {
return 5;
}
/**
* Returns the URL of the online event link.
*
* @return ?string
*/
protected function get_online_event_link_from_attachments() {
$attachments = $this->activitypub_event->get_attachment();
if ( ! is_array( $attachments ) || empty( $attachments ) ) {
return;
}
foreach ( $attachments as $attachment ) {
if ( array_key_exists( 'type', $attachment ) && 'Link' === $attachment['type'] && isset( $attachment['href'] ) ) {
return $attachment['href'];
}
}
}
}

View file

@ -11,10 +11,7 @@
namespace Event_Bridge_For_ActivityPub\Activitypub\Transmogrifier; namespace Event_Bridge_For_ActivityPub\Activitypub\Transmogrifier;
use Activitypub\Activity\Extended_Object\Event;
use DateTime; use DateTime;
use Exception;
use WP_Error;
use function Activitypub\sanitize_url; use function Activitypub\sanitize_url;
@ -30,224 +27,7 @@ use GatherPress\Core\Event as GatherPress_Event;
* *
* @since 1.0.0 * @since 1.0.0
*/ */
class GatherPress { class GatherPress extends Base {
/**
* The current GatherPress Event object.
*
* @var Event
*/
protected $activitypub_event;
/**
* Extend the constructor, to also set the GatherPress objects.
*
* This is a special class object form The Events Calendar which
* has a lot of useful functions, we make use of our getter functions.
*
* @param array $activitypub_event The ActivityPub Event as associative array.
*/
public function __construct( $activitypub_event ) {
$activitypub_event = Event::init_from_array( $activitypub_event );
if ( is_wp_error( $activitypub_event ) ) {
return;
}
$this->activitypub_event = $activitypub_event;
}
/**
* Validate a time string if it is according to the ActivityPub specification.
*
* @param string $time_string The time string.
* @return bool
*/
public static function is_valid_activitypub_time_string( $time_string ) {
// Try to create a DateTime object from the input string.
try {
$date = new DateTime( $time_string );
} catch ( Exception $e ) {
// If parsing fails, it's not valid.
return false;
}
// Ensure the timezone is correctly formatted (e.g., 'Z' or a valid offset).
$timezone = $date->getTimezone();
$formatted_timezone = $timezone->getName();
// Return true only if the time string includes 'Z' or a valid timezone offset.
$valid = 'Z' === $formatted_timezone || preg_match( '/^[+-]\d{2}:\d{2}$/ ', $formatted_timezone );
return $valid;
}
/**
* Get a list of Post IDs of events that have ended.
*
* @param int $cache_retention_period Additional time buffer in seconds.
* @return int[]
*/
public static function get_past_events( $cache_retention_period = 0 ) {
global $wpdb;
$time_limit = gmdate( 'Y-m-d H:i:s', time() - $cache_retention_period );
$results = $wpdb->get_col(
$wpdb->prepare(
"SELECT post_id FROM {$wpdb->prefix}gatherpress_events WHERE datetime_end < %s",
$time_limit
)
);
return $results;
}
/**
* Get post.
*/
private function get_post_id_from_activitypub_id() {
global $wpdb;
return $wpdb->get_var(
$wpdb->prepare(
"SELECT ID FROM $wpdb->posts WHERE guid=%s",
esc_sql( $this->activitypub_event->get_id() )
)
);
}
/**
* Get the image URL and alt-text of an ActivityPub object.
*
* @param array $data The ActivityPub object as ann associative array.
* @return ?array Array containing the images URL and alt-text.
*/
private static function extract_image_alt_and_url( $data ) {
$image = array(
'url' => null,
'alt' => null,
);
// Check whether it is already simple.
if ( ! $data || is_string( $data ) ) {
$image['url'] = $data;
return $image;
}
if ( ! isset( $data['type'] ) ) {
return $image;
}
if ( ! in_array( $data['type'], array( 'Document', 'Image' ), true ) ) {
return $image;
}
if ( isset( $data['url'] ) ) {
$image['url'] = $data['url'];
} elseif ( isset( $data['id'] ) ) {
$image['id'] = $data['id'];
}
if ( isset( $data['name'] ) ) {
$image['alt'] = $data['name'];
}
return $image;
}
/**
* Returns the URL of the featured image.
*
* @return array
*/
private function get_featured_image() {
$event = $this->activitypub_event;
$image = $event->get_image();
if ( $image ) {
return self::extract_image_alt_and_url( $image );
}
$attachment = $event->get_attachment();
if ( is_array( $attachment ) && ! empty( $attachment ) ) {
$supported_types = array( 'Image', 'Document' );
$match = null;
foreach ( $attachment as $item ) {
if ( in_array( $item['type'], $supported_types, true ) ) {
$match = $item;
break;
}
}
$attachment = $match;
}
return self::extract_image_alt_and_url( $attachment );
}
/**
* Given an image URL return an attachment ID. Image will be side-loaded into the media library if it doesn't exist.
*
* Forked from https://gist.github.com/kingkool68/a66d2df7835a8869625282faa78b489a.
*
* @param int $post_id The post ID where the image will be set as featured image.
* @param string $url The image URL to maybe sideload.
* @uses media_sideload_image
* @return string|int|WP_Error
*/
public static function maybe_sideload_image( $post_id, $url = '' ) {
global $wpdb;
// Include necessary WordPress file for media handling.
if ( ! function_exists( 'media_sideload_image' ) ) {
require_once ABSPATH . 'wp-admin/includes/media.php';
require_once ABSPATH . 'wp-admin/includes/file.php';
require_once ABSPATH . 'wp-admin/includes/image.php';
}
// Check to see if the URL has already been fetched, if so return the attachment ID.
$attachment_id = $wpdb->get_var(
$wpdb->prepare( "SELECT `post_id` FROM {$wpdb->postmeta} WHERE `meta_key` = '_source_url' AND `meta_value` = %s", sanitize_url( $url ) )
);
if ( ! empty( $attachment_id ) ) {
return $attachment_id;
}
$attachment_id = $wpdb->get_var(
$wpdb->prepare( "SELECT `ID` FROM {$wpdb->posts} WHERE guid=%s", $url )
);
if ( ! empty( $attachment_id ) ) {
return $attachment_id;
}
// If the URL doesn't exist, sideload it to the media library.
return media_sideload_image( sanitize_url( $url ), $post_id, sanitize_url( $url ), 'id' );
}
/**
* Sideload an image_url set it as featured image and add the alt-text.
*
* @param int $post_id The post ID where the image will be set as featured image.
* @param string $image_url The image URL.
* @param string $alt_text The alt-text of the image.
* @return int The attachment ID
*/
private static function set_featured_image_with_alt( $post_id, $image_url, $alt_text = '' ) {
// Maybe sideload the image or get the Attachment ID of an existing one.
$image_id = self::maybe_sideload_image( $post_id, $image_url );
if ( is_wp_error( $image_id ) ) {
// Handle the error.
return $image_id;
}
// Set the image as the featured image for the post.
set_post_thumbnail( $post_id, $image_id );
// Update the alt text.
if ( ! empty( $alt_text ) ) {
update_post_meta( $image_id, '_wp_attachment_image_alt', sanitize_text_field( $alt_text ) );
}
return $image_id; // Return the attachment ID for further use if needed.
}
/** /**
* Add tags to post. * Add tags to post.
* *
@ -271,94 +51,12 @@ class GatherPress {
// Add the tags as terms to the post. // Add the tags as terms to the post.
if ( ! empty( $tag_names ) ) { if ( ! empty( $tag_names ) ) {
wp_set_object_terms( $post_id, $tag_names, 'gatherpress_topic', true ); // 'true' appends to existing terms. wp_set_object_terms( $post_id, $tag_names, 'gatherpress_topic', true );
} }
return true; return true;
} }
/**
* Returns the URL of the online event link.
*
* @return ?string
*/
protected function get_online_event_link_from_attachments() {
$attachments = $this->activitypub_event->get_attachment();
if ( ! is_array( $attachments ) || empty( $attachments ) ) {
return;
}
foreach ( $attachments as $attachment ) {
if ( array_key_exists( 'type', $attachment ) && 'Link' === $attachment['type'] && isset( $attachment['href'] ) ) {
return $attachment['href'];
}
}
}
/**
* Convert a PostalAddress to a string.
*
* @link https://schema.org/PostalAddress
*
* @param array $postal_address The PostalAddress as an associative array.
* @return string
*/
protected static function postal_address_to_string( $postal_address ) {
if ( ! is_array( $postal_address ) || 'PostalAddress' !== $postal_address['type'] ) {
_doing_it_wrong(
__METHOD__,
'The parameter postal_address must be an associate array like schema.org/PostalAddress.',
esc_html( EVENT_BRIDGE_FOR_ACTIVITYPUB_PLUGIN_VERSION )
);
}
$address = array();
$known_attributes = array(
'streetAddress',
'postalCode',
'addressLocality',
'addressState',
'addressCountry',
);
foreach ( $known_attributes as $attribute ) {
if ( isset( $postal_address[ $attribute ] ) && is_string( $postal_address[ $attribute ] ) ) {
$address[] = $postal_address[ $attribute ];
}
}
$address_string = implode( ' ,', $address );
return $address_string;
}
/**
* Convert an address to a string.
*
* @param mixed $address The address as an object, string or associative array.
* @return string
*/
protected static function address_to_string( $address ) {
if ( is_string( $address ) ) {
return $address;
}
if ( is_object( $address ) ) {
$address = (array) $address;
}
if ( ! is_array( $address ) || ! isset( $address['type'] ) ) {
return '';
}
if ( 'PostalAddress' === $address['type'] ) {
return self::postal_address_to_string( $address );
}
return '';
}
/** /**
* Add venue. * Add venue.
* *
@ -421,8 +119,10 @@ class GatherPress {
/** /**
* Save the ActivityPub event object as GatherPress Event. * Save the ActivityPub event object as GatherPress Event.
*
* @return void
*/ */
public function save() { protected function save_event(): void {
// Limit this as a safety measure. // Limit this as a safety measure.
add_filter( 'wp_revisions_to_keep', array( self::class, 'revisions_to_keep' ) ); add_filter( 'wp_revisions_to_keep', array( self::class, 'revisions_to_keep' ) );
@ -483,36 +183,4 @@ class GatherPress {
// Limit this as a safety measure. // Limit this as a safety measure.
remove_filter( 'wp_revisions_to_keep', array( self::class, 'revisions_to_keep' ) ); remove_filter( 'wp_revisions_to_keep', array( self::class, 'revisions_to_keep' ) );
} }
/**
* Save the ActivityPub event object as GatherPress event.
*/
public function delete() {
$post_id = $this->get_post_id_from_activitypub_id();
if ( ! $post_id ) {
return new WP_Error(
'event_bridge_for_activitypub_remote_event_not_found',
\__( 'Remote event not found in cache', 'event-bridge-for-activitypub' ),
array( 'status' => 404 )
);
}
$thumbnail_id = get_post_thumbnail_id( $post_id );
if ( $thumbnail_id ) {
wp_delete_attachment( $thumbnail_id, true );
}
wp_delete_post( $post_id, true );
}
/**
* Return the number of revisions to keep.
*
* @return int The number of revisions to keep.
*/
public static function revisions_to_keep() {
return 5;
}
} }

View file

@ -0,0 +1,59 @@
<?php
/**
* ActivityPub Transmogrify for the The Events Calendar event plugin.
*
* Handles converting incoming external ActivityPub events to The Events Calendar Events.
*
* @package Event_Bridge_For_ActivityPub
* @since 1.0.0
* @license AGPL-3.0-or-later
*/
namespace Event_Bridge_For_ActivityPub\Activitypub\Transmogrifier;
use DateTime;
use function Activitypub\sanitize_url;
// Exit if accessed directly.
defined( 'ABSPATH' ) || exit; // @codeCoverageIgnore
use GatherPress\Core\Event as GatherPress_Event;
/**
* ActivityPub Transmogrifier for the GatherPress event plugin.
*
* Handles converting incoming external ActivityPub events to GatherPress Events.
*
* @since 1.0.0
*/
class The_Events_Calendar extends Base {
/**
* Get a list of Post IDs of events that have ended.
*
* @param int $cache_retention_period Additional time buffer in seconds.
* @return ?array
*/
public static function get_past_events( $cache_retention_period = 0 ): ?array {
unset( $cache_retention_period );
$results = array();
return $results;
}
/**
* Save the ActivityPub event object as GatherPress Event.
*
* @return void
*/
public function save_event(): void {
// Limit this as a safety measure.
add_filter( 'wp_revisions_to_keep', array( self::class, 'revisions_to_keep' ) );
$this->get_post_id_from_activitypub_id();
// Limit this as a safety measure.
remove_filter( 'wp_revisions_to_keep', array( self::class, 'revisions_to_keep' ) );
}
}

View file

@ -97,7 +97,7 @@ class Health_Check {
// Call the transformer Factory. // Call the transformer Factory.
$transformer = Transformer_Factory::get_transformer( $event_posts[0] ); $transformer = Transformer_Factory::get_transformer( $event_posts[0] );
// Check that we got the right transformer. // Check that we got the right transformer.
$desired_transformer_class = $event_plugin::get_activitypub_event_transformer_class(); $desired_transformer_class = $event_plugin::get_activitypub_event_transformer( $event_posts[0] );
if ( $transformer instanceof $desired_transformer_class ) { if ( $transformer instanceof $desired_transformer_class ) {
return true; return true;
} }

View file

@ -19,6 +19,8 @@ use Event_Bridge_For_ActivityPub\ActivityPub\Model\Event_Source;
use Event_Bridge_For_ActivityPub\Event_Sources; use Event_Bridge_For_ActivityPub\Event_Sources;
use Event_Bridge_For_ActivityPub\ActivityPub\Collection\Event_Sources as Event_Source_Collection; use Event_Bridge_For_ActivityPub\ActivityPub\Collection\Event_Sources as Event_Source_Collection;
use Event_Bridge_For_ActivityPub\Integrations\Event_Plugin; use Event_Bridge_For_ActivityPub\Integrations\Event_Plugin;
use Event_Bridge_For_ActivityPub\Integrations\Event_Plugin_Integration;
use Event_Bridge_For_ActivityPub\Integrations\Feature_Event_Sources;
use Event_Bridge_For_ActivityPub\Setup; use Event_Bridge_For_ActivityPub\Setup;
/** /**
@ -171,15 +173,15 @@ class Settings_Page {
case 'settings': case 'settings':
$event_terms = array(); $event_terms = array();
foreach ( $event_plugins as $event_plugin ) { foreach ( $event_plugins as $event_plugin_integration ) {
$event_terms = array_merge( $event_terms, self::get_event_terms( $event_plugin ) ); $event_terms = array_merge( $event_terms, self::get_event_terms( $event_plugin_integration ) );
} }
$supports_event_sources = array(); $supports_event_sources = array();
foreach ( $event_plugins as $event_plugin ) { foreach ( $event_plugins as $event_plugin_integration ) {
if ( $event_plugin->supports_event_sources() ) { if ( $event_plugin_integration instanceof Feature_Event_Sources && $event_plugin_integration instanceof Event_Plugin_Integration ) {
$supports_event_sources[ $event_plugin::class ] = $event_plugin->get_plugin_name(); $supports_event_sources[ $event_plugin_integration::class ] = $event_plugin_integration::get_plugin_name();
} }
} }

View file

@ -9,6 +9,8 @@
namespace Event_Bridge_For_ActivityPub\Admin; namespace Event_Bridge_For_ActivityPub\Admin;
use Event_Bridge_For_ActivityPub\Event_Sources;
// Exit if accessed directly. // Exit if accessed directly.
defined( 'ABSPATH' ) || exit; // @codeCoverageIgnore defined( 'ABSPATH' ) || exit; // @codeCoverageIgnore
@ -59,7 +61,7 @@ class User_Interface {
*/ */
public static function row_actions( $actions, $post ) { public static function row_actions( $actions, $post ) {
// check if the post is enabled for ActivityPub. // check if the post is enabled for ActivityPub.
if ( ! self::post_is_external_event_post( $post ) ) { if ( ! Event_Sources::is_cached_external_event_post( $post ) ) {
return $actions; return $actions;
} }
@ -72,19 +74,6 @@ class User_Interface {
return $actions; return $actions;
} }
/**
* Check if a post is both an event post and external (from ActivityPub federation).
*
* @param WP_Post $post The post.
* @return bool
*/
private static function post_is_external_event_post( $post ) {
if ( 'gatherpress_event' !== $post->post_type ) {
return false;
}
return str_starts_with( $post->guid, 'https://ga.lan' ) ? true : false;
}
/** /**
* Modify the user capabilities so that nobody can edit external events. * Modify the user capabilities so that nobody can edit external events.
* *
@ -99,7 +88,7 @@ class User_Interface {
if ( 'edit_post' === $cap && isset( $args[0] ) ) { if ( 'edit_post' === $cap && isset( $args[0] ) ) {
$post_id = $args[0]; $post_id = $args[0];
$post = get_post( $post_id ); $post = get_post( $post_id );
if ( $post && self::post_is_external_event_post( $post ) ) { if ( $post && Event_Sources::is_cached_external_event_post( $post ) ) {
// Deny editing by returning 'do_not_allow'. // Deny editing by returning 'do_not_allow'.
return array( 'do_not_allow' ); return array( 'do_not_allow' );
} }

View file

@ -12,6 +12,9 @@ namespace Event_Bridge_For_ActivityPub;
use Activitypub\Model\Blog; use Activitypub\Model\Blog;
use Event_Bridge_For_ActivityPub\ActivityPub\Collection\Event_Sources as Event_Sources_Collection; use Event_Bridge_For_ActivityPub\ActivityPub\Collection\Event_Sources as Event_Sources_Collection;
use Event_Bridge_For_ActivityPub\Activitypub\Transmogrifier\GatherPress; use Event_Bridge_For_ActivityPub\Activitypub\Transmogrifier\GatherPress;
use Event_Bridge_For_ActivityPub\Activitypub\Handler;
use Event_Bridge_For_ActivityPub\Admin\User_Interface;
use function Activitypub\get_remote_metadata_by_actor; use function Activitypub\get_remote_metadata_by_actor;
use function Activitypub\is_activitypub_request; use function Activitypub\is_activitypub_request;
@ -22,6 +25,22 @@ use function Activitypub\is_activitypub_request;
* @package Event_Bridge_For_ActivityPub * @package Event_Bridge_For_ActivityPub
*/ */
class Event_Sources { class Event_Sources {
/**
* Init.
*/
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_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' );
}
\add_action( 'event_bridge_for_activitypub_event_sources_clear_cache', array( self::class, 'clear_cache' ) );
\add_filter( 'activitypub_rest_following', array( self::class, 'add_event_sources_to_following_collection' ), 10, 2 );
\add_filter( 'template_include', array( self::class, 'redirect_activitypub_requests_for_cached_external_events' ), 100 );
}
/** /**
* Get metadata of ActivityPub Actor by ID/URL. * Get metadata of ActivityPub Actor by ID/URL.
* *
@ -88,10 +107,7 @@ class Event_Sources {
if ( $disabled || ! $post ) { if ( $disabled || ! $post ) {
return $disabled; return $disabled;
} }
if ( ! str_starts_with( \get_site_url(), $post->guid ) ) { return ! self::is_cached_external_event_post( $post );
return true;
}
return false;
} }
/** /**
@ -101,13 +117,10 @@ class Event_Sources {
* @return bool * @return bool
*/ */
public static function is_cached_external_event_post( $post ): bool { public static function is_cached_external_event_post( $post ): bool {
if ( 'gatherpress_event' !== $post->post_type ) { if ( get_post_meta( $post->id, 'event_bridge_for_activitypub_is_cached', true ) ) {
return false;
}
if ( ! str_starts_with( \get_site_url(), $post->guid ) ) {
return true; return true;
} }
return false; return false;
} }

View file

@ -15,6 +15,7 @@ namespace Event_Bridge_For_ActivityPub;
defined( 'ABSPATH' ) || exit; // @codeCoverageIgnore defined( 'ABSPATH' ) || exit; // @codeCoverageIgnore
use Activitypub\Activity\Extended_Object\Event; use Activitypub\Activity\Extended_Object\Event;
use Event_Bridge_For_ActivityPub\Integrations\Feature_Event_Sources;
/** /**
* Class responsible for the ActivityPui Event Extension related Settings. * Class responsible for the ActivityPui Event Extension related Settings.
@ -155,7 +156,7 @@ class Settings {
$valid_options = array(); $valid_options = array();
foreach ( $active_event_plugins as $active_event_plugin ) { foreach ( $active_event_plugins as $active_event_plugin ) {
if ( $active_event_plugin->supports_event_sources() ) { if ( $active_event_plugin instanceof Feature_Event_Sources ) {
$full_class = $active_event_plugin::class; $full_class = $active_event_plugin::class;
$valid_options[] = substr( $full_class, strrpos( $full_class, '\\' ) + 1 ); $valid_options[] = substr( $full_class, strrpos( $full_class, '\\' ) + 1 );
} }

View file

@ -15,14 +15,14 @@ namespace Event_Bridge_For_ActivityPub;
// Exit if accessed directly. // Exit if accessed directly.
defined( 'ABSPATH' ) || exit; // @codeCoverageIgnore defined( 'ABSPATH' ) || exit; // @codeCoverageIgnore
use Event_Bridge_For_ActivityPub\ActivityPub\Collection\Event_Sources as Event_Sources_Collection; use Event_Bridge_For_ActivityPub\ActivityPub\Transmogrifier\Base as Transmogrifier_Base;
use Event_Bridge_For_ActivityPub\ActivityPub\Handler;
use Event_Bridge_For_ActivityPub\Admin\Event_Plugin_Admin_Notices; 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\General_Admin_Notices;
use Event_Bridge_For_ActivityPub\Admin\Health_Check; use Event_Bridge_For_ActivityPub\Admin\Health_Check;
use Event_Bridge_For_ActivityPub\Admin\Settings_Page; use Event_Bridge_For_ActivityPub\Admin\Settings_Page;
use Event_Bridge_For_ActivityPub\Admin\User_Interface; use Event_Bridge_For_ActivityPub\Admin\User_Interface;
use Event_Bridge_For_ActivityPub\Integrations\Event_Plugin; use Event_Bridge_For_ActivityPub\Integrations\Event_Plugin;
use Event_Bridge_For_ActivityPub\Integrations\Feature_Event_Sources;
use function Activitypub\is_user_type_disabled; use function Activitypub\is_user_type_disabled;
@ -127,16 +127,16 @@ class Setup {
* *
* @var array * @var array
*/ */
private const EVENT_PLUGIN_CLASSES = array( private const EVENT_PLUGIN_INTEGRATIONS = array(
'\Event_Bridge_For_ActivityPub\Integrations\Events_Manager', \Event_Bridge_For_ActivityPub\Integrations\Events_Manager::class,
'\Event_Bridge_For_ActivityPub\Integrations\GatherPress', \Event_Bridge_For_ActivityPub\Integrations\GatherPress::class,
'\Event_Bridge_For_ActivityPub\Integrations\The_Events_Calendar', \Event_Bridge_For_ActivityPub\Integrations\The_Events_Calendar::class,
'\Event_Bridge_For_ActivityPub\Integrations\VS_Event_List', \Event_Bridge_For_ActivityPub\Integrations\VS_Event_List::class,
'\Event_Bridge_For_ActivityPub\Integrations\WP_Event_Manager', \Event_Bridge_For_ActivityPub\Integrations\WP_Event_Manager::class,
'\Event_Bridge_For_ActivityPub\Integrations\Eventin', \Event_Bridge_For_ActivityPub\Integrations\Eventin::class,
'\Event_Bridge_For_ActivityPub\Integrations\Modern_Events_Calendar_Lite', \Event_Bridge_For_ActivityPub\Integrations\Modern_Events_Calendar_Lite::class,
'\Event_Bridge_For_ActivityPub\Integrations\EventPrime', \Event_Bridge_For_ActivityPub\Integrations\EventPrime::class,
'\Event_Bridge_For_ActivityPub\Integrations\Event_Organiser', \Event_Bridge_For_ActivityPub\Integrations\Event_Organiser::class,
); );
/** /**
@ -169,13 +169,18 @@ class Setup {
$all_plugins = array_merge( get_plugins(), get_mu_plugins() ); $all_plugins = array_merge( get_plugins(), get_mu_plugins() );
$active_event_plugins = array(); $active_event_plugins = array();
foreach ( self::EVENT_PLUGIN_CLASSES as $event_plugin_class ) { foreach ( self::EVENT_PLUGIN_INTEGRATIONS as $event_plugin_integration ) {
$event_plugin_file = call_user_func( array( $event_plugin_class, 'get_relative_plugin_file' ) ); // Get the filename of the main plugin file of the event plugin (relative to the plugin dir).
$event_plugin_file = $event_plugin_integration::get_relative_plugin_file();
// This check should not be needed, but does not hurt.
if ( ! $event_plugin_file ) { if ( ! $event_plugin_file ) {
continue; continue;
} }
// Check if plugin is present on disk and is activated.
if ( array_key_exists( $event_plugin_file, $all_plugins ) && \is_plugin_active( $event_plugin_file ) ) { if ( array_key_exists( $event_plugin_file, $all_plugins ) && \is_plugin_active( $event_plugin_file ) ) {
$active_event_plugins[ $event_plugin_file ] = new $event_plugin_class(); $active_event_plugins[ $event_plugin_file ] = new $event_plugin_integration();
} }
} }
set_transient( 'event_bridge_for_activitypub_active_event_plugins', $active_event_plugins ); set_transient( 'event_bridge_for_activitypub_active_event_plugins', $active_event_plugins );
@ -191,12 +196,9 @@ class Setup {
public static function detect_event_plugins_supporting_event_sources(): array { public static function detect_event_plugins_supporting_event_sources(): array {
$plugins_supporting_event_sources = array(); $plugins_supporting_event_sources = array();
foreach ( self::EVENT_PLUGIN_CLASSES as $event_plugin_class ) { foreach ( self::EVENT_PLUGIN_INTEGRATIONS as $event_plugin_integration ) {
if ( ! class_exists( $event_plugin_class ) || ! method_exists( $event_plugin_class, 'get_plugin_file' ) ) { if ( $event_plugin_integration instanceof Feature_Event_Sources ) {
continue; $plugins_supporting_event_sources[] = new $event_plugin_integration();
}
if ( call_user_func( array( $event_plugin_class, 'supports_event_sources' ) ) ) {
$plugins_supporting_event_sources[] = new $event_plugin_class();
} }
} }
return $plugins_supporting_event_sources; return $plugins_supporting_event_sources;
@ -241,39 +243,8 @@ class Setup {
} }
if ( ! is_user_type_disabled( 'blog' ) && get_option( 'event_bridge_for_activitypub_event_sources_active' ) ) { if ( ! is_user_type_disabled( 'blog' ) && get_option( 'event_bridge_for_activitypub_event_sources_active' ) ) {
add_action( 'init', array( Event_Sources_Collection::class, 'init' ) ); Event_Sources::init();
add_action( 'activitypub_register_handlers', array( Handler::class, 'register_handlers' ) );
add_action( 'admin_init', array( User_Interface::class, 'init' ) );
add_filter( 'allowed_redirect_hosts', array( Event_Sources_Collection::class, 'add_event_sources_hosts_to_allowed_redirect_hosts' ) );
add_filter( 'activitypub_is_post_disabled', array( Event_Sources::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' );
} }
add_action( 'event_bridge_for_activitypub_event_sources_clear_cache', array( Event_Sources::class, 'clear_cache' ) );
add_filter( 'activitypub_rest_following', array( Event_Sources::class, 'add_event_sources_to_following_collection' ), 10, 2 );
add_filter(
'gatherpress_force_online_event_link',
function ( $force_online_event_link ) {
// Get the current post object.
$post = get_post();
// Check if we are in a valid context and the post type is 'gatherpress'.
if ( $post && 'gatherpress_event' === $post->post_type ) {
// Add your custom logic here to decide whether to force the link.
// For example, force it only if a specific meta field exists.
if ( get_post_meta( $post->ID, 'event_bridge_for_activitypub_is_cached', true ) ) {
return true; // Force the online event link.
}
}
return $force_online_event_link; // Default behavior.
},
10,
1
);
}
\add_filter( 'template_include', array( \Event_Bridge_For_ActivityPub\Event_Sources::class, 'redirect_activitypub_requests_for_cached_external_events' ), 100 );
add_filter( 'activitypub_transformer', array( $this, 'register_activitypub_event_transformer' ), 10, 3 ); add_filter( 'activitypub_transformer', array( $this, 'register_activitypub_event_transformer' ), 10, 3 );
} }
@ -348,10 +319,7 @@ class Setup {
// Get the transformer for a specific event plugins event-post type. // Get the transformer for a specific event plugins event-post type.
foreach ( $this->active_event_plugins as $event_plugin ) { foreach ( $this->active_event_plugins as $event_plugin ) {
if ( $wp_object->post_type === $event_plugin->get_post_type() ) { if ( $wp_object->post_type === $event_plugin->get_post_type() ) {
$transformer_class = $event_plugin::get_activitypub_event_transformer_class(); return $event_plugin::get_activitypub_event_transformer( $wp_object, $event_plugin::get_event_category_taxonomy() );
if ( class_exists( $transformer_class ) ) {
return new $transformer_class( $wp_object, $event_plugin::get_event_category_taxonomy() );
}
} }
} }
@ -416,9 +384,9 @@ class Setup {
/** /**
* Get the transmogrifier class. * Get the transmogrifier class.
* *
* Retrieves the appropriate transmogrifier class based on the active event plugin. * Retrieves the appropriate transmogrifier class based on the active event plugins and settings.
* *
* @return string|null The transmogrifier class name or null if not available. * @return Transmogrifier_Base|null The transmogrifier class name or null if not available.
*/ */
public static function get_transmogrifier() { public static function get_transmogrifier() {
// Retrieve singleton instance. // Retrieve singleton instance.
@ -428,7 +396,7 @@ class Setup {
$event_sources_active = (bool) get_option( 'event_bridge_for_activitypub_event_sources_active', false ); $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', '' ); $event_plugin = get_option( 'event_bridge_for_activitypub_plugin_used_for_event_source_feature', '' );
// Bail out if event sources are not active or no plugin is specified. // Exit if event sources are not active or no plugin is specified.
if ( ! $event_sources_active || empty( $event_plugin ) ) { if ( ! $event_sources_active || empty( $event_plugin ) ) {
return null; return null;
} }
@ -439,12 +407,12 @@ class Setup {
// Loop through active plugins to find a match. // Loop through active plugins to find a match.
foreach ( $active_event_plugins as $active_event_plugin ) { foreach ( $active_event_plugins as $active_event_plugin ) {
// Retrieve the class name of the active plugin. // Retrieve the class name of the active plugin.
$active_plugin_class = get_class( $active_event_plugin ); $active_plugin_class_name = get_class( $active_event_plugin );
// Check if the active plugin class name contains the specified event plugin name. // Check if the active plugin class name contains the specified event plugin name.
if ( false !== strpos( $active_plugin_class, $event_plugin ) ) { if ( false !== strpos( $active_plugin_class_name, $event_plugin ) ) {
// Return the transmogrifier class provided by the plugin. // Return the transmogrifier class provided by the plugin.
return $active_event_plugin->get_transmogrifier_class(); return $active_event_plugin->get_transmogrifier();
} }
} }

View file

@ -11,6 +11,8 @@
namespace Event_Bridge_For_ActivityPub\Integrations; namespace Event_Bridge_For_ActivityPub\Integrations;
use Event_Bridge_For_ActivityPub\ActivityPub\Transformer\Event_Organiser as Event_Organiser_Transformer;
// Exit if accessed directly. // Exit if accessed directly.
defined( 'ABSPATH' ) || exit; // @codeCoverageIgnore defined( 'ABSPATH' ) || exit; // @codeCoverageIgnore
@ -21,7 +23,7 @@ defined( 'ABSPATH' ) || exit; // @codeCoverageIgnore
* *
* @since 1.0.0 * @since 1.0.0
*/ */
final class Event_Organiser extends Event_Plugin { final class Event_Organiser extends Event_Plugin_Integration {
/** /**
* Returns the full plugin file. * Returns the full plugin file.
* *
@ -49,15 +51,6 @@ final class Event_Organiser extends Event_Plugin {
return array( 'event-organiser' ); return array( 'event-organiser' );
} }
/**
* Returns the ActivityPub transformer class.
*
* @return string
*/
public static function get_activitypub_transformer_class_name(): string {
return 'Event_Organiser';
}
/** /**
* Returns the taxonomy used for the plugin's event categories. * Returns the taxonomy used for the plugin's event categories.
* *
@ -66,4 +59,14 @@ final class Event_Organiser extends Event_Plugin {
public static function get_event_category_taxonomy(): string { public static function get_event_category_taxonomy(): string {
return 'event-category'; return 'event-category';
} }
/**
* Returns the ActivityPub transformer for a Event_Organiser event post.
*
* @param WP_Post $post The WordPress post object of the Event.
* @return Event_Organiser_Transformer
*/
public static function get_activitypub_event_transformer( $post ): Event_Organiser_Transformer {
return new Event_Organiser_Transformer( $post, self::get_event_category_taxonomy() );
}
} }

View file

@ -11,11 +11,13 @@
namespace Event_Bridge_For_ActivityPub\Integrations; namespace Event_Bridge_For_ActivityPub\Integrations;
use Event_Bridge_For_ActivityPub\Activitypub\Transformer\Event as Event_Transformer; use Event_Bridge_For_ActivityPub\Activitypub\Transformer\Event as ActivityPub_Event_Transformer;
// Exit if accessed directly. // Exit if accessed directly.
defined( 'ABSPATH' ) || exit; // @codeCoverageIgnore defined( 'ABSPATH' ) || exit; // @codeCoverageIgnore
require_once EVENT_BRIDGE_FOR_ACTIVITYPUB_PLUGIN_DIR . 'includes/integrations/interface-feature-event-sources.php';
/** /**
* Interface for a supported event plugin. * Interface for a supported event plugin.
* *
@ -23,7 +25,7 @@ defined( 'ABSPATH' ) || exit; // @codeCoverageIgnore
* *
* @since 1.0.0 * @since 1.0.0
*/ */
abstract class Event_Plugin { abstract class Event_Plugin_Integration {
/** /**
* Returns the plugin file relative to the plugins dir. * Returns the plugin file relative to the plugins dir.
* *
@ -45,6 +47,14 @@ abstract class Event_Plugin {
*/ */
abstract public static function get_event_category_taxonomy(): string; abstract public static function get_event_category_taxonomy(): string;
/**
* Returns the Activitypub transformer for the event plugins event post type.
*
* @param WP_Post $post The WordPress post object of the Event.
* @return ActivityPub_Event_Transformer
*/
abstract public static function get_activitypub_event_transformer( $post ): ActivityPub_Event_Transformer;
/** /**
* Returns the IDs of the admin pages of the plugin. * Returns the IDs of the admin pages of the plugin.
* *
@ -54,19 +64,10 @@ abstract class Event_Plugin {
return array(); return array();
} }
/**
* By default event sources are not supported by an event plugin integration.
*
* @return bool True if event sources are supported.
*/
public static function supports_event_sources(): bool {
return false;
}
/** /**
* Get the plugins name from the main plugin-file's top-level-file-comment. * Get the plugins name from the main plugin-file's top-level-file-comment.
*/ */
final public static function get_plugin_name(): string { public static function get_plugin_name(): string {
$all_plugins = array_merge( get_plugins(), get_mu_plugins() ); $all_plugins = array_merge( get_plugins(), get_mu_plugins() );
if ( isset( $all_plugins[ static::get_relative_plugin_file() ]['Name'] ) ) { if ( isset( $all_plugins[ static::get_relative_plugin_file() ]['Name'] ) ) {
return $all_plugins[ static::get_relative_plugin_file() ]['Name']; return $all_plugins[ static::get_relative_plugin_file() ]['Name'];
@ -88,21 +89,4 @@ abstract class Event_Plugin {
return $is_event_plugins_edit_page || $is_event_plugins_settings_page; return $is_event_plugins_edit_page || $is_event_plugins_settings_page;
} }
/**
* Returns the Activitypub transformer for the event plugins event post type.
*/
public static function get_activitypub_event_transformer_class(): string {
return str_replace( 'Integrations', 'Activitypub\Transformer', static::class );
}
/**
* Returns the class used for transmogrifying an Event (ActivityStreams to Event plugin transformation).
*/
public static function get_transmogrifier_class(): ?string {
if ( ! static::supports_event_sources() ) {
return null;
}
return str_replace( 'Integrations', 'Activitypub\Transmogrifier', static::class );
}
} }

View file

@ -11,6 +11,8 @@
namespace Event_Bridge_For_ActivityPub\Integrations; namespace Event_Bridge_For_ActivityPub\Integrations;
use Event_Bridge_For_ActivityPub\ActivityPub\Transformer\Eventin as Eventin_Transformer;
// Exit if accessed directly. // Exit if accessed directly.
defined( 'ABSPATH' ) || exit; // @codeCoverageIgnore defined( 'ABSPATH' ) || exit; // @codeCoverageIgnore
@ -21,7 +23,7 @@ defined( 'ABSPATH' ) || exit; // @codeCoverageIgnore
* *
* @since 1.0.0 * @since 1.0.0
*/ */
final class Eventin extends Event_plugin { final class Eventin extends Event_Plugin_Integration {
/** /**
* Returns the full plugin file. * Returns the full plugin file.
* *
@ -57,4 +59,14 @@ final class Eventin extends Event_plugin {
public static function get_event_category_taxonomy(): string { public static function get_event_category_taxonomy(): string {
return 'etn_category'; return 'etn_category';
} }
/**
* Returns the ActivityPub transformer for a Eventin event post.
*
* @param WP_Post $post The WordPress post object of the Event.
* @return Eventin_Transformer
*/
public static function get_activitypub_event_transformer( $post ): Eventin_Transformer {
return new Eventin_Transformer( $post, self::get_event_category_taxonomy() );
}
} }

View file

@ -9,6 +9,8 @@
namespace Event_Bridge_For_ActivityPub\Integrations; namespace Event_Bridge_For_ActivityPub\Integrations;
use Event_Bridge_For_ActivityPub\ActivityPub\Transformer\EventPrime as EventPrime_Transformer;
use Activitypub\Signature; use Activitypub\Signature;
use Eventprime_Basic_Functions; use Eventprime_Basic_Functions;
@ -20,7 +22,7 @@ defined( 'ABSPATH' ) || exit; // @codeCoverageIgnore
* *
* @since 1.0.0 * @since 1.0.0
*/ */
final class EventPrime extends Event_Plugin { final class EventPrime extends Event_Plugin_Integration {
/** /**
* Add filter for the template inclusion. * Add filter for the template inclusion.
*/ */
@ -56,12 +58,13 @@ final class EventPrime extends Event_Plugin {
} }
/** /**
* Returns the ActivityPub transformer class. * Returns the ActivityPub transformer for a EventPrime event post.
* *
* @return string * @param WP_Post $post The WordPress post object of the Event.
* @return EventPrime_Transformer
*/ */
public static function get_activitypub_transformer_class_name(): string { public static function get_activitypub_event_transformer( $post ): EventPrime_Transformer {
return 'EventPrime'; return new EventPrime_Transformer( $post, self::get_event_category_taxonomy() );
} }
/** /**

View file

@ -11,6 +11,8 @@
namespace Event_Bridge_For_ActivityPub\Integrations; namespace Event_Bridge_For_ActivityPub\Integrations;
use Event_Bridge_For_ActivityPub\ActivityPub\Transformer\Events_Manager as Events_Manager_Transformer;
// Exit if accessed directly. // Exit if accessed directly.
defined( 'ABSPATH' ) || exit; // @codeCoverageIgnore defined( 'ABSPATH' ) || exit; // @codeCoverageIgnore
@ -21,7 +23,7 @@ defined( 'ABSPATH' ) || exit; // @codeCoverageIgnore
* *
* @since 1.0.0 * @since 1.0.0
*/ */
final class Events_Manager extends Event_Plugin { final class Events_Manager extends Event_Plugin_Integration {
/** /**
* Returns the full plugin file. * Returns the full plugin file.
* *
@ -57,4 +59,14 @@ final class Events_Manager extends Event_Plugin {
public static function get_event_category_taxonomy(): string { public static function get_event_category_taxonomy(): string {
return defined( 'EM_TAXONOMY_CATEGORY' ) ? constant( 'EM_TAXONOMY_CATEGORY' ) : 'event-categories'; return defined( 'EM_TAXONOMY_CATEGORY' ) ? constant( 'EM_TAXONOMY_CATEGORY' ) : 'event-categories';
} }
/**
* Returns the ActivityPub transformer for a Events_Manager event post.
*
* @param WP_Post $post The WordPress post object of the Event.
* @return Events_Manager_Transformer
*/
public static function get_activitypub_event_transformer( $post ): Events_Manager_Transformer {
return new Events_Manager_Transformer( $post, self::get_event_category_taxonomy() );
}
} }

View file

@ -11,6 +11,9 @@
namespace Event_Bridge_For_ActivityPub\Integrations; namespace Event_Bridge_For_ActivityPub\Integrations;
use Event_Bridge_For_ActivityPub\ActivityPub\Transformer\GatherPress as GatherPress_Transformer;
use Event_Bridge_For_ActivityPub\ActivityPub\Transmogrifier\GatherPress as GatherPress_Transmogrifier;
// Exit if accessed directly. // Exit if accessed directly.
defined( 'ABSPATH' ) || exit; // @codeCoverageIgnore defined( 'ABSPATH' ) || exit; // @codeCoverageIgnore
@ -21,7 +24,7 @@ defined( 'ABSPATH' ) || exit; // @codeCoverageIgnore
* *
* @since 1.0.0 * @since 1.0.0
*/ */
final class GatherPress extends Event_Plugin { final class GatherPress extends Event_Plugin_Integration implements Feature_Event_Sources {
/** /**
* Returns the full plugin file. * Returns the full plugin file.
* *
@ -49,15 +52,6 @@ final class GatherPress extends Event_Plugin {
return array( class_exists( '\GatherPress\Core\Utility' ) ? \GatherPress\Core\Utility::prefix_key( 'general' ) : 'gatherpress_general' ); return array( class_exists( '\GatherPress\Core\Utility' ) ? \GatherPress\Core\Utility::prefix_key( 'general' ) : 'gatherpress_general' );
} }
/**
* Returns the ActivityPub transformer class.
*
* @return string
*/
public static function get_activitypub_transformer_class_name(): string {
return 'GatherPress';
}
/** /**
* Returns the taxonomy used for the plugin's event categories. * Returns the taxonomy used for the plugin's event categories.
* *
@ -68,11 +62,67 @@ final class GatherPress extends Event_Plugin {
} }
/** /**
* GatherPress supports the Event Sources feature. * Returns the ActivityPub transformer for a GatherPress event post.
* *
* @return bool True if event sources are supported. * @param WP_Post $post The WordPress post object of the Event.
* @return GatherPress_Transformer
*/ */
public static function supports_event_sources(): bool { public static function get_activitypub_event_transformer( $post ): GatherPress_Transformer {
return true; return new GatherPress_Transformer( $post, self::get_event_category_taxonomy() );
}
/**
* Returns the Transmogrifier for GatherPress.
*/
public static function get_transmogrifier(): GatherPress_Transmogrifier {
return new GatherPress_Transmogrifier();
}
/**
* Get a list of Post IDs of events that have ended.
*
* @param int $ended_before_time Filter: only get events that ended before that datetime as unix-time.
*
* @return array
*/
public static function get_cached_remote_events( $ended_before_time ): array {
global $wpdb;
$ended_before_time_string = gmdate( 'Y-m-d H:i:s', $ended_before_time );
$results = $wpdb->get_col(
$wpdb->prepare(
"SELECT post_id FROM {$wpdb->prefix}gatherpress_events WHERE datetime_end < %s",
$ended_before_time_string
)
);
return $results;
}
/**
* Init function.
*/
public static function init() {
\add_filter(
'gatherpress_force_online_event_link',
function ( $force_online_event_link ) {
// Get the current post object.
$post = get_post();
// Check if we are in a valid context and the post type is 'gatherpress'.
if ( $post && 'gatherpress_event' === $post->post_type ) {
// Add your custom logic here to decide whether to force the link.
// For example, force it only if a specific meta field exists.
if ( get_post_meta( $post->ID, 'event_bridge_for_activitypub_is_cached', true ) ) {
return true; // Force the online event link.
}
}
return $force_online_event_link; // Default behavior.
},
10,
1
);
} }
} }

View file

@ -11,6 +11,8 @@
namespace Event_Bridge_For_ActivityPub\Integrations; namespace Event_Bridge_For_ActivityPub\Integrations;
use Event_Bridge_For_ActivityPub\ActivityPub\Transformer\Modern_Events_Calendar_Lite as Modern_Events_Calendar_Lite_Transformer;
// Exit if accessed directly. // Exit if accessed directly.
defined( 'ABSPATH' ) || exit; // @codeCoverageIgnore defined( 'ABSPATH' ) || exit; // @codeCoverageIgnore
@ -21,7 +23,7 @@ defined( 'ABSPATH' ) || exit; // @codeCoverageIgnore
* *
* @since 1.0.0 * @since 1.0.0
*/ */
final class Modern_Events_Calendar_Lite extends Event_plugin { final class Modern_Events_Calendar_Lite extends Event_Plugin_Integration {
/** /**
* Returns the full plugin file. * Returns the full plugin file.
* *
@ -58,4 +60,14 @@ final class Modern_Events_Calendar_Lite extends Event_plugin {
public static function get_event_category_taxonomy(): string { public static function get_event_category_taxonomy(): string {
return 'mec_category'; return 'mec_category';
} }
/**
* Returns the ActivityPub transformer for a Modern_Events_Calendar_Lite event post.
*
* @param WP_Post $post The WordPress post object of the Event.
* @return Modern_Events_Calendar_Lite_Transformer
*/
public static function get_activitypub_event_transformer( $post ): Modern_Events_Calendar_Lite_Transformer {
return new Modern_Events_Calendar_Lite_Transformer( $post, self::get_event_category_taxonomy() );
}
} }

View file

@ -11,6 +11,9 @@
namespace Event_Bridge_For_ActivityPub\Integrations; namespace Event_Bridge_For_ActivityPub\Integrations;
use Event_Bridge_For_ActivityPub\ActivityPub\Transformer\The_Events_Calendar as The_Events_Calendar_Transformer;
use Event_Bridge_For_ActivityPub\ActivityPub\Transmogrifier\The_Events_Calendar as The_Events_Calendar_Transmogrifier;
// Exit if accessed directly. // Exit if accessed directly.
defined( 'ABSPATH' ) || exit; // @codeCoverageIgnore defined( 'ABSPATH' ) || exit; // @codeCoverageIgnore
@ -21,7 +24,7 @@ defined( 'ABSPATH' ) || exit; // @codeCoverageIgnore
* *
* @since 1.0.0 * @since 1.0.0
*/ */
final class The_Events_Calendar extends Event_plugin { final class The_Events_Calendar extends Event_plugin_Integration implements Feature_Event_Sources {
/** /**
* Returns the full plugin file. * Returns the full plugin file.
* *
@ -40,6 +43,25 @@ final class The_Events_Calendar extends Event_plugin {
return class_exists( '\Tribe__Events__Main' ) ? \Tribe__Events__Main::POSTTYPE : 'tribe_event'; return class_exists( '\Tribe__Events__Main' ) ? \Tribe__Events__Main::POSTTYPE : 'tribe_event';
} }
/**
* Returns the taxonomy used for the plugin's event categories.
*
* @return string
*/
public static function get_event_category_taxonomy(): string {
return class_exists( '\Tribe__Events__Main' ) ? \Tribe__Events__Main::TAXONOMY : 'tribe_events_cat';
}
/**
* Returns the ActivityPub transformer for a The_Events_Calendar event post.
*
* @param WP_Post $post The WordPress post object of the Event.
* @return The_Events_Calendar_Transformer
*/
public static function get_activitypub_event_transformer( $post ): The_Events_Calendar_Transformer {
return new The_Events_Calendar_Transformer( $post, self::get_event_category_taxonomy() );
}
/** /**
* Returns the IDs of the admin pages of the plugin. * Returns the IDs of the admin pages of the plugin.
* *
@ -55,11 +77,20 @@ final class The_Events_Calendar extends Event_plugin {
} }
/** /**
* Returns the taxonomy used for the plugin's event categories. * Returns the Transmogrifier for The_Events_Calendar.
*
* @return string
*/ */
public static function get_event_category_taxonomy(): string { public static function get_transmogrifier(): The_Events_Calendar_Transmogrifier {
return class_exists( '\Tribe__Events__Main' ) ? \Tribe__Events__Main::TAXONOMY : 'tribe_events_cat'; return new The_Events_Calendar_Transmogrifier();
}
/**
* Get a list of Post IDs of events that have ended.
*
* @param int $ended_before_time Filter: only get events that ended before that datetime as unix-time.
*
* @return array
*/
public static function get_cached_remote_events( $ended_before_time ): array {
return array();
} }
} }

View file

@ -12,7 +12,7 @@
namespace Event_Bridge_For_ActivityPub\Integrations; namespace Event_Bridge_For_ActivityPub\Integrations;
use Event_Bridge_For_ActivityPub\Event_Plugins; use Event_Bridge_For_ActivityPub\ActivityPub\Transformer\VS_Event_List as VS_Event_List_Transformer;
// Exit if accessed directly. // Exit if accessed directly.
defined( 'ABSPATH' ) || exit; // @codeCoverageIgnore defined( 'ABSPATH' ) || exit; // @codeCoverageIgnore
@ -24,7 +24,7 @@ defined( 'ABSPATH' ) || exit; // @codeCoverageIgnore
* *
* @since 1.0.0 * @since 1.0.0
*/ */
final class VS_Event_List extends Event_Plugin { final class VS_Event_List extends Event_Plugin_Integration {
/** /**
* Returns the full plugin file. * Returns the full plugin file.
* *
@ -52,15 +52,6 @@ final class VS_Event_List extends Event_Plugin {
return array( 'settings_page_vsel' ); return array( 'settings_page_vsel' );
} }
/**
* Returns the ActivityPub transformer class.
*
* @return string
*/
public static function get_activitypub_transformer_class_name(): string {
return 'VS_Event';
}
/** /**
* Returns the taxonomy used for the plugin's event categories. * Returns the taxonomy used for the plugin's event categories.
* *
@ -69,4 +60,14 @@ final class VS_Event_List extends Event_Plugin {
public static function get_event_category_taxonomy(): string { public static function get_event_category_taxonomy(): string {
return 'event_cat'; return 'event_cat';
} }
/**
* Returns the ActivityPub transformer for a VS_Event_List event post.
*
* @param WP_Post $post The WordPress post object of the Event.
* @return VS_Event_List_Transformer
*/
public static function get_activitypub_event_transformer( $post ): VS_Event_List_Transformer {
return new VS_Event_List_Transformer( $post, self::get_event_category_taxonomy() );
}
} }

View file

@ -12,7 +12,7 @@
namespace Event_Bridge_For_ActivityPub\Integrations; namespace Event_Bridge_For_ActivityPub\Integrations;
use Event_Bridge_For_ActivityPub\Integrations\Event_Plugin; use Event_Bridge_For_ActivityPub\ActivityPub\Transformer\WP_Event_Manager as WP_Event_Manager_Transformer;
// Exit if accessed directly. // Exit if accessed directly.
defined( 'ABSPATH' ) || exit; // @codeCoverageIgnore defined( 'ABSPATH' ) || exit; // @codeCoverageIgnore
@ -24,7 +24,7 @@ defined( 'ABSPATH' ) || exit; // @codeCoverageIgnore
* *
* @since 1.0.0 * @since 1.0.0
*/ */
final class WP_Event_Manager extends Event_Plugin { final class WP_Event_Manager extends Event_Plugin_Integration {
/** /**
* Returns the full plugin file. * Returns the full plugin file.
* *
@ -52,15 +52,6 @@ final class WP_Event_Manager extends Event_Plugin {
return array( 'event-manager-settings' ); return array( 'event-manager-settings' );
} }
/**
* Returns the ActivityPub transformer class.
*
* @return string
*/
public static function get_activitypub_transformer_class_name(): string {
return 'WP_Event_Manager';
}
/** /**
* Returns the taxonomy used for the plugin's event categories. * Returns the taxonomy used for the plugin's event categories.
* *
@ -69,4 +60,14 @@ final class WP_Event_Manager extends Event_Plugin {
public static function get_event_category_taxonomy(): string { public static function get_event_category_taxonomy(): string {
return 'event_listing_category'; return 'event_listing_category';
} }
/**
* Returns the ActivityPub transformer for a WP_Event_Manager event post.
*
* @param WP_Post $post The WordPress post object of the Event.
* @return WP_Event_Manager_Transformer
*/
public static function get_activitypub_event_transformer( $post ): WP_Event_Manager_Transformer {
return new WP_Event_Manager_Transformer( $post, self::get_event_category_taxonomy() );
}
} }

View file

@ -0,0 +1,42 @@
<?php
/**
* Interface for defining supported Event Plugins.
*
* Basic information that each supported event needs for this plugin to work.
*
* @package Event_Bridge_For_ActivityPub
* @since 1.0.0
* @license AGPL-3.0-or-later
*/
namespace Event_Bridge_For_ActivityPub\Integrations;
use Event_Bridge_For_ActivityPub\ActivityPub\Transmogrifier\Base as Transmogrifier;
// Exit if accessed directly.
defined( 'ABSPATH' ) || exit; // @codeCoverageIgnore
/**
* Interface for an event plugin integration that supports the Event Sources feature.
*
* @since 1.0.0
*/
interface Feature_Event_Sources {
/**
* Returns the plugin file relative to the plugins dir.
*
* @return Transmogrifier
*/
public static function get_transmogrifier(): Transmogrifier;
/**
* Retrieves a list of post IDs for cached remote events that have ended.
*
* Filters the events to include only those that ended before the specified timestamp.
*
* @param int $ended_before_time Unix timestamp. Only events ending before this time will be included.
*
* @return int[] List of post IDs for events that match the criteria.
*/
public static function get_cached_remote_events( $ended_before_time ): array;
}