updated plugin Event Bridge for ActivityPub version 1.3.0

This commit is contained in:
2026-06-03 21:28:57 +00:00
committed by Gitium
parent 1f3438440f
commit f2d6714572
84 changed files with 1721 additions and 1361 deletions

View File

@ -1,24 +0,0 @@
name: Plugin asset/readme update on WordPress.org
on: workflow_dispatch
jobs:
trunk:
name: Push assets to trunk on WordPress.org
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: https://code.forgejo.org/actions/checkout@v4
- name: Install and cache rsync
uses: https://github.com/awalsh128/cache-apt-pkgs-action@latest
with:
packages: rsync subversion
version: 1.0
- name: WordPress.org plugin asset/readme update
uses: https://github.com/10up/action-wordpress-plugin-asset-update@stable
env:
SVN_PASSWORD: ${{ secrets.SVN_PASSWORD }}
SVN_USERNAME: ${{ secrets.SVN_USERNAME }}
SLUG: event-bridge-for-activitypub

View File

@ -1,26 +0,0 @@
name: Deploy to WordPress.org
on:
push:
tags: '[0-9]+.[0-9]+.[0-9]+'
jobs:
tag:
name: Deploy tagged release on WordPress.org
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: https://code.forgejo.org/actions/checkout@v4
- name: Install and cache rsync and subversion
uses: https://github.com/awalsh128/cache-apt-pkgs-action@latest
with:
packages: rsync subversion
version: 1.0
- name: WordPress Plugin Deploy
uses: https://github.com/10up/action-wordpress-plugin-deploy@stable
env:
SVN_PASSWORD: ${{ secrets.SVN_PASSWORD }}
SVN_USERNAME: ${{ secrets.SVN_USERNAME }}
SLUG: event-bridge-for-activitypub

View File

@ -1,61 +0,0 @@
name: PHP Code Checker
on:
push:
branches:
- main
pull_request:
jobs:
phpcs:
name: PHP Code Checker
runs-on: ubuntu-latest
strategy:
matrix:
php-versions: ['8.1']
steps:
- name: Checkout
uses: https://code.forgejo.org/actions/checkout@v4
- name: Cache Composer
id: cache-composer
uses: https://code.forgejo.org/actions/cache@v4
with:
path: |
./vendor/
key: cache-composer-3
- name: Setup cache environment for PHP Extensions
id: php-extensions-cache
uses: https://github.com/shivammathur/cache-extensions@v1
with:
php-version: ${{ matrix.php-versions }}
extensions: dom, iconv, json, libxml, zip
key: php-extensions-cache-v1
- name: Cache PHP extensions
uses: https://code.forgejo.org/actions/cache@v4
with:
path: ${{ steps.php-extensions-cache.outputs.dir }}
key: ${{ steps.php-extensions-cache.outputs.key }}
restore-keys: ${{ steps.php-extensions-cache.outputs.key }}
- name: Setup PHP
uses: https://github.com/shivammathur/setup-php@v2
with:
php-version: ${{ matrix.php-versions }}
extensions: dom, iconv, json, libxml, zip
coverage: none
tools: composer, cs2pr
env:
runner: self-hosted
GITHUB_TOKEN: ${{ env.COMPOSER_TOKEN }}
- name: Install Composer dependencies for PHP
if: steps.cache-composer.outputs.cache-hit != 'true'
uses: ramsey/composer-install@v3
env:
COMPOSER_AUTH: '{"github-oauth": {"github.com": "${{ secrets.COMPOSER_TOKEN }}"}}'
- name: Run PHP_CodeSniffer
run: ./vendor/bin/phpcs

View File

@ -1,87 +0,0 @@
name: PHPStan Tests
on:
push:
branches:
- main
pull_request:
env:
WP_TESTS_DIR: /workspace/wordpress-test-lib
WP_CORE_DIR: /workspace/wordpress
jobs:
phpstan:
runs-on: ubuntu-latest
strategy:
matrix:
php-version: ['7.4','8.1','8.4']
name: PHPStan for WordPress ${{ matrix.php-version }}
steps:
- name: Checkout
uses: https://code.forgejo.org/actions/checkout@v4
- name: Cache WordPress Setup and installed plugins (Event plugins and ActivityPub plugin).
id: cache-wordpress
uses: https://code.forgejo.org/actions/cache@v4
with:
path: |
${{ env.WP_CORE_DIR }}
${{ env.WP_TESTS_DIR }}
key: cache-phpstan-wordpress-6.7-activitypub-5.1.0-2
- name: Cache Composer
id: cache-composer
uses: https://code.forgejo.org/actions/cache@v4
with:
path: |
./vendor/
key: cache-composer-3
- name: Setup cache environment for PHP Extensions
id: php-extensions-cache
uses: https://github.com/shivammathur/cache-extensions@v1
with:
php-version: ${{ matrix.php-version }}
extensions: dom, iconv, json, libxml, zip
key: php-extensions-cache-v1
- name: Cache PHP extensions
uses: https://code.forgejo.org/actions/cache@v4
with:
path: ${{ steps.php-extensions-cache.outputs.dir }}
key: ${{ steps.php-extensions-cache.outputs.key }}
restore-keys: ${{ steps.php-extensions-cache.outputs.key }}
- name: Setup PHP
uses: https://github.com/shivammathur/setup-php@v2
with:
php-version: ${{ matrix.php-version }}
extensions: dom, iconv, json, libxml, zip
coverage: none
tools: composer, cs2pr
env:
runner: self-hosted
GITHUB_TOKEN: ${{ env.COMPOSER_TOKEN }}
- name: Install Composer dependencies for PHP
if: steps.cache-composer.outputs.cache-hit != 'true'
uses: ramsey/composer-install@v3
env:
COMPOSER_AUTH: '{"github-oauth": {"github.com": "${{ secrets.COMPOSER_TOKEN }}"}}'
- name: Log debug information
run: |
git --version
php --version
composer --version
- name: Install third party plugins, so that PHP Stan finds that functions
if: steps.cache-wordpress.outputs.cache-hit != 'true'
run: bash bin/install-wp-tests.sh wordpress_test root root 127.0.0.1 latest true false false true
- name: Running PHPStan Analyze
if: ${{ success() || failure() }}
run: |
./vendor/bin/phpstan --version
./vendor/bin/phpstan analyze -vv --memory-limit=2G --error-format=checkstyle | cs2pr

View File

@ -1,147 +0,0 @@
name: PHPUnit
on:
push:
branches:
- main
pull_request:
env:
WP_TESTS_DIR: /workspace/wordpress-test-lib
WP_CORE_DIR: /workspace/wordpress
jobs:
phpunit:
runs-on: ubuntu-latest
services:
mysql:
image: mariadb
env:
MYSQL_ROOT_PASSWORD: root
strategy:
matrix:
php-version: ['7.4', '8.1', '8.3', '8.4']
wordpress-version: ['6.7', '6.8-RC3']
name: "PHPUnit: PHP ${{ matrix.php-version }} - WordPress ${{ matrix.wordpress-version }}"
continue-on-error: true
steps:
- name: Checkout
uses: https://code.forgejo.org/actions/checkout@v4
- name: Cache WordPress Setup and installed plugins (Event plugins and ActivityPub plugin).
id: cache-wordpress
uses: https://code.forgejo.org/actions/cache@v4
with:
path: |
${{ env.WP_CORE_DIR }}
${{ env.WP_TESTS_DIR }}
key: cache-phpunit-wordpress-${{ matrix.wordpress-version }}-activitypub-5.7.0
- name: Cache Composer
id: cache-composer
uses: https://code.forgejo.org/actions/cache@v4
with:
path: |
./vendor/
key: cache-composer-3
- name: Setup cache environment for PHP Extensions
id: php-extensions-cache
uses: https://github.com/shivammathur/cache-extensions@v1
with:
php-version: ${{ matrix.php-version }}
extensions: dom, iconv, json, libxml, zip
key: php-extensions-cache-v1
- name: Cache PHP extensions
uses: https://code.forgejo.org/actions/cache@v4
with:
path: ${{ steps.php-extensions-cache.outputs.dir }}
key: ${{ steps.php-extensions-cache.outputs.key }}
restore-keys: ${{ steps.php-extensions-cache.outputs.key }}
- name: Setup PHP
uses: https://github.com/shivammathur/setup-php@v2
with:
php-version: ${{ matrix.php-version }}
extensions: dom, iconv, json, libxml, zip
coverage: none
tools: composer, cs2pr
env:
runner: self-hosted
GITHUB_TOKEN: ${{ secrets.COMPOSER_TOKEN }}
- name: Install Composer dependencies for PHP
if: steps.cache-composer.outputs.cache-hit != 'true'
uses: ramsey/composer-install@v3
env:
COMPOSER_AUTH: '{"github-oauth": {"github.com": "${{ secrets.COMPOSER_TOKEN }}"}}'
- name: Install and cache mysqladmin needed to initialize the test database
uses: https://github.com/awalsh128/cache-apt-pkgs-action@latest
with:
packages: mysql-client
version: 1.0
- name: Setup Test Environment
if: steps.cache-wordpress.outputs.cache-hit != 'true'
run: bash bin/install-wp-tests.sh wordpress_test root root 127.0.0.1 ${{ matrix.wordpress-version }} false false false false
- name: Initialize WordPress test database
if: steps.cache-wordpress.outputs.cache-hit != 'false'
run: bash bin/install-wp-tests.sh wordpress_test root root 127.0.0.1 ${{ matrix.wordpress-version }} false true true true
- name: Run General Tests
run: cd /workspace/Event-Federation/wordpress-event-bridge-for-activitypub/ && ./vendor/bin/phpunit
env:
PHP_VERSION: ${{ matrix.php-version }}
- name: Run Integration tests for The Events Calendar
run: cd /workspace/Event-Federation/wordpress-event-bridge-for-activitypub/ && ./vendor/bin/phpunit --filter=the_events_calendar
env:
PHP_VERSION: ${{ matrix.php-version }}
- name: Run Integration tests for VS Event List
run: cd /workspace/Event-Federation/wordpress-event-bridge-for-activitypub/ && ./vendor/bin/phpunit --filter=vs_event_list
env:
PHP_VERSION: ${{ matrix.php-version }}
- name: Run Integration tests for GatherPress
run: cd /workspace/Event-Federation/wordpress-event-bridge-for-activitypub/ && ./vendor/bin/phpunit --filter=gatherpress
env:
PHP_VERSION: ${{ matrix.php-version }}
- name: Run Integration tests for Events Manager
run: cd /workspace/Event-Federation/wordpress-event-bridge-for-activitypub/ && ./vendor/bin/phpunit --filter=events_manager
env:
PHP_VERSION: ${{ matrix.php-version }}
- name: Run Integration tests for WP Event Manager
run: cd /workspace/Event-Federation/wordpress-event-bridge-for-activitypub/ && ./vendor/bin/phpunit --filter=wp_event_manager
env:
PHP_VERSION: ${{ matrix.php-version }}
- name: Run Integration tests for Eventin (WP Event Solution)
run: cd /workspace/Event-Federation/wordpress-event-bridge-for-activitypub/ && ./vendor/bin/phpunit --filter=eventin
env:
PHP_VERSION: ${{ matrix.php-version }}
- name: Run Integration tests for Modern Events Calendar Lite
run: cd /workspace/Event-Federation/wordpress-event-bridge-for-activitypub/ && ./vendor/bin/phpunit --filter=modern_events_calendar_lite
env:
PHP_VERSION: ${{ matrix.php-version }}
- name: Run Integration tests for EventPrime
run: cd /workspace/Event-Federation/wordpress-event-bridge-for-activitypub/ && ./vendor/bin/phpunit --filter=eventprime
env:
PHP_VERSION: ${{ matrix.php-version }}
- name: Run Integration tests for Event Organiser
run: cd /workspace/Event-Federation/wordpress-event-bridge-for-activitypub/ && ./vendor/bin/phpunit --filter=event_organiser
env:
PHP_VERSION: ${{ matrix.php-version }}
- name: Run Integration tests for EventON Events Calendar
run: cd /workspace/Event-Federation/wordpress-event-bridge-for-activitypub/ && ./vendor/bin/phpunit --filter=eventon
env:
PHP_VERSION: ${{ matrix.php-version }}

View File

@ -1,17 +0,0 @@
on:
push:
tags:
- '^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-((?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\.(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?$'
jobs:
upload-release:
runs-on: ubuntu-latest
steps:
- uses: https://code.forgejo.org/actions/checkout@v4
- uses: https://code.forgejo.org/actions/forgejo-release@v2
with:
direction: upload
url: https://codeberg.org
release-notes: ${{ TAG }}
token: ${{ secrets.GITHUB_TOKEN }}

View File

@ -1,26 +0,0 @@
.distignore export-ignore
.forgejo export-ignore
.gitattributes export-ignore
.gitignore export-ignore
.wordpress-org export-ignore
.wp-env.json export-ignore
bin export-ignore
CODE_OF_CONDUCT.md export-ignore
composer.json export-ignore
composer.lock export-ignore
docker-compose.yml export-ignore
Dockerfile export-ignore
docs export-ignore
FEDERATION.md export-ignore
Gruntfile.js export-ignore
node_modules export-ignore
package.json export-ignore
package-lock.json export-ignore
phpcs.xml export-ignore
phpstan.neon export-ignore
phpstan.neon.dist export-ignore
phpunit.xml export-ignore
README.md export-ignore
src export-ignore
tests export-ignore
vendor export-ignore

View File

@ -0,0 +1,27 @@
{
"core": null,
"plugins": [
"https://downloads.wordpress.org/plugin/activitypub.8.3.0.zip",
"https://downloads.wordpress.org/plugin/the-events-calendar.6.16.2.zip",
"https://downloads.wordpress.org/plugin/very-simple-event-list.20.0.zip",
"https://downloads.wordpress.org/plugin/gatherpress.0.33.3.zip",
"https://downloads.wordpress.org/plugin/eventprime-event-calendar-management.4.3.4.0.zip",
"https://downloads.wordpress.org/plugin/events-manager.7.2.3.1.zip",
"https://downloads.wordpress.org/plugin/wp-event-solution.4.1.13.zip",
"https://downloads.wordpress.org/plugin/event-organiser.3.12.8.zip",
"https://downloads.wordpress.org/plugin/eventon-lite.2.5.4.zip",
"https://codeberg.org/Event-Federation/modern-events-calendar-lite/releases/download/v7.27.0/modern-events-calendar-lite.zip",
"https://downloads.wordpress.org/plugin/spiffy-calendar.5.0.11.zip",
"wpeventmanager/wp-event-manager#3.3.5",
"https://downloads.wordpress.org/plugin/plugin-check.1.9.0.zip",
"."
],
"testsEnvironment": true,
"config": {
"WP_DEBUG": true,
"WP_DEBUG_LOG": true
},
"lifecycleScripts": {
"afterStart": "wp-env run cli wp plugin deactivate --all --exclude=activitypub,event-bridge-for-activitypub"
}
}

View File

@ -5,6 +5,94 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
## [1.3.0] - 2026-05-24
### Added
* Basic support for Spiffy Calendar Plugin
* Support for WordPress 7
### Changed
* Requires ActivityPub plugin 8.+
### Fixed
* The events calendar integration: remote cached organizers not being public.
## [1.2.4] - 2026-05-17
### Added
* Flag plugin compatible with WordPress 6.9
## [1.2.3] - 2026-05-17
### Added
* ActivityPub `Place` transformer for GatherPress
### Fixed
* Improve timezone detection for Eventin
* GatherPress place name not uses venue name
### Changed
* Time formatting for EventPrime
* Time formatting for VS Event List
* Test up to ActivityPub plugin 8.2.1
## [1.2.2] - 2025-11-21
### Fixed
* Validate event-tag info Events Manager to prevent fatal error
* timezone issues for Modern Events Calendar Lite
## [1.2.1] - 2025-11-13
### Fixed
* Incoming remote events getting federated
### Added
* Hotfix for Mobilizon group behavior to detect correct event source
## [1.2.0] - 2025-11-12
### Added
* New filter hook for incoming events
* Store longitude and latitude information for incoming The Events Calendar events
### Changed
* Update to new method how actor JSON is stored
* Dependency: now requires PHP 8.1+
* Dependency: now requires the WordPress ActivityPub plugin 7.5+
### Fixed
* Time offset for outgoing GatherPress events
* Time offset for importing events with The Events Calendar
## [1.1.1] - 2025-06-17
### Added
* Compatibility with ActivityPub plugin 6.0+
### Changed
* Improved scheduling of sending event posts
### Fixed
* Datetime timezone offset for incoming events when using The Events Calendar
* Accessibility issues in Admin UI
## [1.1.0] - 2025-04-12
### Added

View File

