Add transmogrifier and tests for vs event list
All checks were successful
PHP Code Checker / PHP Code Checker (pull_request) Successful in 48s
PHPUnit / PHPUnit – PHP 7.4 (pull_request) Successful in 1m4s
PHPUnit / PHPUnit – PHP 8.0 (pull_request) Successful in 1m6s
PHPUnit / PHPUnit – PHP 8.1 (pull_request) Successful in 1m2s
PHPUnit / PHPUnit – PHP 8.2 (pull_request) Successful in 1m6s
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-23 21:23:03 +01:00
parent 460b223920
commit 633a5332d1
16 changed files with 455 additions and 119 deletions

View file

@ -61,7 +61,7 @@
], ],
"test-debug": [ "test-debug": [
"@prepare-test", "@prepare-test",
"@test-gatherpress" "@test-vs-event-list"
], ],
"test-vs-event-list": "phpunit --filter=vs_event_list", "test-vs-event-list": "phpunit --filter=vs_event_list",
"test-the-events-calendar": "phpunit --filter=the_events_calendar", "test-the-events-calendar": "phpunit --filter=the_events_calendar",

View file

@ -53,7 +53,6 @@ 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

@ -54,6 +54,13 @@ class Event_Source extends Actor {
return $icon; return $icon;
} }
/**
* Return the Post-IDs of all events cached by this event source.
*/
public static function get_cached_events(): array {
return array();
}
/** /**
* Get the WordPress post which stores the Event Source by the ActivityPub actor id of the event source. * Get the WordPress post which stores the Event Source by the ActivityPub actor id of the event source.
* *

View file

@ -12,6 +12,7 @@
namespace Event_Bridge_For_ActivityPub\ActivityPub\Transmogrifier; namespace Event_Bridge_For_ActivityPub\ActivityPub\Transmogrifier;
use DateTime; use DateTime;
use Event_Bridge_For_ActivityPub\Integrations\GatherPress as IntegrationsGatherPress;
use function Activitypub\sanitize_url; use function Activitypub\sanitize_url;
@ -51,7 +52,7 @@ class GatherPress extends Base {
// 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 ); wp_set_object_terms( $post_id, $tag_names, IntegrationsGatherPress::get_event_category_taxonomy(), true );
} }
return true; return true;

View file

@ -12,6 +12,8 @@
namespace Event_Bridge_For_ActivityPub\ActivityPub\Transmogrifier; namespace Event_Bridge_For_ActivityPub\ActivityPub\Transmogrifier;
use Event_Bridge_For_ActivityPub\Integrations\VS_Event_List as IntegrationsVS_Event_List;
use function Activitypub\sanitize_url; use function Activitypub\sanitize_url;
// Exit if accessed directly. // Exit if accessed directly.
@ -27,94 +29,67 @@ defined( 'ABSPATH' ) || exit; // @codeCoverageIgnore
*/ */
class VS_Event_List extends Base { class VS_Event_List extends Base {
/** /**
* Get a list of Post IDs of events that have ended. * Extract location and address as string.
* *
* @param int $cache_retention_period Additional time buffer in seconds. * @param ?array $location The ActivitySTreams location as an associative array.
* @return ?array * @return string The location and address formatted as a single string.
*/ */
public static function get_past_events( $cache_retention_period = 0 ): ?array { private function get_location_as_string( $location ): string {
unset( $cache_retention_period ); $location_string = '';
$results = array(); // Return empty string when location is not an associative array.
if ( is_null( $location ) || ! is_array( $location ) ) {
return $location_string;
}
return $results; if ( ! isset( $location['type'] ) || 'Place' !== $location['type'] ) {
return $location_string;
}
// Add name of the location.
if ( isset( $location['name'] ) ) {
$location_string .= $location['name'];
}
// Add delimiter between name and address if both are set.
if ( isset( $location['name'] ) && isset( $location['address'] ) ) {
$location_string .= ' ';
}
// Add address.
if ( isset( $location['address'] ) ) {
$location_string .= $this->address_to_string( $location['address'] );
}
return $location_string;
} }
/** /**
* Map an ActivityStreams Place to the Events Calendar venue. * Add tags to post.
* *
* @param array $location An ActivityPub location as an associative array. * @param int $post_id The post ID.
* @link https://www.w3.org/TR/activitystreams-vocabulary/#dfn-place
* @return array
*/ */
private function get_venue_args( $location ) { private function add_tags_to_post( $post_id ) {
$args = array( $tags_array = $this->activitypub_event->get_tag();
'venue' => $location['name'],
'status' => 'publish',
);
if ( is_array( $location['address'] ) && isset( $location['address']['type'] ) && 'PostalAddress' === $location['address']['type'] ) { // Ensure the input is valid.
$mapping = array( if ( empty( $tags_array ) || ! is_array( $tags_array ) || ! $post_id ) {
'streetAddress' => 'address', return false;
'postalCode' => 'zip',
'addressLocality' => 'city',
'addressState' => 'state',
'addressCountry' => 'country',
'url' => 'website',
);
foreach ( $mapping as $postal_address_key => $venue_key ) {
if ( isset( $location['address'][ $postal_address_key ] ) ) {
$args[ $venue_key ] = $location['address'][ $postal_address_key ];
}
}
} elseif ( is_string( $location['address'] ) ) {
// Use the address field for a solely text address.
$args['address'] = $location['address'];
} }
return $args; // Extract and process tag names.
} $tag_names = array();
foreach ( $tags_array as $tag ) {
/** if ( isset( $tag['name'] ) && 'Hashtag' === $tag['type'] ) {
* Add venue. $tag_names[] = ltrim( $tag['name'], '#' ); // Remove the '#' from the name.
*
* @return int|bool $post_id The venues post ID.
*/
private function add_venue() {
$location = $this->activitypub_event->get_location();
if ( ! $location ) {
return;
}
if ( ! isset( $location['name'] ) ) {
return;
}
// Fallback for Gancio instances.
if ( 'online' === $location['name'] ) {
return;
}
$post_ids = tribe_events()->search( $location['name'] )->all();
$post_id = false;
if ( count( $post_ids ) ) {
$post_id = reset( $post_ids );
}
if ( $post_id && get_post_meta( $post_id, '_event_bridge_for_activitypub_is_remote_cached', true ) ) {
tribe_venues()->where( 'id', $post_id )->set_args( $this->get_venue_args( $location ) )->save()[0];
} else {
$post = tribe_venues()->set_args( $this->get_venue_args( $location ) )->create();
if ( $post ) {
$post_id = $post->ID;
} }
} }
return $post_id; // Add the tags as terms to the post.
if ( ! empty( $tag_names ) ) {
wp_set_object_terms( $post_id, $tag_names, IntegrationsVS_Event_List::get_event_category_taxonomy(), true );
}
return true;
} }
/** /**
@ -124,59 +99,62 @@ class VS_Event_List extends Base {
*/ */
public function save_event() { public function save_event() {
// 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' ) );
$post_id = $this->get_post_id_from_activitypub_id(); $post_id = $this->get_post_id_from_activitypub_id();
$duration = $this->get_duration();
$venue_id = $this->add_venue();
$args = array( $args = array(
'title' => sanitize_text_field( $this->activitypub_event->get_name() ), 'post_title' => \sanitize_text_field( $this->activitypub_event->get_name() ),
'content' => wp_kses_post( $this->activitypub_event->get_content() ), 'post_type' => \Event_Bridge_For_ActivityPub\Integrations\VS_Event_List::get_post_type(),
'start_date' => gmdate( 'Y-m-d H:i:s', strtotime( $this->activitypub_event->get_start_time() ) ), 'post_content' => \wp_kses_post( $this->activitypub_event->get_content() ?? '' ),
'duration' => $duration, 'post_excerpt' => \wp_kses_post( $this->activitypub_event->get_summary() ?? '' ),
'status' => 'publish', 'post_status' => 'publish',
'guid' => sanitize_url( $this->activitypub_event->get_id() ), 'guid' => \sanitize_url( $this->activitypub_event->get_id() ),
'meta_input' => array(
'event-start-date' => \strtotime( $this->activitypub_event->get_start_time() ),
'event-link' => \sanitize_url( $this->activitypub_event->get_url() ),
'event-link-label' => \sanitize_text_field( __( 'Original Website', 'event-bridge-for-activitypub' ) ),
'event-link-target' => 'yes', // Open in new window.
'event-link-title' => 'no', // Whether to redirect event title to original source.
'event-link-image' => 'no', // Whether to redirect events featured image to original source.
),
); );
if ( $venue_id ) { // Add end time.
$args['venue'] = $venue_id; $end_time = $this->activitypub_event->get_end_time();
$args['VenueID'] = $venue_id; if ( $end_time ) {
$args['meta_input']['event-date'] = \strtotime( $end_time );
} }
$tribe_event = new The_Events_Calendar_Event_Repository(); // Maybe add location.
$location = $this->get_location_as_string( $this->activitypub_event->get_location() );
if ( $location ) {
$args['meta_input']['event-location'] = $location;
}
if ( $post_id ) { if ( $post_id ) {
$args['post_title'] = $args['title']; // Update existing event post.
$args['post_content'] = $args['content']; $args['ID'] = $post_id;
// Update existing GatherPress event post. $post_id = \wp_update_post( $args );
$post = \Tribe__Events__API::updateEvent( $post_id, $args );
} else { } else {
$post = $tribe_event->set_args( $args )->create(); // Insert new event post.
$post_id = \wp_insert_post( $args );
} }
if ( ! $post ) { if ( ! $post_id || \is_wp_error( $post_id ) ) {
return false; return false;
} }
// Insert featured image.
$image = $this->get_featured_image();
self::set_featured_image_with_alt( $post_id, $image['url'], $image['alt'] );
// Add hashtags.
$this->add_tags_to_post( $post_id );
// 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' ) );
return $post->ID; return $post_id;
}
/**
* Get the events duration in seconds.
*
* @return int
*/
private function get_duration() {
$end_time = $this->activitypub_event->get_end_time();
if ( ! $end_time ) {
return 2 * HOUR_IN_SECONDS;
}
return abs( strtotime( $end_time ) - strtotime( $this->activitypub_event->get_start_time() ) );
} }
} }

