wp_object ); } /** * Set a hardcoded template for the content. * * This actually disabled templates for the content. * Maybe this independent templates for events will be added later. */ protected function get_post_content_template(): string { return '[ap_content]'; } /** * Returns the title of the event. * * @see https://www.w3.org/TR/activitystreams-vocabulary/#dfn-name * * @return string The name. */ protected function get_name(): string { return $this->wp_object->post_title; } /** * Extend the construction of the Post Transformer to also set the according taxonomy of the event post type. * * @param WP_Post $wp_object The WordPress post object (event). * @param string $wp_taxonomy The taxonomy slug of the event post type. */ public function __construct( $wp_object, $wp_taxonomy = 'category' ) { parent::__construct( $wp_object ); $this->wp_taxonomy = $wp_taxonomy; } /** * Extract the join mode. * * Currently we don't handle joins, we always mark events as external. * * @return string */ public function get_join_mode(): ?string { return 'external'; } /** * Extract the external participation url. * * Currently we don't handle joins, we always mark events as external. * We just link back to the events HTML representation on our WordPress site. * * @return ?string The external participation URL. */ public function get_external_participation_url(): ?string { return 'external' === $this->get_join_mode() ? $this->get_url() : null; } /** * Set the event category, via the mapping setting. * * @return ?string */ public function get_category(): ?string { if ( is_null( $this->wp_taxonomy ) ) { return null; } $current_category_mapping = \get_option( 'activitypub_event_bridge_event_category_mappings', array() ); $terms = \get_the_terms( $this->wp_object, $this->wp_taxonomy ); // Check if the event has a category set and if that category has a specific mapping return that one. if ( ! is_wp_error( $terms ) && $terms && array_key_exists( $terms[0]->slug, $current_category_mapping ) ) { return sanitize_text_field( $current_category_mapping[ $terms[0]->slug ] ); } else { // Return the default event category. return sanitize_text_field( \get_option( 'activitypub_event_bridge_default_event_category', 'MEETING' ) ); } } /** * Retrieves the excerpt text (may be HTML). Used for constructing the summary. * * @return ?string */ protected function retrieve_excerpt(): ?string { if ( $this->wp_object->post_excerpt ) { return $this->wp_object->post_excerpt; } else { return null; } } /** * Get the start time. * * This is mandatory and must be implemented in the final event transformer class. */ abstract public function get_start_time(): string; /** * Get the end time. * * This is not mandatory and therefore just return null by default. */ public function get_end_time(): ?string { return null; } /** * Get a default for the location. * * This should be overridden in the actual event transformer. */ public function get_location(): ?Place { return null; } /** * Default value for the event status. */ public function get_status(): ?string { return 'CONFIRMED'; } /** * Compose a human readable formatted start time. */ protected function format_start_time(): string { return $this->format_time( $this->get_start_time() ); } /** * Compose a human readable formatted end time. */ protected function format_end_time(): string { return $this->format_time( $this->get_end_time() ); } /** * Compose a human readable formatted time. * * @param ?string $time The time which needs to be formatted. */ private static function format_time( $time ) { if ( is_null( $time ) ) { return ''; } $start_datetime = new DateTime( $time ); $start_timestamp = $start_datetime->getTimestamp(); $datetime_format = get_option( 'date_format' ) . ' ' . get_option( 'time_format' ); return wp_date( $datetime_format, $start_timestamp ); } /** * Format a human readable address. */ protected function format_address(): string { $location = $this->get_location(); if ( is_null( $location ) ) { return ''; } $address = $location->get_address(); if ( ! $address ) { return $location->get_name(); } if ( is_string( $address ) ) { return $address; } if ( ! is_array( $address ) ) { return ''; } return isset( $address['locality'] ) ? $address['locality'] : ''; } /** * Format the category using the translation. */ protected function format_categories(): string { if ( is_null( $this->wp_taxonomy ) ) { return ''; } $categories = array(); // Add the federated category string. require_once ACTIVITYPUB_EVENT_BRIDGE_PLUGIN_DIR . '/includes/event-categories.php'; $federated_category = $this->get_category(); if ( array_key_exists( $federated_category, ACTIVITYPUB_EVENT_BRIDGE_EVENT_CATEGORIES ) ) { $categories[] = ACTIVITYPUB_EVENT_BRIDGE_EVENT_CATEGORIES[ $federated_category ]; } // Add all category terms. $terms = \get_the_terms( $this->wp_object, $this->wp_taxonomy ); if ( $terms && ! is_wp_error( $terms ) ) { foreach ( $terms as $term ) { $categories[] = $term->name; } } if ( ! empty( $categories ) ) { return implode( ' ยท ', array_unique( $categories ) ); } return ''; } /** * Get the summary. */ public function get_summary(): ?string { if ( 'preset' === get_option( 'activitypub_summary_type', 'preset' ) ) { return $this->get_preset_summary(); } // phpcs:ignore WordPress.WP.GlobalVariablesOverride.Prohibited $post = $this->wp_object; $summary = $this->get_event_summary_template(); // It seems that shortcodes are only applied to published posts. if ( is_preview() ) { $post->post_status = 'publish'; } // Register our shortcodes just in time. Event_Shortcodes::register(); // Fill in the shortcodes. \setup_postdata( $post ); $summary = \do_shortcode( $summary ); \wp_reset_postdata(); $summary = \wpautop( $summary ); $summary = \preg_replace( '/[\n\r\t]/', '', $summary ); $summary = \trim( $summary ); $summary = \apply_filters( 'activitypub_event_bridge_the_summary', $summary, $post ); // Don't need these anymore, should never appear in a post. Event_Shortcodes::unregister(); return $summary; } /** * Create a custom summary. * * It contains also the most important meta-information. The summary is often used when the * ActivityPub object type 'Event' is not supported, e.g. in Mastodon. * * @return string $summary The custom event summary. */ public function get_preset_summary(): ?string { add_filter( 'activitypub_object_content_template', array( self::class, 'remove_ap_permalink_from_template' ), 2, 2 ); $excerpt = $this->retrieve_excerpt(); // BeforeFirstRelease: decide whether this should be a admin setting. $fallback_to_content = false; if ( is_null( $excerpt ) && $fallback_to_content ) { $excerpt = $this->get_content(); } remove_filter( 'activitypub_object_content_template', array( self::class, 'remove_ap_permalink_from_template' ) ); $category = $this->format_categories(); $start_time = $this->format_start_time(); $end_time = $this->format_end_time(); $address = $this->format_address(); $formatted_items = array(); if ( ! empty( $category ) ) { $formatted_items[] = '๐ท๏ธ ' . __( 'Category', 'activitypub-event-bridge' ) . ': ' . $category; } if ( ! empty( $start_time ) ) { $formatted_items[] = '๐๏ธ ' . __( 'Start', 'activitypub-event-bridge' ) . ': ' . $start_time; } if ( ! empty( $end_time ) ) { $formatted_items[] = 'โณ ' . __( 'End', 'activitypub-event-bridge' ) . ': ' . $end_time; } if ( ! empty( $address ) ) { $formatted_items[] = '๐ ' . __( 'Address', 'activitypub-event-bridge' ) . ': ' . $address; } // Compose the summary based on the number of meta items. if ( count( $formatted_items ) > 1 ) { $summary = '