Merge commit 'a8078ce72bc93a7d095f19b3bdde055e68939fe8' into add/transformer-management
This commit is contained in:
commit
ea844ee942
15 changed files with 290 additions and 68 deletions
|
@ -114,6 +114,7 @@ Project maintained on GitHub at [automattic/wordpress-activitypub](https://githu
|
|||
* Added: CSS class for ActivityPub comments to allow custom designs
|
||||
* Added: FEP-2677: Identifying the Application Actor
|
||||
* Added: Basic Comment Federation
|
||||
* Added: Profile Update Activities
|
||||
* Improved: WebFinger endpoints
|
||||
|
||||
### 1.3.0 ###
|
||||
|
|
|
@ -33,6 +33,7 @@ require_once __DIR__ . '/includes/functions.php';
|
|||
\defined( 'ACTIVITYPUB_CUSTOM_POST_CONTENT' ) || \define( 'ACTIVITYPUB_CUSTOM_POST_CONTENT', "<strong>[ap_title]</strong>\n\n[ap_content]\n\n[ap_hashtags]\n\n[ap_shortlink]" );
|
||||
\defined( 'ACTIVITYPUB_AUTHORIZED_FETCH' ) || \define( 'ACTIVITYPUB_AUTHORIZED_FETCH', false );
|
||||
\defined( 'ACTIVITYPUB_DISABLE_REWRITES' ) || \define( 'ACTIVITYPUB_DISABLE_REWRITES', false );
|
||||
\defined( 'ACTIVITYPUB_SHARED_INBOX_FEATURE' ) || \define( 'ACTIVITYPUB_SHARED_INBOX_FEATURE', false );
|
||||
|
||||
\define( 'ACTIVITYPUB_PLUGIN_DIR', plugin_dir_path( __FILE__ ) );
|
||||
\define( 'ACTIVITYPUB_PLUGIN_BASENAME', plugin_basename( __FILE__ ) );
|
||||
|
|
|
@ -28,6 +28,7 @@ class Activity_Dispatcher {
|
|||
public static function init() {
|
||||
\add_action( 'activitypub_send_activity', array( self::class, 'send_activity' ), 10, 2 );
|
||||
\add_action( 'activitypub_send_activity', array( self::class, 'send_activity_or_announce' ), 10, 2 );
|
||||
\add_action( 'activitypub_send_update_profile_activity', array( self::class, 'send_profile_update' ), 10, 1 );
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -74,19 +75,9 @@ class Activity_Dispatcher {
|
|||
return;
|
||||
}
|
||||
|
||||
$activity = $transformer->to_activity( 'Create' );
|
||||
$activity = $transformer->to_activity( $type );
|
||||
|
||||
$follower_inboxes = Followers::get_inboxes( $user_id );
|
||||
$mentioned_inboxes = Mention::get_inboxes( $activity->get_cc() );
|
||||
|
||||
$inboxes = array_merge( $follower_inboxes, $mentioned_inboxes );
|
||||
$inboxes = array_unique( $inboxes );
|
||||
|
||||
$json = $activity->to_json();
|
||||
|
||||
foreach ( $inboxes as $inbox ) {
|
||||
safe_remote_post( $inbox, $json, $user_id );
|
||||
}
|
||||
self::send_activity_to_inboxes( $activity, $user_id );
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -112,12 +103,60 @@ class Activity_Dispatcher {
|
|||
$user_id = $transformer->get_wp_user_id();
|
||||
$activity = $transformer->to_activity( 'Announce' );
|
||||
|
||||
$follower_inboxes = Followers::get_inboxes( $user_id );
|
||||
$mentioned_inboxes = Mention::get_inboxes( $activity->get_cc() );
|
||||
self::send_activity_to_inboxes( $activity, $user_id );
|
||||
}
|
||||
|
||||
/**
|
||||
* Send a "Update" Activity when a user updates their profile.
|
||||
*
|
||||
* @param int $user_id The user ID to send an update for.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public static function send_profile_update( $user_id ) {
|
||||
$user = Users::get_by_various( $user_id );
|
||||
|
||||
// bail if that's not a good user
|
||||
if ( is_wp_error( $user ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
// build the update
|
||||
$activity = new Activity();
|
||||
$activity->set_id( $user->get_url() . '#update' );
|
||||
$activity->set_type( 'Update' );
|
||||
$activity->set_actor( $user->get_url() );
|
||||
$activity->set_object( $user->get_url() );
|
||||
$activity->set_to( 'https://www.w3.org/ns/activitystreams#Public' );
|
||||
|
||||
// send the update
|
||||
self::send_activity_to_inboxes( $activity, $user_id );
|
||||
}
|
||||
|
||||
/**
|
||||
* Send an Activity to all followers and mentioned users.
|
||||
*
|
||||
* @param Activity $activity The ActivityPub Activity.
|
||||
* @param int $user_id The user ID.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
private static function send_activity_to_inboxes( $activity, $user_id ) {
|
||||
$follower_inboxes = Followers::get_inboxes( $user_id );
|
||||
|
||||
$mentioned_inboxes = array();
|
||||
$cc = $activity->get_cc();
|
||||
if ( $cc ) {
|
||||
$mentioned_inboxes = Mention::get_inboxes( $cc );
|
||||
}
|
||||
|
||||
$inboxes = array_merge( $follower_inboxes, $mentioned_inboxes );
|
||||
$inboxes = array_unique( $inboxes );
|
||||
|
||||
if ( empty( $inboxes ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$json = $activity->to_json();
|
||||
|
||||
foreach ( $inboxes as $inbox ) {
|
||||
|
|
|
@ -18,6 +18,7 @@ class Admin {
|
|||
\add_action( 'admin_init', array( self::class, 'register_settings' ) );
|
||||
\add_action( 'personal_options_update', array( self::class, 'save_user_description' ) );
|
||||
\add_action( 'admin_enqueue_scripts', array( self::class, 'enqueue_scripts' ) );
|
||||
\add_action( 'admin_notices', array( self::class, 'admin_notices' ) );
|
||||
|
||||
if ( ! is_user_disabled( get_current_user_id() ) ) {
|
||||
\add_action( 'show_user_profile', array( self::class, 'add_profile' ) );
|
||||
|
@ -46,6 +47,37 @@ class Admin {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Display admin menu notices about configuration problems or conflicts.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public static function admin_notices() {
|
||||
$permalink_structure = \get_option( 'permalink_structure' );
|
||||
if ( empty( $permalink_structure ) ) {
|
||||
$admin_notice = \__( 'You are using the ActivityPub plugin without setting a permalink structure. This will prevent ActivityPub from working. Please set a permalink structure.', 'activitypub' );
|
||||
self::show_admin_notice( $admin_notice, 'error' );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Display one admin menu notice about configuration problems or conflicts.
|
||||
*
|
||||
* @param string $admin_notice The notice to display.
|
||||
* @param string $level The level of the notice (error, warning, success, info).
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
private static function show_admin_notice( $admin_notice, $level ) {
|
||||
?>
|
||||
|
||||
<div class="notice notice-<?php echo esc_attr( $level ); ?>">
|
||||
<p><?php echo wp_kses( $admin_notice, 'data' ); ?></p>
|
||||
</div>
|
||||
|
||||
<?php
|
||||
}
|
||||
|
||||
/**
|
||||
* Load settings page
|
||||
*/
|
||||
|
|
|
@ -6,12 +6,15 @@ use Activitypub\Collection\Users;
|
|||
use Activitypub\Collection\Followers;
|
||||
use Activitypub\Transformer\Post;
|
||||
|
||||
use function Activitypub\is_user_type_disabled;
|
||||
|
||||
/**
|
||||
* ActivityPub Scheduler Class
|
||||
*
|
||||
* @author Matthias Pfefferle
|
||||
*/
|
||||
class Scheduler {
|
||||
|
||||
/**
|
||||
* Initialize the class, registering WordPress hooks
|
||||
*/
|
||||
|
@ -58,6 +61,22 @@ class Scheduler {
|
|||
|
||||
// Migration
|
||||
\add_action( 'admin_init', array( self::class, 'schedule_migration' ) );
|
||||
|
||||
// profile updates for blog options
|
||||
if ( ! is_user_type_disabled( 'blog' ) ) {
|
||||
\add_action( 'update_option_site_icon', array( self::class, 'blog_user_update' ) );
|
||||
\add_action( 'update_option_blogdescription', array( self::class, 'blog_user_update' ) );
|
||||
\add_action( 'update_option_blogname', array( self::class, 'blog_user_update' ) );
|
||||
\add_filter( 'pre_set_theme_mod_custom_logo', array( self::class, 'blog_user_update' ) );
|
||||
\add_filter( 'pre_set_theme_mod_header_image', array( self::class, 'blog_user_update' ) );
|
||||
}
|
||||
|
||||
// profile updates for user options
|
||||
if ( ! is_user_type_disabled( 'user' ) ) {
|
||||
\add_action( 'wp_update_user', array( self::class, 'user_update' ) );
|
||||
\add_action( 'updated_user_meta', array( self::class, 'user_meta_update' ), 10, 3 );
|
||||
// @todo figure out a feasible way of updating the header image since it's not unique to any user.
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -255,4 +274,68 @@ class Scheduler {
|
|||
\wp_schedule_single_event( \time(), 'activitypub_schedule_migration' );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Send a profile update when relevant user meta is updated.
|
||||
*
|
||||
* @param int $meta_id Meta ID being updated.
|
||||
* @param int $user_id User ID being updated.
|
||||
* @param string $meta_key Meta key being updated.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public static function user_meta_update( $meta_id, $user_id, $meta_key ) {
|
||||
// don't bother if the user can't publish
|
||||
if ( ! \user_can( $user_id, 'publish_posts' ) ) {
|
||||
return;
|
||||
}
|
||||
// the user meta fields that affect a profile.
|
||||
$fields = array(
|
||||
'activitypub_user_description',
|
||||
'description',
|
||||
'user_url',
|
||||
'display_name',
|
||||
);
|
||||
if ( in_array( $meta_key, $fields, true ) ) {
|
||||
self::schedule_profile_update( $user_id );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Send a profile update when a user is updated.
|
||||
*
|
||||
* @param int $user_id User ID being updated.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public static function user_update( $user_id ) {
|
||||
// don't bother if the user can't publish
|
||||
if ( ! \user_can( $user_id, 'publish_posts' ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
self::schedule_profile_update( $user_id );
|
||||
}
|
||||
|
||||
/**
|
||||
* Theme mods only have a dynamic filter so we fudge it like this.
|
||||
* @param mixed $value
|
||||
* @return mixed
|
||||
*/
|
||||
public static function blog_user_update( $value = null ) {
|
||||
self::schedule_profile_update( 0 );
|
||||
return $value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Send a profile update to all followers. Gets hooked into all relevant options/meta etc.
|
||||
* @param int $user_id The user ID to update (Could be 0 for Blog-User).
|
||||
*/
|
||||
public static function schedule_profile_update( $user_id ) {
|
||||
\wp_schedule_single_event(
|
||||
\time(),
|
||||
'activitypub_send_update_profile_activity',
|
||||
array( $user_id )
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -15,7 +15,12 @@ class Create {
|
|||
* Initialize the class, registering WordPress hooks
|
||||
*/
|
||||
public static function init() {
|
||||
\add_action( 'activitypub_inbox_create', array( self::class, 'handle_create' ), 10, 3 );
|
||||
\add_action(
|
||||
'activitypub_inbox_create',
|
||||
array( self::class, 'handle_create' ),
|
||||
10,
|
||||
3
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -15,11 +15,24 @@ class Delete {
|
|||
* Initialize the class, registering WordPress hooks
|
||||
*/
|
||||
public static function init() {
|
||||
\add_action( 'activitypub_inbox_delete', array( self::class, 'handle_delete' ), 10, 2 );
|
||||
\add_action(
|
||||
'activitypub_inbox_delete',
|
||||
array( self::class, 'handle_delete' )
|
||||
);
|
||||
|
||||
// defer signature verification for `Delete` requests.
|
||||
\add_filter( 'activitypub_defer_signature_verification', array( self::class, 'defer_signature_verification' ), 10, 2 );
|
||||
\add_filter(
|
||||
'activitypub_defer_signature_verification',
|
||||
array( self::class, 'defer_signature_verification' ),
|
||||
10,
|
||||
2
|
||||
);
|
||||
|
||||
// side effect
|
||||
\add_action( 'activitypub_delete_actor_interactions', array( self::class, 'delete_interactions' ), 10, 1 );
|
||||
\add_action(
|
||||
'activitypub_delete_actor_interactions',
|
||||
array( self::class, 'delete_interactions' )
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -28,7 +41,7 @@ class Delete {
|
|||
* @param array $activity The delete activity.
|
||||
* @param int $user_id The ID of the user performing the delete activity.
|
||||
*/
|
||||
public static function handle_delete( $activity, $user_id ) {
|
||||
public static function handle_delete( $activity ) {
|
||||
$object_type = isset( $activity['object']['type'] ) ? $activity['object']['type'] : '';
|
||||
|
||||
switch ( $object_type ) {
|
||||
|
@ -39,7 +52,7 @@ class Delete {
|
|||
case 'Organization':
|
||||
case 'Service':
|
||||
case 'Application':
|
||||
self::maybe_delete_follower( $user_id, $activity );
|
||||
self::maybe_delete_follower( $activity );
|
||||
break;
|
||||
// Object and Link Types
|
||||
// @see https://www.w3.org/TR/activitystreams-vocabulary/#object-types
|
||||
|
|
|
@ -14,8 +14,17 @@ class Follow {
|
|||
* Initialize the class, registering WordPress hooks
|
||||
*/
|
||||
public static function init() {
|
||||
\add_action( 'activitypub_inbox_follow', array( self::class, 'handle_follow' ), 10, 2 );
|
||||
\add_action( 'activitypub_followers_post_follow', array( self::class, 'send_follow_response' ), 10, 4 );
|
||||
\add_action(
|
||||
'activitypub_inbox_follow',
|
||||
array( self::class, 'handle_follow' )
|
||||
);
|
||||
|
||||
\add_action(
|
||||
'activitypub_followers_post_follow',
|
||||
array( self::class, 'send_follow_response' ),
|
||||
10,
|
||||
4
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -24,11 +33,30 @@ class Follow {
|
|||
* @param array $activity The activity object
|
||||
* @param int $user_id The user ID
|
||||
*/
|
||||
public static function handle_follow( $activity, $user_id ) {
|
||||
// save follower
|
||||
$follower = Followers::add_follower( $user_id, $activity['actor'] );
|
||||
public static function handle_follow( $activity ) {
|
||||
$user = Users::get_by_resource( $activity['object'] );
|
||||
|
||||
do_action( 'activitypub_followers_post_follow', $activity['actor'], $activity, $user_id, $follower );
|
||||
if ( ! $user || is_wp_error( $user ) ) {
|
||||
// If we can not find a user,
|
||||
// we can not initiate a follow process
|
||||
return;
|
||||
}
|
||||
|
||||
$user_id = $user->get__id();
|
||||
|
||||
// save follower
|
||||
$follower = Followers::add_follower(
|
||||
$user_id,
|
||||
$activity['actor']
|
||||
);
|
||||
|
||||
do_action(
|
||||
'activitypub_followers_post_follow',
|
||||
$activity['actor'],
|
||||
$activity,
|
||||
$user_id,
|
||||
$follower
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
<?php
|
||||
namespace Activitypub\Handler;
|
||||
|
||||
use Activitypub\Collection\Users;
|
||||
use Activitypub\Collection\Followers;
|
||||
|
||||
/**
|
||||
|
@ -11,7 +12,10 @@ class Undo {
|
|||
* Initialize the class, registering WordPress hooks
|
||||
*/
|
||||
public static function init() {
|
||||
\add_action( 'activitypub_inbox_undo', array( self::class, 'handle_undo' ), 10, 2 );
|
||||
\add_action(
|
||||
'activitypub_inbox_undo',
|
||||
array( self::class, 'handle_undo' )
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -20,11 +24,23 @@ class Undo {
|
|||
* @param array $activity The JSON "Undo" Activity
|
||||
* @param int $user_id The ID of the ID of the WordPress User
|
||||
*/
|
||||
public static function handle_undo( $activity, $user_id ) {
|
||||
public static function handle_undo( $activity ) {
|
||||
if (
|
||||
isset( $activity['object']['type'] ) &&
|
||||
'Follow' === $activity['object']['type']
|
||||
'Follow' === $activity['object']['type'] &&
|
||||
isset( $activity['object']['object'] ) &&
|
||||
filter_var( $activity['object']['object'], FILTER_VALIDATE_URL )
|
||||
) {
|
||||
$user = Users::get_by_resource( $activity['object']['object'] );
|
||||
|
||||
if ( ! $user || is_wp_error( $user ) ) {
|
||||
// If we can not find a user,
|
||||
// we can not initiate a follow process
|
||||
return;
|
||||
}
|
||||
|
||||
$user_id = $user->get__id();
|
||||
|
||||
Followers::remove_follower( $user_id, $activity['actor'] );
|
||||
}
|
||||
}
|
||||
|
|
|
@ -14,7 +14,10 @@ class Update {
|
|||
* Initialize the class, registering WordPress hooks
|
||||
*/
|
||||
public static function init() {
|
||||
\add_action( 'activitypub_inbox_update', array( self::class, 'handle_update' ), 10, 2 );
|
||||
\add_action(
|
||||
'activitypub_inbox_update',
|
||||
array( self::class, 'handle_update' )
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -23,7 +26,7 @@ class Update {
|
|||
* @param array $array The activity-object
|
||||
* @param int $user_id The id of the local blog-user
|
||||
*/
|
||||
public static function handle_update( $array, $user_id ) {
|
||||
public static function handle_update( $array ) {
|
||||
$object_type = isset( $array['object']['type'] ) ? $array['object']['type'] : '';
|
||||
|
||||
switch ( $object_type ) {
|
||||
|
@ -45,7 +48,7 @@ class Update {
|
|||
case 'Video':
|
||||
case 'Event':
|
||||
case 'Document':
|
||||
self::update_interaction( $array, $user_id );
|
||||
self::update_interaction( $array );
|
||||
break;
|
||||
// Minimal Activity
|
||||
// @see https://www.w3.org/TR/activitystreams-core/#example-1
|
||||
|
@ -62,7 +65,7 @@ class Update {
|
|||
*
|
||||
* @return void
|
||||
*/
|
||||
public static function update_interaction( $activity, $user_id ) {
|
||||
public static function update_interaction( $activity ) {
|
||||
$state = Interactions::update_comment( $activity );
|
||||
$reaction = null;
|
||||
|
||||
|
@ -70,7 +73,7 @@ class Update {
|
|||
$reaction = \get_comment( $state );
|
||||
}
|
||||
|
||||
\do_action( 'activitypub_handled_update', $activity, $user_id, $state, $reaction );
|
||||
\do_action( 'activitypub_handled_update', $activity, null, $state, $reaction );
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -226,6 +226,18 @@ class User extends Actor {
|
|||
return get_rest_url_by_path( sprintf( 'users/%d/collections/featured', $this->get__id() ) );
|
||||
}
|
||||
|
||||
public function get_endpoints() {
|
||||
$endpoints = null;
|
||||
|
||||
if ( ACTIVITYPUB_SHARED_INBOX_FEATURE ) {
|
||||
$endpoints = array(
|
||||
'sharedInbox' => get_rest_url_by_path( 'inbox' ),
|
||||
);
|
||||
}
|
||||
|
||||
return $endpoints;
|
||||
}
|
||||
|
||||
/**
|
||||
* Extend the User-Output with Attachments.
|
||||
*
|
||||
|
|
|
@ -94,11 +94,8 @@ class Inbox {
|
|||
$json->generator = 'http://wordpress.org/?v=' . \get_bloginfo_rss( 'version' );
|
||||
$json->type = 'OrderedCollectionPage';
|
||||
$json->partOf = get_rest_url_by_path( sprintf( 'users/%d/inbox', $user->get__id() ) ); // phpcs:ignore
|
||||
|
||||
$json->totalItems = 0; // phpcs:ignore
|
||||
|
||||
$json->orderedItems = array(); // phpcs:ignore
|
||||
|
||||
$json->first = $json->partOf; // phpcs:ignore
|
||||
|
||||
// filter output
|
||||
|
@ -155,37 +152,10 @@ class Inbox {
|
|||
$data = $request->get_json_params();
|
||||
$activity = Activity::init_from_array( $data );
|
||||
$type = $request->get_param( 'type' );
|
||||
$users = self::get_recipients( $data );
|
||||
$type = \strtolower( $type );
|
||||
|
||||
if ( ! $users ) {
|
||||
return new WP_Error(
|
||||
'rest_invalid_param',
|
||||
\__( 'No recipients found', 'activitypub' ),
|
||||
array(
|
||||
'status' => 400,
|
||||
'params' => array(
|
||||
'to' => \__( 'Please check/validate "to" field', 'activitypub' ),
|
||||
'bto' => \__( 'Please check/validate "bto" field', 'activitypub' ),
|
||||
'cc' => \__( 'Please check/validate "cc" field', 'activitypub' ),
|
||||
'bcc' => \__( 'Please check/validate "bcc" field', 'activitypub' ),
|
||||
'audience' => \__( 'Please check/validate "audience" field', 'activitypub' ),
|
||||
),
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
foreach ( $users as $user ) {
|
||||
$user = User_Collection::get_by_various( $user );
|
||||
|
||||
if ( is_wp_error( $user ) ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$type = \strtolower( $type );
|
||||
|
||||
\do_action( 'activitypub_inbox', $data, $user->ID, $type, $activity );
|
||||
\do_action( "activitypub_inbox_{$type}", $data, $user->ID, $activity );
|
||||
}
|
||||
\do_action( 'activitypub_inbox', $data, null, $type, $activity );
|
||||
\do_action( "activitypub_inbox_{$type}", $data, null, $activity );
|
||||
|
||||
$rest_response = new WP_REST_Response( array(), 202 );
|
||||
$rest_response->header( 'Content-Type', 'application/activity+json; charset=' . get_option( 'blog_charset' ) );
|
||||
|
|
|
@ -395,7 +395,7 @@ class Post extends Base {
|
|||
*
|
||||
* @return string The Object-Type.
|
||||
*/
|
||||
protected function get_object_type() {
|
||||
protected function get_type() {
|
||||
if ( 'wordpress-post-format' !== \get_option( 'activitypub_object_type', 'note' ) ) {
|
||||
return \ucfirst( \get_option( 'activitypub_object_type', 'note' ) );
|
||||
}
|
||||
|
|
|
@ -114,6 +114,7 @@ Project maintained on GitHub at [automattic/wordpress-activitypub](https://githu
|
|||
* Added: CSS class for ActivityPub comments to allow custom designs
|
||||
* Added: FEP-2677: Identifying the Application Actor
|
||||
* Added: Basic Comment Federation
|
||||
* Added: Profile Update Activities
|
||||
* Improved: WebFinger endpoints
|
||||
|
||||
= 1.3.0 =
|
||||
|
|
18
tests/test-class-admin.php
Normal file
18
tests/test-class-admin.php
Normal file
|
@ -0,0 +1,18 @@
|
|||
<?php
|
||||
class Test_Admin extends WP_UnitTestCase {
|
||||
public function test_no_permalink_structure_has_errors() {
|
||||
\add_option( 'permalink_structure', '' );
|
||||
\do_action( 'admin_notices' );
|
||||
$this->expectOutputRegex( "/notice-error/" );
|
||||
|
||||
\delete_option( 'permalink_structure' );
|
||||
}
|
||||
|
||||
public function test_has_permalink_structure_no_errors() {
|
||||
\add_option( 'permalink_structure', '/archives/%post_id%' );
|
||||
\do_action( 'admin_notices' );
|
||||
$this->expectOutputRegex( "/^((?!notice-error).)*$/s" );
|
||||
|
||||
\delete_option( 'permalink_structure' );
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue