diff --git a/activitypub-event-extensions.php b/activitypub-event-extensions.php index 68bea23..e039439 100644 --- a/activitypub-event-extensions.php +++ b/activitypub-event-extensions.php @@ -1,7 +1,7 @@ post_type === 'event' ) { - require_once __DIR__ . '/includes/activitypub/transformer/class-vs-event.php'; - return new \VS_Event( $wp_object ); - } +define( 'ACTIVITYPUB_EVENT_EXTENSIONS_PLUGIN_DIR', plugin_dir_path( __FILE__ ) ); +define( 'ACTIVITYPUB_EVENT_EXTENSIONS_PLUGIN_BASENAME', plugin_basename( __FILE__ ) ); +define( 'ACTIVITYPUB_EVENT_EXTENSIONS_PLUGIN_FILE', plugin_dir_path( __FILE__ ) . '/' . basename( __FILE__ ) ); +define( 'ACTIVITYPUB_EVENT_EXTENSIONS_PLUGIN_URL', plugin_dir_url( __FILE__ ) ); - /** - * Events manager - * @see https://wordpress.org/plugins/events-manager/ - */ - if ( class_exists( 'EM_Events' ) && $wp_object->post_type === 'event' ) { - require_once __DIR__ . '/includes/activitypub/transformer/class-events-manager.php'; - return new \Events_Manager( $wp_object ); - } +// Include and register the autoloader class for automatic loading of plugin classes. +require_once ACTIVITYPUB_EVENT_EXTENSIONS_PLUGIN_DIR . '/includes/class-autoloader.php'; +Activitypub_Event_Extensions\Autoloader::register(); - /** - * Events manager - * @see https://wordpress.org/plugins/events-manager/ - */ - if ( class_exists( 'GatherPress\Core\Event' ) && $wp_object->post_type === 'gp_event' ) { - require_once __DIR__ . '/includes/activitypub/transformer/class-gatherpress.php'; - return new \GatherPress( $wp_object ); - } - - // Return the default transformer. - - return $transformer; - }, - 10, - 3 -); +// Initialize the plugin. +Activitypub_Event_Extensions\Setup::get_instance(); -/** - * Activate the plugin. - */ -function activitypub_event_extensions_activate() { - // Don't allow plugin activation, when the ActivityPub plugin is not activated yet. - if( ! class_exists( 'ActivtiyPub' ) ) { - deactivate_plugins( plugin_basename( __FILE__ ) ); - wp_die( __( 'Please install and Activate ActivityPub.', 'activitypub-event-extensions' ), 'Plugin dependency check', array( 'back_link' => true ) ); - } -} - -register_activation_hook( __FILE__, 'activitypub_event_extensions_activate' ); -// TODO: -// require_once __DIR__ . '/admin/class-admin-notices.php'; -// new \Admin_Notices(); + + +// For local development purposes: TODO. Remove everything after here. /** * Add a filter for http_request_host_is_external * - * TODO: Remove this. + * TODO: Remove this for release. * * @todo This filter is temporary code needed to do local testing. */ add_filter( 'http_request_host_is_external', 'custom_http_request_host_is_external', 10, 3 ); -// Your custom callback function +/** + * Add a filter for http_request_host_is_external + * + * TODO: Remove this for release. + * + * @todo This filter is temporary code needed to do local testing. + */ function custom_http_request_host_is_external( $is_external, $host, $url ) { $is_external = true; @@ -102,7 +64,7 @@ function custom_http_request_host_is_external( $is_external, $host, $url ) { /** * Don't verify ssl certs for testing. * - * TODO: Remove this. + * TODO: Remove this for release. * * @todo This filter is temporary code needed to do local testing. */ diff --git a/admin/class-admin-notices.php b/admin/class-admin-notices.php deleted file mode 100644 index d936461..0000000 --- a/admin/class-admin-notices.php +++ /dev/null @@ -1,115 +0,0 @@ -%1$s settings.', - 'admin notice', - 'your-text-domain' - ), - $activitypub_plugin_data['Name'], - $vsel_plugin_data['Name'], - admin_url( 'options-general.php?page=activitypub&tab=settings' ) - ); - wp_admin_notice( - $notice, - array( - 'type' => 'warning', - 'dismissible' => true, - ) - ); - } -} diff --git a/includes/activitypub/handler/class-join.php b/includes/activitypub/handler/class-join.php deleted file mode 100644 index 20e4d45..0000000 --- a/includes/activitypub/handler/class-join.php +++ /dev/null @@ -1,6 +0,0 @@ -get_id(); + } + + /** + * Returns the ActivityStreams 2.0 Object-Type for an Event. + * + * @see https://www.w3.org/TR/activitystreams-vocabulary/#dfn-event + * + * @return string The Event Object-Type. + */ + protected function get_object_type() { + return 'Event'; + } + + /** + * Generic function that converts an WP-Event object to an ActivityPub-Event object. + * + * @return Event_Object + */ + public function to_object() { + $activitypub_object = new Event_Object(); + $activitypub_object = $this->transform_object_properties( $activitypub_object ); + + $published = \strtotime( $this->wp_object->post_date_gmt ); + + $activitypub_object->set_published( \gmdate( 'Y-m-d\TH:i:s\Z', $published ) ); + + $updated = \strtotime( $this->wp_object->post_modified_gmt ); + + if ( $updated > $published ) { + $activitypub_object->set_updated( \gmdate( 'Y-m-d\TH:i:s\Z', $updated ) ); + } + + $activitypub_object->set_content_map( + array( + $this->get_locale() => $this->get_content(), + ) + ); + $path = sprintf( 'actors/%d/followers', intval( $this->wp_object->post_author ) ); + + $activitypub_object->set_to( + array( + 'https://www.w3.org/ns/activitystreams#Public', + get_rest_url_by_path( $path ), + ) + ); + + return $activitypub_object; + } +} diff --git a/includes/activitypub/transformer/class-events-manager.php b/includes/activitypub/transformer/class-events-manager.php index b0473d8..8fb4b91 100644 --- a/includes/activitypub/transformer/class-events-manager.php +++ b/includes/activitypub/transformer/class-events-manager.php @@ -7,11 +7,10 @@ * @license AGPL-3.0-or-later */ +use Activitypub_Event_Extensions\Activitypub\Transformer\Event as Event_Transformer; use EM_Event; use Activitypub\Activity\Extended_Object\Event; use Activitypub\Activity\Extended_Object\Place; -use Activitypub\Transformer\Post; - use function Activitypub\esc_hashtag; if ( ! defined( 'ABSPATH' ) ) { @@ -25,7 +24,7 @@ if ( ! defined( 'ABSPATH' ) ) { * * @since 1.0.0 */ -class Events_Manager extends Post { +class Events_Manager extends Event_Transformer { /** * Holds the EM_Event object. @@ -44,7 +43,6 @@ class Events_Manager extends Post { * @return string Widget name. */ public function get_transformer_name() { - return 'activitypub-event-transformers/events-manager'; } @@ -58,7 +56,6 @@ class Events_Manager extends Post { * @return string Widget title. */ public function get_transformer_label() { - return 'Events Manager'; } @@ -72,7 +69,6 @@ class Events_Manager extends Post { * @return array Widget categories. */ public static function get_supported_post_types() { - return array(); } @@ -84,23 +80,19 @@ class Events_Manager extends Post { * @return string The Event Object-Type. */ protected function get_type() { - return 'Event'; } protected function get_is_online() { - return 'url' === $this->em_event->event_location_type; } /** * Get the event location. * - * @param int $post_id The WordPress post ID. * @return array The Place. */ public function get_location() { - if ( 'url' === $this->em_event->event_location_type ) { return null; } @@ -132,7 +124,6 @@ class Events_Manager extends Post { * Get the end time from the events metadata. */ protected function get_end_time() { - return null; } @@ -140,7 +131,6 @@ class Events_Manager extends Post { * Get the end time from the events metadata. */ protected function get_start_time() { - $date_string = $this->em_event->event_start_date; $time_string = $this->em_event->event_start_time; $timezone_string = $this->em_event->event_timezone; @@ -157,7 +147,6 @@ class Events_Manager extends Post { } protected function get_maximum_attendee_capacity() { - return $this->em_event->event_spaces; } @@ -165,25 +154,21 @@ class Events_Manager extends Post { * @todo decide whether to include pending bookings or not! */ protected function get_remaining_attendee_capacity() { - $em_bookings = $this->em_event->get_bookings()->get_bookings(); $remaining_attendee_capacity = $this->em_event->event_spaces - count( $em_bookings->bookings ); return $remaining_attendee_capacity; } protected function get_participant_count() { - $em_bookings = $this->em_event->get_bookings()->get_bookings(); return count( $em_bookings->bookings ); } protected function get_content() { - return $this->wp_object->post_content; } protected function get_summary() { - if ( $this->em_event->post_excerpt ) { $excerpt = $this->em_event->post_excerpt; } else { @@ -202,7 +187,6 @@ class Events_Manager extends Post { // } private function get_event_link_attachment() { - $event_link_url = $this->em_event->event_location->data['url']; $event_link_text = $this->em_event->event_location->data['text']; return array( @@ -218,7 +202,7 @@ class Events_Manager extends Post { * Overrides/extends the get_attachments function to also add the event Link. */ protected function get_attachment() { - // Get attachments via parent function + // Get attachments via parent function. $attachments = parent::get_attachment(); // The first attachment is the featured image, make sure it is compatible with Mobilizon. diff --git a/includes/activitypub/transformer/class-gatherpress.php b/includes/activitypub/transformer/class-gatherpress.php index c6fd70a..e0c0b80 100644 --- a/includes/activitypub/transformer/class-gatherpress.php +++ b/includes/activitypub/transformer/class-gatherpress.php @@ -8,7 +8,7 @@ use Activitypub\Transformer\Post; use Activitypub\Model\Blog_user; -use Activitypub\Activity\Extended_Object\Event; +use Activitypub\Activity\Extended_Object\Event as Event_Object; use Activitypub\Activity\Extended_Object\Place; use GatherPress\Core\Event as GatherPress_Event; @@ -26,7 +26,7 @@ if ( ! defined( 'ABSPATH' ) ) { class GatherPress extends Post { /** - * The target transformet ActivityPub Event object. + * The target ActivityPub Event object of the transformer. * * @var Event */ diff --git a/includes/activitypub/transformer/class-tribe.php b/includes/activitypub/transformer/class-tribe.php index b67d3ae..12b2657 100644 --- a/includes/activitypub/transformer/class-tribe.php +++ b/includes/activitypub/transformer/class-tribe.php @@ -1,5 +1,4 @@ tribe_event = tribe_get_event( $wp_post->ID ); // } - /** - * Get widget name. - * - * Retrieve oEmbed widget name. - * - * @since 1.0.0 - * @access public - * @return string Widget name. - */ - public function get_name() { - - return 'activitypub-event-transformers/tribe'; - } - - /** - * Get widget title. - * - * Retrieve Transformer title. - * - * @since 1.0.0 - * @access public - * @return string Widget title. - */ - public function get_label() { - - return 'The Events Calendar'; - } - - /** - * Returns the ActivityStreams 2.0 Object-Type for an Event. - * - * @see https://www.w3.org/TR/activitystreams-vocabulary/#dfn-event - * - * @return string The Event Object-Type. - */ - protected function get_object_type() { - - return 'Event'; - } - - /** - * Get supported post types. - * - * Retrieve the list of supported WordPress post types this transformer widget can handle. - * - * @since 1.0.0 - * @access public - * @return array Widget categories. - */ - public static function get_supported_post_types() { - - return array( 'tribe_events' ); - } - /** * Get tribe category of wp_post * * @return string|null tribe category if it exists */ public function get_tribe_category() { - // todo make it possible that one event can have multiple categories? - - // using cat_slugs isn't 100% nice way to do this, don't know if it's a good idea + // TODO: make it possible that one event can have multiple categories? + // Using cat_slugs isn't the best way to do this, don't know if it's a good idea. $categories = tribe_get_event_cat_slugs( $this->wp_object->ID ); if ( count( $categories ) === 0 ) { @@ -123,7 +69,7 @@ class Tribe extends Post { return 'CANCELLED'; } if ( 'postponed' === $this->tribe_event->event_status ) { - return 'CANCELLED'; // this will be reflected in the cancelled reason + return 'CANCELLED'; // This will be reflected in the cancelled reason. } if ( '' === $this->tribe_event->event_status ) { return 'CONFIRMED'; @@ -142,12 +88,12 @@ class Tribe extends Post { protected function get_content() { $content = parent::get_content(); - // todo remove link at the end of the content + // TODO: remove link at the end of the content. - // todo add organizer - // $this->tribe_event->organizers[0] + // TODO: add organizer + // $this->tribe_event->organizers[0]. - // todo add Canclled reason in the content (maybe at the end) + // TODO: do add Cancelled reason in the content (maybe at the end). return $content; } @@ -155,26 +101,27 @@ class Tribe extends Post { /** * Get the event location. * - * @param int $post_id The WordPress post ID. * @returns array The Place. */ public function get_event_location() { /* - 'post_title' => 'testvenue', - 'post_name' => 'testvenue', - 'guid' => 'http://localhost/venue/testvenue/', - 'post_type' => 'tribe_venue', - 'address' => 'testaddr', - 'country' => 'Austria', - 'city' => 'testcity', - 'state_province' => 'testprovince', - 'state' => '', - 'province' => 'testprovince', - 'zip' => '8000', - 'phone' => '+4312343', - 'permalink' => 'http://localhost/venue/testvenue/', - 'directions_link' => 'https://maps.google.com/maps?f=q&source=s_q&hl=en&geocode=&q=testaddr+testcity+testprovince+8000+Austria', - 'website' => 'https://test.at', + This is how the Tribe event looks like: + TODO: Remove this comment. + 'post_title' => 'testvenue', + 'post_name' => 'testvenue', + 'guid' => 'http://localhost/venue/testvenue/', + 'post_type' => 'tribe_venue', + 'address' => 'testaddr', + 'country' => 'Austria', + 'city' => 'testcity', + 'state_province' => 'testprovince', + 'state' => '', + 'province' => 'testprovince', + 'zip' => '8000', + 'phone' => '+4312343', + 'permalink' => 'http://localhost/venue/testvenue/', + 'directions_link' => 'https://maps.google.com/maps?f=q&source=s_q&hl=en&geocode=&q=testaddr+testcity+testprovince+8000+Austria', + 'website' => 'https://test.at', */ $venue = $this->tribe_event->venues[0]; return ( new Place() ) @@ -184,6 +131,6 @@ class Tribe extends Post { $venue->address . "\n" . $venue->zip . ', ' . $venue->city . "\n" . $venue->country - ); // todo add checks that everything exists here (lol) + ); // TODO: add checks that everything exists here. } } diff --git a/includes/activitypub/transformer/class-vs-event.php b/includes/activitypub/transformer/class-vs-event.php index 4eaa23e..a48e596 100644 --- a/includes/activitypub/transformer/class-vs-event.php +++ b/includes/activitypub/transformer/class-vs-event.php @@ -1,5 +1,4 @@ get_event_link(); if ( $event_link ) { - $attachments[] = $this->get_event_link(); + $attachments[] = $event_link; } return $attachments; } @@ -159,60 +160,7 @@ class VS_Event extends Post { * @return string $category */ protected function get_category() { - - $post_categories = wp_get_post_terms( $this->wp_object->ID, 'event_cat' ); - - if ( empty( $post_categories ) ) { - return 'MEETING'; - } - - // Prepare an array to store all category information for comparison. - $category_info = array(); - - // Extract relevant category information (name, slug, description) from the categories array. - foreach ( $post_categories as $category ) { - $category_info[] = strtolower( $category->name ); - $category_info[] = strtolower( $category->slug ); - $category_info[] = strtolower( $category->description ); - } - - // Convert mobilizon categories to lowercase for case-insensitive comparison. - $mobilizon_categories = array_map( 'strtolower', Event::DEFAULT_EVENT_CATEGORIES ); - - // Initialize variables to track the best match. - $best_mobilizon_category_match = ''; - $best_match_length = 0; - - // Check for the best match. - foreach ( $mobilizon_categories as $mobilizon_category ) { - foreach ( $category_info as $category ) { - foreach ( explode( '_', $mobilizon_category ) as $mobilizon_category_slice ) { - if ( stripos( $category, $mobilizon_category_slice ) !== false ) { - // Check if the current match is longer than the previous best match. - $current_match_legnth = strlen( $mobilizon_category_slice ); - if ( $current_match_legnth > $best_match_length ) { - $best_mobilizon_category_match = $mobilizon_category; - $best_match_length = $current_match_legnth; - } - } - } - } - } - - return ( '' != $best_mobilizon_category_match ) ? strtoupper( $best_mobilizon_category_match ) : 'MEETING'; - } - - /** - * Returns the User-URL of the Author of the Post. - * - * If `single_user` mode is enabled, the URL of the Blog-User is returned. - * - * @return string The User-URL. - */ - protected function get_attributed_to() { - - $user = new Blog_User(); - return $user->get_url(); + return 'MEETING'; } /** @@ -224,7 +172,6 @@ class VS_Event extends Post { * @return string $summary The custom event summary. */ public function get_summary() { - if ( $this->wp_object->excerpt ) { $excerpt = $this->wp_object->post_excerpt; } elseif ( get_post_meta( $this->wp_object->ID, 'event-summary', true ) ) { @@ -315,7 +262,8 @@ class VS_Event extends Post { ->set_in_language( $this->get_locale() ) ->set_actor( get_rest_url_by_path( 'application' ) ) ->set_to( array( 'https://www.w3.org/ns/activitystreams#Public' ) ) - ->set_location(); + ->set_location() + ->set_id(); return $this->ap_object; } } diff --git a/includes/admin/class-event-plugin-admin-notices.php b/includes/admin/class-event-plugin-admin-notices.php new file mode 100644 index 0000000..9c0ba29 --- /dev/null +++ b/includes/admin/class-event-plugin-admin-notices.php @@ -0,0 +1,93 @@ +event_plugin = $event_plugin; + if ( $this->event_post_type_is_not_activitypub_enabled() ) { + add_action( 'admin_notices', array( $this, 'admin_notice_activitypub_not_enabled_for_post_type' ), 10, 1 ); + } + } + + /** + * Check if ActivityPub is enabled for the custom post type of the event plugin. + * + * @return bool + */ + private function event_post_type_is_not_activitypub_enabled() { + return ! in_array( $this->event_plugin['post_type'], get_option( 'activitypub_support_post_types', array() ), true ); + } + + /** + * Display the admin notices for the plugins. + */ + public function admin_notice_activitypub_not_enabled_for_post_type() { + // Get the current page. + $screen = get_current_screen(); + // Check if we are on a edit page for the event, or on the settings page of the event plugin. + $is_event_plugins_edit_page = 'edit' === $screen->base && $this->event_plugin['post_type'] === $screen->post_type; + $is_event_plugins_settings_page = $this->event_plugin['settings_page_id'] === $screen->id; + + if ( $is_event_plugins_edit_page || $is_event_plugins_settings_page ) { + $this->do_admin_notice_post_type_not_activitypub_enabled( $this->event_plugin['plugin_file'] ); + } + } + + /** + * Print admin notice that the current post type is not enabled in the ActivityPub plugin. + * + * @param string $event_plugin_file The event plugin file path. + */ + private function do_admin_notice_post_type_not_activitypub_enabled( $event_plugin_file ) { + $event_plugin_data = get_plugin_data( WP_PLUGIN_DIR . '/' . $event_plugin_file ); + $activitypub_plugin_data = get_plugin_data( ACTIVITYPUB_PLUGIN_FILE ); + $notice = sprintf( + /* translators: 1: the name of the event plugin a admin notice is shown. 2: The name of the ActivityPub plugin. */ + _x( + 'You have installed the %1$s plugin, but the event post type of the plugin %2$s is not enabled in the %1$s settings.', + 'admin notice', + 'activitypub-event-extensions' + ), + esc_html( $activitypub_plugin_data['Name'] ), + esc_html( $event_plugin_data['Name'] ), + admin_url( 'options-general.php?page=activitypub&tab=settings' ) + ); + $allowed_html = array( + 'a' => array( + 'href' => true, + 'title' => true, + ), + 'b' => array(), + 'i' => array(), + ); + echo '