@ -3,16 +3,16 @@
* Plugin Name: Event Bridge for ActivityPub
* Description: Integrating popular event plugins with the ActivityPub plugin. The development of this plugin was funded through the <strong>NGI0 Entrust Fund</strong>, a fund established by <strong>NLnet</strong>.
* Plugin URI: https://event-federation.eu/
* Version: 1.1.0
* Version: 1.3.0
* Author: André Menrath
* Author URI: https://graz.social/@linos
* Text Domain: event-bridge-for-activitypub
* License: AGPL-3.0-or-later
* License URI: https://www.gnu.org/licenses/agpl-3.0.html
* Requires PHP: 7.4
* Requires PHP: 8.1
* Requires Plugins: activitypub
*
* Requires at least ActivityPub plugin with version >= 5.6.1. ActivityPub plugin tested up to: 5.7.0.
* Requires at least ActivityPub plugin with version >= 7.5.0. ActivityPub plugin tested up to: 8.3.0.
*
* @package Event_Bridge_For_ActivityPub
* @license AGPL-3.0-or-later
@ -27,7 +27,7 @@ define( 'EVENT_BRIDGE_FOR_ACTIVITYPUB_PLUGIN_FILE', plugin_dir_path( __FILE__ )
define( 'EVENT_BRIDGE_FOR_ACTIVITYPUB_PLUGIN_URL', plugin_dir_url( __FILE__ ) );
define( 'EVENT_BRIDGE_FOR_ACTIVITYPUB_PLUGIN_VERSION', current( get_file_data( __FILE__, array( 'Version' ), 'plugin' ) ) );
define( 'EVENT_BRIDGE_FOR_ACTIVITYPUB_DOMAIN', 'event-bridge-for-activitypub' );
define( 'EVENT_BRIDGE_FOR_ACTIVITYPUB_ACTIVITYPUB_PLUGIN_MIN_VERSION', '5.6.1' );
define( 'EVENT_BRIDGE_FOR_ACTIVITYPUB_ACTIVITYPUB_PLUGIN_MIN_VERSION', '8.1.0' );
define( 'EVENT_BRIDGE_FOR_ACTIVITYPUB_SUMMARY_TEMPLATE', "[ap_start_time]\n[ap_end_time]\n[ap_location]\n\n[ap_content]" );
define( 'EVENT_BRIDGE_FOR_ACTIVITYPUB_DEFAULT_SUMMARY_TYPE', 'preset' );

View File

@ -10,7 +10,7 @@
namespace Event_Bridge_For_ActivityPub\ActivityPub;
// Exit if accessed directly.
defined( 'ABSPATH' ) || exit; // @codeCoverageIgnore
\defined( 'ABSPATH' ) || exit; // @codeCoverageIgnore
use Event_Bridge_For_ActivityPub\ActivityPub\Handler\Accept;
use Event_Bridge_For_ActivityPub\ActivityPub\Handler\Update;

View File

@ -21,14 +21,15 @@
namespace Event_Bridge_For_ActivityPub\ActivityPub\Collection;
// Exit if accessed directly.
defined( 'ABSPATH' ) || exit; // @codeCoverageIgnore
\defined( 'ABSPATH' ) || exit; // @codeCoverageIgnore
use Activitypub\Model\Blog;
use Activitypub\Tombstone;
use Event_Bridge_For_ActivityPub\ActivityPub\Model\Event_Source;
use WP_Error;
use WP_Post;
use WP_Query;
use function Activitypub\is_tombstone;
use function Activitypub\get_remote_metadata_by_actor;
/**
@ -49,7 +50,7 @@ class Event_Sources {
/**
* The custom post type.
*/
const POST_TYPE = 'ap_event_source';
public const POST_TYPE = 'ap_event_source';
/**
* Init.
@ -173,10 +174,10 @@ class Event_Sources {
*
* @return Event_Source|WP_Error|null The Followed (WP_Post array) or an WP_Error.
*/
public static function add_event_source( $actor ) {
public static function add_event_source( string $actor ) {
$meta = get_remote_metadata_by_actor( $actor );
if ( is_tombstone( $meta ) ) {
if ( Tombstone::exists_in_error( $meta ) ) {
return $meta;
}
@ -211,7 +212,7 @@ class Event_Sources {
* @param string $followed_id The ActivityPub ID of the followed actor.
* @return string The `Follow` ID.
*/
public static function compose_follow_id( $follower_id, $followed_id ) {
public static function compose_follow_id( string $follower_id, string $followed_id ) {
return $follower_id . '#follow-' . \preg_replace( '~^https?://~', '', $followed_id );
}
@ -231,14 +232,14 @@ class Event_Sources {
* @param string|int $attachment_id The numeric post ID of the attachment.
* @return bool
*/
public static function is_attachment_featured_image( $attachment_id ): bool {
public static function is_attachment_featured_image( string|int $attachment_id ): bool {
if ( ! is_numeric( $attachment_id ) ) {
return false;
}
// Query posts with the given attachment ID as their featured image.
$args = array(
'post_type' => 'any',
// phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_query
'meta_query' => array(
array(
'key' => '_thumbnail_id',
@ -261,8 +262,10 @@ class Event_Sources {
* @param int $event_source_post_id The WordPress Post ID of the event source.
* @return void
*/
public static function delete_events_by_event_source( $event_source_post_id ): void {
public static function delete_events_by_event_source( int $event_source_post_id ): void {
global $wpdb;
// phpcs:disable WordPress.DB.DirectDatabaseQuery
$results = $wpdb->get_results(
$wpdb->prepare(
"SELECT post_id FROM $wpdb->postmeta WHERE meta_key = %s AND meta_value = %s",
@ -305,7 +308,7 @@ class Event_Sources {
*
* @return void Post data on success, false or null on failure.
*/
public static function remove_event_source( $event_source_post_id ): void {
public static function remove_event_source( int|string $event_source_post_id ): void {
$event_source = Event_Source::get_by_id( $event_source_post_id );
if ( ! $event_source ) {
@ -320,7 +323,7 @@ class Event_Sources {
$post = \get_post( $event_source->get__id() );
$post->post_status = 'draft';
if ( $post instanceof \WP_Post ) {
if ( $post instanceof WP_Post ) {
$post = \get_object_vars( $post );
$post = \wp_slash( $post );
$post = \wp_update_post( $post );
@ -358,9 +361,9 @@ class Event_Sources {
/**
* Get the Event Sources along with a total count for pagination purposes.
*
* @param int $number Maximum number of results to return.
* @param int $page Page number.
* @param array $args The WP_Query arguments.
* @param int $number Maximum number of results to return.
* @param int|null $page Page number.
* @param array $args The WP_Query arguments.
*
* @return array {
* Data about the event sources.
@ -369,7 +372,7 @@ class Event_Sources {
* @type int $total Total number of followers.
* }
*/
public static function get_event_sources_with_count( $number = -1, $page = null, $args = array() ): array {
public static function get_event_sources_with_count( int $number = -1, int|null $page = null, array $args = array() ): array {
$defaults = array(
'post_type' => self::POST_TYPE,
'posts_per_page' => $number,
@ -396,12 +399,12 @@ class Event_Sources {
/**
* Queue a hook to run async.
*
* @param string $hook The hook name.
* @param array $args The arguments to pass to the hook.
* @param string $unqueue_hook Optional a hook to unschedule before queuing.
* @return void|bool|WP_Error Whether the hook was queued.
* @param string $hook The hook name.
* @param array $args The arguments to pass to the hook.
* @param string $unqueue_hook Optional a hook to unschedule before queuing.
* @return bool|WP_Error Whether the hook was queued.
*/
public static function queue( $hook, $args, $unqueue_hook = null ) {
public static function queue( string $hook, array $args, $unqueue_hook = null ): bool|WP_Error {
if ( $unqueue_hook ) {
$hook_timestamp = \wp_next_scheduled( $unqueue_hook, $args );
if ( $hook_timestamp ) {
@ -410,7 +413,7 @@ class Event_Sources {
}
if ( \wp_next_scheduled( $hook, $args ) ) {
return;
return false;
}
return \wp_schedule_single_event( \time(), $hook, $args );
@ -419,11 +422,11 @@ class Event_Sources {
/**
* Prepare to follow an ActivityPub actor via a scheduled event.
*
* @param string $actor The ActivityPub actor.
* @param string $actor The ActivityPub actor.
*
* @return bool Whether the event was queued.
* @return bool Whether the event was queued.
*/
public static function queue_follow_actor( $actor ): bool {
public static function queue_follow_actor( string $actor ): bool {
$queued = self::queue(
'event_bridge_for_activitypub_follow',
array( $actor ),
@ -435,7 +438,7 @@ class Event_Sources {
}
// Following this actor has already been queued.
if ( null === $queued ) {
if ( false === $queued ) {
return true;
}
@ -447,11 +450,11 @@ class Event_Sources {
*
* @param string $actor_id The ID/URL of the Actor.
*/
public static function activitypub_follow_actor( $actor_id ) {
public static function activitypub_follow_actor( string $actor_id ): void {
$actor = Event_Source::get_by_id( $actor_id );
if ( ! $actor ) {
return $actor;
return;
}
$inbox = $actor->get_shared_inbox();
@ -461,8 +464,7 @@ class Event_Sources {
$activity = new \Activitypub\Activity\Activity();
$activity->set_type( 'Follow' );
$activity->set_to( null );
$activity->set_cc( null );
$activity->set_to( array( $to ) );
$activity->set_actor( $from_actor->get_id() );
$activity->set_object( $to );
$activity->set_id( self::compose_follow_id( $from_actor->get_id(), $to ) );
@ -475,9 +477,9 @@ class Event_Sources {
*
* @param string $actor The ActivityPub actor ID.
*
* @return bool|void|WP_Error Whether the event was queued.
* @return bool|WP_Error Whether the event was queued.
*/
public static function queue_unfollow_actor( $actor ) {
public static function queue_unfollow_actor( string $actor ): bool|WP_Error {
$queued = self::queue(
'event_bridge_for_activitypub_unfollow',
array( $actor ),
@ -489,7 +491,7 @@ class Event_Sources {
}
// Following this actor has already been queued.
if ( null === $queued ) {
if ( false === $queued ) {
return true;
}
@ -502,7 +504,7 @@ class Event_Sources {
* @param string $actor The ActivityPub ID of the actor to unfollow.
* @return void
*/
public static function activitypub_unfollow_actor( $actor ): void {
public static function activitypub_unfollow_actor( string $actor ): void {
$actor = Event_Source::get_by_id( $actor );
if ( ! $actor ) {
@ -520,8 +522,7 @@ class Event_Sources {
$activity = new \Activitypub\Activity\Activity();
$activity->set_type( 'Undo' );
$activity->set_to( null );
$activity->set_cc( null );
$activity->set_to( array( $to ) );
$activity->set_actor( $from_actor->get_id() );
$activity->set_object(
array(

View File

@ -9,7 +9,7 @@
namespace Event_Bridge_For_ActivityPub\ActivityPub\Handler;
// Exit if accessed directly.
defined( 'ABSPATH' ) || exit; // @codeCoverageIgnore
\defined( 'ABSPATH' ) || exit; // @codeCoverageIgnore
use Activitypub\Collection\Actors;
use Activitypub\Model\Blog;
@ -37,10 +37,10 @@ class Accept {
/**
* Handle incoming "Accept" activities.
*
* @param array $activity The activity-object.
* @param int $user_id The id of the local blog-user.
* @param array $activity The activity-object.
* @param int|int[] $user_id The id of the local blog-user.
*/
public static function handle_accept( $activity, $user_id ): void {
public static function handle_accept( array $activity, int|array $user_id ): void {
// We only process activities that are target to the blog actor.
if ( Actors::BLOG_USER_ID !== $user_id ) {
return;

View File

@ -8,7 +8,7 @@
namespace Event_Bridge_For_ActivityPub\ActivityPub\Handler;
// Exit if accessed directly.
defined( 'ABSPATH' ) || exit; // @codeCoverageIgnore
\defined( 'ABSPATH' ) || exit; // @codeCoverageIgnore
use Activitypub\Collection\Actors;
use Event_Bridge_For_ActivityPub\ActivityPub\Model\Event_Source;
@ -36,10 +36,10 @@ class Create {
/**
* Handle incoming "Create" activities.
*
* @param array $activity The activity-object.
* @param int $user_id The id of the local blog-user.
* @param array $activity The activity-object.
* @param int|int[] $user_id The id of the local blog-user.
*/
public static function handle_create( $activity, $user_id ): void {
public static function handle_create( array $activity, int|array $user_id ): void {
// We only process activities that are target to the blog actor.
if ( Actors::BLOG_USER_ID !== $user_id ) {
return;
@ -56,7 +56,7 @@ class Create {
}
// Check that we are actually following/or have a pending follow request this actor.
$event_source_post_id = Event_Source::get_post_id_by_activitypub_id( $activity['actor'] );
$event_source_post_id = self::determine_event_source( $activity );
if ( ! $event_source_post_id ) {
return;
}
@ -65,6 +65,11 @@ class Create {
return;
}
// Apply custom filters whether an Event should be ignored.
if ( \apply_filters( 'event_bridge_for_activitypub_ignore_incoming_event', false, $activity['object'], $event_source_post_id ) ) {
return;
}
$transmogrifier = Setup::get_transmogrifier();
if ( ! $transmogrifier ) {
@ -73,4 +78,36 @@ class Create {
$transmogrifier::save( $activity['object'], $event_source_post_id );
}
/**
* Lookup the post ID of the event source for a given ActivityPub activity.
*
* First tries the actor of the activity. For Mobilizon-like groups, falls back
* to the attributedTo field if it has the same host as the actor.
*
* @param array $activity The ActivityPub activity as associative array.
* @return int|false Post ID of the event source, or false if none found.
*/
public static function determine_event_source( $activity ): int|false {
$event_source_post_id = Event_Source::get_post_id_by_activitypub_id( $activity['actor'] );
if ( $event_source_post_id ) {
return $event_source_post_id;
}
// Fallback for Mobilizon groups, where a member of the group sends them.
if ( ! isset( $activity['object']['attributedTo'] ) ) {
return false;
}
// We only trust this, when the attributedTo of the event object and the actor of the activity have the same hostname.
$actor_host = \wp_parse_url( $activity['actor'], PHP_URL_HOST );
$attributed_to_host = \wp_parse_url( $activity['object']['attributedTo'], PHP_URL_HOST );
if ( $actor_host === $attributed_to_host ) {
return Event_Source::get_post_id_by_activitypub_id( $activity['object']['attributedTo'] );
}
return false;
}
}

View File

@ -8,7 +8,7 @@
namespace Event_Bridge_For_ActivityPub\ActivityPub\Handler;
// Exit if accessed directly.
defined( 'ABSPATH' ) || exit; // @codeCoverageIgnore
\defined( 'ABSPATH' ) || exit; // @codeCoverageIgnore
use Activitypub\Collection\Actors;
use Event_Bridge_For_ActivityPub\Event_Sources;
@ -35,10 +35,10 @@ class Delete {
/**
* Handle "Follow" requests.
*
* @param array $activity The activity-object.
* @param int $user_id The id of the local blog-user.
* @param array $activity The activity-object.
* @param int|int[] $user_id The id of the local blog-user.
*/
public static function handle_delete( $activity, $user_id ): void {
public static function handle_delete( array $activity, int|array $user_id ): void {
// We only process activities that are target to the application user.
if ( Actors::BLOG_USER_ID !== $user_id ) {
return;

View File

@ -1,159 +0,0 @@
<?php
/**
* Join handler file.
*
* @package Event_Bridge_For_ActivityPub
* @license AGPL-3.0-or-later
*/
namespace Event_Bridge_For_ActivityPub\ActivityPub\Handler;
use Activitypub\Activity\Activity;
use Activitypub\Collection\Actors;
use Activitypub\Activity\Actor;
use Activitypub\Http;
use Activitypub\Transformer\Factory;
use Event_Bridge_For_ActivityPub\ActivityPub\Transformer\Event\Event as Event_Transformer;
use function Activitypub\get_remote_metadata_by_actor;
use function Activitypub\object_to_uri;
/**
* Handle Join requests.
*/
class Join {
/**
* Initialize the class, registering WordPress hooks.
*/
public static function init() {
\add_action(
'activitypub_register_handlers',
array( self::class, 'register_join_handler' )
);
\add_action(
'event_bridge_for_activitypub_ignore_join',
array( self::class, 'send_ignore_response' ),
10,
3
);
}
/**
* Register the join handler to the ActivityPub plugin.
*/
public static function register_join_handler() {
\add_action(
'activitypub_inbox_join',
array( self::class, 'handle_join' )
);
}
/**
* Handle ActivityPub "Join" requests.
*
* @param array $activity The activity object.
*/
public static function handle_join( $activity ) {
$actor = get_remote_metadata_by_actor( object_to_uri( $activity['actor'] ) );
// If we cannot fetch the actor, we cannot continue.
if ( \is_wp_error( $actor ) ) {
return;
}
// This should be already validated, but just to be sure.
if ( ! array_key_exists( 'object', $activity ) ) {
return;
}
// Get the WordPress Post ID, via the ActivityPub ID.
$post_id = self::get_post_id_by_activitypub_id( \sanitize_url( object_to_uri( $activity['object'] ) ) );
if ( ! $post_id ) {
// No post is found for this URL/ID.
return;
}
// Check whether the target object/post is an event post.
$transformer = Factory::get_transformer( get_post( $post_id ) );
if ( ! $transformer instanceof Event_Transformer ) {
return;
}
// Pass over to Event plugin specific handler if implemented here. Until then just send an ignore.
do_action(
'event_bridge_for_activitypub_ignore_join',
$transformer->get_actor_object()->get_id(), // Gets the WordPress user that "owns" the object by ActivityPub means.
$activity['id'],
Actor::init_from_array( $actor )
);
}
/**
* Send "Ignore" response.
*
* @param string $actor The actors ActivityPub ID which sends the response.
* @param string $ignored The ID of the Activity that gets ignored.
* @param Actor|mixed $to The target actor.
*/
public static function send_ignore_response( $actor, $ignored, $to ) {
// Get actor object that owns the object that was targeted by the ignored activity.
$actor = Actors::get_by_resource( $actor );
if ( \is_wp_error( $to ) || \is_wp_error( $actor ) ) {
return;
}
// Get inbox.
$inbox = $to->get_inbox();
if ( ! $inbox ) {
return;
}
// Send "Ignore" activity.
$activity = new Activity();
$activity->set_type( 'Ignore' );
$activity->set_object( \esc_url_raw( $ignored ) );
$activity->set_actor( $actor->get_id() );
$activity->set_to( $to->get_id() );
$activity->set_id( $actor->get_id() . '#ignore-' . \preg_replace( '~^https?://~', '', $ignored ) );
$activity->set_sensitive( null );
// @phpstan-ignore-next-line
Http::post( $inbox, $activity->to_json(), $actor->get__id() );
}
/**
* Get the WordPress Post ID by the ActivityPub ID.
*
* @param string $activitypub_id The ActivityPub objects ID.
* @return int The WordPress post ID.
*/
private static function get_post_id_by_activitypub_id( $activitypub_id ) {
// Parse the URL and extract its components.
$parsed_url = wp_parse_url( $activitypub_id );
$home_url = \trailingslashit( \home_url() );
// Ensure the base URL matches the home URL.
if ( \trailingslashit( "{$parsed_url['scheme']}://{$parsed_url['host']}" ) !== $home_url ) {
return 0;
}
// Check for a valid query string and parse it.
if ( isset( $parsed_url['query'] ) ) {
parse_str( $parsed_url['query'], $query_vars );
// Ensure the only parameter is 'p'.
if ( count( $query_vars ) === 1 && isset( $query_vars['p'] ) ) {
return intval( $query_vars['p'] ); // Return the post ID.
}
}
// Fallback: legacy ActivityPub plugin (before version 3.0.0) used pretty permalinks as `id`.
return \url_to_postid( $activitypub_id );
}
}

View File

@ -9,7 +9,7 @@
namespace Event_Bridge_For_ActivityPub\ActivityPub\Handler;
// Exit if accessed directly.
defined( 'ABSPATH' ) || exit; // @codeCoverageIgnore
\defined( 'ABSPATH' ) || exit; // @codeCoverageIgnore
use Activitypub\Collection\Actors;
use Event_Bridge_For_ActivityPub\Event_Sources;
@ -35,10 +35,10 @@ class Undo {
/**
* Handle incoming "Undo" activities.
*
* @param array $activity The activity-object.
* @param int $user_id The id of the local blog-user.
* @param array $activity The activity-object.
* @param int|int[] $user_id The id of the local blog-user.
*/
public static function handle_undo( $activity, $user_id ): void {
public static function handle_undo( array $activity, int|array $user_id ): void {
// We only process activities that are target to the blog actor.
if ( Actors::BLOG_USER_ID !== $user_id ) {
return;
@ -53,6 +53,7 @@ class Undo {
global $wpdb;
// phpcs:disable WordPress.DB.DirectDatabaseQuery
$results = $wpdb->get_results(
$wpdb->prepare(
"SELECT post_id FROM $wpdb->postmeta WHERE meta_key = %s AND meta_value = %s",

View File

@ -8,14 +8,7 @@
namespace Event_Bridge_For_ActivityPub\ActivityPub\Handler;
// Exit if accessed directly.
defined( 'ABSPATH' ) || exit; // @codeCoverageIgnore
use Activitypub\Collection\Actors;
use Event_Bridge_For_ActivityPub\ActivityPub\Model\Event_Source;
use Event_Bridge_For_ActivityPub\Event_Sources;
use Event_Bridge_For_ActivityPub\Setup;
use function Activitypub\is_activity_public;
\defined( 'ABSPATH' ) || exit; // @codeCoverageIgnore
/**
* Handle Update requests.
@ -36,41 +29,11 @@ class Update {
/**
* Handle incoming "Update" activities..
*
* @param array $activity The activity-object.
* @param int $user_id The id of the local blog-user.
* @param array $activity The activity-object.
* @param int|int[] $user_id The id of the local blog-user.
*/
public static function handle_update( $activity, $user_id ): void {
// We only process activities that are target to the application user.
if ( Actors::BLOG_USER_ID !== $user_id ) {
return;
}
// Check if Activity is public or not.
if ( ! is_activity_public( $activity ) ) {
return;
}
// Check if an object is set and it is an object of type `Event`.
if ( ! isset( $activity['object']['type'] ) || 'Event' !== $activity['object']['type'] ) {
return;
}
// Check that we are actually following/or have a pending follow request this actor.
$event_source_post_id = Event_Source::get_post_id_by_activitypub_id( $activity['actor'] );
if ( ! $event_source_post_id ) {
return;
}
if ( Event_Sources::is_time_passed( $activity['object']['startTime'] ) ) {
return;
}
$transmogrifier = Setup::get_transmogrifier();
if ( ! $transmogrifier ) {
return;
}
$transmogrifier::save( $activity['object'], $event_source_post_id );
public static function handle_update( array $activity, int|array $user_id ): void {
// We handle updates the same as we handle creates for now (specification though says we should ignore it).
Create::handle_create( $activity, $user_id );
}
}

View File

@ -13,7 +13,7 @@
namespace Event_Bridge_For_ActivityPub\ActivityPub\Model;
// Exit if accessed directly.
defined( 'ABSPATH' ) || exit; // @codeCoverageIgnore
\defined( 'ABSPATH' ) || exit; // @codeCoverageIgnore
use Activitypub\Activity\Actor;
use Event_Bridge_For_ActivityPub\ActivityPub\Collection\Event_Sources;
@ -46,7 +46,7 @@ use WP_Post;
* @method array get_endpoints()
*/
class Event_Source extends Actor {
const ACTIVITYPUB_USER_HANDLE_REGEXP = '(?:([A-Za-z0-9_.-]+)@((?:[A-Za-z0-9_-]+\.)+[A-Za-z]+))';
public const ACTIVITYPUB_USER_HANDLE_REGEXP = '(?:([A-Za-z0-9_.-]+)@((?:[A-Za-z0-9_-]+\.)+[A-Za-z]+))';
/**
* The WordPress Post ID which stores the event source.
@ -133,7 +133,7 @@ class Event_Source extends Actor {
* @param string $activitypub_actor_id The ActivityPub actor ID.
* @return int|false The Event Sources Post ID, if a WordPress Post representing it is found, false otherwise.
*/
public static function get_post_id_by_activitypub_id( $activitypub_actor_id ) {
public static function get_post_id_by_activitypub_id( string $activitypub_actor_id ) {
$event_sources = Event_Sources::get_event_sources();
return array_search( $activitypub_actor_id, $event_sources, true );
@ -145,7 +145,7 @@ class Event_Source extends Actor {
* @param int|string $event_source_id The ActivityPub actor ID as string or the Post ID as int of the Event Source.
* @return ?Event_Source The Event Sources if it exists, false otherwise.
*/
public static function get_by_id( $event_source_id ): ?Event_Source {
public static function get_by_id( int|string $event_source_id ): ?Event_Source {
$post_id = is_integer( $event_source_id ) ? $event_source_id : self::get_post_id_by_activitypub_id( $event_source_id );
if ( ! $post_id ) {
@ -172,15 +172,21 @@ class Event_Source extends Actor {
/**
* Convert a Custom-Post-Type input to an \Event_Bridge_For_ActivityPub\ActivityPub\Model\Event_Source.
*
* @param \WP_Post $post The post object.
* @param WP_Post $post The post object.
* @return ?Event_Source
*/
public static function init_from_cpt( $post ): ?Event_Source {
public static function init_from_cpt( WP_Post $post ): ?Event_Source {
if ( Event_Sources::POST_TYPE !== $post->post_type ) {
return null;
}
$actor_json = \get_post_meta( $post->ID, '_activitypub_actor_json', true );
$object = static::init_from_json( $actor_json );
if ( empty( $post->post_content ) ) {
$actor_json = \get_post_meta( $post->ID, '_activitypub_actor_json', true );
} else {
$actor_json = $post->post_content;
}
$object = static::init_from_json( $actor_json );
if ( \is_wp_error( $object ) ) {
return null;
@ -202,10 +208,6 @@ class Event_Source extends Actor {
);
}
if ( ! $object instanceof Event_Source ) { // To make phpstan happy.
return null;
}
return $object;
}

View File

@ -0,0 +1,101 @@
<?php
/**
* FEP-8a8e compatible representation of an Event in ActivityStreams.
*
* See: https://codeberg.org/Event-Federation/gatherpress-activitypub/src/branch/main/includes/classes/class-event-fep-8a8e.php
*
* @package Event_Bridge_For_ActivityPub
* @since 1.3.0
* @license AGPL-3.0-or-later
*/
namespace Event_Bridge_For_ActivityPub\ActivityPub\Object;
use Activitypub\Activity\Base_Object;
/**
* Event is an implementation of Activity Streams Event object type.
*
* This class contains extensions from FEP-8a8e: A common approach to using the Event object type.
*
* @see https://www.w3.org/TR/activitystreams-vocabulary/#dfn-event
* @see https://w3id.org/fep/8a8ae
*
* @method int|null get_maximum_attendee_capacity() Gets how many places there can be for an event.
* @method string|null get_name() Gets the title of the event.
* @method string|null get_event_status() Gets the event's status.
* @method string|null get_timezone() Gets the timezone of the event.
*
* @method Event_FEP_8a8e set_maximum_attendee_capacity( int $capacity ) Sets how many places there can be for an event.
* @method Event_FEP_8a8e set_name( string $name ) Sets the title of the event.
* @method Event_FEP_8a8e set_event_status( string $status ) Sets the event's status.
* @method Event_FEP_8a8e set_timezone( string $timezone ) Sets the timezone of the event.
*/
class Event_FEP_8a8e extends Base_Object {
/**
* The JSON-LD Context.
*
* @var array
*/
const JSON_LD_CONTEXT = array(
'https://schema.org/', // The base context is schema.org, because it is used a lot.
'https://w3id.org/fep/8a8e', // FEP-8a8e context.
'https://www.w3.org/ns/activitystreams', // The ActivityStreams context overrides everything also defined in schema.org.
);
/**
* Event is an implementation of one of the Activity Streams.
*
* @var string
*/
protected $type = 'Event';
/**
* The title of the event.
*
* @var string
*/
protected $name;
/**
* Timezone of the event.
*
* @context https://w3id.org/fep/8a8e/timezone
* @var string
*/
protected $timezone;
/**
* The event's status.
*
* @context https://w3id.org/fep/8a8e/eventStatus
* @var string
*/
protected $event_status = 'EventScheduled';
/**
* How many places there can be for an event.
*
* @context https://schema.org/maximumAttendeeCapacity
* @var int
*/
protected $maximum_attendee_capacity;
/**
* Attendees collection
*
* @context https://w3id.org/fep/8a8e/attendees
*
* @var array
*/
protected $attendees;
/**
* URL to an iCal file.
*
* @context https://w3id.org/fep/8a8e/ical
*
* @var string
*/
protected $ical;
}

View File

@ -0,0 +1,119 @@
<?php
/**
* Event is an implementation of one of the Activity Streams Event object type
*
* See https://codeberg.org/Event-Federation/gatherpress-activitypub/src/branch/main/includes/classes/class-place.php
*
* @package GatherPress_ActivityPUb
*/
namespace Event_Bridge_For_ActivityPub\ActivityPub\Object;
use Activitypub\Activity\Base_Object;
/**
* Place is an implementation of the Activity Streams Place object type.
*
* The Place object represents a logical or physical location.
*
* @see https://www.w3.org/TR/activitystreams-vocabulary/#dfn-place
*
* @method float|null get_accuracy() Gets the accuracy of position coordinates.
* @method array|string|null get_address() Gets the address of the place.
* @method float|null get_altitude() Gets the altitude of the place.
* @method float|null get_latitude() Gets the latitude of the place.
* @method float|null get_longitude() Gets the longitude of the place.
* @method float|null get_radius() Gets the radius from the given latitude and longitude.
* @method string|null get_units() Gets the measurement units for radius and altitude.
*
* @method Place set_accuracy( float $accuracy ) Sets the accuracy of position coordinates.
* @method Place set_address( array|string $address ) Sets the address of the place.
* @method Place set_altitude( float $altitude ) Sets the altitude of the place.
* @method Place set_latitude( float $latitude ) Sets the latitude of the place.
* @method Place set_longitude( float $longitude ) Sets the longitude of the place.
* @method Place set_radius( float $radius ) Sets the radius from the given latitude and longitude.
* @method Place set_units( string $units ) Sets the measurement units for radius and altitude.
*/
class Place extends Base_Object {
/**
* Place is an implementation of one of the
* Activity Streams
*
* @var string
*/
protected $type = 'Place';
/**
* Indicates the accuracy of position coordinates on a Place objects.
* Expressed in properties of percentage. e.g. "94.0" means "94.0% accurate".
*
* @see https://www.w3.org/TR/activitystreams-vocabulary/#dfn-accuracy
* @var float xsd:float [>= 0.0f, <= 100.0f]
*/
protected $accuracy;
/**
* Indicates the altitude of a place. The measurement unit is indicated using the unit's property.
* If unit is not specified, the default is assumed to be "m" indicating meters.
*
* @see https://www.w3.org/TR/activitystreams-vocabulary/#dfn-altitude
* @var float xsd:float
*/
protected $altitude;
/**
* The latitude of a place.
*
* @see https://www.w3.org/TR/activitystreams-vocabulary/#dfn-latitude
* @var float xsd:float
*/
protected $latitude;
/**
* The longitude of a place.
*
* @see https://www.w3.org/TR/activitystreams-vocabulary/#dfn-longitude
* @var float xsd:float
*/
protected $longitude;
/**
* The radius from the given latitude and longitude for a Place.
*
* @see https://www.w3.org/TR/activitystreams-vocabulary/#dfn-radius
* @var float
*/
protected $radius;
/**
* Specifies the measurement units for the `radius` and `altitude` properties.
*
* @see https://www.w3.org/TR/activitystreams-vocabulary/#dfn-units
* @var string
*/
protected $units;
/**
* The address of the place.
*
* @see https://schema.org/PostalAddress
* @var array|string
*/
protected $address;
/**
* External URL/Website of the place/venue.
*
* @see https://schema.org/sameAs
* @var string
*/
protected $same_as;
/**
* Telephone number of the place.
*
* @see https://schema.org/telephone
* @var string
*/
protected $telephone;
}

View File

@ -19,7 +19,7 @@ class Event {
* Initialize the class, registering WordPress hooks.
*/
public static function init() {
\add_action( 'transition_post_status', array( self::class, 'maybe_schedule_event_post_activity' ), 50, 3 );
\add_action( 'wp_after_insert_post', array( self::class, 'maybe_schedule_event_post_activity' ), 50, 4 );
\add_action( 'event_bridge_for_activitypub_add_event_post_to_outbox', array( self::class, 'add_event_post_to_outbox' ), 10, 3 );
\add_filter( 'activitypub_is_post_disabled', array( self::class, 'is_post_disabled_for_the_activitypub_plugin' ), 50, 2 );
}
@ -44,13 +44,14 @@ class Event {
}
/**
* Schedule Activities.
* Handle post updates and determine the appropriate Activity type.
*
* @param string $new_status New post status.
* @param string $old_status Old post status.
* @param \WP_Post $post Post object.
* @param int $post_id Post ID.
* @param \WP_Post $post Post object.
* @param bool $update Whether this is an existing post being updated.
* @param null|\WP_Post $post_before Post object before the update.
*/
public static function maybe_schedule_event_post_activity( $new_status, $old_status, $post ): void {
public static function maybe_schedule_event_post_activity( $post_id, $post, $update, $post_before ) {
if ( defined( 'WP_IMPORTING' ) && WP_IMPORTING ) {
return;
}
@ -63,9 +64,17 @@ class Event {
return;
}
// Bail on bulk edits, unless post author or post status changed.
if ( isset( $_REQUEST['bulk_edit'] ) && -1 === (int) $_REQUEST['post_author'] && -1 === (int) $_REQUEST['_status'] ) { // phpcs:ignore WordPress
return;
}
$new_status = get_post_status( $post );
$old_status = $post_before ? get_post_status( $post_before ) : null;
switch ( $new_status ) {
case 'publish':
$type = ( 'publish' === $old_status ) ? 'Update' : 'Create';
$type = $update ? 'Update' : 'Create';
break;
case 'draft':

View File

@ -9,7 +9,7 @@
namespace Event_Bridge_For_ActivityPub\ActivityPub\Transformer\Event;
// Exit if accessed directly.
defined( 'ABSPATH' ) || exit; // @codeCoverageIgnore
\defined( 'ABSPATH' ) || exit; // @codeCoverageIgnore
use Activitypub\Activity\Extended_Object\Place;
use Event_Bridge_For_ActivityPub\ActivityPub\Transformer\Event\Event as Base_Event_Transformer;

View File

@ -9,7 +9,7 @@
namespace Event_Bridge_For_ActivityPub\ActivityPub\Transformer\Event;
// Exit if accessed directly.
defined( 'ABSPATH' ) || exit; // @codeCoverageIgnore
\defined( 'ABSPATH' ) || exit; // @codeCoverageIgnore
use Activitypub\Activity\Extended_Object\Event as Event_Object;
use Activitypub\Activity\Extended_Object\Place;
@ -38,6 +38,20 @@ abstract class Event extends Post {
*/
protected $wp_taxonomy;
/**
* A valid timezone string.
*
* @var ?string
*/
protected $timezone_string = null;
/**
* Bool that stores whether timezone is indeed not known = null.
*
* @var bool
*/
protected $no_valid_timezone_known = false;
/**
* Returns the ActivityStreams 2.0 Object-Type for an Event.
*
@ -188,7 +202,7 @@ abstract class Event extends Post {
* @param ?string $time The time which needs to be formatted.
*/
protected static function format_time( $time ) {
if ( is_null( $time ) ) {
if ( null === $time ) {
return '';
}
$start_datetime = new DateTime( $time );
@ -433,7 +447,6 @@ abstract class Event extends Post {
// Fill in the shortcodes.
\setup_postdata( $this->item );
Shortcodes::register();
$summary = \do_shortcode( $summary );
\wp_reset_postdata();
@ -491,19 +504,20 @@ abstract class Event extends Post {
*/
public function get_formatted_address( $include_location_name = false, $args = array() ) {
$location = $this->get_location();
Shortcodes::register();
if ( $location instanceof Place ) {
$location_name = $location->get_name();
$foramted_address = self::format_address( $location->get_address(), $args );
$location_name = $location->get_name();
$formatted_address = self::format_address( $location->get_address(), $args );
$loaction_parts = array();
$location_parts = array();
if ( $location_name ) {
$location_parts[] = $location_name;
}
if ( $foramted_address ) {
$location_parts[] = $foramted_address;
if ( $formatted_address ) {
$location_parts[] = $formatted_address;
}
if ( ! empty( $location_parts ) ) {
@ -531,16 +545,76 @@ abstract class Event extends Post {
}
/**
* By default set the timezone of the WordPress site.
* Get the timezone of the event if known.
*
* By default gets the timezone of the WordPress site.
* This is not to be overwritten by the event transformers. Instead override get_timezone_string.
* This function does also cache and sanitize the timezone string.
*
* @return string|null The timezone string of the event. Or no null if timezone information.
*/
final public function get_timezone(): ?string {
// Return cached timezone, if a valid one is known.
if ( $this->timezone_string ) {
return $this->timezone_string;
}
// Return cached failure.
if ( $this->no_valid_timezone_known ) {
return null;
}
// Step 1: Get timezone string.
$timezone_string = $this->get_timezone_string();
// Step 2: Fallback to site default if child class override returned null or empty string.
if ( ! $timezone_string ) {
$timezone_string = $this->get_site_timezone_string();
}
// Step 3: Reject non-strings early.
if ( ! \is_string( $timezone_string ) ) {
$this->no_valid_timezone_known = true;
return null;
}
// Step 4: Validate against known timezone identifiers.
$valid_timezone_strings = \DateTimeZone::listIdentifiers();
if ( \in_array( $timezone_string, $valid_timezone_strings, true ) ) {
// Cache and return valid timezone identifier string.
$this->timezone_string = $timezone_string;
return $this->timezone_string;
}
// Cache failure.
$this->no_valid_timezone_known = true;
return null;
}
/**
* Get the timezone string of the event. Will be validated later.
*
* This is likely to be overwritten by the actual transformer.
*
* @return string The timezone string of the site.
* @return mixed
*/
public function get_timezone(): string {
protected function get_timezone_string(): mixed {
return $this->get_site_timezone_string();
}
/**
* Non-overridable fallback method for timezone.
*
* Often in WordPress timezone string might be a UTC offset like "+01:00". But this will be sanitized be later in get_timezone().
*
* @return string
*/
final protected function get_site_timezone_string(): string {
return \wp_timezone_string();
}
/**
* Remove the permalink shortcode from a WordPress template.
*

View File

@ -11,7 +11,7 @@
namespace Event_Bridge_For_ActivityPub\ActivityPub\Transformer\Event;
// Exit if accessed directly.
defined( 'ABSPATH' ) || exit; // @codeCoverageIgnore
\defined( 'ABSPATH' ) || exit; // @codeCoverageIgnore
use Activitypub\Activity\Extended_Object\Place;
use Event_Bridge_For_ActivityPub\ActivityPub\Transformer\Event\Event;
@ -51,20 +51,20 @@ final class Eventin extends Event {
* Get the end time from the event object.
*/
public function get_start_time(): string {
return \gmdate( 'Y-m-d\TH:i:s\Z', strtotime( $this->event_model->get_start_datetime() ) );
return $this->event_model->get_start_datetime( 'Y-m-d\TH:i:sP' );
}
/**
* Get the end time from the event object.
*/
public function get_end_time(): string {
return \gmdate( 'Y-m-d\TH:i:s\Z', strtotime( $this->event_model->get_end_datetime() ) );
return $this->event_model->get_end_datetime( 'Y-m-d\TH:i:sP' );
}
/**
* Get the timezone of the event.
*/
public function get_timezone(): string {
public function get_timezone_string(): string {
return $this->event_model->get_timezone();
}

View File

@ -9,7 +9,7 @@
namespace Event_Bridge_For_ActivityPub\ActivityPub\Transformer\Event;
// Exit if accessed directly.
defined( 'ABSPATH' ) || exit; // @codeCoverageIgnore
\defined( 'ABSPATH' ) || exit; // @codeCoverageIgnore
use Activitypub\Activity\Extended_Object\Place;
use Event_Bridge_For_ActivityPub\ActivityPub\Transformer\Event\Event as Event_Transformer;
@ -20,6 +20,8 @@ use Event_Bridge_For_ActivityPub\ActivityPub\Transformer\Place\EventOn as EventO
*
* This transformer tries a different principle: The setters are chainable.
*
* @link https://docs.myeventon.com/documentations/event-post-meta-variables/
*
* @since 1.0.0
*/
final class EventOn extends Event_Transformer {
@ -102,8 +104,8 @@ final class EventOn extends Event_Transformer {
* Get the end time from the events metadata.
*/
public function get_end_time(): ?string {
$end_time = \get_post_meta( $this->item->ID, '_unix_end_ev', true );
$timezone = \get_post_meta( $this->item->ID, '_evo_tz', true );
$end_time = \get_post_meta( $this->item->ID, 'evcal_erow', true );
$timezone = \get_post_meta( $this->item->ID, 'evo_event_timezone', true );
$timezone = $timezone ? new \DateTimeZone( $timezone ) : null;
if ( is_null( $end_time ) || empty( $end_time ) ) {
@ -117,18 +119,16 @@ final class EventOn extends Event_Transformer {
*
* @return string
*/
public function get_timezone(): string {
$timezone = \get_post_meta( $this->item->ID, '_evo_tz', true );
return $timezone ?? \wp_timezone_string();
public function get_timezone_string(): string {
return \get_post_meta( $this->item->ID, 'evo_event_timezone', true );
}
/**
* Get the end time from the events metadata.
*/
public function get_start_time(): string {
$start_time = \get_post_meta( $this->item->ID, '_unix_start_ev', true );
$timezone = \get_post_meta( $this->item->ID, '_evo_tz', true );
$start_time = \get_post_meta( $this->item->ID, 'evcal_srow', true );
$timezone = \get_post_meta( $this->item->ID, 'evo_event_timezone', true );
$timezone = $timezone ? new \DateTimeZone( $timezone ) : null;
return \wp_date( 'Y-m-d\TH:i:sP', (int) $start_time, $timezone );

View File

@ -9,7 +9,7 @@
namespace Event_Bridge_For_ActivityPub\ActivityPub\Transformer\Event;
// Exit if accessed directly.
defined( 'ABSPATH' ) || exit; // @codeCoverageIgnore
\defined( 'ABSPATH' ) || exit; // @codeCoverageIgnore
use Activitypub\Activity\Extended_Object\Place;
use Event_Bridge_For_ActivityPub\ActivityPub\Transformer\Event\Event as Base_Event_Transformer;
@ -27,7 +27,7 @@ final class EventPrime extends Base_Event_Transformer {
public function get_end_time(): ?string {
$timestamp = \get_post_meta( $this->wp_object->ID, 'em_end_date', true );
if ( $timestamp ) {
return \gmdate( 'Y-m-d\TH:i:s\Z', $timestamp );
return \wp_date( 'Y-m-d\TH:i:sP', $timestamp );
} else {
return null;
}
@ -39,7 +39,7 @@ final class EventPrime extends Base_Event_Transformer {
public function get_start_time(): string {
$timestamp = \get_post_meta( $this->wp_object->ID, 'em_start_date', true );
if ( $timestamp ) {
return \gmdate( 'Y-m-d\TH:i:s\Z', $timestamp );
return \wp_date( 'Y-m-d\TH:i:sP', $timestamp );
} else {
return '';
}

View File

@ -9,7 +9,7 @@
namespace Event_Bridge_For_ActivityPub\ActivityPub\Transformer\Event;
// Exit if accessed directly.
defined( 'ABSPATH' ) || exit; // @codeCoverageIgnore
\defined( 'ABSPATH' ) || exit; // @codeCoverageIgnore
use Activitypub\Activity\Extended_Object\Place;
use Event_Bridge_For_ActivityPub\ActivityPub\Transformer\Event\Event as Event_Transformer;
@ -66,11 +66,7 @@ final class Events_Manager extends Event_Transformer {
*/
public function get_location() {
if ( $this->is_online() ) {
if ( property_exists( $this->em_event->event_location, 'data' ) ) {
$event_location = $this->em_event->event_location->data;
} else {
$event_location = array();
}
$event_location = $this->em_event->event_location->data;
$event_link_url = isset( $event_location['url'] ) ? $event_location['url'] : null;
$event_link_text = isset( $event_location['text'] ) ? $event_location['text'] : esc_html__( 'Link', 'event-bridge-for-activitypub' );
@ -103,29 +99,26 @@ final class Events_Manager extends Event_Transformer {
}
/**
* Get the end time from the events metadata.
* Get the start time of the event.
*/
public function get_end_time(): ?string {
return null;
public function get_start_time(): string {
return $this->em_event->start()->format( 'Y-m-d\TH:i:sP' );
}
/**
* Get the end time from the events metadata.
* Get the end time of the event.
*/
public function get_start_time(): string {
$date_string = $this->em_event->event_start_date;
$time_string = $this->em_event->event_start_time;
$timezone_string = $this->em_event->event_timezone;
public function get_end_time(): string {
return $this->em_event->end()->format( 'Y-m-d\TH:i:sP' );
}
// Create a DateTime object with the given date, time, and timezone.
$datetime = new DateTime( $date_string . ' ' . $time_string, new DateTimeZone( $timezone_string ) );
// Set the timezone for proper formatting.
$datetime->setTimezone( new DateTimeZone( 'UTC' ) );
// Format the DateTime object as 'Y-m-d\TH:i:s\Z'.
$formatted_date = $datetime->format( 'Y-m-d\TH:i:s\Z' );
return $formatted_date;
/**
* Get the timezone. Events calendar also supports "UTC-offset timezones", ActivityPub federation does not.
*
* @return string
*/
public function get_timezone_string(): string {
return $this->em_event->get_timezone()->getName();
}
/**
@ -170,12 +163,7 @@ final class Events_Manager extends Event_Transformer {
*/
private function get_event_link_attachment(): ?array {
if ( $this->is_online() ) {
if ( property_exists( $this->em_event->event_location, 'data' ) ) {
$event_location = $this->em_event->event_location->data;
} else {
$event_location = array();
}
$event_location = $this->em_event->event_location->data;
$event_link_url = isset( $event_location['url'] ) ? $event_location['url'] : null;
$event_link_text = isset( $event_location['text'] ) ? $event_location['text'] : __( 'Link', 'event-bridge-for-activitypub' );
@ -224,16 +212,23 @@ final class Events_Manager extends Event_Transformer {
$post_tags = \wp_get_post_terms( $this->item->ID, 'event-tags' );
if ( $post_tags ) {
foreach ( $post_tags as $post_tag ) {
$tag = array(
if ( \is_wp_error( $post_tags ) ) {
return $tags;
}
foreach ( $post_tags as $post_tag ) {
// @phpstan-ignore-next-line
if ( $post_tag instanceof \WP_Term ) {
$tag = array(
'type' => 'Hashtag',
'href' => \esc_url( \get_tag_link( $post_tag->term_id ) ),
'name' => esc_hashtag( $post_tag->name ),
);
$tags[] = $tag;
}
}
return $tags;
}

View File

@ -1,28 +1,34 @@
<?php
/**
* ActivityPub Transformer for the GatherPress event plugin.
* ActivityPub Transformer for the GatherPress events.
*
* @package Event_Bridge_For_ActivityPub
* @package GatherPress_ActivityPub
* @since 1.0.0
* @license AGPL-3.0-or-later
*/
namespace Event_Bridge_For_ActivityPub\ActivityPub\Transformer\Event;
// Exit if accessed directly.
defined( 'ABSPATH' ) || exit; // @codeCoverageIgnore
\defined( 'ABSPATH' ) || exit; // @codeCoverageIgnore
use Activitypub\Activity\Extended_Object\Event as Event_Object;
use Activitypub\Activity\Extended_Object\Place;
use Event_Bridge_For_ActivityPub\ActivityPub\Transformer\Event\Event;
use Event_Bridge_For_ActivityPub\ActivityPub\Object\Place;
use Event_Bridge_For_ActivityPub\ActivityPub\Object\Event_FEP_8a8e;
use Event_Bridge_For_ActivityPub\ActivityPub\Transformer\Place\GatherPress as Venue_Transformer;
use Event_Bridge_For_ActivityPub\ActivityPub\Transformer\Event\Event as Event_Transformer;
use Activitypub\Activity\Base_Object;
use GatherPress\Core\Event as GatherPress_Event;
use GatherPress\Core\Venue;
use WP_Error;
use function Activitypub\get_content_warning;
/**
* ActivityPub Transformer for VS Event
* Class GatherPress Event Transformer.
*
* @since 1.0.0
* Manages the transformation of a GatherPress event post to ActivityStreams.
*/
final class GatherPress extends Event {
final class GatherPress extends Event_Transformer {
/**
* The current GatherPress Event object.
*
@ -38,12 +44,9 @@ final class GatherPress extends Event {
protected $gp_venue;
/**
* Extend the constructor, to also set the GatherPress objects.
* 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 \WP_Post $item The WordPress object.
* @param \WP_Post $item The WordPress object.
* @param string $wp_taxonomy The taxonomy slug of the event post type.
*/
public function __construct( $item, $wp_taxonomy ) {
@ -52,70 +55,59 @@ final class GatherPress extends Event {
$this->gp_venue = $this->gp_event->get_venue_information();
}
/**
* Return the correct type of an Event.
*
* @return string
*/
public function get_type(): string {
return 'Event';
}
/**
* Get the event location.
*
* @return ?Place The place objector null if not place set.
* @return Place|array|null A Place object or VirtualLocation.
*/
public function get_location(): ?Place {
$address = $this->gp_venue['full_address'];
if ( $address ) {
$place = new Place();
$place->set_type( 'Place' );
$place->set_name( $address );
$place->set_address( $address );
return $place;
public function get_location(): mixed {
$event_link = $this->gp_event->maybe_get_online_event_link();
if ( $event_link ) {
return array(
'type' => 'VirtualLocation',
'url' => $event_link,
);
}
$term = current( (array) get_the_terms( $this->gp_event->event, Venue::TAXONOMY ) );
if ( ! empty( $term ) && is_a( $term, 'WP_Term' ) ) {
$venue_post = Venue::get_instance()->get_venue_post_from_term_slug( $term->slug );
if ( ! $venue_post ) {
return null;
}
} else {
return null;
}
$venue_transformer = new Venue_Transformer( $venue_post );
$full_location_object = false;
$location = $venue_transformer->to_object( $full_location_object );
return $location;
}
/**
* Get the end time from the event object.
*/
public function get_end_time(): string {
return $this->gp_event->get_datetime_end( 'Y-m-d\TH:i:s\Z' );
return $this->gp_event->get_datetime_end( 'Y-m-d\TH:i:sP' );
}
/**
* Get the end time from the event object.
*/
public function get_start_time(): string {
return $this->gp_event->get_datetime_start( 'Y-m-d\TH:i:s\Z' );
}
/**
* Get the event link from the events metadata.
*/
private function get_event_link(): array {
$event_link = get_post_meta( $this->item->ID, 'event-link', true );
if ( $event_link ) {
return array(
'type' => 'Link',
'name' => 'Website',
'href' => \esc_url( $event_link ),
'mediaType' => 'text/html',
);
}
return array();
}
/**
* Overrides/extends the get_attachments function to also add the event Link.
*/
protected function get_attachment(): array {
$attachments = parent::get_attachment();
if ( count( $attachments ) ) {
$attachments[0]['type'] = 'Document';
$attachments[0]['name'] = 'Banner';
}
$event_link = $this->get_event_link();
if ( $event_link ) {
$attachments[] = $this->get_event_link();
}
return $attachments;
return $this->gp_event->get_datetime_start( 'Y-m-d\TH:i:sP' );
}
/**
@ -130,27 +122,44 @@ final class GatherPress extends Event {
return ''; // Skip rendering this block.
}
return $block_content; // Return the content for other blocks.
return str_replace( '<p></p>', '', $block_content ); // Return the content for other blocks.
}
/**
* Apply the filter for preventing the rendering off gatherpress blocks just in time.
* Transform to an the Event object.
*
* @return Event_Object
* @return Base_Object|WP_Error
*/
public function to_object(): Event_Object {
/**
* Transform to an the Event object.
*
* @return Base_Object|WP_Error
*/
public function to_object(): Base_Object|WP_Error {
// Apply the filter for preventing the rendering off gatherpress blocks just in time.
add_filter( 'render_block', array( self::class, 'filter_gatherpress_blocks' ), 10, 2 );
$activitypub_object = parent::to_object();
remove_filter( 'render_block', array( self::class, 'filter_gatherpress_blocks' ) );
return $activitypub_object;
}
/**
* Determine whether the event is online.
*
* @return bool
*/
public function get_is_online(): bool {
return $this->gp_event->maybe_get_online_event_link() ? true : false;
// Transform all properties via getter functions.
$object = new Event_FEP_8a8e();
$object = $this->transform_object_properties( $object );
if ( \is_wp_error( $object ) ) {
return $object;
}
$this->set_audience( $object );
remove_filter( 'render_block', array( self::class, 'filter_gatherpress_blocks' ) );
// Maybe modify object for content warning.
$content_warning = get_content_warning( $this->item );
if ( ! empty( $content_warning ) ) {
$object->set_sensitive( true );
$object->set_summary( $content_warning );
$object->set_summary_map( null );
$object->set_dcterms( array( 'subject' => $content_warning ) );
}
return $object;
}
}

View File

@ -9,7 +9,7 @@
namespace Event_Bridge_For_ActivityPub\ActivityPub\Transformer\Event;
// Exit if accessed directly.
defined( 'ABSPATH' ) || exit; // @codeCoverageIgnore
\defined( 'ABSPATH' ) || exit; // @codeCoverageIgnore
use Activitypub\Activity\Extended_Object\Place;
use Event_Bridge_For_ActivityPub\ActivityPub\Transformer\Event\Event;
@ -67,7 +67,10 @@ final class Modern_Events_Calendar_Lite extends Event {
* @return string
*/
public function get_start_time(): string {
return \gmdate( 'Y-m-d\TH:i:s\Z', $this->mec_event->get_datetime()['start']['timestamp'] );
$datetime = $this->mec_event->get_datetime()['start']['datetime'];
$timezone = $this->get_timezone() ? new \DateTimeZone( $this->get_timezone() ) : null;
$start_time = new \DateTime( $datetime, $timezone );
return $start_time->format( 'Y-m-d\TH:i:sP' );
}
/**
@ -76,7 +79,10 @@ final class Modern_Events_Calendar_Lite extends Event {
* @return string
*/
public function get_end_time(): string {
return \gmdate( 'Y-m-d\TH:i:s\Z', $this->mec_event->get_datetime()['end']['timestamp'] );
$datetime = $this->mec_event->get_datetime()['end']['datetime'];
$timezone = $this->get_timezone() ? new \DateTimeZone( $this->get_timezone() ) : null;
$end_time = new \DateTime( $datetime, $timezone );
return $end_time->format( 'Y-m-d\TH:i:sP' );
}
/**
@ -113,15 +119,11 @@ final class Modern_Events_Calendar_Lite extends Event {
}
/**
* Get the location.
* Get the timezone string of the current event.
*
* @return mixed
*/
public function get_timezone(): string {
$timezone = get_post_meta( $this->item->ID, 'mec_timezone', true );
if ( 'global' === $timezone ) {
return parent::get_timezone();
}
return $timezone;
public function get_timezone_string(): mixed {
return \get_post_meta( $this->item->ID, 'mec_timezone', true );
}
}

View File

@ -0,0 +1,140 @@
<?php
/**
* ActivityPub Transformer for Events managed with Eventin.
*
* @link https://support.themewinter.com/docs/plugins/docs-category/eventin/
*
* @package Event_Bridge_For_ActivityPub
* @license AGPL-3.0-or-later
*/
namespace Event_Bridge_For_ActivityPub\ActivityPub\Transformer\Event;
use DateTimeImmutable;
use DateTimeZone;
// Exit if accessed directly.
\defined( 'ABSPATH' ) || exit; // @codeCoverageIgnore
use Activitypub\Activity\Extended_Object\Place;
use Event_Bridge_For_ActivityPub\ActivityPub\Transformer\Event\Event;
/**
* ActivityPub Transformer for Events managed with Eventin.
*
* @since 1.0.0
*/
final class Spiffy_Calendar extends Event {
/**
* Get the end time from the event object.
*/
public function get_start_time(): string {
$start_date = \get_post_meta( $this->item->ID, '_spiffy_event_begin', true );
$start_time = \get_post_meta( $this->item->ID, '_spiffy_event_begin_time', true );
$start_datetime = $this->get_datetime_from_fuzzy_input( $start_date, $start_time );
return $start_datetime->format( 'Y-m-d\TH:i:sP' );
}
/**
* Get the end time from the event object.
*/
public function get_end_time(): string {
$end_date = \get_post_meta( $this->item->ID, '_spiffy_event_end', true );
$end_time = \get_post_meta( $this->item->ID, '_spiffy_event_end_time', true );
$end_datetime = $this->get_datetime_from_fuzzy_input( $end_date, $end_time );
return $end_datetime->format( 'Y-m-d\TH:i:sP' );
}
/**
* Get the location.
*
* @return ?Place
*/
public function get_location(): ?Place {
$location = \get_post_meta( $this->item->ID, '_spiffy_event_location', true );
if ( ! $location ) {
return null;
}
$place = new Place();
$place->set_address( $location );
$place->set_name( $location );
return $place;
}
/**
* Overrides/extends the get_attachments function to also add the event Link.
*
* @return array
*/
protected function get_attachment(): array {
$attachments = parent::get_attachment();
if ( \count( $attachments ) ) {
$attachments[0]['type'] = 'Document';
$attachments[0]['name'] = 'Banner';
}
$event_link = $this->get_event_link();
if ( $event_link ) {
$attachments[] = $event_link;
}
return $attachments;
}
/**
* Get the event link from the events metadata.
*
* @return array|null Associated array of an ActivityStreams Link object with the event link.
*/
private function get_event_link(): ?array {
$event_link = \get_post_meta( $this->item->ID, '_spiffy_event_link', true );
if ( $event_link ) {
return array(
'type' => 'Link',
'name' => __( 'Event Link', 'event-bridge-for-activitypub' ),
'href' => \esc_url( $event_link ),
'mediaType' => 'text/html',
);
}
return null;
}
/**
* Generate the best datetime object of the fuzzy human made data Spiffy Calendar provides.
*
* @param string $date The date as 2026-05-18 string.
* @param string $time Fuzzy time like 14:00, 2pm, 2 PM, noon, midnight, etc.
* @return DateTimeImmutable
*/
private function get_datetime_from_fuzzy_input( string $date = '', string $time = '' ): DateTimeImmutable {
$timezone = new DateTimeZone( $this->get_timezone_string() );
// Normalize fuzzy human time.
$time = trim( strtolower( $time ) );
$map = array(
__( 'noon', 'event-bridge-for-activitypub' ) => '12:00',
__( 'midday', 'event-bridge-for-activitypub' ) => '12:00',
__( 'midnight', 'event-bridge-for-activitypub' ) => '00:00',
__( 'morning', 'event-bridge-for-activitypub' ) => '09:00',
__( 'afternoon', 'event-bridge-for-activitypub' ) => '15:00',
__( 'evening', 'event-bridge-for-activitypub' ) => '19:00',
);
$time = $map[ $time ] ?? $time;
$input = trim( "$date $time" );
try {
$datetime = new DateTimeImmutable( $input, $timezone );
} catch ( \Throwable $e ) {
$datetime = new DateTimeImmutable( "$date 00:00", $timezone );
}
return $datetime;
}
}

View File

@ -9,7 +9,7 @@
namespace Event_Bridge_For_ActivityPub\ActivityPub\Transformer\Event;
// Exit if accessed directly.
defined( 'ABSPATH' ) || exit; // @codeCoverageIgnore
\defined( 'ABSPATH' ) || exit; // @codeCoverageIgnore
use Activitypub\Activity\Extended_Object\Event as Event_Object;
use Activitypub\Activity\Extended_Object\Place;
@ -81,7 +81,7 @@ final class The_Events_Calendar extends Event {
*/
public function get_end_time(): string {
$utc_time = get_post_meta( $this->tribe_event->ID, '_EventEndDateUTC', true );
$timezone = new \DateTimeZone( $this->get_timezone() );
$timezone = new \DateTimeZone( $this->get_timezone_string() );
$time = new \DateTime( $utc_time );
$time->setTimezone( $timezone );
return $time->format( 'Y-m-d\TH:i:sP' );
@ -92,7 +92,7 @@ final class The_Events_Calendar extends Event {
*/
public function get_start_time(): string {
$utc_time = get_post_meta( $this->tribe_event->ID, '_EventStartDateUTC', true );
$timezone = new \DateTimeZone( $this->get_timezone() );
$timezone = new \DateTimeZone( $this->get_timezone_string() );
$time = new \DateTime( $utc_time );
$time->setTimezone( $timezone );
return $time->format( 'Y-m-d\TH:i:sP' );
@ -101,17 +101,11 @@ final class The_Events_Calendar extends Event {
/**
* Get the timezone of the event.
*
* @return string The timezone string of the site.
* @return string The timezone string of the event.
*/
public function get_timezone(): string {
public function get_timezone_string(): string {
// @phpstan-ignore-next-line
$timezone = $this->tribe_event->timezone;
if ( ! $timezone || ! is_string( $timezone ) ) {
return parent::get_timezone();
}
return $timezone;
return (string) $this->tribe_event->timezone;
}
/**
@ -144,8 +138,10 @@ final class The_Events_Calendar extends Event {
/**
* Check if the event is an online event.
*
* @return false
*/
public function get_is_online(): bool {
public function get_is_online() {
return false;
}

View File

@ -9,7 +9,7 @@
namespace Event_Bridge_For_ActivityPub\ActivityPub\Transformer\Event;
// Exit if accessed directly.
defined( 'ABSPATH' ) || exit; // @codeCoverageIgnore
\defined( 'ABSPATH' ) || exit; // @codeCoverageIgnore
use Activitypub\Activity\Extended_Object\Place;
use Event_Bridge_For_ActivityPub\ActivityPub\Transformer\Event\Event as Event_Transformer;
@ -52,7 +52,7 @@ final class VS_Event_List extends Event_Transformer {
if ( is_null( $end_time ) || empty( $end_time ) || 'no' === $end_time ) {
return null;
}
return \gmdate( 'Y-m-d\TH:i:s\Z', (int) $end_time );
return \wp_date( 'Y-m-d\TH:i:sP', (int) $end_time );
}
/**
@ -60,7 +60,7 @@ final class VS_Event_List extends Event_Transformer {
*/
public function get_start_time(): string {
$start_time = \get_post_meta( $this->item->ID, 'event-start-date', true );
return \gmdate( 'Y-m-d\TH:i:s\Z', (int) $start_time );
return \wp_date( 'Y-m-d\TH:i:sP', (int) $start_time );
}
/**

View File

@ -9,7 +9,7 @@
namespace Event_Bridge_For_ActivityPub\ActivityPub\Transformer\Event;
// Exit if accessed directly.
defined( 'ABSPATH' ) || exit; // @codeCoverageIgnore
\defined( 'ABSPATH' ) || exit; // @codeCoverageIgnore
use Activitypub\Activity\Extended_Object\Place;
use Event_Bridge_For_ActivityPub\ActivityPub\Transformer\Event\Event as Event_Transformer;
@ -30,7 +30,7 @@ final class WP_Event_Manager extends Event_Transformer {
* @return bool
*/
protected function get_is_online(): bool {
$is_online_text = get_post_meta( $this->item->ID, '_event_online', true );
$is_online_text = \get_post_meta( $this->item->ID, '_event_online', true );
$is_online = false;
// Radio buttons.
if ( 'yes' === $is_online_text ) {
@ -49,7 +49,7 @@ final class WP_Event_Manager extends Event_Transformer {
* @return ?Place The Place.
*/
public function get_location(): ?Place {
$location_name = get_post_meta( $this->item->ID, '_event_location', true );
$location_name = \get_post_meta( $this->item->ID, '_event_location', true );
if ( $location_name ) {
$location = new Place();
@ -68,11 +68,12 @@ final class WP_Event_Manager extends Event_Transformer {
* @return ?string The events end-datetime if is set, null otherwise.
*/
public function get_end_time(): ?string {
$end_date = get_post_meta( $this->item->ID, '_event_end_date', true );
$end_date = \get_post_meta( $this->item->ID, '_event_end_date', true );
if ( ! $end_date ) {
return null;
}
$timezone = new DateTimeZone( $this->get_timezone() );
$timezone = $this->get_timezone() ? new DateTimeZone( $this->get_timezone() ) : null;
if ( is_numeric( $end_date ) ) {
$end_date = '@' . $end_date;
@ -86,22 +87,19 @@ final class WP_Event_Manager extends Event_Transformer {
/**
* Get timezone.
*
* @return string
* @return mixed
*/
public function get_timezone(): string {
$time_zone = get_post_meta( $this->item->ID, '_event_timezone', true );
if ( $time_zone ) {
return $time_zone;
}
return parent::get_timezone();
public function get_timezone_string(): mixed {
return \get_post_meta( $this->item->ID, '_event_timezone', true );
}
/**
* Get the end time from the events metadata.
*/
public function get_start_time(): string {
$start_date = get_post_meta( $this->item->ID, '_event_start_date', true );
$timezone = new DateTimeZone( $this->get_timezone() );
$start_date = \get_post_meta( $this->item->ID, '_event_start_date', true );
$timezone = $this->get_timezone() ? new DateTimeZone( $this->get_timezone() ) : null;
if ( is_numeric( $start_date ) ) {
$start_date = '@' . $start_date;
@ -118,7 +116,7 @@ final class WP_Event_Manager extends Event_Transformer {
* @return ?array
*/
private function get_event_link_attachment(): ?array {
$event_link_url = get_post_meta( $this->item->ID, '_event_video_url', true );
$event_link_url = \get_post_meta( $this->item->ID, '_event_video_url', true );
if ( str_starts_with( $event_link_url, 'http' ) ) {
return array(

View File

@ -9,7 +9,7 @@
namespace Event_Bridge_For_ActivityPub\ActivityPub\Transformer\Place;
// Exit if accessed directly.
defined( 'ABSPATH' ) || exit; // @codeCoverageIgnore
\defined( 'ABSPATH' ) || exit; // @codeCoverageIgnore
use Activitypub\Activity\Extended_Object\Place as Place_Object;
use Activitypub\Transformer\Post;

View File

@ -9,7 +9,7 @@
namespace Event_Bridge_For_ActivityPub\ActivityPub\Transformer\Place;
// Exit if accessed directly.
defined( 'ABSPATH' ) || exit; // @codeCoverageIgnore
\defined( 'ABSPATH' ) || exit; // @codeCoverageIgnore
use Activitypub\Activity\Extended_Object\Place as Place_Object;
use Activitypub\Transformer\Base;
@ -128,7 +128,7 @@ abstract class Base_Term_Place extends Base {
/**
* Don't set sensitive per default.
*
* @return null
* @return null|bool
*/
public function get_sensitive() {
return null;

View File

@ -9,7 +9,7 @@
namespace Event_Bridge_For_ActivityPub\ActivityPub\Transformer\Place;
// Exit if accessed directly.
defined( 'ABSPATH' ) || exit; // @codeCoverageIgnore
\defined( 'ABSPATH' ) || exit; // @codeCoverageIgnore
/**
* Class for the ActivityPub transformer of the venues of The Events Calendar to `as:Place`.

View File

@ -9,7 +9,7 @@
namespace Event_Bridge_For_ActivityPub\ActivityPub\Transformer\Place;
// Exit if accessed directly.
defined( 'ABSPATH' ) || exit; // @codeCoverageIgnore
\defined( 'ABSPATH' ) || exit; // @codeCoverageIgnore
use Activitypub\Activity\Extended_Object\Place as Place_Object;

View File

@ -9,7 +9,7 @@
namespace Event_Bridge_For_ActivityPub\ActivityPub\Transformer\Place;
// Exit if accessed directly.
defined( 'ABSPATH' ) || exit; // @codeCoverageIgnore
\defined( 'ABSPATH' ) || exit; // @codeCoverageIgnore
/**
* Class for the ActivityPub transformer of the venues of The Events Calendar to `as:Place`.

View File

@ -9,7 +9,7 @@
namespace Event_Bridge_For_ActivityPub\ActivityPub\Transformer\Place;
// Exit if accessed directly.
defined( 'ABSPATH' ) || exit; // @codeCoverageIgnore
\defined( 'ABSPATH' ) || exit; // @codeCoverageIgnore
use Event_Bridge_For_ActivityPub\ActivityPub\Transformer\Place\Base_Post_Place;

View File

@ -0,0 +1,152 @@
<?php
/**
* ActivityPub Transformer for the GatherPress venues.
*
* See https://codeberg.org/Event-Federation/gatherpress-activitypub/src/branch/main/includes/classes/class-venue-transformer.php
*
* @package GatherPress_ActivityPub
* @since 1.0.0
* @license AGPL-3.0-or-later
*/
namespace Event_Bridge_For_ActivityPub\ActivityPub\Transformer\Place;
// Exit if accessed directly.
\defined( 'ABSPATH' ) || exit; // @codeCoverageIgnore
use Event_Bridge_For_ActivityPub\ActivityPub\Object\Place;
use Activitypub\Activity\Base_Object;
use Activitypub\Transformer\Post;
use stdClass;
use WP_Error;
use WP_Post;
/**
* Class GatherPress Venue Transformer.
*
* Manages the transformation of a GatherPress event post to ActivityStreams.
*/
class GatherPress extends Post {
/**
* The current GatherPress Venue WP_Post.
*
* @var stdClass
*/
protected $venue_meta;
/**
* Extend the constructor to also set the GatherPress objects.
*
* @param WP_Post $item The WordPress object.
*/
public function __construct( WP_Post $item ) {
parent::__construct( $item );
$this->venue_meta = json_decode( get_post_meta( $this->item->ID, 'gatherpress_venue_information', true ) );
}
/**
* Return the correct type of an Event.
*
* @return string
*/
public function get_type(): string {
return 'Place';
}
/**
* Get the event location.
*
* @return string The name of the venue.
*/
public function get_name(): string {
return $this->item->post_title;
}
/**
* The full address of the venue.
*
* @return string|null
*/
public function get_address(): ?string {
return $this->venue_meta->fullAddress ?? null;
}
/**
* Latitude of the venue.
*
* @return float|null
*/
public function get_latitude(): ?float {
return $this->venue_meta->latitude ?? null;
}
/**
* Longitude of the venue.
*
* @return float|null
*/
public function get_longitude(): ?float {
return $this->venue_meta->longitude ?? null;
}
/**
* The telephone number of the venue.
*
* @return string|null
*/
public function get_telephone(): ?string {
return $this->venue_meta->phone_number ?? null;
}
/**
* The website of the venue.
*
* @return string|null
*/
public function get_same_as(): ?string {
return $this->venue_meta->website ?? null;
}
/**
* Generic function that converts an WordPress location object to an ActivityPub-Place object.
*
* @param bool $full_object bool Return an object with all properties set, or a minimal one as used within an `as:Event`s location.
* @return Place|\WP_Error
*/
public function to_object( $full_object = true ): Base_Object|WP_Error {
$activitypub_object = new Place();
$activitypub_object = $this->transform_object_properties( $activitypub_object );
if ( \is_wp_error( $activitypub_object ) ) {
return $activitypub_object;
}
if ( ! empty( $activitypub_object->get_content() ) ) {
$activitypub_object->set_content_map(
array(
$this->get_locale() => $this->get_content(),
)
);
}
$updated = \strtotime( $this->item->post_modified_gmt );
$activitypub_object->set_updated( \gmdate( 'Y-m-d\TH:i:s\Z', $updated ) );
if ( $full_object ) {
$published = \strtotime( $this->item->post_date_gmt );
$activitypub_object->set_published( \gmdate( 'Y-m-d\TH:i:s\Z', $published ) );
$activitypub_object->set_to(
array(
'https://www.w3.org/ns/activitystreams#Public',
$this->get_actor_object()->get_followers(),
)
);
}
// @phpstan-ignore-next-line
return $activitypub_object;
}
}

View File

@ -9,7 +9,7 @@
namespace Event_Bridge_For_ActivityPub\ActivityPub\Transformer\Place;
// Exit if accessed directly.
defined( 'ABSPATH' ) || exit; // @codeCoverageIgnore
\defined( 'ABSPATH' ) || exit; // @codeCoverageIgnore
use Event_Bridge_For_ActivityPub\ActivityPub\Transformer\Place\Base_Post_Place;
@ -60,4 +60,22 @@ final class The_Events_Calendar extends Base_Post_Place {
return $postal_address;
}
/**
* Get the latitude of the place.
*
* @return ?float The latitude if it is known.
*/
public function get_latitude() {
return tribe_get_coordinates( $this->item->ID )['lat'] ?? null;
}
/**
* Get the longitude of the place.
*
* @return ?float The longitude if it is known.
*/
public function get_longitude() {
return tribe_get_coordinates( $this->item->ID )['lng'] ?? null;
}
}

View File

@ -10,7 +10,7 @@
namespace Event_Bridge_For_ActivityPub\ActivityPub\Transmogrifier;
// Exit if accessed directly.
defined( 'ABSPATH' ) || exit; // @codeCoverageIgnore
\defined( 'ABSPATH' ) || exit; // @codeCoverageIgnore
use Activitypub\Activity\Extended_Object\Event;
use Event_Bridge_For_ActivityPub\ActivityPub\Collection\Event_Sources;
@ -48,9 +48,18 @@ abstract class Base {
return;
}
// Add hook to insert post meta to flag post as remote and remember where we received it from.
$add_origin_post_meta_callback = function ( $post_id ) use ( $event_source_post_id ): void {
self::add_origin_post_meta( $post_id, $event_source_post_id );
};
\add_action( 'wp_after_insert_post', $add_origin_post_meta_callback, 10, 1 );
// Pass the saving to the actual Transmogrifier implementation.
$post_id = static::save_event( $activitypub_event, $event_source_post_id );
// Remove hook added above.
\remove_action( 'wp_after_insert_post', $add_origin_post_meta_callback, 10 );
// Post processing: Logging and marking the imported event's origin.
$event_activitypub_id = $activitypub_event->get_id();
$event_source_activitypub_id = \get_the_guid( $event_source_post_id );
@ -60,9 +69,6 @@ abstract class Base {
'event_bridge_for_activitypub_write_log',
array( "[ACTIVITYPUB] Processed incoming event {$event_activitypub_id} from {$event_source_activitypub_id}" )
);
// Use post meta to remember who we received this event from.
\update_post_meta( $post_id, '_event_bridge_for_activitypub_event_source', absint( $event_source_post_id ) );
\update_post_meta( $post_id, 'activitypub_content_visibility', defined( 'ACTIVITYPUB_CONTENT_VISIBILITY_LOCAL' ) ? constant( 'ACTIVITYPUB_CONTENT_VISIBILITY_LOCAL' ) : '' );
} else {
\do_action(
'event_bridge_for_activitypub_write_log',
@ -71,6 +77,20 @@ abstract class Base {
}
}
/**
* Insert post meta to remember who we received an event from.
*
* @param int $post_id The WordPress post ID of the event itself.
* @param int $event_source_post_id The WordPress post ID of the event source custom post type.
*
* @return void
*/
public static function add_origin_post_meta( $post_id, $event_source_post_id ): void {
// Use post meta to remember who we received this event from.
\update_post_meta( $post_id, '_event_bridge_for_activitypub_event_source', absint( $event_source_post_id ) );
\update_post_meta( $post_id, 'activitypub_content_visibility', defined( 'ACTIVITYPUB_CONTENT_VISIBILITY_LOCAL' ) ? constant( 'ACTIVITYPUB_CONTENT_VISIBILITY_LOCAL' ) : '' );
}
/**
* Delete a local event in WordPress that is a cached remote one.
*
@ -129,6 +149,8 @@ abstract class Base {
*/
protected static function get_post_id_from_activitypub_id( $activitypub_id ): int {
global $wpdb;
// phpcs:disable WordPress.DB.DirectDatabaseQuery
return (int) $wpdb->get_var(
$wpdb->prepare(
"SELECT ID FROM $wpdb->posts WHERE guid=%s",
@ -227,15 +249,13 @@ abstract class Base {
// Include necessary WordPress file for media handling.
if ( ! function_exists( 'media_sideload_image' ) ) {
// @phpstan-ignore-next-line
require_once ABSPATH . 'wp-admin/includes/media.php';
// @phpstan-ignore-next-line
require_once ABSPATH . 'wp-admin/includes/file.php';
// @phpstan-ignore-next-line
require_once ABSPATH . 'wp-admin/includes/image.php';
}
// Check to see if the URL has already been fetched, if so return the attachment ID.
// phpcs:disable WordPress.DB.DirectDatabaseQuery
$attachment_id = $wpdb->get_var(
$wpdb->prepare( "SELECT `post_id` FROM {$wpdb->postmeta} WHERE `meta_key` = '_source_url' AND `meta_value` = %s", $url )
);
@ -243,6 +263,7 @@ abstract class Base {
return $attachment_id;
}
// phpcs:disable WordPress.DB.DirectDatabaseQuery
$attachment_id = $wpdb->get_var(
$wpdb->prepare( "SELECT `ID` FROM {$wpdb->posts} WHERE guid=%s", $url )
);

View File

@ -12,7 +12,7 @@
namespace Event_Bridge_For_ActivityPub\ActivityPub\Transmogrifier;
// Exit if accessed directly.
defined( 'ABSPATH' ) || exit; // @codeCoverageIgnore
\defined( 'ABSPATH' ) || exit; // @codeCoverageIgnore
use Activitypub\Activity\Extended_Object\Event;
use Activitypub\Activity\Extended_Object\Place;
@ -40,7 +40,7 @@ class GatherPress extends Base {
$tags_array = $event->get_tag();
// Ensure the input is valid.
if ( empty( $tags_array ) || ! is_array( $tags_array ) || ! $post_id ) {
if ( empty( $tags_array ) || ! $post_id ) {
return false;
}

View File

@ -12,7 +12,7 @@
namespace Event_Bridge_For_ActivityPub\ActivityPub\Transmogrifier;
// Exit if accessed directly.
defined( 'ABSPATH' ) || exit; // @codeCoverageIgnore
\defined( 'ABSPATH' ) || exit; // @codeCoverageIgnore
use Activitypub\Activity\Extended_Object\Event;
use Activitypub\Activity\Extended_Object\Place;
@ -43,19 +43,18 @@ class The_Events_Calendar extends Base {
add_filter( 'wp_revisions_to_keep', array( self::class, 'revisions_to_keep' ) );
$post_id = self::get_post_id_from_activitypub_id( $activitypub_event->get_id() );
$duration = self::get_duration( $activitypub_event );
$venue_id = self::add_venue( $activitypub_event, $event_source_post_id );
$organizer_id = self::add_organizer( $activitypub_event );
$args = array(
'title' => $activitypub_event->get_name(),
'content' => $activitypub_event->get_content() ?? '',
'start_date' => gmdate( 'Y-m-d H:i:s', strtotime( $activitypub_event->get_start_time() ) ),
'duration' => $duration,
'status' => 'publish',
'guid' => $activitypub_event->get_id(),
'title' => $activitypub_event->get_name(),
'content' => $activitypub_event->get_content() ?? '',
'status' => 'publish',
'guid' => $activitypub_event->get_id(),
);
$args = self::enrich_event_args_with_date_info( $args, $activitypub_event );
if ( $venue_id ) {
$args['venue'] = $venue_id;
$args['VenueID'] = $venue_id;
@ -103,6 +102,33 @@ class The_Events_Calendar extends Base {
return $post_id;
}
/**
* Enrich event arguments with normalized start date and timezone.
*
* @param array $args Existing event arguments.
* @param Event $activitypub_event The ActivityPub event as associative array.
* @return array Modified $args array including 'start_date' and 'timezone' and 'duration'.
*/
private static function enrich_event_args_with_date_info( $args, $activitypub_event ): array {
$start_time_str = $activitypub_event->get_start_time();
$timezone_string = $activitypub_event->get_timezone();
$start_time = new \DateTime( $start_time_str );
if ( empty( $timezone_string ) ) {
$timezone_string = 'UTC';
}
$start_time->setTimezone( new \DateTimeZone( $timezone_string ) );
$args['timezone'] = $timezone_string;
$args['start_date'] = $start_time->format( 'Y-m-d H:i:s' );
$args['duration'] = self::get_duration( $activitypub_event );
return $args;
}
/**
* Map an ActivityStreams Place to the Events Calendar venue.
*
@ -148,6 +174,14 @@ class The_Events_Calendar extends Base {
$args['guid'] = $location['id'];
}
if ( isset( $location['latitude'] ) ) {
$args['latitude'] = $location['latitude'];
}
if ( isset( $location['longitude'] ) ) {
$args['longitude'] = $location['longitude'];
}
return $args;
}
@ -210,7 +244,6 @@ class The_Events_Calendar extends Base {
$results = $tribe_venue->search( $location['name'] )->all();
foreach ( $results as $potential_matching_post_id ) {
// @phpstan-ignore-next-line
if ( $potential_matching_post_id instanceof \WP_Post ) {
$potential_matching_post_id = $potential_matching_post_id->ID;
}
@ -251,6 +284,13 @@ class The_Events_Calendar extends Base {
// This might likely change, because of FEP-8a8e.
$actor = $activitypub_event->get_attributed_to();
/**
* Allow filtering of incoming organizer.
*
* @var mixed
*/
$actor = \apply_filters( 'event_bridge_for_activitypub_remote_organizer', $actor, $activitypub_event );
if ( is_null( $actor ) ) {
return false;
}
@ -270,6 +310,7 @@ class The_Events_Calendar extends Base {
'website' => $event_source->get_url(),
'excerpt' => $event_source->get_summary(),
'post_parent' => $event_source->get__id(), // Maybe just use post meta too here.
'post_status' => 'publish',
);
if ( $event_source->get_published() ) {
@ -334,7 +375,7 @@ class The_Events_Calendar extends Base {
$tags_array = $activitypub_event->get_tag();
// Ensure the input is valid.
if ( empty( $tags_array ) || ! is_array( $tags_array ) || ! $post_id ) {
if ( empty( $tags_array ) || ! $post_id ) {
return false;
}

View File

@ -13,7 +13,7 @@
namespace Event_Bridge_For_ActivityPub\ActivityPub\Transmogrifier;
// Exit if accessed directly.
defined( 'ABSPATH' ) || exit; // @codeCoverageIgnore
\defined( 'ABSPATH' ) || exit; // @codeCoverageIgnore
use Activitypub\Activity\Extended_Object\Event;
use Activitypub\Activity\Extended_Object\Place;
@ -77,7 +77,7 @@ class VS_Event_List extends Base {
$tags_array = $activitypub_event->get_tag();
// Ensure the input is valid.
if ( empty( $tags_array ) || ! is_array( $tags_array ) || ! $post_id ) {
if ( empty( $tags_array ) || ! $post_id ) {
return false;
}

View File

@ -13,7 +13,7 @@
namespace Event_Bridge_For_ActivityPub\ActivityPub\Transmogrifier\Helper;
// Exit if accessed directly.
defined( 'ABSPATH' ) || exit; // @codeCoverageIgnore
\defined( 'ABSPATH' ) || exit; // @codeCoverageIgnore
use Activitypub\Activity\Extended_Object\Event;
use Activitypub\Activity\Extended_Object\Place;
@ -67,6 +67,10 @@ class Sanitizer {
$event->set_end_time( \sanitize_text_field( $data['endTime'] ) );
}
if ( isset( $data['timezone'] ) && in_array( $data['timezone'], \DateTimeZone::listIdentifiers(), true ) ) {
$event->set_timezone( \sanitize_text_field( $data['timezone'] ) );
}
if ( isset( $data['published'] ) ) {
$event->set_published( \sanitize_text_field( $data['published'] ) );
}
@ -177,6 +181,17 @@ class Sanitizer {
return array_is_list( $arr );
}
/**
* Sanitize an validate a float.
*
* @param mixed $value The input value.
* @return float|null
*/
private static function validate_and_sanitize_float( $value ) {
$sanitized = filter_var( $value, FILTER_SANITIZE_NUMBER_FLOAT, FILTER_FLAG_ALLOW_FRACTION );
return is_numeric( $sanitized ) ? (float) $sanitized : null;
}
/**
* Convert input array to an Location.
*
@ -189,9 +204,18 @@ class Sanitizer {
return null;
}
// If the array is a list, work with the first item.
if ( array_key_exists( 0, $data ) ) {
$data = $data[0];
// If the array is a list, search for the first item with 'type' === 'Place'.
if ( self::array_is_list( $data ) ) {
foreach ( $data as $item ) {
if ( is_array( $item ) && ( 'Place' === ( $item['type'] ?? null ) ) ) {
$data = $item;
break;
}
}
}
if ( ! isset( $data['type'] ) || 'Place' !== $data['type'] ) {
return null;
}
$place = new Place();
@ -204,6 +228,14 @@ class Sanitizer {
$place->set_id( \sanitize_url( $data['id'] ) );
}
if ( isset( $data['latitude'] ) ) {
$place->set_latitude( self::validate_and_sanitize_float( $data['latitude'] ) );
}
if ( isset( $data['longitude'] ) ) {
$place->set_longitude( self::validate_and_sanitize_float( $data['longitude'] ) );
}
if ( isset( $data['url'] ) ) {
$place->set_url( \sanitize_url( $data['url'] ) );
}

View File

@ -10,7 +10,7 @@
namespace Event_Bridge_For_ActivityPub\ActivityPub\Transmogrifier\Helper;
// Exit if accessed directly.
defined( 'ABSPATH' ) || exit; // @codeCoverageIgnore
\defined( 'ABSPATH' ) || exit; // @codeCoverageIgnore
/**
* Extending the Tribe Events API to allow setting of the guid.

View File

@ -10,7 +10,7 @@
namespace Event_Bridge_For_ActivityPub\ActivityPub\Transmogrifier\Helper;
// Exit if accessed directly.
defined( 'ABSPATH' ) || exit; // @codeCoverageIgnore
\defined( 'ABSPATH' ) || exit; // @codeCoverageIgnore
/**
* Extending the Organizer Venue API to allow setting of the guid.

View File

@ -10,7 +10,7 @@
namespace Event_Bridge_For_ActivityPub\ActivityPub\Transmogrifier\Helper;
// Exit if accessed directly.
defined( 'ABSPATH' ) || exit; // @codeCoverageIgnore
\defined( 'ABSPATH' ) || exit; // @codeCoverageIgnore
/**
* Extending the Tribe Venue API to allow setting of the guid.
@ -29,6 +29,30 @@ class The_Events_Calendar_Venue_Repository extends \Tribe__Events__Repositories_
'comment_count',
);
/**
* Tribe__Events__Repositories__Venue constructor.
*
* Add aliases for longitude an latitude.
*
* @since 4.9
* @since 6.10.1 Added `show_map` and `show_map_link` aliases.
*/
public function __construct() {
parent::__construct();
// Add venue specific aliases.
$this->update_fields_aliases = array_merge(
$this->update_fields_aliases,
array(
'latitude' => '_VenueLat',
'longitude' => '_VenueLng',
)
);
$this->add_simple_meta_schema_entry( 'latitude', '_VenueLat' );
$this->add_simple_meta_schema_entry( 'longitude', '_VenueLng' );
}
/**
* Whether the current key can be updated by this repository or not.
*

View File

@ -12,7 +12,7 @@
namespace Event_Bridge_For_ActivityPub\Admin;
// Exit if accessed directly.
defined( 'ABSPATH' ) || exit; // @codeCoverageIgnore
\defined( 'ABSPATH' ) || exit; // @codeCoverageIgnore
use Event_Bridge_For_ActivityPub\Integrations\Event_Plugin_Integration;
@ -49,7 +49,7 @@ class Event_Plugin_Admin_Notices {
* @return bool
*/
private function event_post_type_is_not_activitypub_enabled(): bool {
return ! in_array( $this->event_plugin::get_post_type(), get_option( 'activitypub_support_post_types', array() ), true );
return ! \in_array( $this->event_plugin::get_post_type(), get_option( 'activitypub_support_post_types', array() ), true );
}
/**
@ -79,7 +79,7 @@ class Event_Plugin_Admin_Notices {
return;
}
$activitypub_plugin_data = get_plugin_data( ACTIVITYPUB_PLUGIN_FILE );
$notice = sprintf(
$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 <i>%1$s</i> plugin, but the event post type of the plugin <i>%2$s</i> is <b>not enabled</b> in the <a href="%3$s">%1$s settings</a>.',

View File

@ -12,7 +12,7 @@
namespace Event_Bridge_For_ActivityPub\Admin;
// Exit if accessed directly.
defined( 'ABSPATH' ) || exit; // @codeCoverageIgnore
\defined( 'ABSPATH' ) || exit; // @codeCoverageIgnore
/**
* Class responsible for general admin notices.
@ -52,7 +52,7 @@ class General_Admin_Notices {
* @return string
*/
public static function get_admin_notice_activitypub_plugin_not_enabled(): string {
return sprintf(
return \sprintf(
/* translators: 1: An URL that points to the ActivityPub plugin. */
_x(
'For the Event Bridge for ActivityPub to work, you will need to install and activate the <a href="%1$s">ActivityPub</a> plugin.',
@ -69,7 +69,7 @@ class General_Admin_Notices {
* @return string
*/
public static function get_admin_notice_activitypub_plugin_version_too_old(): string {
return sprintf(
return \sprintf(
/* translators: 1: The name of the ActivityPub plugin. 2: The minimum required version number of the ActivityPub plugin. */
_x(
'Please upgrade your <a href="%1$s">ActivityPub</a> plugin. At least version %2$s is required for the Event Bridge for ActivityPub to work.',

View File

@ -8,7 +8,7 @@
namespace Event_Bridge_For_ActivityPub\Admin;
// Exit if accessed directly.
defined( 'ABSPATH' ) || exit; // @codeCoverageIgnore
\defined( 'ABSPATH' ) || exit; // @codeCoverageIgnore
use Activitypub\Transformer\Factory as Transformer_Factory;
use Event_Bridge_For_ActivityPub\Integrations\Event_Plugin_Integration;
@ -90,10 +90,6 @@ class Health_Check {
* @return bool True if the check passed.
*/
public static function test_if_event_transformer_is_used( $event_plugin ): bool {
if ( ! Setup::get_instance()->is_activitypub_plugin_active() ) {
return false;
}
// Get a (random) event post.
$event_posts = self::get_most_recent_event_posts( $event_plugin->get_post_type(), 1 );
@ -121,10 +117,6 @@ class Health_Check {
* @return \WP_Post[] Array of event posts, or false if none are found.
*/
public static function get_most_recent_event_posts( $event_post_type = null, $number_of_posts = 5 ): array {
if ( ! Setup::get_instance()->is_activitypub_plugin_active() ) {
return array();
}
if ( ! $event_post_type ) {
$active_event_plugins = Setup::get_instance()->get_active_event_plugins();
$active_event_plugin = reset( $active_event_plugins );
@ -141,6 +133,7 @@ class Health_Check {
'order' => 'DESC',
'include' => array(),
'exclude' => array(),
// phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_query
'meta_query' => array(
'relation' => 'OR',
array(

View File

@ -12,7 +12,7 @@
namespace Event_Bridge_For_ActivityPub\Admin;
// Exit if accessed directly.
defined( 'ABSPATH' ) || exit; // @codeCoverageIgnore
\defined( 'ABSPATH' ) || exit; // @codeCoverageIgnore
use Activitypub\Webfinger;
use Event_Bridge_For_ActivityPub\ActivityPub\Model\Event_Source;
@ -69,7 +69,7 @@ class Settings_Page {
* @return void
*/
public static function maybe_add_event_source() {
if ( ! isset( $_POST['event_bridge_for_activitypub_add_event_source'] ) ) {
if ( ! \array_key_exists( '_wpnonce', $_REQUEST ) ) {
return;
}
@ -78,11 +78,15 @@ class Settings_Page {
return;
}
if ( ! isset( $_POST['event_bridge_for_activitypub_add_event_source'] ) ) {
return;
}
if ( ! \current_user_can( 'manage_options' ) ) {
return;
}
$event_source = \sanitize_text_field( $_POST['event_bridge_for_activitypub_add_event_source'] );
$event_source = \sanitize_text_field( \wp_unslash( $_POST['event_bridge_for_activitypub_add_event_source'] ) );
$actor_url = false;
$url = \wp_parse_url( $event_source );
@ -203,13 +207,13 @@ class Settings_Page {
private static function get_event_terms( $event_plugin ): array {
$taxonomy = $event_plugin::get_event_category_taxonomy();
if ( $taxonomy ) {
$event_terms = get_terms(
$event_terms = \get_terms(
array(
'taxonomy' => $taxonomy,
'hide_empty' => true,
)
);
return ! is_wp_error( $event_terms ) ? $event_terms : array();
return ! \is_wp_error( $event_terms ) ? $event_terms : array();
} else {
return array();
}

View File

@ -9,10 +9,11 @@
namespace Event_Bridge_For_ActivityPub\Admin;
// Exit if accessed directly.
defined( 'ABSPATH' ) || exit; // @codeCoverageIgnore
use WP_Post;
// Exit if accessed directly.
\defined( 'ABSPATH' ) || exit; // @codeCoverageIgnore
use Event_Bridge_For_ActivityPub\ActivityPub\Model\Event_Source;
use Event_Bridge_For_ActivityPub\ActivityPub\Collection\Event_Sources as Event_Sources_Collection;
use Event_Bridge_For_ActivityPub\Event_Sources;
@ -39,7 +40,7 @@ class User_Interface {
* @param array $columns The current columns.
* @return array
*/
public static function add_origin_column( $columns ) {
public static function add_origin_column( array $columns ) {
// Add a new column after the title column.
$columns['activitypub_origin'] = __( 'ActivityPub origin', 'event-bridge-for-activitypub' );
return $columns;
@ -48,12 +49,12 @@ class User_Interface {
/**
* Add a "⁂ Preview" link to the row actions.
*
* @param array $actions The existing actions.
* @param \WP_Post $post The post object.
* @param array $actions The existing actions.
* @param WP_Post $post The post object.
*
* @return array The modified actions.
*/
public static function row_actions( $actions, $post ): array {
public static function row_actions( array $actions, WP_Post $post ): array {
// check if the post is enabled for ActivityPub.
if ( ! Event_Sources::is_cached_external_post( $post ) ) {
return $actions;
@ -67,7 +68,7 @@ class User_Interface {
$url = \get_post_meta( $parent->ID, '_activitypub_actor_id', true );
}
$actions['view_origin'] = sprintf(
$actions['view_origin'] = \sprintf(
'<a href="%s" target="_blank">⁂ %s</a>',
\esc_url( $url ),
\esc_html__( 'Open original page', 'event-bridge-for-activitypub' )
@ -79,14 +80,14 @@ class User_Interface {
/**
* Modify the user capabilities so that nobody can edit external events.
*
* @param array $caps Concerned user's capabilities.
* @param mixed $cap Required primitive capabilities for the requested capability.
* @param array $user_id The WordPress user ID.
* @param array $args Additional args.
* @param string[] $caps Concerned user's capabilities.
* @param string $cap Required primitive capabilities for the requested capability.
* @param int $user_id The WordPress user ID.
* @param array $args Additional args.
*
* @return array
*/
public static function disable_editing_for_external_events( $caps, $cap, $user_id, $args ) {
public static function disable_editing_for_external_events( array $caps, string $cap, int $user_id, array $args ) {
if ( 'edit_post' === $cap && isset( $args[0] ) ) {
$post_id = $args[0];
$post = get_post( $post_id );

View File

@ -14,7 +14,7 @@
namespace Event_Bridge_For_ActivityPub;
// Exit if accessed directly.
defined( 'ABSPATH' ) || exit; // @codeCoverageIgnore
\defined( 'ABSPATH' ) || exit; // @codeCoverageIgnore
/**
* Class Autoloader.
@ -37,7 +37,7 @@ class Autoloader {
$base_dir = EVENT_BRIDGE_FOR_ACTIVITYPUB_PLUGIN_DIR . '/includes/';
$base = 'Event_Bridge_For_ActivityPub\\';
if ( strncmp( $full_class, $base, strlen( $base ) ) === 0 ) {
if ( strncmp( $full_class, $base, \strlen( $base ) ) === 0 ) {
$maybe_uppercase = str_replace( $base, '', $full_class );
$class = strtolower( $maybe_uppercase );
// All classes should be capitalized. If this is instead looking for a lowercase method, we ignore that.
@ -58,7 +58,7 @@ class Autoloader {
if ( file_exists( $file ) && is_readable( $file ) ) {
require_once $file;
} else {
\wp_die( sprintf( esc_html( 'Required class not found or not readable: %s' ), esc_html( $full_class ) ) );
\wp_die( \sprintf( esc_html( 'Required class not found or not readable: %s' ), esc_html( $full_class ) ) );
}
}
}

View File

@ -10,7 +10,7 @@
namespace Event_Bridge_For_ActivityPub;
// Exit if accessed directly.
defined( 'ABSPATH' ) || exit; // @codeCoverageIgnore
\defined( 'ABSPATH' ) || exit; // @codeCoverageIgnore
/**
* Debug Class.
@ -24,7 +24,7 @@ class Debug {
* Initialize the class, registering WordPress hooks.
*/
public static function init() {
if ( defined( 'WP_DEBUG_LOG' ) && constant( 'WP_DEBUG_LOG' ) ) {
if ( \defined( 'WP_DEBUG_LOG' ) && constant( 'WP_DEBUG_LOG' ) && ! getenv( 'WP_TESTS_DIR' ) ) {
\add_action( 'event_bridge_for_activitypub_write_log', array( self::class, 'write_log' ), 10, 1 );
}
}

View File

@ -10,7 +10,7 @@
namespace Event_Bridge_For_ActivityPub;
// Exit if accessed directly.
defined( 'ABSPATH' ) || exit; // @codeCoverageIgnore
\defined( 'ABSPATH' ) || exit; // @codeCoverageIgnore
use Activitypub\Model\Blog;
use DateTime;
@ -34,7 +34,7 @@ class Event_Sources {
/**
* Init.
*/
public static function init() {
public static function init(): void {
// Register the Event Sources Collection which takes care of managing the event sources.
\add_action( 'init', array( Event_Sources_Collection::class, 'init' ) );
@ -87,7 +87,7 @@ class Event_Sources {
*
* @return void
*/
public static function register_post_meta() {
public static function register_post_meta(): void {
$setup = Setup::get_instance();
foreach ( $setup->get_active_event_plugins() as $event_plugin_integration ) {
@ -116,7 +116,7 @@ class Event_Sources {
* @param string $post_type The post type to register the meta for.
* @return void
*/
private static function register_post_meta_event_bridge_for_activitypub_event_source( $post_type ) {
private static function register_post_meta_event_bridge_for_activitypub_event_source( string $post_type ): void {
\register_post_meta(
$post_type,
'_event_bridge_for_activitypub_event_source',
@ -132,9 +132,9 @@ class Event_Sources {
* Get the Application actor via FEP-2677.
*
* @param string $domain The domain without scheme.
* @return bool|string The URL/ID of the application actor, false if not found.
* @return string|false The URL/ID of the application actor, false if not found.
*/
public static function get_application_actor( $domain ) {
public static function get_application_actor( string $domain ): string|false {
$result = wp_remote_get( 'https://' . $domain . '/.well-known/nodeinfo' );
if ( is_wp_error( $result ) ) {
@ -146,11 +146,11 @@ class Event_Sources {
$nodeinfo = json_decode( $body, true );
// Check if 'links' exists and is an array.
if ( isset( $nodeinfo['links'] ) && is_array( $nodeinfo['links'] ) ) {
if ( isset( $nodeinfo['links'] ) && \is_array( $nodeinfo['links'] ) ) {
foreach ( $nodeinfo['links'] as $link ) {
// Check if this link matches the application actor rel.
if ( isset( $link['rel'] ) && 'https://www.w3.org/ns/activitystreams#Application' === $link['rel'] ) {
if ( is_string( $link['href'] ) ) {
if ( \is_string( $link['href'] ) ) {
return $link['href'];
}
break;
@ -172,7 +172,7 @@ class Event_Sources {
* @param WP_Post $post The WordPress post object.
* @return bool False if the post is not disabled for federation via ActivityPub.
*/
public static function is_post_disabled_for_activitypub( $disabled, $post = null ): bool {
public static function is_post_disabled_for_activitypub( bool $disabled, WP_Post $post ): bool {
if ( $disabled ) {
return $disabled;
}
@ -185,7 +185,7 @@ class Event_Sources {
* @param WP_Post|int $post The WordPress post object or post ID.
* @return bool
*/
public static function is_cached_external_post( $post ): bool {
public static function is_cached_external_post( WP_Post|int $post ): bool {
$post_id = $post instanceof WP_Post ? $post->ID : $post;
if ( \get_post_meta( $post_id, '_event_bridge_for_activitypub_event_source', true ) ) {
@ -196,13 +196,13 @@ class Event_Sources {
}
/**
* Add the ActivityPub template for EventPrime.
* Maybe redirect cached external events to origin.
*
* @param string $template The path to the template object.
* @return string The new path to the JSON template.
*/
public static function redirect_activitypub_requests_for_cached_external_events( $template ) {
if ( defined( 'REST_REQUEST' ) && REST_REQUEST ) {
public static function redirect_activitypub_requests_for_cached_external_events( string $template ) {
if ( \defined( 'REST_REQUEST' ) && REST_REQUEST ) {
return $template;
}
@ -259,7 +259,7 @@ class Event_Sources {
*
* @return array The array of following urls.
*/
public static function add_event_sources_to_follow_collection( $follow_list, $user ): array {
public static function add_event_sources_to_follow_collection( array $follow_list, mixed $user ): array {
if ( ! $user instanceof Blog ) {
return $follow_list;
}
@ -274,7 +274,7 @@ class Event_Sources {
*
* @return array A list with all unique hosts of all Event Sources' ActivityPub IDs.
*/
public static function get_event_sources_hosts() {
public static function get_event_sources_hosts(): array {
$hosts = get_transient( 'event_bridge_for_activitypub_event_sources_hosts' );
if ( $hosts ) {
@ -304,7 +304,7 @@ class Event_Sources {
* @param array $hosts The hosts before the filter.
* @return array
*/
public static function add_event_sources_hosts_to_allowed_redirect_hosts( $hosts ) {
public static function add_event_sources_hosts_to_allowed_redirect_hosts( array $hosts ): array {
$event_sources_hosts = self::get_event_sources_hosts();
return array_merge( $hosts, $event_sources_hosts );
}
@ -313,18 +313,18 @@ class Event_Sources {
* Mark incoming accept activities as valid.
*
* @param bool $valid The validation state.
* @param string $param The object parameter.
* @param mixed $param The object parameter.
* @param WP_REST_Request $request The request object.
*
* @return bool|WP_Error The validation state: true if valid, false if not.
*/
public static function validate_activity( $valid, $param, $request ) {
public static function validate_activity( bool $valid, mixed $param, WP_REST_Request $request ) {
if ( $valid ) {
return $valid;
}
$json_params = $request->get_json_params();
if ( isset( $json_params['object']['type'] ) && in_array( $json_params['object']['type'], array( 'Accept', 'Undo' ), true ) ) {
if ( isset( $json_params['object']['type'] ) && \in_array( $json_params['object']['type'], array( 'Accept', 'Undo' ), true ) ) {
return true;
}
@ -335,12 +335,12 @@ class Event_Sources {
* Validate the event object.
*
* @param bool $valid The validation state.
* @param string $param The object parameter.
* @param mixed $param The object parameter.
* @param WP_REST_Request $request The request object.
*
* @return bool|WP_Error The validation state: true if valid, false if not.
*/
public static function validate_event_object( $valid, $param, $request ) {
public static function validate_event_object( bool $valid, mixed $param, WP_REST_Request $request ): bool|WP_Error {
$json_params = $request->get_json_params();
// Check if we should continue with the validation.
@ -379,7 +379,7 @@ class Event_Sources {
* @param string ...$urls List of URLs to compare.
* @return bool True if all URLs have the same host, false otherwise.
*/
public static function same_host( ...$urls ) {
public static function same_host( string ...$urls ): bool {
if ( empty( $urls ) ) {
return false; // No URLs given, can't compare hosts.
}
@ -412,8 +412,8 @@ class Event_Sources {
* @param mixed $event_object The (event) object as an associative array.
* @return bool True if the object is an valid ActivityPub Event, false if not.
*/
public static function is_valid_activitypub_event_object( $event_object ): bool {
if ( ! is_array( $event_object ) ) {
public static function is_valid_activitypub_event_object( mixed $event_object ): bool {
if ( ! \is_array( $event_object ) ) {
return false;
}
@ -446,7 +446,7 @@ class Event_Sources {
* @param string $id The ID to validate.
* @return bool
*/
public static function is_valid_activitypub_id( $id ) {
public static function is_valid_activitypub_id( string $id ): bool {
return \sanitize_url( $id ) ? true : false;
}
@ -469,7 +469,7 @@ class Event_Sources {
* @param string|DateTime $time The ActivityPub like time string or DateTime object.
* @return bool
*/
public static function is_time_passed( $time ) {
public static function is_time_passed( string|DateTime $time ): bool {
if ( ! $time instanceof DateTime ) {
// Create a DateTime object from the ActivityPub time string.
$time = new DateTime( $time, new DateTimeZone( 'UTC' ) );
@ -488,7 +488,7 @@ class Event_Sources {
* @param array $event_object The ActivityPub Event as an associative array.
* @return bool
*/
public static function is_ongoing_or_future_event( $event_object ) {
public static function is_ongoing_or_future_event( array $event_object ): bool {
if ( isset( $event_object['endTime'] ) ) {
$time = $event_object['endTime'];
} else {
@ -504,9 +504,9 @@ class Event_Sources {
* @param string $actor_id The actor ID.
* @return bool True if the ActivityPub actor ID is followed, false otherwise.
*/
public static function actor_is_event_source( $actor_id ) {
public static function actor_is_event_source( string $actor_id ): bool {
$event_sources = Event_Sources_Collection::get_event_sources();
if ( in_array( $actor_id, $event_sources, true ) ) {
if ( \in_array( $actor_id, $event_sources, true ) ) {
return true;
}
return false;

View File

@ -13,7 +13,7 @@
namespace Event_Bridge_For_ActivityPub;
// Exit if accessed directly.
defined( 'ABSPATH' ) || exit; // @codeCoverageIgnore
\defined( 'ABSPATH' ) || exit; // @codeCoverageIgnore
use Activitypub\Http;
use Event_Bridge_For_ActivityPub\ActivityPub\Model\Event_Source;
@ -31,7 +31,7 @@ class Outbox_Parser {
/**
* Maximum number of events to backfill per actor.
*/
const MAX_EVENTS_TO_IMPORT = 20;
public const MAX_EVENTS_TO_IMPORT = 20;
/**
* Init actions.
@ -48,7 +48,7 @@ class Outbox_Parser {
* @param int $event_source_post_id The Post ID of Event Source we want to backfill the events for.
* @return void
*/
public static function backfill_events( $event_source_post_id ): void {
public static function backfill_events( int $event_source_post_id ): void {
$event_source = Event_Source::get_by_id( $event_source_post_id );
if ( ! $event_source ) {
@ -72,12 +72,7 @@ class Outbox_Parser {
* @param int $event_source_post_id The Post ID of the Event Source that owns the outbox.
* @return void
*/
public static function import_events_from_outbox( $url, $event_source_post_id ) {
$setup = Setup::get_instance();
if ( ! $setup->is_activitypub_plugin_active() ) {
return;
}
public static function import_events_from_outbox( string $url, int $event_source_post_id ): void {
$outbox = self::fetch_outbox( $url );
if ( ! $outbox ) {
@ -91,7 +86,7 @@ class Outbox_Parser {
}
// Process orderedItems if they exist (non-paginated outbox).
if ( isset( $outbox['orderedItems'] ) && is_array( $outbox['orderedItems'] ) ) {
if ( isset( $outbox['orderedItems'] ) && \is_array( $outbox['orderedItems'] ) ) {
$current_count += self::import_events_from_items(
$outbox['orderedItems'],
$event_source_post_id,
@ -120,11 +115,11 @@ class Outbox_Parser {
* @param array $activity The Activity as associative array.
* @return bool
*/
private static function is_create_or_update_activity( $activity ) {
private static function is_create_or_update_activity( array $activity ): bool {
if ( ! isset( $activity['type'] ) ) {
return false;
}
if ( in_array( $activity['type'], array( 'Update', 'Create' ), true ) ) {
if ( \in_array( $activity['type'], array( 'Update', 'Create' ), true ) ) {
return true;
}
return false;
@ -137,12 +132,12 @@ class Outbox_Parser {
* @param int $max_items The maximum number of items to parse.
* @return array Parsed events from the collection.
*/
private static function parse_outbox_items_for_events( $items, $max_items ) {
private static function parse_outbox_items_for_events( array $items, int $max_items ): array {
$parsed_events = array();
foreach ( $items as $activity ) {
// Abort if we have exceeded the maximal events to return.
if ( $max_items > 0 && count( $parsed_events ) >= $max_items ) {
if ( $max_items > 0 && \count( $parsed_events ) >= $max_items ) {
break;
}
@ -179,7 +174,7 @@ class Outbox_Parser {
* @param int $limit The limit of how many events to save locally.
* @return int The number of saved events (at least attempted).
*/
private static function import_events_from_items( $items, $event_source_post_id, $limit = -1 ): int {
private static function import_events_from_items( array $items, int $event_source_post_id, int $limit = -1 ): int {
$events = self::parse_outbox_items_for_events( $items, $limit );
$transmogrifier = Setup::get_transmogrifier();
@ -209,7 +204,7 @@ class Outbox_Parser {
* @param int $delay The delay of the current time in seconds.
* @return bool
*/
private static function queue_importing_from_outbox( $url, $event_source_post_id, $delay = 10 ): bool {
private static function queue_importing_from_outbox( string $url, int $event_source_post_id, int $delay = 10 ): bool {
$hook = 'event_bridge_for_activitypub_import_events_from_outbox';
$args = array( $url, $event_source_post_id );
@ -237,7 +232,7 @@ class Outbox_Parser {
* @param int $count The new count of imported events.
* @return void
*/
private static function update_import_count( $event_source_post_id, $count ) {
private static function update_import_count( int $event_source_post_id, int $count ): void {
\update_post_meta( $event_source_post_id, '_event_bridge_for_activitypub_event_count', $count );
}
@ -247,7 +242,7 @@ class Outbox_Parser {
* @param string $url The URL of the outbox.
* @return array|null The decoded outbox data, or null if fetching fails.
*/
private static function fetch_outbox( $url ) {
private static function fetch_outbox( string $url ): ?array {
$response = Http::get( $url );
if ( \is_wp_error( $response ) ) {
@ -266,9 +261,9 @@ class Outbox_Parser {
* @param array $outbox The outbox data.
* @return string|null The pagination URL, or null if not found.
*/
private static function get_pagination_url( $outbox ) {
private static function get_pagination_url( array $outbox ): ?string {
// If we are on a collection page simply use the next key.
if ( 'OrderedCollectionPage' === $outbox['type'] && ! empty( $outbox['next'] ) && is_string( $outbox['next'] ) ) {
if ( 'OrderedCollectionPage' === $outbox['type'] && ! empty( $outbox['next'] ) && \is_string( $outbox['next'] ) ) {
return $outbox['next'];
}

View File

@ -10,7 +10,7 @@
namespace Event_Bridge_For_ActivityPub;
// Exit if accessed directly.
defined( 'ABSPATH' ) || exit; // @codeCoverageIgnore
\defined( 'ABSPATH' ) || exit; // @codeCoverageIgnore
/**
* Class for initializing the custom ActivityPub preview(s).
@ -31,7 +31,7 @@ class Preview {
public static function maybe_apply_event_preview_template() {
$event_post_types = Setup::get_instance()->get_active_event_plugins_post_types();
if ( in_array( \get_post_type(), $event_post_types, true ) ) {
if ( \in_array( \get_post_type(), $event_post_types, true ) ) {
return EVENT_BRIDGE_FOR_ACTIVITYPUB_PLUGIN_DIR . '/templates/event-preview.php';
}

View File

@ -12,7 +12,7 @@
namespace Event_Bridge_For_ActivityPub;
// Exit if accessed directly.
defined( 'ABSPATH' ) || exit; // @codeCoverageIgnore
\defined( 'ABSPATH' ) || exit; // @codeCoverageIgnore
use Activitypub\Transformer\Factory as Transformer_Factory;
use Event_Bridge_For_ActivityPub\Setup;
@ -21,7 +21,7 @@ use DateTime;
use WP_Post;
use function ActivityPub\add_to_outbox;
use function Activitypub\is_user_disabled;
use function Activitypub\user_can_activitypub;
/**
* Adds automatic announcing or sending of reminders before the events start time.
@ -71,7 +71,7 @@ class Reminder {
// Check for our supported post types.
$current_screen = \get_current_screen();
$event_post_types = Setup::get_instance()->get_active_event_plugins_post_types();
if ( ! $current_screen || ! in_array( $current_screen->post_type, $event_post_types, true ) ) {
if ( ! $current_screen || ! \in_array( $current_screen->post_type, $event_post_types, true ) ) {
return;
}
$asset_data = include EVENT_BRIDGE_FOR_ACTIVITYPUB_PLUGIN_DIR . 'build/reminder/plugin.asset.php';
@ -91,11 +91,11 @@ class Reminder {
/**
* Schedule Activities.
*
* @param string $new_status New post status.
* @param string $old_status Old post status.
* @param ?WP_Post $post Post object.
* @param string $new_status New post status.
* @param string $old_status Old post status.
* @param WP_Post|null $post Post object.
*/
public static function maybe_schedule_event_reminder( $new_status, $old_status, $post ): void {
public static function maybe_schedule_event_reminder( string $new_status, string $old_status, WP_Post|null $post ): void {
if ( ! $post instanceof WP_Post ) {
return;
}
@ -170,7 +170,7 @@ class Reminder {
*
* @param int $post_id The WordPress post ID of the event post.
*/
public static function send_event_reminder( $post_id ) {
public static function send_event_reminder( int $post_id ): void {
$post = \get_post( $post_id );
$transformer = Transformer_Factory::get_transformer( $post );
@ -182,7 +182,7 @@ class Reminder {
$actor = $transformer->get_actor_object();
$user_id = $actor->get__id();
if ( $user_id > 0 && is_user_disabled( $user_id ) ) {
if ( $user_id > 0 && ! user_can_activitypub( $user_id ) ) {
return;
}

View File

@ -14,7 +14,7 @@ namespace Event_Bridge_For_ActivityPub;
use Event_Bridge_For_ActivityPub\ActivityPub\Collection\Event_Sources;
// Exit if accessed directly.
defined( 'ABSPATH' ) || exit; // @codeCoverageIgnore
\defined( 'ABSPATH' ) || exit; // @codeCoverageIgnore
use Event_Bridge_For_ActivityPub\Integrations\Feature_Event_Sources;
@ -26,14 +26,14 @@ use Event_Bridge_For_ActivityPub\Integrations\Feature_Event_Sources;
* @since 1.0.0
*/
class Settings {
const SETTINGS_SLUG = 'event-bridge-for-activitypub';
public const SETTINGS_SLUG = 'event-bridge-for-activitypub';
/**
* The default ActivityPub event category.
*
* @var string
*/
const DEFAULT_EVENT_CATEGORY = 'MEETING';
private const DEFAULT_EVENT_CATEGORY = 'MEETING';
/**
* Register the settings for the Event Bridge for ActivityPub plugin.
@ -168,10 +168,11 @@ class Settings {
/**
* Do not allow the event sources feature to get deactivated, when event sources are still followed.
*
* @param mixed $value The optios value.
* @param mixed $value The options value.
* @return bool
*/
public static function sanitize_event_sources_feature_active( $value ) {
$count = count( Event_Sources::get_event_sources() );
public static function sanitize_event_sources_feature_active( mixed $value ): bool {
$count = \count( Event_Sources::get_event_sources() );
$value = (bool) $value;
@ -198,7 +199,7 @@ class Settings {
* @return string
*/
public static function sanitize_event_plugin_integration_used_for_event_sources( $event_plugin_integration ): string {
if ( ! is_string( $event_plugin_integration ) ) {
if ( ! \is_string( $event_plugin_integration ) ) {
return '';
}
$setup = Setup::get_instance();
@ -207,10 +208,10 @@ class Settings {
$valid_options = array();
foreach ( $active_event_plugins as $active_event_plugin ) {
if ( $active_event_plugin instanceof Feature_Event_Sources ) {
$valid_options[] = get_class( $active_event_plugin );
$valid_options[] = \get_class( $active_event_plugin );
}
}
if ( in_array( $event_plugin_integration, $valid_options, true ) ) {
if ( \in_array( $event_plugin_integration, $valid_options, true ) ) {
return $event_plugin_integration;
}
return Setup::get_default_integration_class_name_used_for_event_sources_feature();
@ -220,8 +221,9 @@ class Settings {
* Sanitize the target ActivityPub Event category.
*
* @param string $event_category The ActivityPUb event category.
* @return string
*/
public static function sanitize_mapped_event_category( $event_category ): string {
public static function sanitize_mapped_event_category( string $event_category ): string {
return self::is_allowed_event_category( $event_category ) ? $event_category : self::DEFAULT_EVENT_CATEGORY;
}
@ -231,10 +233,9 @@ class Settings {
* Currently only the default event categories are allowed to be target of a mapping.
*
* @param array $event_category_mappings The settings value.
*
* @return array An array that contains only valid mapping pairs.
*/
public static function sanitize_event_category_mappings( $event_category_mappings ): array {
public static function sanitize_event_category_mappings( array $event_category_mappings ): array {
if ( empty( $event_category_mappings ) ) {
return array();
}
@ -253,9 +254,9 @@ class Settings {
*
* @return bool True if allowed, false otherwise.
*/
private static function is_allowed_event_category( $event_category ): bool {
private static function is_allowed_event_category( string $event_category ): bool {
require_once EVENT_BRIDGE_FOR_ACTIVITYPUB_PLUGIN_DIR . '/includes/event-categories.php';
$allowed_event_categories = array_keys( EVENT_BRIDGE_FOR_ACTIVITYPUB_EVENT_CATEGORIES );
return in_array( $event_category, $allowed_event_categories, true );
return \in_array( $event_category, $allowed_event_categories, true );
}
}

View File

@ -13,10 +13,9 @@
namespace Event_Bridge_For_ActivityPub;
// Exit if accessed directly.
defined( 'ABSPATH' ) || exit; // @codeCoverageIgnore
\defined( 'ABSPATH' ) || exit; // @codeCoverageIgnore
use Event_Bridge_For_ActivityPub\ActivityPub\Collection\Event_Sources as Event_Sources_Collection;
use Event_Bridge_For_ActivityPub\ActivityPub\Handler\Join as Join_Handler;
use Event_Bridge_For_ActivityPub\ActivityPub\Scheduler\Event as Event_Scheduler;
use Event_Bridge_For_ActivityPub\Admin\Event_Plugin_Admin_Notices;
use Event_Bridge_For_ActivityPub\Admin\General_Admin_Notices;
@ -25,10 +24,14 @@ use Event_Bridge_For_ActivityPub\Admin\Settings_Page;
use Event_Bridge_For_ActivityPub\Integrations\Event_Plugin_Integration;
use Event_Bridge_For_ActivityPub\Integrations\Feature_Event_Sources;
use Event_Bridge_For_ActivityPub\Reminder;
use WP_Comment;
use WP_Post;
use WP_Post_Type;
use WP_User;
use WP_Term;
use function Activitypub\is_user_type_disabled;
// @phpstan-ignore-next-line
require_once ABSPATH . 'wp-admin/includes/plugin.php';
/**
@ -39,13 +42,6 @@ require_once ABSPATH . 'wp-admin/includes/plugin.php';
* @since 1.0.0
*/
class Setup {
/**
* Keep the information whether the ActivityPub plugin is active.
*
* @var boolean
*/
protected $activitypub_plugin_is_active = false;
/**
* Keep the current version of the current ActivityPub plugin.
*
@ -68,9 +64,8 @@ class Setup {
* @since 1.0.0
*/
protected function __construct() {
// Detect the presence/active-status and version of the ActivityPub plugin.
$this->activitypub_plugin_is_active = defined( 'ACTIVITYPUB_PLUGIN_VERSION' ) || \is_plugin_active( 'activitypub/activitypub.php' );
$this->activitypub_plugin_version = self::get_activitypub_plugin_version();
// Detect the version of the ActivityPub plugin.
$this->activitypub_plugin_version = self::get_activitypub_plugin_version();
// Register main action that load the Event Bridge For ActivityPub.
\add_action( 'plugins_loaded', array( $this, 'setup_hooks' ) );
@ -101,22 +96,13 @@ class Setup {
return self::$instance;
}
/**
* Getter function for whether the ActivityPub plugin is active.
*
* @return bool True when the ActivityPub plugin is active.
*/
public function is_activitypub_plugin_active(): bool {
return $this->activitypub_plugin_is_active;
}
/**
* Get the current version of the ActivityPub plugin.
*
* @return string The semantic Version.
*/
private static function get_activitypub_plugin_version(): string {
if ( defined( 'ACTIVITYPUB_PLUGIN_VERSION' ) ) {
if ( \defined( 'ACTIVITYPUB_PLUGIN_VERSION' ) ) {
return constant( 'ACTIVITYPUB_PLUGIN_VERSION' );
}
return '0.0.0';
@ -178,6 +164,7 @@ class Setup {
\Event_Bridge_For_ActivityPub\Integrations\Event_Organiser::class,
\Event_Bridge_For_ActivityPub\Integrations\EventPrime::class,
\Event_Bridge_For_ActivityPub\Integrations\EventOn::class,
\Event_Bridge_For_ActivityPub\Integrations\Spiffy_Calendar::class,
);
/**
@ -186,9 +173,6 @@ class Setup {
* @return void
*/
public function redetect_active_event_plugins(): void {
if ( ! $this->activitypub_plugin_is_active ) {
return;
}
\delete_transient( 'event_bridge_for_activitypub_active_event_plugins' );
$this->detect_active_event_plugins();
@ -200,11 +184,6 @@ class Setup {
* @return array List of supported event plugins as keys from the SUPPORTED_EVENT_PLUGINS const.
*/
public function detect_active_event_plugins(): array {
// Detection will fail in case the ActivityPub plugin is not active.
if ( ! $this->activitypub_plugin_is_active ) {
return array();
}
$active_event_plugins = \get_transient( 'event_bridge_for_activitypub_active_event_plugins' );
if ( $active_event_plugins ) {
@ -213,7 +192,6 @@ class Setup {
}
if ( ! function_exists( 'get_plugins' ) ) {
// @phpstan-ignore-next-line
require_once ABSPATH . 'wp-admin/includes/plugin.php';
}
@ -289,7 +267,7 @@ class Setup {
);
// 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 ) {
if ( empty( $this->active_event_plugins ) ) {
self::shut_down();
return;
}
@ -312,9 +290,6 @@ class Setup {
// Register the event reminders.
\add_action( 'init', array( Reminder::class, 'init' ) );
// Initialize the handling of "Join" activities.
Join_Handler::init();
// If the Event-Sources feature is enabled and all requirements are met, initialize it.
if ( ! is_user_type_disabled( 'blog' ) && \get_option( 'event_bridge_for_activitypub_event_sources_active' ) ) {
Event_Sources::init();
@ -340,7 +315,7 @@ class Setup {
* @return void
*/
private function register_plugin_specific_hooks(): void {
if ( array_key_exists( \Event_Bridge_For_ActivityPub\Integrations\EventPrime::get_relative_plugin_file(), $this->active_event_plugins ) ) {
if ( \array_key_exists( \Event_Bridge_For_ActivityPub\Integrations\EventPrime::get_relative_plugin_file(), $this->active_event_plugins ) ) {
\Event_Bridge_For_ActivityPub\Integrations\EventPrime::init();
}
}
@ -403,12 +378,6 @@ class Setup {
foreach ( $this->active_event_plugins as $event_plugin ) {
new Event_Plugin_Admin_Notices( $event_plugin );
}
// Check if any general admin notices are needed and add actions to insert the needed admin notices.
if ( ! $this->activitypub_plugin_is_active ) {
// The ActivityPub plugin is not active.
\add_action( 'admin_notices', array( General_Admin_Notices::class, 'activitypub_plugin_not_enabled' ), 10, 0 );
return;
}
if ( ! version_compare( $this->activitypub_plugin_version, EVENT_BRIDGE_FOR_ACTIVITYPUB_ACTIVITYPUB_PLUGIN_MIN_VERSION, '>=' ) ) {
// The ActivityPub plugin is too old.
\add_action( 'admin_notices', array( General_Admin_Notices::class, 'activitypub_plugin_version_too_old' ), 10, 0 );
@ -429,7 +398,7 @@ class Setup {
*
* @return \Activitypub\Transformer\Base|null|\WP_Error
*/
public function register_activitypub_transformer( $transformer, $data, $object_class ) {
public function register_activitypub_transformer( $transformer, mixed $data, string $object_class ) {
// If the current WordPress object is not a post (e.g., a WP_Comment), don't change the transformer.
if ( 'WP_Post' === $object_class ) {
// Get the transformer for a specific event plugins event or location post type.
@ -506,7 +475,7 @@ class Setup {
// If someone installs this plugin, we simply enable ActivityPub support for all currently active event post types.
$activitypub_supported_post_types = get_option( 'activitypub_support_post_types', array() );
foreach ( $this->active_event_plugins as $event_plugin ) {
if ( ! in_array( $event_plugin->get_post_type(), $activitypub_supported_post_types, true ) ) {
if ( ! \in_array( $event_plugin->get_post_type(), $activitypub_supported_post_types, true ) ) {
$activitypub_supported_post_types[] = $event_plugin->get_post_type();
add_post_type_support( $event_plugin->get_post_type(), 'activitypub' );
}
@ -525,17 +494,6 @@ class Setup {
*/
public function activate(): void {
$this->redetect_active_event_plugins();
// Don't allow plugin activation, when the ActivityPub plugin is not activated yet.
if ( ! $this->activitypub_plugin_is_active ) {
\deactivate_plugins( plugin_basename( EVENT_BRIDGE_FOR_ACTIVITYPUB_PLUGIN_FILE ) );
$notice = General_Admin_Notices::get_admin_notice_activitypub_plugin_not_enabled();
\wp_die(
// @phpstan-ignore-next-line
wp_kses( $notice, General_Admin_Notices::ALLOWED_HTML ),
'Plugin dependency check',
array( 'back_link' => true ),
);
}
if ( empty( $this->active_event_plugins ) ) {
\deactivate_plugins( plugin_basename( EVENT_BRIDGE_FOR_ACTIVITYPUB_PLUGIN_FILE ) );
@ -552,7 +510,7 @@ class Setup {
}
/**
* Maybe (depending on active event plugins) make it possible to querly event terms by `?term_id=<term_id>`.
* Maybe (depending on active event plugins) make it possible to query event terms by `?term_id=<term_id>`.
*
* @return void
*/
@ -579,7 +537,7 @@ class Setup {
*
* @return array The query variables.
*/
public static function add_term_query_var( $vars ) {
public static function add_term_query_var( array $vars ): array {
$vars[] = 'term_id';
return $vars;
@ -588,9 +546,9 @@ class Setup {
/**
* Filters the queried object.
*
* @param \WP_Term|\WP_Post_Type|\WP_Post|\WP_User|\WP_Comment|null $queried_object The queried object.
* @param WP_Term|WP_Post_Type|WP_Post|WP_User|WP_Comment|null $queried_object The queried object.
*/
public function maybe_detect_event_plugins_location_term( $queried_object ) {
public function maybe_detect_event_plugins_location_term( mixed $queried_object ) {
if ( $queried_object ) {
return $queried_object;
}
@ -601,7 +559,7 @@ class Setup {
$queried_object = \get_term( $term_id );
}
if ( $queried_object instanceof \WP_Term && $this->is_place_taxonomy_of_active_event_plugin( $queried_object->taxonomy ) ) {
if ( $queried_object instanceof WP_Term && $this->is_place_taxonomy_of_active_event_plugin( $queried_object->taxonomy ) ) {
return $queried_object;
}
@ -614,7 +572,7 @@ class Setup {
* @param string $taxonomy The taxonomy.
* @return boolean
*/
private function is_place_taxonomy_of_active_event_plugin( $taxonomy ): bool {
private function is_place_taxonomy_of_active_event_plugin( string $taxonomy ): bool {
foreach ( $this->active_event_plugins as $event_plugin ) {
if ( $event_plugin::get_place_taxonomy() === $taxonomy ) {
return true;
@ -684,7 +642,7 @@ class Setup {
$event_plugin_integrations = $setup->get_active_event_plugins();
foreach ( $event_plugin_integrations as $event_plugin_integration ) {
if ( $event_plugin_integration instanceof Feature_Event_Sources ) {
return get_class( $event_plugin_integration );
return \get_class( $event_plugin_integration );
}
}
return '';

View File

@ -10,7 +10,7 @@
namespace Event_Bridge_For_ActivityPub;
// Exit if accessed directly.
defined( 'ABSPATH' ) || exit; // @codeCoverageIgnore
\defined( 'ABSPATH' ) || exit; // @codeCoverageIgnore
define(
'EVENT_BRIDGE_FOR_ACTIVITYPUB_EVENT_CATEGORIES',

View File

@ -13,11 +13,13 @@
namespace Event_Bridge_For_ActivityPub\Integrations;
// Exit if accessed directly.
defined( 'ABSPATH' ) || exit; // @codeCoverageIgnore
\defined( 'ABSPATH' ) || exit; // @codeCoverageIgnore
use Activitypub\Query;
use Event_Bridge_For_ActivityPub\ActivityPub\Transformer\Event\Event_Organiser as Event_Organiser_Transformer;
use Event_Bridge_For_ActivityPub\ActivityPub\Transformer\Place\Event_Organiser as Event_Organiser_Place_Transformer;
use WP_Post;
use WP_Term;
/**
* Event Organiser.
@ -79,17 +81,21 @@ final class Event_Organiser extends Event_Plugin_Integration {
* @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 {
public static function get_activitypub_event_transformer( WP_Post $post ): Event_Organiser_Transformer {
return new Event_Organiser_Transformer( $post, self::get_event_category_taxonomy() );
}
/**
* Returns the ActivityPub transformer for a Event_Organiser event venue which is stored in a taxonomy.
*
* @param \WP_Term $term The WordPress Term/Taxonomy of the venue.
* @param WP_Post|WP_Term $term The WordPress Term/Taxonomy of the venue.
* @return Event_Organiser_Place_Transformer
*/
public static function get_activitypub_place_transformer( $term ): Event_Organiser_Place_Transformer {
public static function get_activitypub_place_transformer( WP_Post|WP_Term $term ): ?Event_Organiser_Place_Transformer {
if ( $term instanceof WP_Post ) {
return null;
}
if ( Query::get_instance()->is_activitypub_request() && defined( 'EVENT_ORGANISER_DIR' ) ) {
$class_path = constant( EVENT_ORGANISER_DIR ) . 'includes/class-eo-theme-compatability.php';

View File

@ -12,12 +12,13 @@
namespace Event_Bridge_For_ActivityPub\Integrations;
// Exit if accessed directly.
defined( 'ABSPATH' ) || exit; // @codeCoverageIgnore
\defined( 'ABSPATH' ) || exit; // @codeCoverageIgnore
use Event_Bridge_For_ActivityPub\ActivityPub\Transformer\Event\Event as ActivityPub_Event_Transformer;
use Event_Bridge_For_ActivityPub\ActivityPub\Transformer\Place\Base_Post_Place;
use Event_Bridge_For_ActivityPub\ActivityPub\Transformer\Place\Base_Term_Place;
use WP_Post;
use WP_Term;
require_once EVENT_BRIDGE_FOR_ACTIVITYPUB_PLUGIN_DIR . 'includes/integrations/interface-feature-event-sources.php';
@ -56,7 +57,7 @@ abstract class Event_Plugin_Integration {
* @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;
abstract public static function get_activitypub_event_transformer( WP_Post $post ): ActivityPub_Event_Transformer;
/**
* In case an event plugin uses a custom post type for the locations/venues return it here.
@ -79,10 +80,10 @@ abstract class Event_Plugin_Integration {
/**
* Returns the Activitypub transformer for places of the event plugins location post type.
*
* @param WP_Post $post The WordPress post object of the Event.
* @param WP_Post|WP_Term $data The WordPress post object of the Event.
* @return Base_Post_Place|Base_Term_Place|null
*/
public static function get_activitypub_place_transformer( $post ) { // phpcs:ignore Generic.CodeAnalysis.UnusedFunctionParameter
public static function get_activitypub_place_transformer( WP_Post|WP_Term $data ) { // phpcs:ignore Generic.CodeAnalysis.UnusedFunctionParameter
return null;
}
@ -108,7 +109,7 @@ abstract class Event_Plugin_Integration {
* Get the plugins name from the main plugin-file's top-level-file-comment.
*/
public static function get_plugin_name(): string {
$all_plugins = array_merge( get_plugins(), get_mu_plugins() );
$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'];
} else {
@ -121,11 +122,11 @@ abstract class Event_Plugin_Integration {
*/
public static function is_plugin_page(): bool {
// Get the current page.
$screen = get_current_screen();
$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 && static::get_post_type() === $screen->post_type;
$is_event_plugins_settings_page = in_array( $screen->id, static::get_settings_pages(), true );
$is_event_plugins_settings_page = \in_array( $screen->id, static::get_settings_pages(), true );
return $is_event_plugins_edit_page || $is_event_plugins_settings_page;
}

View File

@ -13,9 +13,10 @@
namespace Event_Bridge_For_ActivityPub\Integrations;
// Exit if accessed directly.
defined( 'ABSPATH' ) || exit; // @codeCoverageIgnore
\defined( 'ABSPATH' ) || exit; // @codeCoverageIgnore
use Event_Bridge_For_ActivityPub\ActivityPub\Transformer\Event\Eventin as Eventin_Transformer;
use WP_Post;
/**
* Eventin.
@ -65,10 +66,10 @@ final class Eventin extends Event_Plugin_Integration {
/**
* Returns the ActivityPub transformer for a Eventin event post.
*
* @param \WP_Post $post The WordPress post object of the Event.
* @param WP_Post $post The WordPress post object of the Event.
* @return Eventin_Transformer
*/
public static function get_activitypub_event_transformer( $post ): Eventin_Transformer {
public static function get_activitypub_event_transformer( WP_Post $post ): Eventin_Transformer {
return new Eventin_Transformer( $post, self::get_event_category_taxonomy() );
}
}

View File

@ -13,10 +13,12 @@
namespace Event_Bridge_For_ActivityPub\Integrations;
// Exit if accessed directly.
defined( 'ABSPATH' ) || exit; // @codeCoverageIgnore
\defined( 'ABSPATH' ) || exit; // @codeCoverageIgnore
use Event_Bridge_For_ActivityPub\ActivityPub\Transformer\Event\EventOn as EventOn_Event_Transformer;
use Event_Bridge_For_ActivityPub\ActivityPub\Transformer\Place\EventOn as EventOn_Location_Transformer;
use WP_Post;
use WP_Term;
/**
* EventON Events Calendar
@ -66,10 +68,10 @@ final class EventOn extends Event_Plugin_Integration {
/**
* Returns the ActivityPub transformer for a VS_Event_List event post.
*
* @param \WP_Post $post The WordPress post object of the Event.
* @param WP_Post $post The WordPress post object of the Event.
* @return EventOn_Event_Transformer
*/
public static function get_activitypub_event_transformer( $post ): EventOn_Event_Transformer {
public static function get_activitypub_event_transformer( WP_Post $post ): EventOn_Event_Transformer {
return new EventOn_Event_Transformer( $post, self::get_event_category_taxonomy() );
}
@ -85,10 +87,10 @@ final class EventOn extends Event_Plugin_Integration {
/**
* Returns the ActivityPub transformer for a Event_Organiser event venue which is stored in a taxonomy.
*
* @param \WP_Term $term The WordPress Term/Taxonomy of the venue.
* @param WP_Post|WP_Term $term The WordPress Term/Taxonomy of the venue.
* @return EventOn_Location_Transformer
*/
public static function get_activitypub_place_transformer( $term ): EventOn_Location_Transformer {
return new EventOn_Location_Transformer( $term );
public static function get_activitypub_place_transformer( WP_Post|WP_Term $term ): ?EventOn_Location_Transformer {
return $term instanceof WP_Term ? new EventOn_Location_Transformer( $term ) : null;
}
}

View File

@ -12,11 +12,12 @@ namespace Event_Bridge_For_ActivityPub\Integrations;
use Event_Bridge_For_ActivityPub\ActivityPub\Transformer\Event\EventPrime as EventPrime_Event_Transformer;
use Event_Bridge_For_ActivityPub\ActivityPub\Transformer\Place\EventPrime as EventPrime_Place_Transformer;
use Eventprime_Basic_Functions;
use WP_Post;
use function Activitypub\is_activitypub_request;
// Exit if accessed directly.
defined( 'ABSPATH' ) || exit; // @codeCoverageIgnore
\defined( 'ABSPATH' ) || exit; // @codeCoverageIgnore
/**
* This class defines which information is necessary for the EventPrime event plugin.
@ -72,10 +73,10 @@ final class EventPrime extends Event_Plugin_Integration {
/**
* Returns the ActivityPub transformer.
*
* @param \WP_Post $post The WordPress post object of the Event.
* @param WP_Post $post The WordPress post object of the Event.
* @return EventPrime_Event_Transformer
*/
public static function get_activitypub_event_transformer( $post ): EventPrime_Event_Transformer {
public static function get_activitypub_event_transformer( WP_Post $post ): EventPrime_Event_Transformer {
return new EventPrime_Event_Transformer( $post, self::get_event_category_taxonomy() );
}
@ -150,7 +151,7 @@ final class EventPrime extends Event_Plugin_Integration {
* @return bool|int The post ID, or term ID if found, false otherwise.
*/
private static function get_object_id( $type = 'event' ) {
if ( ! in_array( $type, array( 'venue', 'event' ), true ) ) {
if ( ! \in_array( $type, array( 'venue', 'event' ), true ) ) {
return false;
}

View File

@ -13,11 +13,12 @@
namespace Event_Bridge_For_ActivityPub\Integrations;
// Exit if accessed directly.
defined( 'ABSPATH' ) || exit; // @codeCoverageIgnore
\defined( 'ABSPATH' ) || exit; // @codeCoverageIgnore
use Event_Bridge_For_ActivityPub\ActivityPub\Transformer\Event\Events_Manager as Events_Manager_Event_Transformer;
use Event_Bridge_For_ActivityPub\ActivityPub\Transformer\Place\Events_Manager as Events_Manager_Place_Transformer;
use WP_Post;
use WP_Term;
/**
* Events Manager.
@ -43,7 +44,7 @@ final class Events_Manager extends Event_Plugin_Integration {
* @return string
*/
public static function get_post_type(): string {
return defined( 'EM_POST_TYPE_EVENT' ) ? constant( 'EM_POST_TYPE_EVENT' ) : 'event';
return \defined( 'EM_POST_TYPE_EVENT' ) ? constant( 'EM_POST_TYPE_EVENT' ) : 'event';
}
/**
@ -52,17 +53,17 @@ final class Events_Manager extends Event_Plugin_Integration {
* @return string
*/
public static function get_place_post_type(): string {
return defined( 'EM_POST_TYPE_LOCATION' ) ? constant( 'EM_POST_TYPE_LOCATION' ) : 'location';
return \defined( 'EM_POST_TYPE_LOCATION' ) ? constant( 'EM_POST_TYPE_LOCATION' ) : 'location';
}
/**
* Returns the Activitypub transformer for places of the event plugins location post type.
*
* @param \WP_Post $post The WordPress post object of the Event.
* @param WP_Post|WP_Term $post The WordPress post object of the Event.
* @return Events_Manager_Place_Transformer
*/
public static function get_activitypub_place_transformer( $post ): Events_Manager_Place_Transformer {
return new Events_Manager_Place_Transformer( $post );
public static function get_activitypub_place_transformer( WP_Post|WP_Term $post ): ?Events_Manager_Place_Transformer {
return $post instanceof WP_Post ? new Events_Manager_Place_Transformer( $post ) : null;
}
/**
@ -80,16 +81,16 @@ final class Events_Manager extends Event_Plugin_Integration {
* @return string
*/
public static function get_event_category_taxonomy(): string {
return defined( 'EM_TAXONOMY_CATEGORY' ) ? constant( 'EM_TAXONOMY_CATEGORY' ) : 'event-categories';
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.
* @param WP_Post $post The WordPress post object of the Event.
* @return Events_Manager_Event_Transformer
*/
public static function get_activitypub_event_transformer( $post ): Events_Manager_Event_Transformer {
public static function get_activitypub_event_transformer( WP_Post $post ): Events_Manager_Event_Transformer {
return new Events_Manager_Event_Transformer( $post, self::get_event_category_taxonomy() );
}
}

View File

@ -13,10 +13,11 @@
namespace Event_Bridge_For_ActivityPub\Integrations;
// Exit if accessed directly.
defined( 'ABSPATH' ) || exit; // @codeCoverageIgnore
\defined( 'ABSPATH' ) || exit; // @codeCoverageIgnore
use Event_Bridge_For_ActivityPub\ActivityPub\Transformer\Event\GatherPress as GatherPress_Transformer;
use Event_Bridge_For_ActivityPub\ActivityPub\Transmogrifier\GatherPress as GatherPress_Transmogrifier;
use WP_Post;
/**
* GatherPress.
@ -66,10 +67,10 @@ final class GatherPress extends Event_Plugin_Integration implements Feature_Even
/**
* Returns the ActivityPub transformer for a GatherPress event post.
*
* @param \WP_Post $post The WordPress post object of the Event.
* @param WP_Post $post The WordPress post object of the Event.
* @return GatherPress_Transformer
*/
public static function get_activitypub_event_transformer( $post ): GatherPress_Transformer {
public static function get_activitypub_event_transformer( WP_Post $post ): GatherPress_Transformer {
return new GatherPress_Transformer( $post, self::get_event_category_taxonomy() );
}
@ -80,6 +81,15 @@ final class GatherPress extends Event_Plugin_Integration implements Feature_Even
return GatherPress_Transmogrifier::class;
}
/**
* GatherPress uses a taxonomy to store venues.
*
* @return string
*/
public static function get_place_taxonomy() {
return '_gatherpress_venue';
}
/**
* Get a list of Post IDs of events that have ended.
*
@ -87,11 +97,12 @@ final class GatherPress extends Event_Plugin_Integration implements Feature_Even
*
* @return array
*/
public static function get_cached_remote_events( $ends_before_time ): array {
public static function get_cached_remote_events( int $ends_before_time ): array {
global $wpdb;
$ends_before_time_string = gmdate( 'Y-m-d H:i:s', $ends_before_time );
// phpcs:disable WordPress.DB.DirectDatabaseQuery
$results = $wpdb->get_results(
$wpdb->prepare(
"SELECT DISTINCT {$wpdb->prefix}posts.ID

View File

@ -13,9 +13,10 @@
namespace Event_Bridge_For_ActivityPub\Integrations;
// Exit if accessed directly.
defined( 'ABSPATH' ) || exit; // @codeCoverageIgnore
\defined( 'ABSPATH' ) || exit; // @codeCoverageIgnore
use Event_Bridge_For_ActivityPub\ActivityPub\Transformer\Event\Modern_Events_Calendar_Lite as Modern_Events_Calendar_Lite_Transformer;
use WP_Post;
/**
* Modern Events Calendar (Lite)
@ -66,10 +67,10 @@ final class Modern_Events_Calendar_Lite extends Event_Plugin_Integration {
/**
* Returns the ActivityPub transformer for a Modern_Events_Calendar_Lite event post.
*
* @param \WP_Post $post The WordPress post object of the Event.
* @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 {
public static function get_activitypub_event_transformer( WP_Post $post ): Modern_Events_Calendar_Lite_Transformer {
return new Modern_Events_Calendar_Lite_Transformer( $post, self::get_event_category_taxonomy() );
}
}

View File

@ -0,0 +1,81 @@
<?php
/**
* Integration with the Plugin "Spiffy Calendar".
*
* Defines all the necessary meta information and methods for the integration of the
* WordPress plugin "Spiffy Calendar".
*
* @link https://wordpress.org/plugins/eventin/
* @package Event_Bridge_For_ActivityPub
* @since 1.0.0
*/
namespace Event_Bridge_For_ActivityPub\Integrations;
// Exit if accessed directly.
\defined( 'ABSPATH' ) || exit; // @codeCoverageIgnore
use Event_Bridge_For_ActivityPub\ActivityPub\Transformer\Event\Spiffy_Calendar as Spiffy_Calendar_Transformer;
use WP_Post;
/**
* Eventin.
*
* Defines all the necessary meta information and methods for the integration of the
* WordPress plugin "Eventin".
*
* @since 1.0.0
*/
final class Spiffy_Calendar extends Event_Plugin_Integration {
/**
* Returns the full plugin file.
*
* @return string
*/
public static function get_relative_plugin_file(): string {
return 'spiffy-calendar/spiffy-calendar.php';
}
/**
* Returns the event post type of the plugin.
*
* @return string
*/
public static function get_post_type(): string {
return 'spiffy_event';
}
/**
* Returns the IDs of the admin pages of the plugin.
*
* @return array The settings page url.
*/
public static function get_settings_pages(): array {
return array(
'spiffy_event_page_settings',
'spiffy_event_page_theme',
'spiffy_event_page_frontend_submit',
'spiffy_event_page_custom_fields',
'edit-spiffy_event',
);
}
/**
* Returns the taxonomy used for the plugin's event categories.
*
* @return string
*/
public static function get_event_category_taxonomy(): string {
return 'spiffy_categories';
}
/**
* Returns the ActivityPub transformer for a Eventin event post.
*
* @param WP_Post $post The WordPress post object of the Event.
* @return Spiffy_Calendar_Transformer
*/
public static function get_activitypub_event_transformer( WP_Post $post ): Spiffy_Calendar_Transformer {
return new Spiffy_Calendar_Transformer( $post, self::get_event_category_taxonomy() );
}
}

View File

@ -12,12 +12,15 @@
namespace Event_Bridge_For_ActivityPub\Integrations;
use WP_Term;
// Exit if accessed directly.
defined( 'ABSPATH' ) || exit; // @codeCoverageIgnore
\defined( 'ABSPATH' ) || exit; // @codeCoverageIgnore
use Event_Bridge_For_ActivityPub\ActivityPub\Transformer\Event\The_Events_Calendar as The_Events_Calendar_Event_Transformer;
use Event_Bridge_For_ActivityPub\ActivityPub\Transformer\Place\The_Events_Calendar as The_Events_Calendar_Place_Transformer;
use Event_Bridge_For_ActivityPub\ActivityPub\Transmogrifier\The_Events_Calendar as The_Events_Calendar_Transmogrifier;
use WP_Post;
/**
* The Events Calendar.
@ -61,7 +64,7 @@ final class The_Events_Calendar extends Event_Plugin_Integration implements Feat
* @param \WP_Post $post The WordPress post object of the Event.
* @return The_Events_Calendar_Event_Transformer
*/
public static function get_activitypub_event_transformer( $post ): The_Events_Calendar_Event_Transformer {
public static function get_activitypub_event_transformer( WP_Post $post ): The_Events_Calendar_Event_Transformer {
return new The_Events_Calendar_Event_Transformer( $post, self::get_event_category_taxonomy() );
}
@ -86,10 +89,10 @@ final class The_Events_Calendar extends Event_Plugin_Integration implements Feat
/**
* Returns the ActivityPub transformer for a The_Events_Calendar venue post.
*
* @param \WP_Post $post The WordPress post object of the venue.
* @param mixed $post The WordPress post object of the venue.
* @return The_Events_Calendar_Place_Transformer
*/
public static function get_activitypub_place_transformer( $post ): The_Events_Calendar_Place_Transformer {
public static function get_activitypub_place_transformer( mixed $post ): The_Events_Calendar_Place_Transformer {
return new The_Events_Calendar_Place_Transformer( $post );
}

View File

@ -13,10 +13,11 @@
namespace Event_Bridge_For_ActivityPub\Integrations;
// Exit if accessed directly.
defined( 'ABSPATH' ) || exit; // @codeCoverageIgnore
\defined( 'ABSPATH' ) || exit; // @codeCoverageIgnore
use Event_Bridge_For_ActivityPub\ActivityPub\Transformer\Event\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_Post;
use WP_Query;
/**
@ -67,10 +68,10 @@ final class VS_Event_List extends Event_Plugin_Integration implements Feature_Ev
/**
* Returns the ActivityPub transformer for a VS_Event_List event post.
*
* @param \WP_Post $post The WordPress post object of the Event.
* @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 {
public static function get_activitypub_event_transformer( WP_Post $post ): VS_Event_List_Transformer {
return new VS_Event_List_Transformer( $post, self::get_event_category_taxonomy() );
}
@ -88,11 +89,12 @@ final class VS_Event_List extends Event_Plugin_Integration implements Feature_Ev
*
* @return array<int>
*/
public static function get_cached_remote_events( $ends_before_time ): array {
public static function get_cached_remote_events( int $ends_before_time ): array {
$args = array(
'post_type' => 'event',
'posts_per_page' => -1,
'fields' => 'ids',
// phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_query
'meta_query' => array(
'relation' => 'AND',
array(

View File

@ -13,9 +13,10 @@
namespace Event_Bridge_For_ActivityPub\Integrations;
// Exit if accessed directly.
defined( 'ABSPATH' ) || exit; // @codeCoverageIgnore
\defined( 'ABSPATH' ) || exit; // @codeCoverageIgnore
use Event_Bridge_For_ActivityPub\ActivityPub\Transformer\Event\WP_Event_Manager as WP_Event_Manager_Transformer;
use WP_Post;
/**
* Interface for a supported event plugin.
@ -64,10 +65,10 @@ final class WP_Event_Manager extends Event_Plugin_Integration {
/**
* Returns the ActivityPub transformer for a WP_Event_Manager event post.
*
* @param \WP_Post $post The WordPress post object of the Event.
* @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 {
public static function get_activitypub_event_transformer( WP_Post $post ): WP_Event_Manager_Transformer {
return new WP_Event_Manager_Transformer( $post, self::get_event_category_taxonomy() );
}
}

View File

@ -14,7 +14,7 @@
namespace Event_Bridge_For_ActivityPub\Integrations;
// Exit if accessed directly.
defined( 'ABSPATH' ) || exit; // @codeCoverageIgnore
\defined( 'ABSPATH' ) || exit; // @codeCoverageIgnore
/**
* Interface for an event plugin integration that supports the Event Sources feature.
@ -38,5 +38,5 @@ interface Feature_Event_Sources {
*
* @return int[] List of post IDs for events that match the criteria.
*/
public static function get_cached_remote_events( $ends_before_time ): array;
public static function get_cached_remote_events( int $ends_before_time ): array;
}

View File

@ -13,14 +13,13 @@
namespace Event_Bridge_For_ActivityPub\Table;
// Exit if accessed directly.
defined( 'ABSPATH' ) || exit; // @codeCoverageIgnore
\defined( 'ABSPATH' ) || exit; // @codeCoverageIgnore
use WP_List_Table;
use Event_Bridge_For_ActivityPub\ActivityPub\Collection\Event_Sources as Event_Sources_Collection;
use Event_Bridge_For_ActivityPub\ActivityPub\Model\Event_Source;
use WP_List_Table;
if ( ! \class_exists( '\WP_List_Table' ) ) {
// @phpstan-ignore-next-line
require_once ABSPATH . 'wp-admin/includes/class-wp-list-table.php';
}
@ -118,7 +117,7 @@ class Event_Sources extends WP_List_Table {
foreach ( $event_sources['actors'] as $event_source_post_id => $event_source_activitypub_id ) {
$event_source = Event_Source::get_by_id( (int) $event_source_post_id );
if ( ! $event_source || ! in_array( $event_source->get_status(), array( 'publish', 'pending' ), true ) ) {
if ( ! $event_source || ! \in_array( $event_source->get_status(), array( 'publish', 'pending' ), true ) ) {
continue;
}
@ -155,7 +154,7 @@ class Event_Sources extends WP_List_Table {
* @return string
*/
public function column_default( $item, $column_name ) {
if ( ! array_key_exists( $column_name, $item ) ) {
if ( ! \array_key_exists( $column_name, $item ) ) {
return __( 'None', 'event-bridge-for-activitypub' );
}
return $item[ $column_name ];
@ -167,8 +166,8 @@ class Event_Sources extends WP_List_Table {
* @param array $item Item.
* @return string
*/
public function column_icon( $item ): string {
return sprintf(
public function column_icon( array $item ): string {
return \sprintf(
'<img src="%s" width="25px;" />',
$item['icon']
);
@ -181,7 +180,7 @@ class Event_Sources extends WP_List_Table {
* @return string
*/
public function column_url( $item ): string {
return sprintf(
return \sprintf(
'<a href="%s" target="_blank">%s</a>',
esc_url( $item['url'] ),
$item['url']
@ -195,7 +194,7 @@ class Event_Sources extends WP_List_Table {
* @return string
*/
public function column_cb( $item ): string {
return sprintf( '<input type="checkbox" name="event_sources[]" value="%s" />', esc_attr( $item['identifier'] ) );
return \sprintf( '<input type="checkbox" name="event_sources[]" value="%s" />', esc_attr( $item['identifier'] ) );
}
/**
@ -204,7 +203,7 @@ class Event_Sources extends WP_List_Table {
* @param array $item Item.
* @return string
*/
public function column_accepted( $item ): string {
public function column_accepted( array $item ): string {
if ( $item['accepted'] ) {
return esc_html__( 'Accepted', 'event-bridge-for-activitypub' );
} else {

View File

@ -2,9 +2,9 @@
Contributors: andremenrath, pfefferle
Tags: events, fediverse, activitypub, calendar
Requires at least: 6.5
Tested up to: 6.8
Stable tag: 1.1.0
Requires PHP: 7.4
Tested up to: 7.0
Stable tag: 1.3.0
Requires PHP: 8.1
License: AGPL-3.0-or-later
License URI: https://www.gnu.org/licenses/agpl-3.0.html
Integrating popular event plugins with the ActivityPub plugin.
@ -53,13 +53,14 @@ Even platforms that don't yet fully support events, like [Mastodon](https://join
**Improved Event Discoverability:** Your custom event categories are mapped to a set of default categories used in the Fediverse, helping your events reach a wider audience. This improves the chances that users searching for similar events on other platforms will find yours.
**Event Reminders for Your Followers:** Often, events are planned well in advance. To keep your followers informed right in time, you can set up reminders that are supposed to trigger the events showing up in their timelines right before the event starts. At the moment this reminder is implemented as a self-boost of your original event post. While this feature may behave differently across various platforms, we are working on a more robust solution that will let you schedule dedicated reminder notes that appear in all followers' timelines.
**Event Reminders for Your Followers:** Often, events are planned well in advance. To keep your followers informed right in time, you can set up reminders that are supposed to trigger the events showing up in their timelines right before the event starts.
**External Event Sources:** This functionality is only available for a subset of the supported event plugins. It enables your WordPress site to act as a hub for displaying events from other ActivityPub profiles, aggregating them into a cohesive calendar view.
== Installation ==
This plugin depends on the [ActivityPub plugin](https://wordpress.org/plugins/activitypub/). Additionally, you need to use one of the supported event Plugins.
1. This plugin depends on the [ActivityPub plugin](https://wordpress.org/plugins/activitypub/).
2. Additionally, you need to use one of the supported event Plugins.
= Supported Event Plugins =
@ -77,6 +78,7 @@ Basic support (outgoing events):
* [Modern Events Calendar Lite](https://webnus.net/modern-events-calendar/)
* [Event Organiser](https://wordpress.org/plugins/event-organiser/)
* [EventPrime Events Calendar, Bookings and Tickets](https://wordpress.org/plugins/eventprime-event-calendar-management/)
* [Spiffy Calendar](https://wordpress.org/plugins/spiffy-calendar/)
= Configuration =
@ -92,61 +94,63 @@ Yes, this plugin works as an add-on and requires both the ActivityPub plugin and
Your events can be followed on platforms that support ActivityPub like [Mobilizon](https://joinmobilizon.org/), [Gancio](https://gancio.org), [Friendica](https://friendi.ca), [Hubzilla](https://hubzilla.org), and [Pleroma](https://pleroma.social/). Even other applications like [Mastodon](https://joinmastodon.org), which don't fully support events yet, will display all important information about the events.
= Why does Mastodon not show any updates? ==
Mastodon does not yet handle updates of `Event` objects. See the related tracking issue [#31114](https://github.com/mastodon/mastodon/issues/31114).
= How much extra work is required to maintain my events across the decentralized Web? =
None! Once the plugin is set up, your events are automatically sent to all connected platforms or account that follow you (your Website). Any updates you make to your events are synced without additional effort.
= Can I still use social media to promote my events? =
Yes, you can still use traditional social media if you wish. However, this plugin helps reduce reliance on commercial platforms by connecting your events to the decentralized Fediverse.
= Will this plugin work if I don't use the ActivityPub plugin? =
No, the Event Federation Plugin depends on the [ActivityPub plugin](https://wordpress.org/plugins/activitypub/) to deliver your events across decentralized platforms, so it's essential to have it installed and configured.
= My event plugin is not supported, what can I do? =
If you know about coding have a look at the documentation of how to add your plugin or open an [issue](https://codeberg.org/Event-Federation/wordpress-event-bridge-for-activitypub/issues), if we can spare some free hours we might add it.
= What if I experience problems? =
We're always interested in your feedback. Feel free to reach out to us via [E-Mail](https://event-federation.eu/contact/) or create an [issue](https://codeberg.org/Event-Federation/wordpress-event-bridge-for-activitypub/issues).
== Acknowledgement ==
The development of this WordPress plugin was funded through the [NGI0 Entrust](https://NLnet.nl/entrust) Fund, a fund established by [NLnet](https://nlnet.nl) with financial support from the European Commission's [Next Generation Internet](https://ngi.eu) programme, under the aegis of [Communications Networks, Content and Technology](https://commission.europa.eu/about-european-commission/departments-and-executive-agencies/communications-networks-content-and-technology_en) under grant agreement number 101069594.
== Changelog ==
= [1.1.0] - 2025-04-12 =
= 1.3.0 - 2025-05-22 =
* Added: Basic support for Starter Kits
* Fixed: Uncatched error in following process (issue #145)
* Fixed: Compatibility with ActivityPub plugin version 5.7.0
* Added: Basic support for Spiffy Calendar Plugin
* Added: Support for WordPress 7
* Changed: Requires ActivityPub plugin 8.+
* Fixed: The events calendar integration: remote cached organizers not being public.
= [1.0.0] - 2025-02-11 =
= 1.2.4 - 2025-05-17 =
* Added: Support for the EventPrime event plugin
* Added: Event self-announce feature at configurable time before event starts
* Added: Blueprint (Preview via WordPress Playground)
* Added: Event Sources feature: cache and list events from remote ActivityPub profiles on your site
* Added: Custom ActivityPub preview
* Added: Admin setting to enfore sending summary of events as plain text
* Changed: Now depends on ActivityPub plugin version greater than 5.1.0
* Fixed: The Events Calendar date times when using the Gutenberg editor
* Fixed: Improved admin UI for event-category mapping
* Added: Flag plugin compatible with WordPress 6.9
= [0.3.5] - 2025-01-03 =
= 1.2.3 - 2025-05-17 =
* Fixed: Images of Acknowledgements in Admin UI
* Added: ActivityPub `Place` transformer for GatherPress
* Fixed: Improve timezone detection for Eventin
* Fixed: GatherPress place name not uses venue name
* Changed: Time formatting for EventPrime
* Changed: Time formatting for VS Event List
* Changed: Test up to ActivityPub plugin 8.2.1
= [0.3.4] - 2024-12-21 =
= 1.2.2 - 2025-11-21 =
* Initial release on https://wordpress.org/
* Fixed: timezone issues for Modern Events Calendar Lite
* Fixed: Validate event-tag info Events Manager to prevent fatal error
= 1.2.1 - 2025-11-13 =
* Fixed: Incoming remote events getting federated
* Add: Hotfix Mobilizon group behavior to detect correct event source
= 1.2.0 - 2025-11-12 =
* Added: New filter hook for incoming events
* Added: Store longitude and latitude information for incoming The Events Calendar events
* Changed: Update to new method how actor json is stored
* Changed: Plugin now requires PHP 8.1+
* Changed: Plugin now requires the WordPress ActivityPub plugin 7.5+
* Fixed: Time offset for outgoing GatherPress events
* Fixed: Time offset for importing events with The Events Calendar
= Full Changelog =
[See the CHANGELOG.md](https://codeberg.org/Event-Federation/wordpress-event-bridge-for-activitypub/src/branch/main/CHANGELOG.md)
== Contributing ==

View File

@ -8,7 +8,7 @@
*/
// Exit if accessed directly.
defined( 'ABSPATH' ) || exit; // @codeCoverageIgnore
\defined( 'ABSPATH' ) || exit; // @codeCoverageIgnore
/* @var array $args Template arguments. */
$args = wp_parse_args(

View File

@ -8,7 +8,7 @@
*/
// Exit if accessed directly.
defined( 'ABSPATH' ) || exit; // @codeCoverageIgnore
\defined( 'ABSPATH' ) || exit; // @codeCoverageIgnore
use Event_Bridge_For_ActivityPub\Setup;
@ -24,8 +24,6 @@ if ( ! current_user_can( 'manage_options' ) ) {
return;
}
$activitypub_plugin_is_active = Setup::get_instance()->is_activitypub_plugin_active();
\get_option( 'event_bridge_for_activitypub_event_sources_active', false );
if ( ! isset( $args ) || ! array_key_exists( 'supports_event_sources', $args ) ) {
@ -38,199 +36,198 @@ $event_sources_active = \get_option( 'event_bridge_for_activitypub_event_sourc
$cache_retention_period = \get_option( 'event_bridge_for_activitypub_event_source_cache_retention', DAY_IN_SECONDS );
?>
<?php if ( $activitypub_plugin_is_active ) { ?>
<div class="activitypub-settings hide-if-no-js">
<form method="post" action="options.php">
<?php \settings_fields( 'event-bridge-for-activitypub_event-sources' ); ?>
<div class="box">
<h2><?php \esc_html_e( 'Event Sources', 'event-bridge-for-activitypub' ); ?></h2>
<p id="event-sources-description"><?php esc_html_e( 'This feature allows your WordPress site to retrieve and display events from external sources via ActivityPub. Once enabled, you can add any ActivityPub account as a source of events by following that profile. These events will be cached on your site and seamlessly integrated into your existing event calendar, creating a unified view of events from both internal and external sources.', 'event-bridge-for-activitypub' ); ?></p>
<div class="activitypub-settings hide-if-no-js">
<form method="post" action="options.php">
<?php \settings_fields( 'event-bridge-for-activitypub_event-sources' ); ?>
<div class="box">
<h2><?php \esc_html_e( 'Event Sources', 'event-bridge-for-activitypub' ); ?></h2>
<p id="event-sources-description"><?php esc_html_e( 'This feature allows your WordPress site to retrieve and display events from external sources via ActivityPub. Once enabled, you can add any ActivityPub account as a source of events by following that profile. These events will be cached on your site and seamlessly integrated into your existing event calendar, creating a unified view of events from both internal and external sources.', 'event-bridge-for-activitypub' ); ?></p>
<?php
if ( ! \Activitypub\is_user_type_disabled( 'blog' ) && count( $event_plugins_supporting_event_sources ) ) {
?>
<table class="form-table">
<tbody>
<tr>
<th scope="row">
<label for="event_bridge_for_activitypub_event_sources_active"><?php \esc_html_e( 'Enable External Event Sources', 'event-bridge-for-activitypub' ); ?></label>
</th>
<td>
<input
type="checkbox"
name="event_bridge_for_activitypub_event_sources_active"
id="event_bridge_for_activitypub_event_sources_active"
aria-describedby="event-sources-description"
value="1"
<?php echo \checked( $event_sources_active ); ?>
>
</td>
</tr>
<?php
if ( ! \Activitypub\is_user_type_disabled( 'blog' ) && count( $event_plugins_supporting_event_sources ) ) {
if ( $event_sources_active ) {
?>
<table class="form-table">
<tbody>
<tr>
<th scope="row">
<label for="event_bridge_for_activitypub_event_sources_active"><?php \esc_html_e( 'Enable External Event Sources', 'event-bridge-for-activitypub' ); ?></label>
</th>
<td>
<input
type="checkbox"
name="event_bridge_for_activitypub_event_sources_active"
id="event_bridge_for_activitypub_event_sources_active"
aria-describedby="event-sources-description"
value="1"
<?php echo \checked( $event_sources_active ); ?>
>
</td>
</tr>
<tr>
<th scope="row">
<label for="event_bridge_for_activitypub_integration_used_for_event_sources_feature"><?php \esc_html_e( 'Event Plugin', 'event-bridge-for-activitypub' ); ?></label>
</th>
<td>
<select
name="event_bridge_for_activitypub_integration_used_for_event_sources_feature"
id="event_bridge_for_activitypub_integration_used_for_event_sources_feature"
value="gatherpress"
aria-describedby="event-sources-used-plugin-description"
>
<?php
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, Setup::get_event_plugin_integration_used_for_event_sources_feature(), true ) . '>' . esc_attr( $event_plugin_name ) . '</option>';
}
?>
</select>
<p id="event-sources-used-plugin-description"><?php esc_html_e( 'In case you have multiple event plugins installed you might choose which event plugin is utilized.', 'event-bridge-for-activitypub' ); ?></p>
</td>
<tr>
<tr>
<th scope="row">
<label for="event_bridge_for_activitypub_event_source_cache"><?php \esc_html_e( 'Retention Period for External Events', 'event-bridge-for-activitypub' ); ?></label>
</th>
<td>
<select
name="event_bridge_for_activitypub_event_source_cache_retention"
id="event_bridge_for_activitypub_event_source_cache_retention"
aria-describedby="event_bridge_for_activitypub_event-sources-cache-clear-time-frame"
>
<?php
$choices = array(
0 => __( 'Immediately', 'event-bridge-for-activitypub' ),
DAY_IN_SECONDS => __( 'One Day', 'event-bridge-for-activitypub' ),
WEEK_IN_SECONDS => __( 'One Week', 'event-bridge-for-activitypub' ),
MONTH_IN_SECONDS => __( 'One Month', 'event-bridge-for-activitypub' ),
YEAR_IN_SECONDS => __( 'One Year', 'event-bridge-for-activitypub' ),
);
foreach ( $choices as $time => $string ) {
echo '<option value="' . \esc_attr( $time ) . '" ' . \selected( $cache_retention_period, $time, false ) . '>' . \esc_html( $string ) . '</option>';
}
?>
</select>
<p id="event_bridge_for_activitypub_event-sources-cache-clear-time-frame"><?php esc_html_e( 'External events from your event sources will be automatically removed from your site after the selected time period has passed since the event ended. Choose a time frame that works best for your needs.', 'event-bridge-for-activitypub' ); ?></p>
</td>
<tr>
<?php
if ( $event_sources_active ) {
?>
<tr>
<th scope="row">
<label for="event_bridge_for_activitypub_integration_used_for_event_sources_feature"><?php \esc_html_e( 'Event Plugin', 'event-bridge-for-activitypub' ); ?></label>
</th>
<td>
<select
name="event_bridge_for_activitypub_integration_used_for_event_sources_feature"
id="event_bridge_for_activitypub_integration_used_for_event_sources_feature"
value="gatherpress"
aria-describedby="event-sources-used-plugin-description"
>
<?php
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, Setup::get_event_plugin_integration_used_for_event_sources_feature(), true ) . '>' . esc_attr( $event_plugin_name ) . '</option>';
}
?>
</select>
<p id="event-sources-used-plugin-description"><?php esc_html_e( 'In case you have multiple event plugins installed you might choose which event plugin is utilized.', 'event-bridge-for-activitypub' ); ?></p>
</td>
<tr>
<tr>
<th scope="row">
<label for="event_bridge_for_activitypub_event_source_cache"><?php \esc_html_e( 'Retention Period for External Events', 'event-bridge-for-activitypub' ); ?></label>
</th>
<td>
<select
name="event_bridge_for_activitypub_event_source_cache_retention"
id="event_bridge_for_activitypub_event_source_cache_retention"
value="0"
aria-describedby="event_bridge_for_activitypub_event-sources-cache-clear-time-frame"
>
<?php
$choices = array(
0 => __( 'Immediately', 'event-bridge-for-activitypub' ),
DAY_IN_SECONDS => __( 'One Day', 'event-bridge-for-activitypub' ),
WEEK_IN_SECONDS => __( 'One Week', 'event-bridge-for-activitypub' ),
MONTH_IN_SECONDS => __( 'One Month', 'event-bridge-for-activitypub' ),
YEAR_IN_SECONDS => __( 'One Year', 'event-bridge-for-activitypub' ),
);
foreach ( $choices as $time => $string ) {
echo '<option value="' . \esc_attr( $time ) . '" ' . \selected( $cache_retention_period, $time, true ) . '>' . \esc_attr( $string ) . '</option>';
}
?>
</select>
<p id="event_bridge_for_activitypub_event-sources-cache-clear-time-frame"><?php esc_html_e( 'External events from your event sources will be automatically removed from your site after the selected time period has passed since the event ended. Choose a time frame that works best for your needs.', 'event-bridge-for-activitypub' ); ?></p>
</td>
<tr>
<?php
}
?>
<tbody>
</table>
<?php
} elseif ( ! \Activitypub\is_user_type_disabled( 'blog' ) ) {
?>
<div class="notice-warning"><p><?php esc_html_e( 'You do not have an Event Plugin installed that supports this feature.', 'event-bridge-for-activitypub' ); ?></p></div>
<p><?php \esc_html_e( 'The following Event Plugins are supported:', 'event-bridge-for-activitypub' ); ?></p>
<?php
$plugins_supporting_event_sources = Setup::detect_event_plugins_supporting_event_sources();
echo '<ul class="event_bridge_for_activitypub-list">';
foreach ( $plugins_supporting_event_sources as $event_plugin ) {
echo '<li>' . esc_attr( $event_plugin->get_plugin_name() ) . '</li>';
}
echo '</ul>';
} else {
$activitypub_plugin_data = \get_plugin_data( ACTIVITYPUB_PLUGIN_FILE );
$notice = sprintf(
/* translators: 1: The name of the ActivityPub plugin. */
_x(
'In order to use this feature your have to enable the Blog-Actor in the the <a href="%1$s">%2$s settings</a>.',
'admin notice',
'event-bridge-for-activitypub'
),
\admin_url( 'options-general.php?page=activitypub&tab=event-bridge-for-activitypub&subpage=settings' ),
\esc_html( $activitypub_plugin_data['Name'] )
);
$allowed_html = array(
'a' => array(
'href' => true,
'title' => true,
),
);
echo '<div class="notice-warning"><p>' . \wp_kses( $notice, $allowed_html ) . '</p></div>';
}
?>
</div>
<?php \submit_button(); ?>
</form>
</div>
<div class="wrap event_bridge_for_activitypub-admin-table-container">
<br>
<?php
if ( \get_option( 'event_bridge_for_activitypub_event_sources_active', false ) ) {
<tbody>
</table>
<?php
} elseif ( ! \Activitypub\is_user_type_disabled( 'blog' ) ) {
?>
<div class="notice-warning"><p><?php esc_html_e( 'You do not have an Event Plugin installed that supports this feature.', 'event-bridge-for-activitypub' ); ?></p></div>
<p><?php \esc_html_e( 'The following Event Plugins are supported:', 'event-bridge-for-activitypub' ); ?></p>
<?php
$plugins_supporting_event_sources = Setup::detect_event_plugins_supporting_event_sources();
echo '<ul class="event_bridge_for_activitypub-list">';
foreach ( $plugins_supporting_event_sources as $event_plugin ) {
echo '<li>' . esc_attr( $event_plugin->get_plugin_name() ) . '</li>';
}
echo '</ul>';
} else {
$activitypub_plugin_data = \get_plugin_data( ACTIVITYPUB_PLUGIN_FILE );
$notice = sprintf(
/* translators: 1: The name of the ActivityPub plugin. */
_x(
'In order to use this feature your have to enable the Blog-Actor in the the <a href="%1$s">%2$s settings</a>.',
'admin notice',
'event-bridge-for-activitypub'
),
\admin_url( 'options-general.php?page=activitypub&tab=event-bridge-for-activitypub&subpage=settings' ),
\esc_html( $activitypub_plugin_data['Name'] )
);
$allowed_html = array(
'a' => array(
'href' => true,
'title' => true,
),
);
echo '<div class="notice-warning"><p>' . \wp_kses( $notice, $allowed_html ) . '</p></div>';
}
?>
<!-- ThickBox content (hidden initially) -->
<div id="Event_Bridge_For_ActivityPub_add_new_source" style="display:none;">
<h2><?php \esc_html_e( 'Add a Trusted Event Source', 'event-bridge-for-activitypub' ); ?></h2>
<ul class="event-bridge-for-activitypub-syntax-list" id="event_bridge_for_activitypub_add_event_source_description">
<?php \esc_html_e( 'Use one of the following syntax:', 'event-bridge-for-activitypub' ); ?>
<li>
<?php \esc_html_e( 'Enter a Fediverse user handle', 'event-bridge-for-activitypub' ); ?> ( <?php esc_html_e( 'e.g.', 'event-bridge-for-activitypub' ); ?> <code>@username@example.social</code>)
</li>
<li>
<?php \esc_html_e( 'ActivityPub account URL or ID', 'event-bridge-for-activitypub' ); ?> ( <?php esc_html_e( 'e.g.', 'event-bridge-for-activitypub' ); ?> <code>https://example.social/user/username</code>)
</li>
<li>
<?php \esc_html_e( 'The domain or URL of a Gancio instance', 'event-bridge-for-activitypub' ); ?> ( <?php esc_html_e( 'e.g.', 'event-bridge-for-activitypub' ); ?> <code>https://demo.gancio.org</code>)
</li>
</ul>
<div class="notice notice-info inline">
<p>
<?php
$number_of_imports = \Event_Bridge_For_ActivityPub\Outbox_Parser::MAX_EVENTS_TO_IMPORT;
$notice = sprintf(
/* translators: 1: The maximum number of imported events. */
__( 'To ensure a smooth start, up to %d upcoming events from this source will be automatically imported soon after adding it.', 'event-bridge-for-activitypub' ),
$number_of_imports
);
echo esc_html( $notice );
?>
</p>
</div>
<form method="post" action="options.php">
<?php \settings_fields( 'event-bridge-for-activitypub_add-event-source' ); ?>
<label for="event_bridge_for_activitypub_add_event_source">
<p>
<?php \esc_html_e( 'Event Source (handle, URL, or instance)', 'event-bridge-for-activitypub' ); ?>:
</p>
</label>
<input
type="text"
style="width: 100%"
name="event_bridge_for_activitypub_add_event_source"
id="event_bridge_for_activitypub_add_event_source"
aria-describedby="event_bridge_for_activitypub_add_event_source_description"
placeholder="@username@example.social or https://example.social/user/username">
<?php \submit_button( __( 'Follow Event Source', 'event-bridge-for-activitypub' ) ); ?>
</form>
</div>
<div class="wrap activitypub-followers-page">
<!-- Table title with add new button like on post edit pages -->
<div class="event_bridge_for_activitypub-admin-table-top">
<h2 class="wp-heading-inline"> <?php esc_html_e( 'Manage Event Sources', 'event-bridge-for-activitypub' ); ?> </h2>
<!-- Button that triggers ThickBox -->
<a href="#TB_inline?width=600&height=400&inlineId=Event_Bridge_For_ActivityPub_add_new_source" class="thickbox page-title-action">
<?php \esc_html_e( 'Add Event Source', 'event-bridge-for-activitypub' ); ?>
</a>
</div>
<form method="get">
<input type="hidden" name="page" value="activitypub" />
<input type="hidden" name="tab" value="event-bridge-for-activitypub" />
<input type="hidden" name="subpage" value="event-sources" />
</div>
<?php \submit_button(); ?>
</form>
</div>
<div class="wrap event_bridge_for_activitypub-admin-table-container">
<br>
<?php
if ( \get_option( 'event_bridge_for_activitypub_event_sources_active', false ) ) {
?>
<!-- ThickBox content (hidden initially) -->
<div id="Event_Bridge_For_ActivityPub_add_new_source" style="display:none;">
<h2><?php \esc_html_e( 'Add a Trusted Event Source', 'event-bridge-for-activitypub' ); ?></h2>
<p><?php \esc_html_e( 'Use one of the following syntax:', 'event-bridge-for-activitypub' ); ?></p>
<ul class="event-bridge-for-activitypub-syntax-list" id="event_bridge_for_activitypub_add_event_source_description">
<li>
<?php \esc_html_e( 'Enter a Fediverse user handle', 'event-bridge-for-activitypub' ); ?> ( <?php esc_html_e( 'e.g.', 'event-bridge-for-activitypub' ); ?> <code>@username@example.social</code>)
</li>
<li>
<?php \esc_html_e( 'ActivityPub account URL or ID', 'event-bridge-for-activitypub' ); ?> ( <?php esc_html_e( 'e.g.', 'event-bridge-for-activitypub' ); ?> <code>https://example.social/user/username</code>)
</li>
<li>
<?php \esc_html_e( 'The domain or URL of a Gancio instance', 'event-bridge-for-activitypub' ); ?> ( <?php esc_html_e( 'e.g.', 'event-bridge-for-activitypub' ); ?> <code>https://demo.gancio.org</code>)
</li>
</ul>
<div class="notice notice-info inline">
<p>
<?php
$table = new \Event_Bridge_For_ActivityPub\Table\Event_Sources();
$table->prepare_items();
$table->search_box( 'Search', 'search' );
$table->display();
$number_of_imports = \Event_Bridge_For_ActivityPub\Outbox_Parser::MAX_EVENTS_TO_IMPORT;
$notice = sprintf(
/* translators: 1: The maximum number of imported events. */
__( 'To ensure a smooth start, up to %d upcoming events from this source will be automatically imported soon after adding it.', 'event-bridge-for-activitypub' ),
$number_of_imports
);
echo esc_html( $notice );
?>
</form>
</p>
</div>
<?php } ?>
</div>
<?php } ?>
<form method="post" action="options.php">
<?php \settings_fields( 'event-bridge-for-activitypub_add-event-source' ); ?>
<p>
<label id="event_bridge_for_activitypub_add_event_source_label" for="event_bridge_for_activitypub_add_event_source">
<?php \esc_html_e( 'Event Source (handle, URL, or instance)', 'event-bridge-for-activitypub' ); ?>:
</label>
</p>
<input
type="text"
style="width: 100%"
name="event_bridge_for_activitypub_add_event_source"
id="event_bridge_for_activitypub_add_event_source"
aria-describedby="event_bridge_for_activitypub_add_event_source_description"
aria-labelledby="event_bridge_for_activitypub_add_event_source_label"
placeholder="@username@example.social or https://example.social/user/username">
<?php \submit_button( __( 'Follow Event Source', 'event-bridge-for-activitypub' ) ); ?>
</form>
</div>
<div class="wrap activitypub-followers-page">
<!-- Table title with add new button like on post edit pages -->
<div class="event_bridge_for_activitypub-admin-table-top">
<h2 class="wp-heading-inline"> <?php esc_html_e( 'Manage Event Sources', 'event-bridge-for-activitypub' ); ?> </h2>
<!-- Button that triggers ThickBox -->
<a href="#TB_inline?width=600&height=400&inlineId=Event_Bridge_For_ActivityPub_add_new_source" class="thickbox page-title-action">
<?php \esc_html_e( 'Add Event Source', 'event-bridge-for-activitypub' ); ?>
</a>
</div>
<form method="get">
<input type="hidden" name="page" value="activitypub" />
<input type="hidden" name="tab" value="event-bridge-for-activitypub" />
<input type="hidden" name="subpage" value="event-sources" />
<?php
$table = new \Event_Bridge_For_ActivityPub\Table\Event_Sources();
$table->prepare_items();
$table->search_box( 'Search', 'search' );
$table->display();
?>
</form>
</div>
<?php } ?>
</div>

View File

@ -12,7 +12,7 @@
*/
// Exit if accessed directly.
defined( 'ABSPATH' ) || exit; // @codeCoverageIgnore
\defined( 'ABSPATH' ) || exit; // @codeCoverageIgnore
use Activitypub\Activity\Extended_Object\Event;
use Event_Bridge_For_ActivityPub\Setup;
@ -25,8 +25,6 @@ use Event_Bridge_For_ActivityPub\Setup;
)
);
$activitypub_plugin_is_active = Setup::get_instance()->is_activitypub_plugin_active();
if ( ! isset( $args ) || ! array_key_exists( 'event_terms', $args ) ) {
return;
}

View File

@ -8,7 +8,7 @@
*/
// Exit if accessed directly.
defined( 'ABSPATH' ) || exit; // @codeCoverageIgnore
\defined( 'ABSPATH' ) || exit; // @codeCoverageIgnore
use Event_Bridge_For_ActivityPub\Setup;
use Event_Bridge_For_ActivityPub\Admin\General_Admin_Notices;
@ -24,7 +24,6 @@ use Event_Bridge_For_ActivityPub\Admin\Health_Check;
);
$active_event_plugins = Setup::get_instance()->get_active_event_plugins();
$activitypub_plugin_is_active = Setup::get_instance()->is_activitypub_plugin_active();
$event_bridge_for_activitypub_status_ok = true;
$example_event_post = Health_Check::get_most_recent_event_posts();
@ -46,10 +45,7 @@ WP_Filesystem();
<h2><?php \esc_html_e( 'Status', 'event-bridge-for-activitypub' ); ?></h2>
<p><?php \esc_html_e( 'The Event Bridge for ActivityPub detected the following (activated) event plugins:', 'event-bridge-for-activitypub' ); ?></p>
<?php
if ( ! $activitypub_plugin_is_active ) {
$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 ) ) {
if ( empty( $active_event_plugins ) ) {
$notice = General_Admin_Notices::get_admin_notice_no_supported_event_plugin_active();
echo '<p>⚠' . \wp_kses( $notice, General_Admin_Notices::ALLOWED_HTML ) . '</p>';
}
@ -239,7 +235,7 @@ WP_Filesystem();
<div class="box">
<h2><?php \esc_html_e( 'Acknowledgement', 'event-bridge-for-activitypub' ); ?></h2>
<p><a href="https://NLnet.nl"><img src="<?php echo esc_url( plugins_url( '/assets/img/acknowledgement-NLnet.svg', EVENT_BRIDGE_FOR_ACTIVITYPUB_PLUGIN_FILE ) ); ?>" alt="Logo NLnet: abstract logo of four people seen from above" class="logo-center"></a> <a href="https://NLnet.nl/NGI0"><img src="<?php echo esc_url( plugins_url( '/assets/img/acknowledgement-NGI0Entrust.svg', EVENT_BRIDGE_FOR_ACTIVITYPUB_PLUGIN_FILE ) ); ?>" alt="Logo NGI Zero: letterlogo shaped like a tag" class="logo-center"> </a></p>
<p><a href="https://NLnet.nl"><img src="<?php echo esc_url( plugins_url( '/assets/img/acknowledgement-NLnet.svg', EVENT_BRIDGE_FOR_ACTIVITYPUB_PLUGIN_FILE ) ); ?>" alt="" class="logo-center"></a> <a href="https://NLnet.nl/NGI0"><img src="<?php echo esc_url( plugins_url( '/assets/img/acknowledgement-NGI0Entrust.svg', EVENT_BRIDGE_FOR_ACTIVITYPUB_PLUGIN_FILE ) ); ?>" alt="" class="logo-center"> </a></p>
<p>The development of this plugin was funded through the <a href="https://NLnet.nl/entrust">NGI0 Entrust</a> Fund, a fund established by <a href="https://nlnet.nl">NLnet</a> with financial support from the European Commission's <a href="https://ngi.eu">Next Generation Internet</a> programme, under the aegis of <a href="https://commission.europa.eu/about-european-commission/departments-and-executive-agencies/communications-networks-content-and-technology_en">DG Communications Networks, Content and Technology</a> under grant agreement N<sup>o</sup> 101069594.</p>
</div>
<?php endif; ?>

View File

@ -8,6 +8,6 @@
*/
// Exit if accessed directly.
defined( 'ABSPATH' ) || exit; // @codeCoverageIgnore
\defined( 'ABSPATH' ) || exit; // @codeCoverageIgnore
\Event_Bridge_For_ActivityPub\Admin\Settings_Page::do_settings_page();