diff --git a/wp-content/plugins/event-bridge-for-activitypub/.forgejo/workflows/assets.yml b/wp-content/plugins/event-bridge-for-activitypub/.forgejo/workflows/assets.yml deleted file mode 100644 index f32e729c..00000000 --- a/wp-content/plugins/event-bridge-for-activitypub/.forgejo/workflows/assets.yml +++ /dev/null @@ -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 diff --git a/wp-content/plugins/event-bridge-for-activitypub/.forgejo/workflows/deploy.yml b/wp-content/plugins/event-bridge-for-activitypub/.forgejo/workflows/deploy.yml deleted file mode 100644 index 45fae27a..00000000 --- a/wp-content/plugins/event-bridge-for-activitypub/.forgejo/workflows/deploy.yml +++ /dev/null @@ -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 diff --git a/wp-content/plugins/event-bridge-for-activitypub/.forgejo/workflows/phpcs.yml b/wp-content/plugins/event-bridge-for-activitypub/.forgejo/workflows/phpcs.yml deleted file mode 100644 index 21622e2e..00000000 --- a/wp-content/plugins/event-bridge-for-activitypub/.forgejo/workflows/phpcs.yml +++ /dev/null @@ -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 diff --git a/wp-content/plugins/event-bridge-for-activitypub/.forgejo/workflows/phpstan.yml b/wp-content/plugins/event-bridge-for-activitypub/.forgejo/workflows/phpstan.yml deleted file mode 100644 index ce87dacc..00000000 --- a/wp-content/plugins/event-bridge-for-activitypub/.forgejo/workflows/phpstan.yml +++ /dev/null @@ -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 diff --git a/wp-content/plugins/event-bridge-for-activitypub/.forgejo/workflows/phpunit.yml b/wp-content/plugins/event-bridge-for-activitypub/.forgejo/workflows/phpunit.yml deleted file mode 100644 index 9c07a0db..00000000 --- a/wp-content/plugins/event-bridge-for-activitypub/.forgejo/workflows/phpunit.yml +++ /dev/null @@ -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 }} diff --git a/wp-content/plugins/event-bridge-for-activitypub/.forgejo/workflows/release.yml b/wp-content/plugins/event-bridge-for-activitypub/.forgejo/workflows/release.yml deleted file mode 100644 index 78d84211..00000000 --- a/wp-content/plugins/event-bridge-for-activitypub/.forgejo/workflows/release.yml +++ /dev/null @@ -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 }} diff --git a/wp-content/plugins/event-bridge-for-activitypub/.gitattributes b/wp-content/plugins/event-bridge-for-activitypub/.gitattributes deleted file mode 100644 index f5aa0a25..00000000 --- a/wp-content/plugins/event-bridge-for-activitypub/.gitattributes +++ /dev/null @@ -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 diff --git a/wp-content/plugins/event-bridge-for-activitypub/.wp-env.test.json b/wp-content/plugins/event-bridge-for-activitypub/.wp-env.test.json new file mode 100644 index 00000000..08f56d64 --- /dev/null +++ b/wp-content/plugins/event-bridge-for-activitypub/.wp-env.test.json @@ -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" + } +} diff --git a/wp-content/plugins/event-bridge-for-activitypub/CHANGELOG.md b/wp-content/plugins/event-bridge-for-activitypub/CHANGELOG.md index d87b1e66..c97f4858 100644 --- a/wp-content/plugins/event-bridge-for-activitypub/CHANGELOG.md +++ b/wp-content/plugins/event-bridge-for-activitypub/CHANGELOG.md @@ -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 diff --git a/wp-content/plugins/event-bridge-for-activitypub/event-bridge-for-activitypub.php b/wp-content/plugins/event-bridge-for-activitypub/event-bridge-for-activitypub.php index 179cbd7e..678909a5 100644 --- a/wp-content/plugins/event-bridge-for-activitypub/event-bridge-for-activitypub.php +++ b/wp-content/plugins/event-bridge-for-activitypub/event-bridge-for-activitypub.php @@ -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 NGI0 Entrust Fund, a fund established by NLnet. * 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' ); diff --git a/wp-content/plugins/event-bridge-for-activitypub/includes/activitypub/class-handler.php b/wp-content/plugins/event-bridge-for-activitypub/includes/activitypub/class-handler.php index 3081c01d..a165686c 100644 --- a/wp-content/plugins/event-bridge-for-activitypub/includes/activitypub/class-handler.php +++ b/wp-content/plugins/event-bridge-for-activitypub/includes/activitypub/class-handler.php @@ -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; diff --git a/wp-content/plugins/event-bridge-for-activitypub/includes/activitypub/collection/class-event-sources.php b/wp-content/plugins/event-bridge-for-activitypub/includes/activitypub/collection/class-event-sources.php index 9edd3634..c71d6ebd 100644 --- a/wp-content/plugins/event-bridge-for-activitypub/includes/activitypub/collection/class-event-sources.php +++ b/wp-content/plugins/event-bridge-for-activitypub/includes/activitypub/collection/class-event-sources.php @@ -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( diff --git a/wp-content/plugins/event-bridge-for-activitypub/includes/activitypub/handler/class-accept.php b/wp-content/plugins/event-bridge-for-activitypub/includes/activitypub/handler/class-accept.php index ab7b63f1..4b03d1f8 100644 --- a/wp-content/plugins/event-bridge-for-activitypub/includes/activitypub/handler/class-accept.php +++ b/wp-content/plugins/event-bridge-for-activitypub/includes/activitypub/handler/class-accept.php @@ -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; diff --git a/wp-content/plugins/event-bridge-for-activitypub/includes/activitypub/handler/class-create.php b/wp-content/plugins/event-bridge-for-activitypub/includes/activitypub/handler/class-create.php index 74ff5438..a343796b 100644 --- a/wp-content/plugins/event-bridge-for-activitypub/includes/activitypub/handler/class-create.php +++ b/wp-content/plugins/event-bridge-for-activitypub/includes/activitypub/handler/class-create.php @@ -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; + } } diff --git a/wp-content/plugins/event-bridge-for-activitypub/includes/activitypub/handler/class-delete.php b/wp-content/plugins/event-bridge-for-activitypub/includes/activitypub/handler/class-delete.php index 1fcf526a..5d08af7e 100644 --- a/wp-content/plugins/event-bridge-for-activitypub/includes/activitypub/handler/class-delete.php +++ b/wp-content/plugins/event-bridge-for-activitypub/includes/activitypub/handler/class-delete.php @@ -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; diff --git a/wp-content/plugins/event-bridge-for-activitypub/includes/activitypub/handler/class-join.php b/wp-content/plugins/event-bridge-for-activitypub/includes/activitypub/handler/class-join.php deleted file mode 100644 index d4023d38..00000000 --- a/wp-content/plugins/event-bridge-for-activitypub/includes/activitypub/handler/class-join.php +++ /dev/null @@ -1,159 +0,0 @@ -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 ); - } -} diff --git a/wp-content/plugins/event-bridge-for-activitypub/includes/activitypub/handler/class-undo.php b/wp-content/plugins/event-bridge-for-activitypub/includes/activitypub/handler/class-undo.php index c2ad5aaa..5859492a 100644 --- a/wp-content/plugins/event-bridge-for-activitypub/includes/activitypub/handler/class-undo.php +++ b/wp-content/plugins/event-bridge-for-activitypub/includes/activitypub/handler/class-undo.php @@ -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", diff --git a/wp-content/plugins/event-bridge-for-activitypub/includes/activitypub/handler/class-update.php b/wp-content/plugins/event-bridge-for-activitypub/includes/activitypub/handler/class-update.php index 1281a95c..03a572ed 100644 --- a/wp-content/plugins/event-bridge-for-activitypub/includes/activitypub/handler/class-update.php +++ b/wp-content/plugins/event-bridge-for-activitypub/includes/activitypub/handler/class-update.php @@ -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 ); } } diff --git a/wp-content/plugins/event-bridge-for-activitypub/includes/activitypub/model/class-event-source.php b/wp-content/plugins/event-bridge-for-activitypub/includes/activitypub/model/class-event-source.php index eb380c42..719d0aa7 100644 --- a/wp-content/plugins/event-bridge-for-activitypub/includes/activitypub/model/class-event-source.php +++ b/wp-content/plugins/event-bridge-for-activitypub/includes/activitypub/model/class-event-source.php @@ -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; } diff --git a/wp-content/plugins/event-bridge-for-activitypub/includes/activitypub/object/class-event-fep-8a8e.php b/wp-content/plugins/event-bridge-for-activitypub/includes/activitypub/object/class-event-fep-8a8e.php new file mode 100644 index 00000000..43fc8db6 --- /dev/null +++ b/wp-content/plugins/event-bridge-for-activitypub/includes/activitypub/object/class-event-fep-8a8e.php @@ -0,0 +1,101 @@ += 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; +} diff --git a/wp-content/plugins/event-bridge-for-activitypub/includes/activitypub/scheduler/class-event.php b/wp-content/plugins/event-bridge-for-activitypub/includes/activitypub/scheduler/class-event.php index 668abc39..d8648548 100644 --- a/wp-content/plugins/event-bridge-for-activitypub/includes/activitypub/scheduler/class-event.php +++ b/wp-content/plugins/event-bridge-for-activitypub/includes/activitypub/scheduler/class-event.php @@ -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': diff --git a/wp-content/plugins/event-bridge-for-activitypub/includes/activitypub/transformer/event/class-event-organiser.php b/wp-content/plugins/event-bridge-for-activitypub/includes/activitypub/transformer/event/class-event-organiser.php index 4407c54f..5cc6688c 100644 --- a/wp-content/plugins/event-bridge-for-activitypub/includes/activitypub/transformer/event/class-event-organiser.php +++ b/wp-content/plugins/event-bridge-for-activitypub/includes/activitypub/transformer/event/class-event-organiser.php @@ -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; diff --git a/wp-content/plugins/event-bridge-for-activitypub/includes/activitypub/transformer/event/class-event.php b/wp-content/plugins/event-bridge-for-activitypub/includes/activitypub/transformer/event/class-event.php index 09468e20..1fb66182 100644 --- a/wp-content/plugins/event-bridge-for-activitypub/includes/activitypub/transformer/event/class-event.php +++ b/wp-content/plugins/event-bridge-for-activitypub/includes/activitypub/transformer/event/class-event.php @@ -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. * diff --git a/wp-content/plugins/event-bridge-for-activitypub/includes/activitypub/transformer/event/class-eventin.php b/wp-content/plugins/event-bridge-for-activitypub/includes/activitypub/transformer/event/class-eventin.php index fd518845..6d01b302 100644 --- a/wp-content/plugins/event-bridge-for-activitypub/includes/activitypub/transformer/event/class-eventin.php +++ b/wp-content/plugins/event-bridge-for-activitypub/includes/activitypub/transformer/event/class-eventin.php @@ -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(); } diff --git a/wp-content/plugins/event-bridge-for-activitypub/includes/activitypub/transformer/event/class-eventon.php b/wp-content/plugins/event-bridge-for-activitypub/includes/activitypub/transformer/event/class-eventon.php index c005e1aa..0ccd82be 100644 --- a/wp-content/plugins/event-bridge-for-activitypub/includes/activitypub/transformer/event/class-eventon.php +++ b/wp-content/plugins/event-bridge-for-activitypub/includes/activitypub/transformer/event/class-eventon.php @@ -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 ); diff --git a/wp-content/plugins/event-bridge-for-activitypub/includes/activitypub/transformer/event/class-eventprime.php b/wp-content/plugins/event-bridge-for-activitypub/includes/activitypub/transformer/event/class-eventprime.php index b3008f69..1d642ef8 100644 --- a/wp-content/plugins/event-bridge-for-activitypub/includes/activitypub/transformer/event/class-eventprime.php +++ b/wp-content/plugins/event-bridge-for-activitypub/includes/activitypub/transformer/event/class-eventprime.php @@ -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 ''; } diff --git a/wp-content/plugins/event-bridge-for-activitypub/includes/activitypub/transformer/event/class-events-manager.php b/wp-content/plugins/event-bridge-for-activitypub/includes/activitypub/transformer/event/class-events-manager.php index f61f50a6..a8a74938 100644 --- a/wp-content/plugins/event-bridge-for-activitypub/includes/activitypub/transformer/event/class-events-manager.php +++ b/wp-content/plugins/event-bridge-for-activitypub/includes/activitypub/transformer/event/class-events-manager.php @@ -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; } diff --git a/wp-content/plugins/event-bridge-for-activitypub/includes/activitypub/transformer/event/class-gatherpress.php b/wp-content/plugins/event-bridge-for-activitypub/includes/activitypub/transformer/event/class-gatherpress.php index 9b4f49f7..75dfd0d0 100644 --- a/wp-content/plugins/event-bridge-for-activitypub/includes/activitypub/transformer/event/class-gatherpress.php +++ b/wp-content/plugins/event-bridge-for-activitypub/includes/activitypub/transformer/event/class-gatherpress.php @@ -1,28 +1,34 @@ 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( '
', '', $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; } } diff --git a/wp-content/plugins/event-bridge-for-activitypub/includes/activitypub/transformer/event/class-modern-events-calendar-lite.php b/wp-content/plugins/event-bridge-for-activitypub/includes/activitypub/transformer/event/class-modern-events-calendar-lite.php index 48011a65..0da29e5b 100644 --- a/wp-content/plugins/event-bridge-for-activitypub/includes/activitypub/transformer/event/class-modern-events-calendar-lite.php +++ b/wp-content/plugins/event-bridge-for-activitypub/includes/activitypub/transformer/event/class-modern-events-calendar-lite.php @@ -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 ); } } diff --git a/wp-content/plugins/event-bridge-for-activitypub/includes/activitypub/transformer/event/class-spiffy-calendar.php b/wp-content/plugins/event-bridge-for-activitypub/includes/activitypub/transformer/event/class-spiffy-calendar.php new file mode 100644 index 00000000..9030e739 --- /dev/null +++ b/wp-content/plugins/event-bridge-for-activitypub/includes/activitypub/transformer/event/class-spiffy-calendar.php @@ -0,0 +1,140 @@ +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; + } +} diff --git a/wp-content/plugins/event-bridge-for-activitypub/includes/activitypub/transformer/event/class-the-events-calendar.php b/wp-content/plugins/event-bridge-for-activitypub/includes/activitypub/transformer/event/class-the-events-calendar.php index 61cb979e..29480433 100644 --- a/wp-content/plugins/event-bridge-for-activitypub/includes/activitypub/transformer/event/class-the-events-calendar.php +++ b/wp-content/plugins/event-bridge-for-activitypub/includes/activitypub/transformer/event/class-the-events-calendar.php @@ -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; } diff --git a/wp-content/plugins/event-bridge-for-activitypub/includes/activitypub/transformer/event/class-vs-event-list.php b/wp-content/plugins/event-bridge-for-activitypub/includes/activitypub/transformer/event/class-vs-event-list.php index 1ea96415..cd793041 100644 --- a/wp-content/plugins/event-bridge-for-activitypub/includes/activitypub/transformer/event/class-vs-event-list.php +++ b/wp-content/plugins/event-bridge-for-activitypub/includes/activitypub/transformer/event/class-vs-event-list.php @@ -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 ); } /** diff --git a/wp-content/plugins/event-bridge-for-activitypub/includes/activitypub/transformer/event/class-wp-event-manager.php b/wp-content/plugins/event-bridge-for-activitypub/includes/activitypub/transformer/event/class-wp-event-manager.php index eda001e5..7da0f16f 100644 --- a/wp-content/plugins/event-bridge-for-activitypub/includes/activitypub/transformer/event/class-wp-event-manager.php +++ b/wp-content/plugins/event-bridge-for-activitypub/includes/activitypub/transformer/event/class-wp-event-manager.php @@ -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( diff --git a/wp-content/plugins/event-bridge-for-activitypub/includes/activitypub/transformer/place/class-base-post-place.php b/wp-content/plugins/event-bridge-for-activitypub/includes/activitypub/transformer/place/class-base-post-place.php index 355af17f..511b48aa 100644 --- a/wp-content/plugins/event-bridge-for-activitypub/includes/activitypub/transformer/place/class-base-post-place.php +++ b/wp-content/plugins/event-bridge-for-activitypub/includes/activitypub/transformer/place/class-base-post-place.php @@ -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; diff --git a/wp-content/plugins/event-bridge-for-activitypub/includes/activitypub/transformer/place/class-base-term-place.php b/wp-content/plugins/event-bridge-for-activitypub/includes/activitypub/transformer/place/class-base-term-place.php index 2a70b88a..703758f6 100644 --- a/wp-content/plugins/event-bridge-for-activitypub/includes/activitypub/transformer/place/class-base-term-place.php +++ b/wp-content/plugins/event-bridge-for-activitypub/includes/activitypub/transformer/place/class-base-term-place.php @@ -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; diff --git a/wp-content/plugins/event-bridge-for-activitypub/includes/activitypub/transformer/place/class-event-organiser.php b/wp-content/plugins/event-bridge-for-activitypub/includes/activitypub/transformer/place/class-event-organiser.php index f9fa4050..adfdaf6e 100644 --- a/wp-content/plugins/event-bridge-for-activitypub/includes/activitypub/transformer/place/class-event-organiser.php +++ b/wp-content/plugins/event-bridge-for-activitypub/includes/activitypub/transformer/place/class-event-organiser.php @@ -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`. diff --git a/wp-content/plugins/event-bridge-for-activitypub/includes/activitypub/transformer/place/class-eventon.php b/wp-content/plugins/event-bridge-for-activitypub/includes/activitypub/transformer/place/class-eventon.php index e71cc55c..7092945c 100644 --- a/wp-content/plugins/event-bridge-for-activitypub/includes/activitypub/transformer/place/class-eventon.php +++ b/wp-content/plugins/event-bridge-for-activitypub/includes/activitypub/transformer/place/class-eventon.php @@ -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; diff --git a/wp-content/plugins/event-bridge-for-activitypub/includes/activitypub/transformer/place/class-eventprime.php b/wp-content/plugins/event-bridge-for-activitypub/includes/activitypub/transformer/place/class-eventprime.php index f78964cd..668b6681 100644 --- a/wp-content/plugins/event-bridge-for-activitypub/includes/activitypub/transformer/place/class-eventprime.php +++ b/wp-content/plugins/event-bridge-for-activitypub/includes/activitypub/transformer/place/class-eventprime.php @@ -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`. diff --git a/wp-content/plugins/event-bridge-for-activitypub/includes/activitypub/transformer/place/class-events-manager.php b/wp-content/plugins/event-bridge-for-activitypub/includes/activitypub/transformer/place/class-events-manager.php index 21b381e2..dad8fcad 100644 --- a/wp-content/plugins/event-bridge-for-activitypub/includes/activitypub/transformer/place/class-events-manager.php +++ b/wp-content/plugins/event-bridge-for-activitypub/includes/activitypub/transformer/place/class-events-manager.php @@ -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; diff --git a/wp-content/plugins/event-bridge-for-activitypub/includes/activitypub/transformer/place/class-gatherpress.php b/wp-content/plugins/event-bridge-for-activitypub/includes/activitypub/transformer/place/class-gatherpress.php new file mode 100644 index 00000000..acd2f5bd --- /dev/null +++ b/wp-content/plugins/event-bridge-for-activitypub/includes/activitypub/transformer/place/class-gatherpress.php @@ -0,0 +1,152 @@ +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; + } +} diff --git a/wp-content/plugins/event-bridge-for-activitypub/includes/activitypub/transformer/place/class-the-events-calendar.php b/wp-content/plugins/event-bridge-for-activitypub/includes/activitypub/transformer/place/class-the-events-calendar.php index 5632bcc0..92219e63 100644 --- a/wp-content/plugins/event-bridge-for-activitypub/includes/activitypub/transformer/place/class-the-events-calendar.php +++ b/wp-content/plugins/event-bridge-for-activitypub/includes/activitypub/transformer/place/class-the-events-calendar.php @@ -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; + } } diff --git a/wp-content/plugins/event-bridge-for-activitypub/includes/activitypub/transmogrifier/class-base.php b/wp-content/plugins/event-bridge-for-activitypub/includes/activitypub/transmogrifier/class-base.php index 00a5c031..f1b2244b 100644 --- a/wp-content/plugins/event-bridge-for-activitypub/includes/activitypub/transmogrifier/class-base.php +++ b/wp-content/plugins/event-bridge-for-activitypub/includes/activitypub/transmogrifier/class-base.php @@ -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 ) ); diff --git a/wp-content/plugins/event-bridge-for-activitypub/includes/activitypub/transmogrifier/class-gatherpress.php b/wp-content/plugins/event-bridge-for-activitypub/includes/activitypub/transmogrifier/class-gatherpress.php index 47aafe79..b9979d00 100644 --- a/wp-content/plugins/event-bridge-for-activitypub/includes/activitypub/transmogrifier/class-gatherpress.php +++ b/wp-content/plugins/event-bridge-for-activitypub/includes/activitypub/transmogrifier/class-gatherpress.php @@ -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; } diff --git a/wp-content/plugins/event-bridge-for-activitypub/includes/activitypub/transmogrifier/class-the-events-calendar.php b/wp-content/plugins/event-bridge-for-activitypub/includes/activitypub/transmogrifier/class-the-events-calendar.php index d36f448b..aed1863e 100644 --- a/wp-content/plugins/event-bridge-for-activitypub/includes/activitypub/transmogrifier/class-the-events-calendar.php +++ b/wp-content/plugins/event-bridge-for-activitypub/includes/activitypub/transmogrifier/class-the-events-calendar.php @@ -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; } diff --git a/wp-content/plugins/event-bridge-for-activitypub/includes/activitypub/transmogrifier/class-vs-event-list.php b/wp-content/plugins/event-bridge-for-activitypub/includes/activitypub/transmogrifier/class-vs-event-list.php index bb738b37..0c7fc5da 100644 --- a/wp-content/plugins/event-bridge-for-activitypub/includes/activitypub/transmogrifier/class-vs-event-list.php +++ b/wp-content/plugins/event-bridge-for-activitypub/includes/activitypub/transmogrifier/class-vs-event-list.php @@ -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; } diff --git a/wp-content/plugins/event-bridge-for-activitypub/includes/activitypub/transmogrifier/helper/class-sanitizer.php b/wp-content/plugins/event-bridge-for-activitypub/includes/activitypub/transmogrifier/helper/class-sanitizer.php index aae19039..b4bb043e 100644 --- a/wp-content/plugins/event-bridge-for-activitypub/includes/activitypub/transmogrifier/helper/class-sanitizer.php +++ b/wp-content/plugins/event-bridge-for-activitypub/includes/activitypub/transmogrifier/helper/class-sanitizer.php @@ -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'] ) ); } diff --git a/wp-content/plugins/event-bridge-for-activitypub/includes/activitypub/transmogrifier/helper/class-the-events-calendar-event-repository.php b/wp-content/plugins/event-bridge-for-activitypub/includes/activitypub/transmogrifier/helper/class-the-events-calendar-event-repository.php index a4f1f846..818e8b39 100644 --- a/wp-content/plugins/event-bridge-for-activitypub/includes/activitypub/transmogrifier/helper/class-the-events-calendar-event-repository.php +++ b/wp-content/plugins/event-bridge-for-activitypub/includes/activitypub/transmogrifier/helper/class-the-events-calendar-event-repository.php @@ -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. diff --git a/wp-content/plugins/event-bridge-for-activitypub/includes/activitypub/transmogrifier/helper/class-the-events-calendar-organizer-repository.php b/wp-content/plugins/event-bridge-for-activitypub/includes/activitypub/transmogrifier/helper/class-the-events-calendar-organizer-repository.php index 03720bfe..becb5da2 100644 --- a/wp-content/plugins/event-bridge-for-activitypub/includes/activitypub/transmogrifier/helper/class-the-events-calendar-organizer-repository.php +++ b/wp-content/plugins/event-bridge-for-activitypub/includes/activitypub/transmogrifier/helper/class-the-events-calendar-organizer-repository.php @@ -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. diff --git a/wp-content/plugins/event-bridge-for-activitypub/includes/activitypub/transmogrifier/helper/class-the-events-calendar-venue-repository.php b/wp-content/plugins/event-bridge-for-activitypub/includes/activitypub/transmogrifier/helper/class-the-events-calendar-venue-repository.php index 5b842c24..47d0e11d 100644 --- a/wp-content/plugins/event-bridge-for-activitypub/includes/activitypub/transmogrifier/helper/class-the-events-calendar-venue-repository.php +++ b/wp-content/plugins/event-bridge-for-activitypub/includes/activitypub/transmogrifier/helper/class-the-events-calendar-venue-repository.php @@ -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. * diff --git a/wp-content/plugins/event-bridge-for-activitypub/includes/admin/class-event-plugin-admin-notices.php b/wp-content/plugins/event-bridge-for-activitypub/includes/admin/class-event-plugin-admin-notices.php index 1b23768f..c7ccd2b8 100644 --- a/wp-content/plugins/event-bridge-for-activitypub/includes/admin/class-event-plugin-admin-notices.php +++ b/wp-content/plugins/event-bridge-for-activitypub/includes/admin/class-event-plugin-admin-notices.php @@ -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 %1$s plugin, but the event post type of the plugin %2$s is not enabled in the %1$s settings.', diff --git a/wp-content/plugins/event-bridge-for-activitypub/includes/admin/class-general-admin-notices.php b/wp-content/plugins/event-bridge-for-activitypub/includes/admin/class-general-admin-notices.php index 6a5302f0..5e7fb343 100644 --- a/wp-content/plugins/event-bridge-for-activitypub/includes/admin/class-general-admin-notices.php +++ b/wp-content/plugins/event-bridge-for-activitypub/includes/admin/class-general-admin-notices.php @@ -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 ActivityPub 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 ActivityPub plugin. At least version %2$s is required for the Event Bridge for ActivityPub to work.', diff --git a/wp-content/plugins/event-bridge-for-activitypub/includes/admin/class-health-check.php b/wp-content/plugins/event-bridge-for-activitypub/includes/admin/class-health-check.php index ecf4652d..87ac98b2 100644 --- a/wp-content/plugins/event-bridge-for-activitypub/includes/admin/class-health-check.php +++ b/wp-content/plugins/event-bridge-for-activitypub/includes/admin/class-health-check.php @@ -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( diff --git a/wp-content/plugins/event-bridge-for-activitypub/includes/admin/class-settings-page.php b/wp-content/plugins/event-bridge-for-activitypub/includes/admin/class-settings-page.php index 0b58c39b..f7e352d7 100644 --- a/wp-content/plugins/event-bridge-for-activitypub/includes/admin/class-settings-page.php +++ b/wp-content/plugins/event-bridge-for-activitypub/includes/admin/class-settings-page.php @@ -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(); } diff --git a/wp-content/plugins/event-bridge-for-activitypub/includes/admin/class-user-interface.php b/wp-content/plugins/event-bridge-for-activitypub/includes/admin/class-user-interface.php index 9d673a2e..cb0a6210 100644 --- a/wp-content/plugins/event-bridge-for-activitypub/includes/admin/class-user-interface.php +++ b/wp-content/plugins/event-bridge-for-activitypub/includes/admin/class-user-interface.php @@ -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( '⁂ %s', \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 ); diff --git a/wp-content/plugins/event-bridge-for-activitypub/includes/class-autoloader.php b/wp-content/plugins/event-bridge-for-activitypub/includes/class-autoloader.php index 40f99cdf..27f7de61 100644 --- a/wp-content/plugins/event-bridge-for-activitypub/includes/class-autoloader.php +++ b/wp-content/plugins/event-bridge-for-activitypub/includes/class-autoloader.php @@ -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 ) ) ); } } } diff --git a/wp-content/plugins/event-bridge-for-activitypub/includes/class-debug.php b/wp-content/plugins/event-bridge-for-activitypub/includes/class-debug.php index 5e28ec53..54911537 100644 --- a/wp-content/plugins/event-bridge-for-activitypub/includes/class-debug.php +++ b/wp-content/plugins/event-bridge-for-activitypub/includes/class-debug.php @@ -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 ); } } diff --git a/wp-content/plugins/event-bridge-for-activitypub/includes/class-event-sources.php b/wp-content/plugins/event-bridge-for-activitypub/includes/class-event-sources.php index e9463092..1b822b97 100644 --- a/wp-content/plugins/event-bridge-for-activitypub/includes/class-event-sources.php +++ b/wp-content/plugins/event-bridge-for-activitypub/includes/class-event-sources.php @@ -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; diff --git a/wp-content/plugins/event-bridge-for-activitypub/includes/class-outbox-parser.php b/wp-content/plugins/event-bridge-for-activitypub/includes/class-outbox-parser.php index f970383e..4557e967 100644 --- a/wp-content/plugins/event-bridge-for-activitypub/includes/class-outbox-parser.php +++ b/wp-content/plugins/event-bridge-for-activitypub/includes/class-outbox-parser.php @@ -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']; } diff --git a/wp-content/plugins/event-bridge-for-activitypub/includes/class-preview.php b/wp-content/plugins/event-bridge-for-activitypub/includes/class-preview.php index 637947bf..dc347120 100644 --- a/wp-content/plugins/event-bridge-for-activitypub/includes/class-preview.php +++ b/wp-content/plugins/event-bridge-for-activitypub/includes/class-preview.php @@ -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'; } diff --git a/wp-content/plugins/event-bridge-for-activitypub/includes/class-reminder.php b/wp-content/plugins/event-bridge-for-activitypub/includes/class-reminder.php index 67153ebe..9ef5db78 100644 --- a/wp-content/plugins/event-bridge-for-activitypub/includes/class-reminder.php +++ b/wp-content/plugins/event-bridge-for-activitypub/includes/class-reminder.php @@ -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; } diff --git a/wp-content/plugins/event-bridge-for-activitypub/includes/class-settings.php b/wp-content/plugins/event-bridge-for-activitypub/includes/class-settings.php index e4dca80d..f60af6b8 100644 --- a/wp-content/plugins/event-bridge-for-activitypub/includes/class-settings.php +++ b/wp-content/plugins/event-bridge-for-activitypub/includes/class-settings.php @@ -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 ); } } diff --git a/wp-content/plugins/event-bridge-for-activitypub/includes/class-setup.php b/wp-content/plugins/event-bridge-for-activitypub/includes/class-setup.php index 8f9ffe02..fad91e8e 100644 --- a/wp-content/plugins/event-bridge-for-activitypub/includes/class-setup.php +++ b/wp-content/plugins/event-bridge-for-activitypub/includes/class-setup.php @@ -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=