View file

@ -36,6 +36,9 @@ class Event_Sources {
// Register the Event Sources Collection which takes care of managing the event sources. // Register the Event Sources Collection which takes care of managing the event sources.
\add_action( 'init', array( Event_Sources_Collection::class, 'init' ) ); \add_action( 'init', array( Event_Sources_Collection::class, 'init' ) );
// Allow wp_safe_redirect to all followed event sources hosts.
\add_filter( 'allowed_redirect_hosts', array( self::class, 'add_event_sources_hosts_to_allowed_redirect_hosts' ) );
// Register handlers for incoming activities to the ActivityPub plugin, e.g. incoming `Event` objects. // Register handlers for incoming activities to the ActivityPub plugin, e.g. incoming `Event` objects.
\add_action( 'activitypub_register_handlers', array( Handler::class, 'register_handlers' ) ); \add_action( 'activitypub_register_handlers', array( Handler::class, 'register_handlers' ) );

View file

@ -122,7 +122,7 @@ class Settings {
'event-bridge-for-activitypub', 'event-bridge-for-activitypub',
'event_bridge_for_activitypub_integration_used_for_event_sources_feature', 'event_bridge_for_activitypub_integration_used_for_event_sources_feature',
array( array(
'type' => 'array', 'type' => 'string',
'description' => \__( 'Define which plugin/integration is used for the event sources feature', 'event-bridge-for-activitypub' ), 'description' => \__( 'Define which plugin/integration is used for the event sources feature', 'event-bridge-for-activitypub' ),
'default' => array(), 'default' => array(),
'sanitize_callback' => array( self::class, 'sanitize_event_plugin_integration_used_for_event_sources' ), 'sanitize_callback' => array( self::class, 'sanitize_event_plugin_integration_used_for_event_sources' ),

View file

@ -13,6 +13,8 @@
namespace Event_Bridge_For_ActivityPub\Integrations; namespace Event_Bridge_For_ActivityPub\Integrations;
use Event_Bridge_For_ActivityPub\ActivityPub\Transformer\VS_Event_List as VS_Event_List_Transformer; use Event_Bridge_For_ActivityPub\ActivityPub\Transformer\VS_Event_List as VS_Event_List_Transformer;
use Event_Bridge_For_ActivityPub\ActivityPub\Transmogrifier\VS_Event_List as VS_Event_List_Transmogrifier;
use WP_Query;
// Exit if accessed directly. // Exit if accessed directly.
defined( 'ABSPATH' ) || exit; // @codeCoverageIgnore defined( 'ABSPATH' ) || exit; // @codeCoverageIgnore
@ -25,7 +27,7 @@ defined( 'ABSPATH' ) || exit; // @codeCoverageIgnore
* *
* @since 1.0.0 * @since 1.0.0
*/ */
final class VS_Event_List extends Event_Plugin_Integration { final class VS_Event_List extends Event_Plugin_Integration implements Feature_Event_Sources {
/** /**
* Returns the full plugin file. * Returns the full plugin file.
* *
@ -71,4 +73,45 @@ final class VS_Event_List extends Event_Plugin_Integration {
public static function get_activitypub_event_transformer( $post ): 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() ); return new VS_Event_List_Transformer( $post, self::get_event_category_taxonomy() );
} }
/**
* Returns the Transmogrifier for The_Events_Calendar.
*/
public static function get_transmogrifier(): VS_Event_List_Transmogrifier {
return new VS_Event_List_Transmogrifier();
}
/**
* Get a list of Post IDs of events that have ended.
*
* @param int $ends_before_time Filter to only get events that ended before that datetime as unix-time.
*
* @return array<int>
*/
public static function get_cached_remote_events( $ends_before_time ): array {
$args = array(
'post_type' => 'event',
'posts_per_page' => -1,
'fields' => 'ids',
'meta_query' => array(
'relation' => 'AND',
array(
'key' => '_event_bridge_for_activitypub_is_remote_cached',
'compare' => 'EXISTS',
),
array(
'key' => 'event-date',
'value' => $ends_before_time,
'type' => 'NUMERIC',
'compare' => '<',
),
),
);
$query = new WP_Query( $args );
$post_ids = $query->posts;
return $post_ids;
}
} }

View file

@ -143,7 +143,7 @@ $current_category_mapping = \get_option( 'event_bridge_for_activitypub_ev
> >
<?php <?php
foreach ( $event_plugins_supporting_event_sources as $event_plugin_class_name => $event_plugin_name ) { foreach ( $event_plugins_supporting_event_sources as $event_plugin_class_name => $event_plugin_name ) {
echo '<option value="' . esc_attr( $event_plugin_class_name ) . '" ' . selected( $event_plugin_class_name, $event_plugin, true ) . '>' . esc_attr( $event_plugin_name ) . '</option>'; echo '<option value="' . esc_attr( $event_plugin_class_name ) . '" ' . selected( $event_plugin_class_name, get_option( 'event_bridge_for_activitypub_integration_used_for_event_sources_feature', $event_plugin ), true ) . '>' . esc_attr( $event_plugin_name ) . '</option>';
} }
?> ?>
</select> </select>

View file

@ -48,10 +48,11 @@ WP_Filesystem();
<?php <?php
if ( ! $activitypub_plugin_is_active ) { if ( ! $activitypub_plugin_is_active ) {
$notice = General_Admin_Notices::get_admin_notice_activitypub_plugin_not_enabled(); $notice = General_Admin_Notices::get_admin_notice_activitypub_plugin_not_enabled();
echo '<p>⚠' . \wp_kses( $notice, General_Admin_Notices::ALLOWED_HTML ) . '</p>';
} elseif ( empty( $active_event_plugins ) ) { } elseif ( empty( $active_event_plugins ) ) {
$notice = General_Admin_Notices::get_admin_notice_no_supported_event_plugin_active(); $notice = General_Admin_Notices::get_admin_notice_no_supported_event_plugin_active();
echo '<p>⚠' . \wp_kses( $notice, General_Admin_Notices::ALLOWED_HTML ) . '</p>';
} }
echo '<p>⚠' . \wp_kses( $notice, General_Admin_Notices::ALLOWED_HTML ) . '</p>';
?> ?>
<?php foreach ( $active_event_plugins as $active_event_plugin ) { ?> <?php foreach ( $active_event_plugins as $active_event_plugin ) { ?>
<h3><?php echo esc_html( $active_event_plugin->get_plugin_name() ); ?>:</h3> <h3><?php echo esc_html( $active_event_plugin->get_plugin_name() ); ?>:</h3>

View file

@ -81,6 +81,12 @@ function _manually_load_plugin() {
break; break;
case 'vs_event_list': case 'vs_event_list':
$plugin_file = 'very-simple-event-list/vsel.php'; $plugin_file = 'very-simple-event-list/vsel.php';
\update_option( 'event_bridge_for_activitypub_event_sources_active', true );
\update_option(
'event_bridge_for_activitypub_integration_used_for_event_sources_feature',
\Event_Bridge_For_ActivityPub\Integrations\VS_Event_List::class
);
\update_option( 'activitypub_actor_mode', ACTIVITYPUB_BLOG_MODE );
break; break;
case 'events_manager': case 'events_manager':
$plugin_file = 'events-manager/events-manager.php'; $plugin_file = 'events-manager/events-manager.php';

View file

@ -113,7 +113,7 @@ class Test_GatherPress extends \WP_UnitTestCase {
'endTime' => \gmdate( 'Y-m-d\TH:i:s\Z', time() + WEEK_IN_SECONDS + HOUR_IN_SECONDS ), 'endTime' => \gmdate( 'Y-m-d\TH:i:s\Z', time() + WEEK_IN_SECONDS + HOUR_IN_SECONDS ),
'name' => 'Fediverse Party', 'name' => 'Fediverse Party',
'to' => 'https://www.w3.org/ns/activitystreams#Public', 'to' => 'https://www.w3.org/ns/activitystreams#Public',
'published' => '2020-01-01T00:00:00Z', 'published' => \gmdate( 'Y-m-d\TH:i:s\Z', time() ),
'location' => array( 'location' => array(
'type' => 'Place', 'type' => 'Place',
'name' => 'Fediverse Concert Hall', 'name' => 'Fediverse Concert Hall',

View file

@ -93,7 +93,7 @@ class Test_The_Events_Calendar extends \WP_UnitTestCase {
'endTime' => \gmdate( 'Y-m-d\TH:i:s\Z', time() + WEEK_IN_SECONDS + HOUR_IN_SECONDS ), 'endTime' => \gmdate( 'Y-m-d\TH:i:s\Z', time() + WEEK_IN_SECONDS + HOUR_IN_SECONDS ),
'name' => 'Fediverse Party for The Events Calendar', 'name' => 'Fediverse Party for The Events Calendar',
'to' => 'https://www.w3.org/ns/activitystreams#Public', 'to' => 'https://www.w3.org/ns/activitystreams#Public',
'published' => '2020-01-01T00:00:00Z', 'published' => \gmdate( 'Y-m-d\TH:i:s\Z', time() ),
'location' => array( 'location' => array(
'type' => 'Place', 'type' => 'Place',
'name' => 'Fediverse Concert Hall', 'name' => 'Fediverse Concert Hall',
@ -155,7 +155,7 @@ class Test_The_Events_Calendar extends \WP_UnitTestCase {
'endTime' => \gmdate( 'Y-m-d\TH:i:s\Z', time() + WEEK_IN_SECONDS + HOUR_IN_SECONDS ), 'endTime' => \gmdate( 'Y-m-d\TH:i:s\Z', time() + WEEK_IN_SECONDS + HOUR_IN_SECONDS ),
'name' => 'Fediverse Party for The Events Calendar', 'name' => 'Fediverse Party for The Events Calendar',
'to' => 'https://www.w3.org/ns/activitystreams#Public', 'to' => 'https://www.w3.org/ns/activitystreams#Public',
'published' => '2020-01-01T00:00:00Z', 'published' => \gmdate( 'Y-m-d\TH:i:s\Z', time() ),
'location' => array( 'location' => array(
'type' => 'Place', 'type' => 'Place',
'name' => 'Fediverse Concert Hall', 'name' => 'Fediverse Concert Hall',

View file

@ -0,0 +1,127 @@
<?php
/**
* Test file for the Transmogrifier (import of ActivityPub Event objects) in VS Event List.
*
* @package Event_Bridge_For_ActivityPub
* @since 1.0.0
* @license AGPL-3.0-or-later
*/
namespace Event_Bridge_For_ActivityPub\Tests\ActivityPub\Transmogrifier;
use Event_Bridge_For_ActivityPub\ActivityPub\Model\Event_Source;
use Event_Bridge_For_ActivityPub\ActivityPub\Transformer\VS_Event_List as TransformerVS_Event_List;
use Event_Bridge_For_ActivityPub\ActivityPub\Transmogrifier\VS_Event_List;
use Event_Bridge_For_ActivityPub\Integrations\VS_Event_List as IntegrationsVS_Event_List;
use WP_REST_Request;
use WP_REST_Server;
/**
* Test class for the Transmogrifier (import of ActivityPub Event objects) in VS Event List.
*
* @coversDefaultClass \Event_Bridge_For_ActivityPub\ActivityPub\Transmogrifier\VS_Event_List
*/
class Test_VS_Event_List extends \WP_UnitTestCase {
const FOLLOWED_ACTOR = array(
'id' => 'https://remote.example/@organizer',
'type' => 'Person',
'inbox' => 'https://remote.example/@organizer/inbox',
'outbox' => 'https://remote.example/@organizer/outbox',
'name' => 'The Organizer',
'summary' => 'Just a random organizer of events in the Fediverse',
);
/**
* REST Server.
*
* @var WP_REST_Server
*/
protected $server;
/**
* Set up the test.
*/
public function set_up() {
if ( ! function_exists( 'vsel_custom_post_type' ) ) {
self::markTestSkipped( 'VS Event List plugin is not active.' );
}
\add_option( 'permalink_structure', '/%postname%/' );
global $wp_rest_server;
$wp_rest_server = new WP_REST_Server();
$this->server = $wp_rest_server;
do_action( 'rest_api_init' );
\Activitypub\Rest\Server::add_hooks();
// Make sure that ActivityPub support is enabled for The Events Calendar.
$aec = \Event_Bridge_For_ActivityPub\Setup::get_instance();
$aec->activate_activitypub_support_for_active_event_plugins();
// Add event source (ActivityPub follower).
_delete_all_posts();
\Event_Bridge_For_ActivityPub\ActivityPub\Model\Event_Source::init_from_array( self::FOLLOWED_ACTOR )->save();
\update_option( 'event_bridge_for_activitypub_event_sources_active', true );
\update_option(
'event_bridge_for_activitypub_integration_used_for_event_sources_feature',
\Event_Bridge_For_ActivityPub\Integrations\VS_Event_List::class
);
\update_option( 'activitypub_actor_mode', ACTIVITYPUB_BLOG_MODE );
}
/**
* Tear down the test.
*/
public function tear_down() {
\delete_option( 'permalink_structure' );
}
/**
* Test receiving event from followed actor.
*/
public function test_incoming_event() {
\add_filter( 'activitypub_defer_signature_verification', '__return_true' );
$json = array(
'id' => 'https://remote.example/@organizer/events/new-year-party#create',
'type' => 'Create',
'actor' => 'https://remote.example/@organizer',
'object' => array(
'id' => 'https://remote.example/@organizer/events/new-year-party',
'type' => 'Event',
'startTime' => \gmdate( 'Y-m-d\TH:i:s\Z', time() + WEEK_IN_SECONDS ),
'endTime' => \gmdate( 'Y-m-d\TH:i:s\Z', time() + WEEK_IN_SECONDS + HOUR_IN_SECONDS ),
'name' => 'Fediverse Party Test Event',
'to' => 'https://www.w3.org/ns/activitystreams#Public',
'published' => \gmdate( 'Y-m-d\TH:i:s\Z', time() ),
'location' => array(
'type' => 'Place',
'name' => 'Fediverse Concert Hall',
'address' => 'Fedistreet 13, Feditown 1337',
),
),
);
$request = new WP_REST_Request( 'POST', '/activitypub/1.0/users/0/inbox' );
$request->set_header( 'Content-Type', 'application/activity+json' );
$request->set_body( \wp_json_encode( $json ) );
// Dispatch the request.
$response = \rest_do_request( $request );
$this->assertEquals( 202, $response->get_status() );
$events = get_posts( array( 'post_type' => IntegrationsVS_Event_List::get_post_type() ) );
$this->assertCount( 1, $events );
$event = $events[0];
$this->assertEquals( $json['object']['name'], $event->post_title );
$this->assertEquals( $json['object']['startTime'], \gmdate( 'Y-m-d\TH:i:s\Z', get_post_meta( $event->ID, 'event-start-date', true ) ) );
$this->assertEquals( $json['object']['endTime'], \gmdate( 'Y-m-d\TH:i:s\Z', get_post_meta( $event->ID, 'event-date', true ) ) );
$this->assertStringStartsWith( $json['object']['location']['name'], get_post_meta( $event->ID, 'event-location', true ) );
\remove_filter( 'activitypub_defer_signature_verification', '__return_true' );
}
}

View file

@ -0,0 +1,171 @@
<?php
/**
* Test file for the Transmogrifier (import of ActivityPub Event objects) of GatherPress.
*
* @package Event_Bridge_For_ActivityPub
* @since 1.0.0
* @license AGPL-3.0-or-later
*/
namespace Event_Bridge_For_ActivityPub\Tests\Integrations;
use Event_Bridge_For_ActivityPub\Integrations\VS_Event_List;
use WP_REST_Request;
use WP_REST_Server;
/**
* Test class for the Transmogrifier (import of ActivityPub Event objects) of GatherPress.
*
* @coversDefaultClass \Event_Bridge_For_ActivityPub\ActivityPub\Transmogrifier\The_Events_Calendar
*/
class Test_VS_Event_List extends \WP_UnitTestCase {
const FOLLOWED_ACTOR = array(
'id' => 'https://remote.example/@organizer',
'type' => 'Person',
'inbox' => 'https://remote.example/@organizer/inbox',
'outbox' => 'https://remote.example/@organizer/outbox',
'name' => 'The Organizer',
'summary' => 'Just a random organizer of events in the Fediverse',
);
/**
* REST Server.
*
* @var WP_REST_Server
*/
protected $server;
/**
* Set up the test.
*/
public function set_up() {
if ( ! function_exists( 'vsel_custom_post_type' ) ) {
self::markTestSkipped( 'VS Event List plugin is not active.' );
}
\add_option( 'permalink_structure', '/%postname%/' );
global $wp_rest_server;
$wp_rest_server = new WP_REST_Server();
$this->server = $wp_rest_server;
do_action( 'rest_api_init' );
\Activitypub\Rest\Server::add_hooks();
// Make sure that ActivityPub support is enabled for The Events Calendar.
$aec = \Event_Bridge_For_ActivityPub\Setup::get_instance();
$aec->activate_activitypub_support_for_active_event_plugins();
// Add event source (ActivityPub follower).
_delete_all_posts();
\Event_Bridge_For_ActivityPub\ActivityPub\Model\Event_Source::init_from_array( self::FOLLOWED_ACTOR )->save();
\update_option( 'event_bridge_for_activitypub_event_sources_active', true );
\update_option(
'event_bridge_for_activitypub_integration_used_for_event_sources_feature',
\Event_Bridge_For_ActivityPub\Integrations\VS_Event_List::class
);
\update_option( 'activitypub_actor_mode', ACTIVITYPUB_BLOG_MODE );
}
/**
* Tear down the test.
*/
public function tear_down() {
\delete_option( 'permalink_structure' );
}
/**
* Test receiving event from followed actor.
*/
public function test_getting_past_remote_events() {
\add_filter( 'activitypub_defer_signature_verification', '__return_true' );
// Federated event 1: starts in one week.
$event_in_one_week = array(
'id' => 'https://remote.example/@organizer/events/in-one-week#create',
'type' => 'Create',
'actor' => 'https://remote.example/@organizer',
'object' => array(
'id' => 'https://remote.example/@organizer/events/in-one-week',
'type' => 'Event',
'startTime' => \gmdate( 'Y-m-d\TH:i:s\Z', time() + WEEK_IN_SECONDS ),
'endTime' => \gmdate( 'Y-m-d\TH:i:s\Z', time() + WEEK_IN_SECONDS + HOUR_IN_SECONDS ),
'name' => 'Remote Event in One Week',
'to' => 'https://www.w3.org/ns/activitystreams#Public',
'published' => '2020-01-01T00:00:00Z',
'location' => array(
'type' => 'Place',
'name' => 'Fediverse Concert Hall',
'address' => 'Fedistreet 13, Feditown 1337',
),
),
);
// Federated event 1: starts in two months.
$event_in_two_months = array(
'id' => 'https://remote.example/@organizer/events/in-two-months#create',
'type' => 'Create',
'actor' => 'https://remote.example/@organizer',
'object' => array(
'id' => 'https://remote.example/@organizer/events/in-two-months',
'type' => 'Event',
'startTime' => \gmdate( 'Y-m-d\TH:i:s\Z', time() + 2 * MONTH_IN_SECONDS ),
'endTime' => \gmdate( 'Y-m-d\TH:i:s\Z', time() + 2 * MONTH_IN_SECONDS + HOUR_IN_SECONDS ),
'name' => 'Remote Event in Two Months',
'to' => 'https://www.w3.org/ns/activitystreams#Public',
'published' => '2020-01-01T00:00:00Z',
'location' => array(
'type' => 'Place',
'name' => 'Fediverse Concert Hall',
'address' => 'Fedistreet 13, Feditown 1337',
),
),
);
$request = new WP_REST_Request( 'POST', '/activitypub/1.0/users/0/inbox' );
$request->set_header( 'Content-Type', 'application/activity+json' );
// Receive both events.
$request->set_body( \wp_json_encode( $event_in_one_week ) );
$response = \rest_do_request( $request );
$this->assertEquals( 202, $response->get_status() );
$request->set_body( \wp_json_encode( $event_in_two_months ) );
$response = \rest_do_request( $request );
$this->assertEquals( 202, $response->get_status() );
// Create a local event in VS Event List.
$post_id = \wp_insert_post(
array(
'post_title' => 'VSEL Local Test Event',
'post_status' => 'publish',
'post_type' => 'event',
'meta_input' => array(
'event-start-date' => \strtotime( '+10 days 15:00:00' ),
'event-date' => \strtotime( '+10 days 16:00:00' ),
'event-link' => 'https://event-federation.eu/vsel-test-event',
'event-link-label' => 'Website',
),
)
);
$this->assertNotEquals( false, $post_id );
// Only one event should show up in the remote events query.
$events = VS_Event_List::get_cached_remote_events( time() + MONTH_IN_SECONDS );
$this->assertEquals( 1, count( $events ) );
$this->assertEquals( $event_in_one_week['object']['id'], get_post( $events[0] )->guid );
// Include the even in two months in the time_span.
$events = VS_Event_List::get_cached_remote_events( time() + 3 * MONTH_IN_SECONDS );
$this->assertEquals( 2, count( $events ) );
// All events are in the future, so no events should be in past.
$events = VS_Event_List::get_cached_remote_events( time() );
$this->assertEquals( 0, count( $events ) );
\remove_filter( 'activitypub_defer_signature_verification', '__return_true' );
}
}