' . \wp_kses( $notice, $allowed_html ) . '

'; + } +} diff --git a/includes/admin/class-general-admin-notices.php b/includes/admin/class-general-admin-notices.php new file mode 100644 index 0000000..d264638 --- /dev/null +++ b/includes/admin/class-general-admin-notices.php @@ -0,0 +1,73 @@ +ActivityPub plugin.', + 'admin notice', + 'activitypub-event-extensions' + ), + esc_html( $activitypub_plugin_url ), + admin_url( 'options-general.php?page=activitypub&tab=settings' ) + ); + $allowed_html = array( + 'a' => array( + 'href' => true, + 'title' => true, + ), + ); + echo '

' . \wp_kses( $notice, $allowed_html ) . '

'; + } + + /** + * Warning that no supported event plugin can be found. + */ + public static function do_admin_notice_no_supported_event_plugin_active() { + $supported_event_plugins_url = 'https://code.event-federation.eu/Event-Federation/wordpress-activitypub-event-extensions#events-plugin-that-will-be-supported-at-first'; + + $notice = sprintf( + /* translators: 1: the name of the event plugin a admin notice is shown. 2: The name of the ActivityPub plugin. */ + _x( + 'The Plugin ActivityPub Event Extensions is of no use, because you do not have installed and activated a supported Event Plugin. +
For a list of supported Event Plugins see here.', + 'admin notice', + 'activitypub-event-extensions' + ), + esc_html( $supported_event_plugins_url ), + admin_url( 'options-general.php?page=activitypub&tab=settings' ) + ); + $allowed_html = array( + 'a' => array( + 'href' => true, + 'title' => true, + ), + 'br', + 'i', + ); + echo '

