diff --git a/docs/add_your_event_plugin.md b/docs/add_your_event_plugin.md index e104340..3b79f84 100644 --- a/docs/add_your_event_plugin.md +++ b/docs/add_your_event_plugin.md @@ -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 { ``` -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 - private const EVENT_PLUGIN_CLASSES = array( + private const EVENT_PLUGIN_INTEGRATIONS = array( ... '\Event_Bridge_For_ActivityPub\Integrations\My_Event_Plugin', ); diff --git a/includes/activitypub/collection/class-event-sources.php b/includes/activitypub/collection/class-event-sources.php index 6e9350b..79a5a93 100644 --- a/includes/activitypub/collection/class-event-sources.php +++ b/includes/activitypub/collection/class-event-sources.php @@ -31,6 +31,7 @@ class Event_Sources { */ public static function init() { 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_unfollow', array( self::class, 'activitypub_unfollow_actor' ), 10, 1 ); } diff --git a/includes/activitypub/handler/class-update.php b/includes/activitypub/handler/class-update.php index 6d77fef..06d046d 100644 --- a/includes/activitypub/handler/class-update.php +++ b/includes/activitypub/handler/class-update.php @@ -50,13 +50,12 @@ class Update { 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'] ); } } diff --git a/includes/activitypub/transmogrifier/class-base.php b/includes/activitypub/transmogrifier/class-base.php new file mode 100644 index 0000000..0aceb09 --- /dev/null +++ b/includes/activitypub/transmogrifier/class-base.php @@ -0,0 +1,339 @@ +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']; + } + } + } +} diff --git a/includes/activitypub/transmogrifier/class-gatherpress.php b/includes/activitypub/transmogrifier/class-gatherpress.php index d30eefb..bb0170d 100644 --- a/includes/activitypub/transmogrifier/class-gatherpress.php +++ b/includes/activitypub/transmogrifier/class-gatherpress.php @@ -11,10 +11,7 @@ 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; @@ -30,224 +27,7 @@ use GatherPress\Core\Event as GatherPress_Event; * * @since 1.0.0 */ -class GatherPress { - /** - * 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. - } - +class GatherPress extends Base { /** * Add tags to post. * @@ -271,94 +51,12 @@ class GatherPress { // Add the tags as terms to the post. 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; } - /** - * 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. * @@ -421,8 +119,10 @@ class GatherPress { /** * Save the ActivityPub event object as GatherPress Event. + * + * @return void */ - public function save() { + protected function save_event(): void { // Limit this as a safety measure. add_filter( 'wp_revisions_to_keep', array( self::class, 'revisions_to_keep' ) ); @@ -483,36 +183,4 @@ class GatherPress { // Limit this as a safety measure. 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; - } } diff --git a/includes/activitypub/transmogrifier/class-the-events-calendar.php b/includes/activitypub/transmogrifier/class-the-events-calendar.php new file mode 100644 index 0000000..ff08f15 --- /dev/null +++ b/includes/activitypub/transmogrifier/class-the-events-calendar.php @@ -0,0 +1,59 @@ +get_post_id_from_activitypub_id(); + + // Limit this as a safety measure. + remove_filter( 'wp_revisions_to_keep', array( self::class, 'revisions_to_keep' ) ); + } +} diff --git a/includes/admin/class-health-check.php b/includes/admin/class-health-check.php index 4c81c84..8364cac 100644 --- a/includes/admin/class-health-check.php +++ b/includes/admin/class-health-check.php @@ -97,7 +97,7 @@ class Health_Check { // Call the transformer Factory. $transformer = Transformer_Factory::get_transformer( $event_posts[0] ); // 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 ) { return true; } diff --git a/includes/admin/class-settings-page.php b/includes/admin/class-settings-page.php index 2986bb5..54f29ed 100644 --- a/includes/admin/class-settings-page.php +++ b/includes/admin/class-settings-page.php @@ -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\ActivityPub\Collection\Event_Sources as Event_Source_Collection; 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; /** @@ -171,15 +173,15 @@ class Settings_Page { case 'settings': $event_terms = array(); - foreach ( $event_plugins as $event_plugin ) { - $event_terms = array_merge( $event_terms, self::get_event_terms( $event_plugin ) ); + foreach ( $event_plugins as $event_plugin_integration ) { + $event_terms = array_merge( $event_terms, self::get_event_terms( $event_plugin_integration ) ); } $supports_event_sources = array(); - foreach ( $event_plugins as $event_plugin ) { - if ( $event_plugin->supports_event_sources() ) { - $supports_event_sources[ $event_plugin::class ] = $event_plugin->get_plugin_name(); + foreach ( $event_plugins as $event_plugin_integration ) { + if ( $event_plugin_integration instanceof Feature_Event_Sources && $event_plugin_integration instanceof Event_Plugin_Integration ) { + $supports_event_sources[ $event_plugin_integration::class ] = $event_plugin_integration::get_plugin_name(); } } diff --git a/includes/admin/class-user-interface.php b/includes/admin/class-user-interface.php index 9aa00da..9a3046f 100644 --- a/includes/admin/class-user-interface.php +++ b/includes/admin/class-user-interface.php @@ -9,6 +9,8 @@ namespace Event_Bridge_For_ActivityPub\Admin; +use Event_Bridge_For_ActivityPub\Event_Sources; + // Exit if accessed directly. defined( 'ABSPATH' ) || exit; // @codeCoverageIgnore @@ -59,7 +61,7 @@ class User_Interface { */ public static function row_actions( $actions, $post ) { // 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; } @@ -72,19 +74,6 @@ class User_Interface { 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. * @@ -99,7 +88,7 @@ class User_Interface { if ( 'edit_post' === $cap && isset( $args[0] ) ) { $post_id = $args[0]; $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'. return array( 'do_not_allow' ); } diff --git a/includes/class-event-sources.php b/includes/class-event-sources.php index 633e4ff..25520de 100644 --- a/includes/class-event-sources.php +++ b/includes/class-event-sources.php @@ -12,6 +12,9 @@ namespace Event_Bridge_For_ActivityPub; use Activitypub\Model\Blog; 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\Handler; +use Event_Bridge_For_ActivityPub\Admin\User_Interface; + use function Activitypub\get_remote_metadata_by_actor; use function Activitypub\is_activitypub_request; @@ -22,6 +25,22 @@ use function Activitypub\is_activitypub_request; * @package Event_Bridge_For_ActivityPub */ 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. * @@ -88,10 +107,7 @@ class Event_Sources { if ( $disabled || ! $post ) { return $disabled; } - if ( ! str_starts_with( \get_site_url(), $post->guid ) ) { - return true; - } - return false; + return ! self::is_cached_external_event_post( $post ); } /** @@ -101,13 +117,10 @@ class Event_Sources { * @return bool */ public static function is_cached_external_event_post( $post ): bool { - if ( 'gatherpress_event' !== $post->post_type ) { - return false; - } - - if ( ! str_starts_with( \get_site_url(), $post->guid ) ) { + if ( get_post_meta( $post->id, 'event_bridge_for_activitypub_is_cached', true ) ) { return true; } + return false; } diff --git a/includes/class-settings.php b/includes/class-settings.php index d2dfd3a..d1fa7b4 100644 --- a/includes/class-settings.php +++ b/includes/class-settings.php @@ -15,6 +15,7 @@ namespace Event_Bridge_For_ActivityPub; defined( 'ABSPATH' ) || exit; // @codeCoverageIgnore use Activitypub\Activity\Extended_Object\Event; +use Event_Bridge_For_ActivityPub\Integrations\Feature_Event_Sources; /** * Class responsible for the ActivityPui Event Extension related Settings. @@ -155,7 +156,7 @@ class Settings { $valid_options = array(); 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; $valid_options[] = substr( $full_class, strrpos( $full_class, '\\' ) + 1 ); } diff --git a/includes/class-setup.php b/includes/class-setup.php index b2cd172..e3d3ca3 100644 --- a/includes/class-setup.php +++ b/includes/class-setup.php @@ -15,14 +15,14 @@ namespace Event_Bridge_For_ActivityPub; // Exit if accessed directly. defined( 'ABSPATH' ) || exit; // @codeCoverageIgnore -use Event_Bridge_For_ActivityPub\ActivityPub\Collection\Event_Sources as Event_Sources_Collection; -use Event_Bridge_For_ActivityPub\ActivityPub\Handler; +use Event_Bridge_For_ActivityPub\ActivityPub\Transmogrifier\Base as Transmogrifier_Base; 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; use function Activitypub\is_user_type_disabled; @@ -127,16 +127,16 @@ class Setup { * * @var array */ - private const EVENT_PLUGIN_CLASSES = array( - '\Event_Bridge_For_ActivityPub\Integrations\Events_Manager', - '\Event_Bridge_For_ActivityPub\Integrations\GatherPress', - '\Event_Bridge_For_ActivityPub\Integrations\The_Events_Calendar', - '\Event_Bridge_For_ActivityPub\Integrations\VS_Event_List', - '\Event_Bridge_For_ActivityPub\Integrations\WP_Event_Manager', - '\Event_Bridge_For_ActivityPub\Integrations\Eventin', - '\Event_Bridge_For_ActivityPub\Integrations\Modern_Events_Calendar_Lite', - '\Event_Bridge_For_ActivityPub\Integrations\EventPrime', - '\Event_Bridge_For_ActivityPub\Integrations\Event_Organiser', + private const EVENT_PLUGIN_INTEGRATIONS = array( + \Event_Bridge_For_ActivityPub\Integrations\Events_Manager::class, + \Event_Bridge_For_ActivityPub\Integrations\GatherPress::class, + \Event_Bridge_For_ActivityPub\Integrations\The_Events_Calendar::class, + \Event_Bridge_For_ActivityPub\Integrations\VS_Event_List::class, + \Event_Bridge_For_ActivityPub\Integrations\WP_Event_Manager::class, + \Event_Bridge_For_ActivityPub\Integrations\Eventin::class, + \Event_Bridge_For_ActivityPub\Integrations\Modern_Events_Calendar_Lite::class, + \Event_Bridge_For_ActivityPub\Integrations\EventPrime::class, + \Event_Bridge_For_ActivityPub\Integrations\Event_Organiser::class, ); /** @@ -169,13 +169,18 @@ class Setup { $all_plugins = array_merge( get_plugins(), get_mu_plugins() ); $active_event_plugins = array(); - foreach ( self::EVENT_PLUGIN_CLASSES as $event_plugin_class ) { - $event_plugin_file = call_user_func( array( $event_plugin_class, 'get_relative_plugin_file' ) ); + foreach ( self::EVENT_PLUGIN_INTEGRATIONS as $event_plugin_integration ) { + // 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 ) { 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 ) ) { - $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 ); @@ -191,12 +196,9 @@ class Setup { public static function detect_event_plugins_supporting_event_sources(): array { $plugins_supporting_event_sources = array(); - foreach ( self::EVENT_PLUGIN_CLASSES as $event_plugin_class ) { - if ( ! class_exists( $event_plugin_class ) || ! method_exists( $event_plugin_class, 'get_plugin_file' ) ) { - continue; - } - if ( call_user_func( array( $event_plugin_class, 'supports_event_sources' ) ) ) { - $plugins_supporting_event_sources[] = new $event_plugin_class(); + foreach ( self::EVENT_PLUGIN_INTEGRATIONS as $event_plugin_integration ) { + if ( $event_plugin_integration instanceof Feature_Event_Sources ) { + $plugins_supporting_event_sources[] = new $event_plugin_integration(); } } 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' ) ) { - 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( '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 - ); + Event_Sources::init(); } - \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 ); } @@ -348,10 +319,7 @@ class Setup { // Get the transformer for a specific event plugins event-post type. foreach ( $this->active_event_plugins as $event_plugin ) { if ( $wp_object->post_type === $event_plugin->get_post_type() ) { - $transformer_class = $event_plugin::get_activitypub_event_transformer_class(); - if ( class_exists( $transformer_class ) ) { - return new $transformer_class( $wp_object, $event_plugin::get_event_category_taxonomy() ); - } + return $event_plugin::get_activitypub_event_transformer( $wp_object, $event_plugin::get_event_category_taxonomy() ); } } @@ -416,9 +384,9 @@ class Setup { /** * 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() { // Retrieve singleton instance. @@ -428,7 +396,7 @@ class Setup { $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', '' ); - // 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 ) ) { return null; } @@ -439,12 +407,12 @@ class Setup { // 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 = 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. - 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 $active_event_plugin->get_transmogrifier_class(); + return $active_event_plugin->get_transmogrifier(); } } diff --git a/includes/integrations/class-event-organiser.php b/includes/integrations/class-event-organiser.php index 0fbd190..d6c0348 100644 --- a/includes/integrations/class-event-organiser.php +++ b/includes/integrations/class-event-organiser.php @@ -11,6 +11,8 @@ namespace Event_Bridge_For_ActivityPub\Integrations; +use Event_Bridge_For_ActivityPub\ActivityPub\Transformer\Event_Organiser as Event_Organiser_Transformer; + // Exit if accessed directly. defined( 'ABSPATH' ) || exit; // @codeCoverageIgnore @@ -21,7 +23,7 @@ defined( 'ABSPATH' ) || exit; // @codeCoverageIgnore * * @since 1.0.0 */ -final class Event_Organiser extends Event_Plugin { +final class Event_Organiser extends Event_Plugin_Integration { /** * Returns the full plugin file. * @@ -49,15 +51,6 @@ final class Event_Organiser extends Event_Plugin { 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. * @@ -66,4 +59,14 @@ final class Event_Organiser extends Event_Plugin { public static function get_event_category_taxonomy(): string { 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() ); + } } diff --git a/includes/integrations/class-event-plugin.php b/includes/integrations/class-event-plugin-integration.php similarity index 73% rename from includes/integrations/class-event-plugin.php rename to includes/integrations/class-event-plugin-integration.php index fc21b04..252bb64 100644 --- a/includes/integrations/class-event-plugin.php +++ b/includes/integrations/class-event-plugin-integration.php @@ -11,11 +11,13 @@ 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. 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. * @@ -23,7 +25,7 @@ defined( 'ABSPATH' ) || exit; // @codeCoverageIgnore * * @since 1.0.0 */ -abstract class Event_Plugin { +abstract class Event_Plugin_Integration { /** * 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; + /** + * 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. * @@ -54,19 +64,10 @@ abstract class Event_Plugin { 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. */ - final public static function get_plugin_name(): string { + public static function get_plugin_name(): string { $all_plugins = array_merge( get_plugins(), get_mu_plugins() ); if ( isset( $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; } - - /** - * 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 ); - } } diff --git a/includes/integrations/class-eventin.php b/includes/integrations/class-eventin.php index 364a2a8..9cdb90c 100644 --- a/includes/integrations/class-eventin.php +++ b/includes/integrations/class-eventin.php @@ -11,6 +11,8 @@ namespace Event_Bridge_For_ActivityPub\Integrations; +use Event_Bridge_For_ActivityPub\ActivityPub\Transformer\Eventin as Eventin_Transformer; + // Exit if accessed directly. defined( 'ABSPATH' ) || exit; // @codeCoverageIgnore @@ -21,7 +23,7 @@ defined( 'ABSPATH' ) || exit; // @codeCoverageIgnore * * @since 1.0.0 */ -final class Eventin extends Event_plugin { +final class Eventin extends Event_Plugin_Integration { /** * Returns the full plugin file. * @@ -57,4 +59,14 @@ final class Eventin extends Event_plugin { public static function get_event_category_taxonomy(): string { 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() ); + } } diff --git a/includes/integrations/class-eventprime.php b/includes/integrations/class-eventprime.php index a50f7ff..abab86d 100644 --- a/includes/integrations/class-eventprime.php +++ b/includes/integrations/class-eventprime.php @@ -9,6 +9,8 @@ namespace Event_Bridge_For_ActivityPub\Integrations; +use Event_Bridge_For_ActivityPub\ActivityPub\Transformer\EventPrime as EventPrime_Transformer; + use Activitypub\Signature; use Eventprime_Basic_Functions; @@ -20,7 +22,7 @@ defined( 'ABSPATH' ) || exit; // @codeCoverageIgnore * * @since 1.0.0 */ -final class EventPrime extends Event_Plugin { +final class EventPrime extends Event_Plugin_Integration { /** * 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 { - return 'EventPrime'; + public static function get_activitypub_event_transformer( $post ): EventPrime_Transformer { + return new EventPrime_Transformer( $post, self::get_event_category_taxonomy() ); } /** diff --git a/includes/integrations/class-events-manager.php b/includes/integrations/class-events-manager.php index 9208126..099a4f7 100644 --- a/includes/integrations/class-events-manager.php +++ b/includes/integrations/class-events-manager.php @@ -11,6 +11,8 @@ namespace Event_Bridge_For_ActivityPub\Integrations; +use Event_Bridge_For_ActivityPub\ActivityPub\Transformer\Events_Manager as Events_Manager_Transformer; + // Exit if accessed directly. defined( 'ABSPATH' ) || exit; // @codeCoverageIgnore @@ -21,7 +23,7 @@ defined( 'ABSPATH' ) || exit; // @codeCoverageIgnore * * @since 1.0.0 */ -final class Events_Manager extends Event_Plugin { +final class Events_Manager extends Event_Plugin_Integration { /** * Returns the full plugin file. * @@ -57,4 +59,14 @@ final class Events_Manager extends Event_Plugin { public static function get_event_category_taxonomy(): string { 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() ); + } } diff --git a/includes/integrations/class-gatherpress.php b/includes/integrations/class-gatherpress.php index 8cc4393..1f446cc 100644 --- a/includes/integrations/class-gatherpress.php +++ b/includes/integrations/class-gatherpress.php @@ -11,6 +11,9 @@ 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. defined( 'ABSPATH' ) || exit; // @codeCoverageIgnore @@ -21,7 +24,7 @@ defined( 'ABSPATH' ) || exit; // @codeCoverageIgnore * * @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. * @@ -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' ); } - /** - * 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. * @@ -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 { - return true; + public static function get_activitypub_event_transformer( $post ): GatherPress_Transformer { + 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 + ); } } diff --git a/includes/integrations/class-modern-events-calendar-lite.php b/includes/integrations/class-modern-events-calendar-lite.php index 95bfef7..6abbd75 100644 --- a/includes/integrations/class-modern-events-calendar-lite.php +++ b/includes/integrations/class-modern-events-calendar-lite.php @@ -11,6 +11,8 @@ 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. defined( 'ABSPATH' ) || exit; // @codeCoverageIgnore @@ -21,7 +23,7 @@ defined( 'ABSPATH' ) || exit; // @codeCoverageIgnore * * @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. * @@ -58,4 +60,14 @@ final class Modern_Events_Calendar_Lite extends Event_plugin { public static function get_event_category_taxonomy(): string { 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() ); + } } diff --git a/includes/integrations/class-the-events-calendar.php b/includes/integrations/class-the-events-calendar.php index 6f6e17b..141d3fa 100644 --- a/includes/integrations/class-the-events-calendar.php +++ b/includes/integrations/class-the-events-calendar.php @@ -11,6 +11,9 @@ 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. defined( 'ABSPATH' ) || exit; // @codeCoverageIgnore @@ -21,7 +24,7 @@ defined( 'ABSPATH' ) || exit; // @codeCoverageIgnore * * @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. * @@ -40,6 +43,25 @@ final class The_Events_Calendar extends Event_plugin { 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. * @@ -55,11 +77,20 @@ final class The_Events_Calendar extends Event_plugin { } /** - * Returns the taxonomy used for the plugin's event categories. - * - * @return string + * Returns the Transmogrifier for The_Events_Calendar. */ - public static function get_event_category_taxonomy(): string { - return class_exists( '\Tribe__Events__Main' ) ? \Tribe__Events__Main::TAXONOMY : 'tribe_events_cat'; + public static function get_transmogrifier(): The_Events_Calendar_Transmogrifier { + 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(); } } diff --git a/includes/integrations/class-vs-event-list.php b/includes/integrations/class-vs-event-list.php index dc2747d..17715d8 100644 --- a/includes/integrations/class-vs-event-list.php +++ b/includes/integrations/class-vs-event-list.php @@ -12,7 +12,7 @@ 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. defined( 'ABSPATH' ) || exit; // @codeCoverageIgnore @@ -24,7 +24,7 @@ defined( 'ABSPATH' ) || exit; // @codeCoverageIgnore * * @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. * @@ -52,15 +52,6 @@ final class VS_Event_List extends Event_Plugin { 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. * @@ -69,4 +60,14 @@ final class VS_Event_List extends Event_Plugin { public static function get_event_category_taxonomy(): string { 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() ); + } } diff --git a/includes/integrations/class-wp-event-manager.php b/includes/integrations/class-wp-event-manager.php index 2adc473..7e7c3e3 100644 --- a/includes/integrations/class-wp-event-manager.php +++ b/includes/integrations/class-wp-event-manager.php @@ -12,7 +12,7 @@ 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. defined( 'ABSPATH' ) || exit; // @codeCoverageIgnore @@ -24,7 +24,7 @@ defined( 'ABSPATH' ) || exit; // @codeCoverageIgnore * * @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. * @@ -52,15 +52,6 @@ final class WP_Event_Manager extends Event_Plugin { 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. * @@ -69,4 +60,14 @@ final class WP_Event_Manager extends Event_Plugin { public static function get_event_category_taxonomy(): string { 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() ); + } } diff --git a/includes/integrations/interface-feature-event-sources.php b/includes/integrations/interface-feature-event-sources.php new file mode 100644 index 0000000..b80f97d --- /dev/null +++ b/includes/integrations/interface-feature-event-sources.php @@ -0,0 +1,42 @@ +