From 55c70ce83143fa4d957f9205b6b176e49263f513 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Menrath?= Date: Sun, 15 Dec 2024 09:52:20 +0100 Subject: [PATCH] wip --- includes/activitypub/handler/class-create.php | 23 +++++ .../transmogrifier/class-gatherpress.php | 98 +++++++++++++++++-- includes/admin/class-user-interface.php | 4 +- includes/class-event-sources.php | 20 ++++ includes/class-settings.php | 12 +++ includes/class-setup.php | 28 +++++- templates/settings.php | 34 ++++++- 7 files changed, 203 insertions(+), 16 deletions(-) diff --git a/includes/activitypub/handler/class-create.php b/includes/activitypub/handler/class-create.php index c8e1713..ff75359 100644 --- a/includes/activitypub/handler/class-create.php +++ b/includes/activitypub/handler/class-create.php @@ -8,6 +8,8 @@ namespace Event_Bridge_For_ActivityPub\ActivityPub\Handler; use Activitypub\Collection\Actors; +use DateTime; +use DateTimeZone; use Event_Bridge_For_ActivityPub\ActivityPub\Collection\Event_Sources; use Event_Bridge_For_ActivityPub\Setup; @@ -61,6 +63,10 @@ class Create { return; } + if ( self::is_time_passed( $activity['object']['startTime'] ) ) { + return; + } + $transmogrifier_class = Setup::get_transmogrifier(); if ( ! $transmogrifier_class ) { @@ -120,6 +126,23 @@ class Create { return $valid; } + /** + * Check if a given DateTime is already passed. + * + * @param string $time_string The ActivityPub like time string. + * @return bool + */ + private static function is_time_passed( $time_string ) { + // Create a DateTime object from the ActivityPub time string. + $time = new DateTime( $time_string, new DateTimeZone( 'UTC' ) ); + + // Get the current time in UTC. + $current_time = new DateTime( 'now', new DateTimeZone( 'UTC' ) ); + + // Compare the event time with the current time. + return $time < $current_time; + } + /** * Check if an ActivityPub actor is an event source. * diff --git a/includes/activitypub/transmogrifier/class-gatherpress.php b/includes/activitypub/transmogrifier/class-gatherpress.php index aadf2a7..4a52f75 100644 --- a/includes/activitypub/transmogrifier/class-gatherpress.php +++ b/includes/activitypub/transmogrifier/class-gatherpress.php @@ -12,8 +12,8 @@ namespace Event_Bridge_For_ActivityPub\Activitypub\Transmogrifier; use Activitypub\Activity\Extended_Object\Event; -use Activitypub\Activity\Extended_Object\Place; use DateTime; +use Exception; use function Activitypub\object_to_uri; use function Activitypub\sanitize_url; @@ -56,6 +56,51 @@ class GatherPress { $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. */ @@ -232,6 +277,25 @@ class GatherPress { 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']; + } + } + } + /** * Add venue. * @@ -248,6 +312,17 @@ class GatherPress { return; } + // Fallback for Gancio instances. + if ( 'online' === $location['name'] ) { + $online_event_link = $this->get_online_event_link_from_attachments(); + if ( ! $online_event_link ) { + return; + } + update_post_meta( $post_id, 'gatherpress_online_event_link', sanitize_url( $online_event_link ) ); + wp_set_object_terms( $post_id, 'online-event', '_gatherpress_venue', false ); + return; + } + $venue_instance = \GatherPress\Core\Venue::get_instance(); $venue_name = sanitize_title( $location['name'] ); $venue_slug = $venue_instance->get_venue_term_slug( $venue_name ); @@ -276,14 +351,14 @@ class GatherPress { update_post_meta( $venue_id, 'gatherpress_venue_information', $venue_json ); - wp_set_object_terms( $post_id, $venue_slug, '_gatherpress_venue', true ); // 'true' appends to existing terms. + wp_set_object_terms( $post_id, $venue_slug, '_gatherpress_venue', false ); // 'true' appends to existing terms. } /** * Save the ActivityPub event object as GatherPress Event. */ public function create() { - // Insert new GatherPress Event post. + // Insert new GatherPress event post. $post_id = wp_insert_post( array( 'post_title' => sanitize_text_field( $this->activitypub_event->get_name() ), @@ -292,6 +367,10 @@ class GatherPress { 'post_excerpt' => wp_kses_post( $this->activitypub_event->get_summary() ), 'post_status' => 'publish', 'guid' => sanitize_url( $this->activitypub_event->get_id() ), + 'meta_input' => array( + 'event_bridge_for_activitypub_is_cached' => 'GatherPress', + 'activitypub_content_visibility' => ACTIVITYPUB_CONTENT_VISIBILITY_LOCAL, + ), ) ); @@ -299,7 +378,7 @@ class GatherPress { return; } - // Insert the Dates. + // Insert the dates. $event = new GatherPress_Event( $post_id ); $start_time = $this->activitypub_event->get_start_time(); $end_time = $this->activitypub_event->get_end_time(); @@ -313,18 +392,17 @@ class GatherPress { 'datetime_end' => $end_time, 'timezone' => $this->activitypub_event->get_timezone(), ); + // Sanitization of the params is done in the save_datetimes function just in time. + $event->save_datetimes( $params ); // Insert featured image. $image = $this->get_featured_image(); self::set_featured_image_with_alt( $post_id, $image['url'], $image['alt'] ); - // Add hashtags as terms. + // Add hashtags. $this->add_tags_to_post( $post_id ); $this->add_venue( $post_id ); - - // Sanitization of the params is done in the save_datetimes function just in time. - $event->save_datetimes( $params ); } /** @@ -346,6 +424,10 @@ class GatherPress { 'post_excerpt' => wp_kses_post( $this->activitypub_event->get_summary() ), 'post_status' => 'publish', 'guid' => sanitize_url( $this->activitypub_event->get_id() ), + 'meta_input' => array( + 'event_bridge_for_activitypub_is_cached' => 'GatherPress', + 'activitypub_content_visibility' => ACTIVITYPUB_CONTENT_VISIBILITY_LOCAL, + ), ) ); diff --git a/includes/admin/class-user-interface.php b/includes/admin/class-user-interface.php index b6a9c3e..9aa00da 100644 --- a/includes/admin/class-user-interface.php +++ b/includes/admin/class-user-interface.php @@ -12,8 +12,6 @@ namespace Event_Bridge_For_ActivityPub\Admin; // Exit if accessed directly. defined( 'ABSPATH' ) || exit; // @codeCoverageIgnore -use Event_Bridge_For_ActivityPub\Integrations\Event_Plugin; - /** * Class responsible for Event Plugin related admin notices. * @@ -108,4 +106,4 @@ class User_Interface { } return $caps; } -} \ No newline at end of file +} diff --git a/includes/class-event-sources.php b/includes/class-event-sources.php index 760c81a..91c9f65 100644 --- a/includes/class-event-sources.php +++ b/includes/class-event-sources.php @@ -12,6 +12,9 @@ namespace Event_Bridge_For_ActivityPub; use Activitypub\Activity\Extended_Object\Event; use Activitypub\Collection\Actors; use Event_Bridge_For_ActivityPub\ActivityPub\Collection\Event_Sources as Event_Sources_Collection; +use Event_Bridge_For_ActivityPub\Activitypub\Transformer\GatherPress as TransformerGatherPress; +use Event_Bridge_For_ActivityPub\Activitypub\Transmogrifier\GatherPress; +use Event_Bridge_For_ActivityPub\Integrations\GatherPress as IntegrationsGatherPress; use function Activitypub\get_remote_metadata_by_actor; use function Activitypub\is_activitypub_request; @@ -139,4 +142,21 @@ class Event_Sources { return $template; } + + /** + * Delete old cached events that took place in the past. + */ + public static function clear_cache() { + $cache_retention_period = get_option( 'event_bridge_for_activitypub_event_source_cache_retention', WEEK_IN_SECONDS ); + + $past_event_ids = GatherPress::get_past_events( $cache_retention_period ); + + foreach ( $past_event_ids as $post_id ) { + if ( has_post_thumbnail( $post_id ) ) { + $attachment_id = get_post_thumbnail_id( $post_id ); + wp_delete_attachment( $attachment_id, true ); + } + wp_delete_post( $post_id, true ); + } + } } diff --git a/includes/class-settings.php b/includes/class-settings.php index ab88721..d2dfd3a 100644 --- a/includes/class-settings.php +++ b/includes/class-settings.php @@ -105,6 +105,18 @@ class Settings { ) ); + \register_setting( + 'event-bridge-for-activitypub', + 'event_bridge_for_activitypub_event_source_cache_retention', + array( + 'type' => 'int', + 'show_in_rest' => true, + 'description' => \__( 'The cache retention period for external event sources.', 'event-bridge-for-activitypub' ), + 'default' => WEEK_IN_SECONDS, + 'sanitize_callback' => 'absint', + ) + ); + \register_setting( 'event-bridge-for-activitypub', 'event_bridge_for_activitypub_plugin_used_for_event_source_feature', diff --git a/includes/class-setup.php b/includes/class-setup.php index 58f5778..3b65430 100644 --- a/includes/class-setup.php +++ b/includes/class-setup.php @@ -242,12 +242,36 @@ class Setup { if ( 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_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( + '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 ); } diff --git a/templates/settings.php b/templates/settings.php index b56e27c..174930d 100644 --- a/templates/settings.php +++ b/templates/settings.php @@ -31,15 +31,16 @@ if ( ! isset( $args ) || ! array_key_exists( 'event_terms', $args ) ) { if ( ! current_user_can( 'manage_options' ) ) { return; } - +\get_option( 'event_bridge_for_activitypub_event_sources_active', false ); if ( ! isset( $args ) || ! array_key_exists( 'supports_event_sources', $args ) ) { return; } $event_plugins_supporting_event_sources = $args['supports_event_sources']; -$selected_plugin = \get_option( 'event_bridge_for_activitypub_plugin_used_for_event_source_feature', '' ); -$event_sources_active = \get_option( 'event_bridge_for_activitypub_event_sources_active', false ); +$selected_plugin = \get_option( 'event_bridge_for_activitypub_plugin_used_for_event_source_feature', '' ); +$event_sources_active = \get_option( 'event_bridge_for_activitypub_event_sources_active', false ); +$cache_retention_period = \get_option( 'event_bridge_for_activitypub_event_source_cache_retention', DAY_IN_SECONDS ); $event_terms = $args['event_terms']; @@ -145,6 +146,33 @@ $current_category_mapping = \get_option( 'event_bridge_for_activitypub_ev

+ + + + + + +

+ +