' . \wp_kses( $notice, $allowed_html ) . '

'; + } +} diff --git a/includes/category-mapper.php b/includes/category-mapper.php deleted file mode 100644 index 3ba9f75..0000000 --- a/includes/category-mapper.php +++ /dev/null @@ -1,67 +0,0 @@ -name ); - $category_info[] = strtolower( $category->slug ); - $category_info[] = strtolower( $category->description ); - } - - // Convert mobilizon categories to lowercase for case-insensitive comparison. - $mobilizon_categories = array_map( 'strtolower', Event::DEFAULT_EVENT_CATEGORIES ); - - // Initialize variables to track the best match. - $best_mobilizon_category_match = ''; - $best_match_length = 0; - - // Check for the best match. - foreach ( $mobilizon_categories as $mobilizon_category ) { - foreach ( $category_info as $category ) { - foreach ( explode( '_', $mobilizon_category ) as $mobilizon_category_slice ) { - if ( stripos( $category, $mobilizon_category_slice ) !== false ) { - // Check if the current match is longer than the previous best match. - $current_match_legnth = strlen( $mobilizon_category_slice ); - if ( $current_match_legnth > $best_match_length ) { - $best_mobilizon_category_match = $mobilizon_category; - $best_match_length = $current_match_legnth; - } - } - } - } - } - - return ( '' != $best_mobilizon_category_match ) ? strtoupper( $best_mobilizon_category_match ) : 'MEETING'; - } -} diff --git a/includes/class-autoloader.php b/includes/class-autoloader.php new file mode 100644 index 0000000..dcc18dc --- /dev/null +++ b/includes/class-autoloader.php @@ -0,0 +1,66 @@ + array( + 'plugin_file' => 'events-manager/events-manager.php', + 'post_type' => 'event', + 'settings_page' => 'options-general.php?page=vsel', + 'transformer_class' => 'Events_Manager', + ), + 'gatherpress' => array( + 'plugin_file' => 'gatherpress/gatherpress.php', + 'post_type' => 'gatherpress_event', + 'transformer_class' => 'GatherPress', + 'settings_page_id' => 'gatherpress_general', + ), + 'the_events_calendar' => array( + 'plugin_file' => 'the-events-calendar/the-events-calendar.php', + 'post_type' => 'tribe_events', + 'transformer_class' => 'Tribe', + 'settings_page_id' => 'tribe_general', + ), + 'vsel' => array( + 'plugin_file' => 'very-simple-event-list/vsel.php', + 'post_type' => 'event', + 'settings_page_id' => 'settings_page_vsel', + 'transformer_class' => 'VS_Event', + ), + ); + + /** + * Keep the information whether the ActivityPub plugin is active. + * + * @var boolean + */ + protected $activitypub_plugin_is_active = false; + + /** + * Holds an array of the currently activated supported event plugins. + * + * @var array + */ + protected $active_event_plugins = array(); + + /** + * Constructor for the Setup class. + * + * Initializes and sets up various components of the plugin. + * + * @since 1.0.0 + */ + protected function __construct() { + $this->activitypub_plugin_is_active = is_plugin_active( 'activitypub/activitypub.php' ); + $this->active_event_plugins = self::detect_supported_event_plugins(); + $this->setup_hooks(); + } + + /** + * The single instance of the class. + * + * @since 1.0.0 + * @var ?self|null The instance of the class. + */ + private static $instance = null; + + /** + * Get the instance of the Singleton class. + * + * If an instance does not exist, it creates one; otherwise, it returns the existing instance. + * + * @since 1.0.0 + * + * @return self The instance of the class. + */ + public static function get_instance(): self { + if ( null === self::$instance ) { + self::$instance = new self(); + } + + return self::$instance; + } + + /** + * Function that checks for supported activated event plugins. + * + * @return array List of supported event plugins as keys from the SUPPORTED_EVENT_PLUGINS const. + */ + public static function detect_supported_event_plugins(): array { + $active_event_plugins = array(); + foreach ( self::SUPPORTED_EVENT_PLUGINS as $event_plugin_key => $event_plugin ) { + if ( \is_plugin_active( $event_plugin['plugin_file'] ) ) { + $active_event_plugins[ $event_plugin_key ] = $event_plugin; + } + } + return $active_event_plugins; + } + + /** + * Set up hooks for various purposes. + * + * This method adds hooks for different purposes as needed. + * + * @since 1.0.0 + * + * @return void + */ + protected function setup_hooks(): void { + register_activation_hook( ACTIVITYPUB_EVENT_EXTENSIONS_PLUGIN_FILE, array( $this, 'activate' ) ); + + add_action( 'admin_init', array( $this, 'do_admin_notices' ) ); + + // If we don't have any active event plugins, or the ActivityPub plugin is not enabled, abort here. + if ( empty( $this->active_event_plugins ) || ! $this->activitypub_plugin_is_active ) { + return; + } + + add_filter( 'activitypub_transformer', array( $this, 'register_activitypub_event_transformer' ), 10, 3 ); + } + + /** + * Fires the initialization of admin notices for all active supported event plugins.s + */ + public function do_admin_notices(): void { + foreach ( $this->active_event_plugins as $event_plugin ) { + new Event_Plugin_Admin_Notices( $event_plugin ); + } + if ( ! $this->activitypub_plugin_is_active ) { + add_action( 'admin_notices', array( new General_Admin_Notices(), 'do_admin_notice_activitypub_plugin_not_enabled' ), 10, 1 ); + } + if ( empty( $this->active_event_plugins ) ) { + add_action( 'admin_notices', array( new General_Admin_Notices(), 'do_admin_notice_no_supported_event_plugin_active' ), 10, 1 ); + } + } + + /** + * Add the custom transformers for the events of several WordPress event plugins. + * + * @param Activitypub\Transformer\Base $transformer The transformer to use. + * @param mixed $wp_object The WordPress object to transform. + * @param string $object_class The class of the object to transform. + * + * @return \Activitypub\Transformer\Base|null + */ + public function register_activitypub_event_transformer( $transformer, $wp_object, $object_class ): \Activitypub\Transformer\Base|null { + // If the current WordPress object is not a post (e.g., a WP_Comment), don't change the transformer. + if ( 'WP_Post' !== $object_class ) { + return $transformer; + } + + // 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['post_type'] ) { + $transformer_class = 'Activitypub_Event_Extensions\Activitypub\Transformer\\' . $event_plugin['transformer_class']; + return new $transformer_class( $wp_object ); + } + } + + // Return the default transformer. + return $transformer; + } + + /** + * Activates the ActivityPub Event Extensions plugin. + * + * This method handles the activation of the ActivityPub Event Extensions plugin. + * + * @since 1.0.0 + * + * @return void + */ + public function activate() { + // Don't allow plugin activation, when the ActivityPub plugin is not activated yet. + if ( ! $this->activitypub_plugin_is_active ) { + deactivate_plugins( plugin_basename( ACTIVITYPUB_EVENT_EXTENSIONS_PLUGIN_FILE ) ); + $notice = sprintf( + /* translators: 1: the name of the event plugin a admin notice is shown. 2: The name of the ActivityPub plugin. */ + _x( + 'To use this plugin install and activate the %2$s plugin first.', + 'admin notice', + 'activitypub-event-extensions' + ), + esc_html( 'https://wordpress.org/plugins/activitypub' ), + esc_html( 'ActivityPub' ) + ); + $allowed_html = array( + 'a' => array( + 'href' => true, + 'title' => true, + ), + ); + wp_die( + wp_kses( $notice, $allowed_html ), + 'Plugin dependency check', + array( 'back_link' => true ), + ); + } + // If someone installs this plugin, we simply enable ActivityPub support for the event post type, without asking. + $activitypub_supported_post_types = get_option( 'activitypub_support_post_types', array() ); + foreach ( $this->active_event_plugins as $event_plugin ) { + if ( ! in_array( $event_plugin['post_type'], $activitypub_supported_post_types, true ) ) { + $activitypub_supported_post_types[] = $event_plugin['post_type']; + } + } + update_option( 'activitypub_support_post_types', $activitypub_supported_post_types ); + } +}