Merge pull request #353 from Automattic/friends-list-fixes
A Follower is now an ActivityPub Actor
This commit is contained in:
commit
a7eeee904d
15 changed files with 963 additions and 338 deletions
117
includes/activity/class-actor.php
Normal file
117
includes/activity/class-actor.php
Normal file
|
@ -0,0 +1,117 @@
|
|||
<?php
|
||||
/**
|
||||
* Inspired by the PHP ActivityPub Library by @Landrok
|
||||
*
|
||||
* @link https://github.com/landrok/activitypub
|
||||
*/
|
||||
|
||||
namespace Activitypub\Activity;
|
||||
|
||||
class Actor extends Base_Object {
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $type = 'Person';
|
||||
|
||||
/**
|
||||
* A reference to an ActivityStreams OrderedCollection comprised of
|
||||
* all the messages received by the actor.
|
||||
*
|
||||
* @see https://www.w3.org/TR/activitypub/#inbox
|
||||
*
|
||||
* @var string
|
||||
* | null
|
||||
*/
|
||||
protected $inbox;
|
||||
|
||||
/**
|
||||
* A reference to an ActivityStreams OrderedCollection comprised of
|
||||
* all the messages produced by the actor.
|
||||
*
|
||||
* @see https://www.w3.org/TR/activitypub/#outbox
|
||||
*
|
||||
* @var string
|
||||
* | null
|
||||
*/
|
||||
protected $outbox;
|
||||
|
||||
/**
|
||||
* A link to an ActivityStreams collection of the actors that this
|
||||
* actor is following.
|
||||
*
|
||||
* @see https://www.w3.org/TR/activitypub/#following
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $following;
|
||||
|
||||
/**
|
||||
* A link to an ActivityStreams collection of the actors that
|
||||
* follow this actor.
|
||||
*
|
||||
* @see https://www.w3.org/TR/activitypub/#followers
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $followers;
|
||||
|
||||
/**
|
||||
* A link to an ActivityStreams collection of objects this actor has
|
||||
* liked.
|
||||
*
|
||||
* @see https://www.w3.org/TR/activitypub/#liked
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $liked;
|
||||
|
||||
/**
|
||||
* A list of supplementary Collections which may be of interest.
|
||||
*
|
||||
* @see https://www.w3.org/TR/activitypub/#streams-property
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $streams = array();
|
||||
|
||||
/**
|
||||
* A short username which may be used to refer to the actor, with no
|
||||
* uniqueness guarantees.
|
||||
*
|
||||
* @see https://www.w3.org/TR/activitypub/#preferredUsername
|
||||
*
|
||||
* @var string|null
|
||||
*/
|
||||
protected $preferred_username;
|
||||
|
||||
/**
|
||||
* A JSON object which maps additional typically server/domain-wide
|
||||
* endpoints which may be useful either for this actor or someone
|
||||
* referencing this actor. This mapping may be nested inside the
|
||||
* actor document as the value or may be a link to a JSON-LD
|
||||
* document with these properties.
|
||||
*
|
||||
* @see https://www.w3.org/TR/activitypub/#endpoints
|
||||
*
|
||||
* @var string|array|null
|
||||
*/
|
||||
protected $endpoints;
|
||||
|
||||
/**
|
||||
* It's not part of the ActivityPub protocol but it's a quite common
|
||||
* practice to handle an actor public key with a publicKey array:
|
||||
* [
|
||||
* 'id' => 'https://my-example.com/actor#main-key'
|
||||
* 'owner' => 'https://my-example.com/actor',
|
||||
* 'publicKeyPem' => '-----BEGIN PUBLIC KEY-----
|
||||
* MIIBI [...]
|
||||
* DQIDAQAB
|
||||
* -----END PUBLIC KEY-----'
|
||||
* ]
|
||||
*
|
||||
* @see https://www.w3.org/wiki/SocialCG/ActivityPub/Authentication_Authorization#Signing_requests_using_HTTP_Signatures
|
||||
*
|
||||
* @var string|array|null
|
||||
*/
|
||||
protected $public_key;
|
||||
}
|
592
includes/activity/class-base-object.php
Normal file
592
includes/activity/class-base-object.php
Normal file
|
@ -0,0 +1,592 @@
|
|||
<?php
|
||||
/**
|
||||
* Inspired by the PHP ActivityPub Library by @Landrok
|
||||
*
|
||||
* @link https://github.com/landrok/activitypub
|
||||
*/
|
||||
|
||||
namespace Activitypub\Activity;
|
||||
|
||||
use WP_Error;
|
||||
|
||||
use function Activitypub\camel_to_snake_case;
|
||||
use function Activitypub\snake_to_camel_case;
|
||||
/**
|
||||
* ObjectType is an implementation of one of the
|
||||
* Activity Streams Core Types.
|
||||
*
|
||||
* The Object is the primary base type for the Activity Streams
|
||||
* vocabulary.
|
||||
*
|
||||
* Note: Object is a reserved keyword in PHP. It has been suffixed with
|
||||
* 'Type' for this reason.
|
||||
*
|
||||
* @see https://www.w3.org/TR/activitystreams-core/#object
|
||||
*/
|
||||
class Base_Object {
|
||||
/**
|
||||
* The object's unique global identifier
|
||||
*
|
||||
* @see https://www.w3.org/TR/activitypub/#obj-id
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $id;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $type = 'Object';
|
||||
|
||||
/**
|
||||
* A resource attached or related to an object that potentially
|
||||
* requires special handling.
|
||||
* The intent is to provide a model that is at least semantically
|
||||
* similar to attachments in email.
|
||||
*
|
||||
* @see https://www.w3.org/TR/activitystreams-vocabulary/#dfn-attachment
|
||||
*
|
||||
* @var string
|
||||
* | ObjectType
|
||||
* | Link
|
||||
* | array<ObjectType>
|
||||
* | array<Link>
|
||||
* | null
|
||||
*/
|
||||
protected $attachment;
|
||||
|
||||
/**
|
||||
* One or more entities to which this object is attributed.
|
||||
* The attributed entities might not be Actors. For instance, an
|
||||
* object might be attributed to the completion of another activity.
|
||||
*
|
||||
* @see https://www.w3.org/TR/activitystreams-vocabulary/#dfn-attributedto
|
||||
*
|
||||
* @var string
|
||||
* | ObjectType
|
||||
* | Link
|
||||
* | array<ObjectType>
|
||||
* | array<Link>
|
||||
* | null
|
||||
*/
|
||||
protected $attributed_to;
|
||||
|
||||
/**
|
||||
* One or more entities that represent the total population of
|
||||
* entities for which the object can considered to be relevant.
|
||||
*
|
||||
* @see https://www.w3.org/TR/activitystreams-vocabulary/#dfn-audience
|
||||
*
|
||||
* @var string
|
||||
* | ObjectType
|
||||
* | Link
|
||||
* | array<ObjectType>
|
||||
* | array<Link>
|
||||
* | null
|
||||
*/
|
||||
protected $audience;
|
||||
|
||||
/**
|
||||
* The content or textual representation of the Object encoded as a
|
||||
* JSON string. By default, the value of content is HTML.
|
||||
* The mediaType property can be used in the object to indicate a
|
||||
* different content type.
|
||||
*
|
||||
* The content MAY be expressed using multiple language-tagged
|
||||
* values.
|
||||
*
|
||||
* @see https://www.w3.org/TR/activitystreams-vocabulary/#dfn-content
|
||||
*
|
||||
* @var string|null
|
||||
*/
|
||||
protected $content;
|
||||
|
||||
/**
|
||||
* The context within which the object exists or an activity was
|
||||
* performed.
|
||||
* The notion of "context" used is intentionally vague.
|
||||
* The intended function is to serve as a means of grouping objects
|
||||
* and activities that share a common originating context or
|
||||
* purpose. An example could be all activities relating to a common
|
||||
* project or event.
|
||||
*
|
||||
* @see https://www.w3.org/TR/activitystreams-vocabulary/#dfn-context
|
||||
*
|
||||
* @var string
|
||||
* | ObjectType
|
||||
* | Link
|
||||
* | null
|
||||
*/
|
||||
protected $context;
|
||||
|
||||
/**
|
||||
* The content MAY be expressed using multiple language-tagged
|
||||
* values.
|
||||
*
|
||||
* @see https://www.w3.org/TR/activitystreams-vocabulary/#dfn-content
|
||||
*
|
||||
* @var array|null
|
||||
*/
|
||||
protected $content_map;
|
||||
|
||||
/**
|
||||
* A simple, human-readable, plain-text name for the object.
|
||||
* HTML markup MUST NOT be included.
|
||||
*
|
||||
* @see https://www.w3.org/TR/activitystreams-vocabulary/#dfn-name
|
||||
*
|
||||
* @var string|null xsd:string
|
||||
*/
|
||||
protected $name;
|
||||
|
||||
/**
|
||||
* The name MAY be expressed using multiple language-tagged values.
|
||||
*
|
||||
* @see https://www.w3.org/TR/activitystreams-vocabulary/#dfn-name
|
||||
*
|
||||
* @var array|null rdf:langString
|
||||
*/
|
||||
protected $name_map;
|
||||
|
||||
/**
|
||||
* The date and time describing the actual or expected ending time
|
||||
* of the object.
|
||||
* When used with an Activity object, for instance, the endTime
|
||||
* property specifies the moment the activity concluded or
|
||||
* is expected to conclude.
|
||||
*
|
||||
* @see https://www.w3.org/TR/activitystreams-vocabulary/#dfn-endtime
|
||||
*
|
||||
* @var string|null
|
||||
*/
|
||||
protected $end_time;
|
||||
|
||||
/**
|
||||
* The entity (e.g. an application) that generated the object.
|
||||
*
|
||||
* @var string|null
|
||||
*/
|
||||
protected $generator;
|
||||
|
||||
/**
|
||||
* An entity that describes an icon for this object.
|
||||
* The image should have an aspect ratio of one (horizontal)
|
||||
* to one (vertical) and should be suitable for presentation
|
||||
* at a small size.
|
||||
*
|
||||
* @see https://www.w3.org/TR/activitystreams-vocabulary/#dfn-icon
|
||||
*
|
||||
* @var string
|
||||
* | Image
|
||||
* | Link
|
||||
* | array<Image>
|
||||
* | array<Link>
|
||||
* | null
|
||||
*/
|
||||
protected $icon;
|
||||
|
||||
/**
|
||||
* An entity that describes an image for this object.
|
||||
* Unlike the icon property, there are no aspect ratio
|
||||
* or display size limitations assumed.
|
||||
*
|
||||
* @see https://www.w3.org/TR/activitystreams-vocabulary/#dfn-image-term
|
||||
*
|
||||
* @var string
|
||||
* | Image
|
||||
* | Link
|
||||
* | array<Image>
|
||||
* | array<Link>
|
||||
* | null
|
||||
*/
|
||||
protected $image;
|
||||
|
||||
/**
|
||||
* One or more entities for which this object is considered a
|
||||
* response.
|
||||
*
|
||||
* @see https://www.w3.org/TR/activitystreams-vocabulary/#dfn-inreplyto
|
||||
*
|
||||
* @var string
|
||||
* | ObjectType
|
||||
* | Link
|
||||
* | array<ObjectType>
|
||||
* | array<Link>
|
||||
* | null
|
||||
*/
|
||||
protected $in_reply_to;
|
||||
|
||||
/**
|
||||
* One or more physical or logical locations associated with the
|
||||
* object.
|
||||
*
|
||||
* @see https://www.w3.org/TR/activitystreams-vocabulary/#dfn-location
|
||||
*
|
||||
* @var string
|
||||
* | ObjectType
|
||||
* | Link
|
||||
* | array<ObjectType>
|
||||
* | array<Link>
|
||||
* | null
|
||||
*/
|
||||
protected $location;
|
||||
|
||||
/**
|
||||
* An entity that provides a preview of this object.
|
||||
*
|
||||
* @see https://www.w3.org/TR/activitystreams-vocabulary/#dfn-preview
|
||||
*
|
||||
* @var string
|
||||
* | ObjectType
|
||||
* | Link
|
||||
* | null
|
||||
*/
|
||||
protected $preview;
|
||||
|
||||
/**
|
||||
* The date and time at which the object was published
|
||||
*
|
||||
* @see https://www.w3.org/TR/activitystreams-vocabulary/#dfn-published
|
||||
*
|
||||
* @var string|null xsd:dateTime
|
||||
*/
|
||||
protected $published;
|
||||
|
||||
/**
|
||||
* A Collection containing objects considered to be responses to
|
||||
* this object.
|
||||
*
|
||||
* @see https://www.w3.org/TR/activitystreams-vocabulary/#dfn-replies
|
||||
*
|
||||
* @var string
|
||||
* | Collection
|
||||
* | Link
|
||||
* | null
|
||||
*/
|
||||
protected $replies;
|
||||
|
||||
/**
|
||||
* The date and time describing the actual or expected starting time
|
||||
* of the object.
|
||||
* When used with an Activity object, for instance, the startTime
|
||||
* property specifies the moment the activity began
|
||||
* or is scheduled to begin.
|
||||
*
|
||||
* @see https://www.w3.org/TR/activitystreams-vocabulary/#dfn-starttime
|
||||
*
|
||||
* @var string|null xsd:dateTime
|
||||
*/
|
||||
protected $start_time;
|
||||
|
||||
/**
|
||||
* A natural language summarization of the object encoded as HTML.
|
||||
* Multiple language tagged summaries MAY be provided.
|
||||
*
|
||||
* @see https://www.w3.org/TR/activitystreams-vocabulary/#dfn-summary
|
||||
*
|
||||
* @var string
|
||||
* | ObjectType
|
||||
* | Link
|
||||
* | null
|
||||
*/
|
||||
protected $summary;
|
||||
|
||||
/**
|
||||
* The content MAY be expressed using multiple language-tagged
|
||||
* values.
|
||||
*
|
||||
* @see https://www.w3.org/TR/activitystreams-vocabulary/#dfn-summary
|
||||
*
|
||||
* @var array<string>|null
|
||||
*/
|
||||
protected $summary_map;
|
||||
|
||||
/**
|
||||
* One or more "tags" that have been associated with an objects.
|
||||
* A tag can be any kind of Object.
|
||||
* The key difference between attachment and tag is that the former
|
||||
* implies association by inclusion, while the latter implies
|
||||
* associated by reference.
|
||||
*
|
||||
* @see https://www.w3.org/TR/activitystreams-vocabulary/#dfn-tag
|
||||
*
|
||||
* @var string
|
||||
* | ObjectType
|
||||
* | Link
|
||||
* | array<ObjectType>
|
||||
* | array<Link>
|
||||
* | null
|
||||
*/
|
||||
protected $tag;
|
||||
|
||||
/**
|
||||
* The date and time at which the object was updated
|
||||
*
|
||||
* @see https://www.w3.org/TR/activitystreams-vocabulary/#dfn-updated
|
||||
*
|
||||
* @var string|null xsd:dateTime
|
||||
*/
|
||||
protected $updated;
|
||||
|
||||
/**
|
||||
* One or more links to representations of the object.
|
||||
*
|
||||
* @var string
|
||||
* | array<string>
|
||||
* | Link
|
||||
* | array<Link>
|
||||
* | null
|
||||
*/
|
||||
protected $url;
|
||||
|
||||
/**
|
||||
* An entity considered to be part of the public primary audience
|
||||
* of an Object
|
||||
*
|
||||
* @see https://www.w3.org/TR/activitystreams-vocabulary/#dfn-to
|
||||
*
|
||||
* @var string
|
||||
* | ObjectType
|
||||
* | Link
|
||||
* | array<ObjectType>
|
||||
* | array<Link>
|
||||
* | null
|
||||
*/
|
||||
protected $to;
|
||||
|
||||
/**
|
||||
* An Object that is part of the private primary audience of this
|
||||
* Object.
|
||||
*
|
||||
* @see https://www.w3.org/TR/activitystreams-vocabulary/#dfn-bto
|
||||
*
|
||||
* @var string
|
||||
* | ObjectType
|
||||
* | Link
|
||||
* | array<ObjectType>
|
||||
* | array<Link>
|
||||
* | null
|
||||
*/
|
||||
protected $bto;
|
||||
|
||||
/**
|
||||
* An Object that is part of the public secondary audience of this
|
||||
* Object.
|
||||
*
|
||||
* @see https://www.w3.org/TR/activitystreams-vocabulary/#dfn-cc
|
||||
*
|
||||
* @var string
|
||||
* | ObjectType
|
||||
* | Link
|
||||
* | array<ObjectType>
|
||||
* | array<Link>
|
||||
* | null
|
||||
*/
|
||||
protected $cc;
|
||||
|
||||
/**
|
||||
* One or more Objects that are part of the private secondary
|
||||
* audience of this Object.
|
||||
*
|
||||
* @see https://www.w3.org/TR/activitystreams-vocabulary/#dfn-bcc
|
||||
*
|
||||
* @var string
|
||||
* | ObjectType
|
||||
* | Link
|
||||
* | array<ObjectType>
|
||||
* | array<Link>
|
||||
* | null
|
||||
*/
|
||||
protected $bcc;
|
||||
|
||||
/**
|
||||
* The MIME media type of the value of the content property.
|
||||
* If not specified, the content property is assumed to contain
|
||||
* text/html content.
|
||||
*
|
||||
* @see https://www.w3.org/TR/activitystreams-vocabulary/#dfn-mediatype
|
||||
*
|
||||
* @var string|null
|
||||
*/
|
||||
protected $media_type;
|
||||
|
||||
/**
|
||||
* When the object describes a time-bound resource, such as an audio
|
||||
* or video, a meeting, etc, the duration property indicates the
|
||||
* object's approximate duration.
|
||||
* The value MUST be expressed as an xsd:duration as defined by
|
||||
* xmlschema11-2, section 3.3.6 (e.g. a period of 5 seconds is
|
||||
* represented as "PT5S").
|
||||
*
|
||||
* @see https://www.w3.org/TR/activitystreams-vocabulary/#dfn-duration
|
||||
*
|
||||
* @var string|null
|
||||
*/
|
||||
protected $duration;
|
||||
|
||||
/**
|
||||
* Intended to convey some sort of source from which the content
|
||||
* markup was derived, as a form of provenance, or to support
|
||||
* future editing by clients.
|
||||
*
|
||||
* @see https://www.w3.org/TR/activitypub/#source-property
|
||||
*
|
||||
* @var ObjectType
|
||||
*/
|
||||
protected $source;
|
||||
|
||||
/**
|
||||
* Magic function to implement getter and setter
|
||||
*
|
||||
* @param string $method The method name.
|
||||
* @param string $params The method params.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function __call( $method, $params ) {
|
||||
$var = \strtolower( \substr( $method, 4 ) );
|
||||
|
||||
if ( \strncasecmp( $method, 'get', 3 ) === 0 ) {
|
||||
if ( $this->has( $var ) ) {
|
||||
return $this->get( $var );
|
||||
}
|
||||
return $this->$var;
|
||||
}
|
||||
|
||||
if ( \strncasecmp( $method, 'set', 3 ) === 0 ) {
|
||||
$this->set( $var, $params[0] );
|
||||
}
|
||||
|
||||
if ( \strncasecmp( $method, 'add', 3 ) === 0 ) {
|
||||
$this->add( $var, $params[0] );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Generic getter.
|
||||
*
|
||||
* @param string $key The key to get.
|
||||
*
|
||||
* @return mixed The value.
|
||||
*/
|
||||
public function get( $key ) {
|
||||
if ( ! $this->has( $key ) ) {
|
||||
return new WP_Error( 'invalid_key', 'Invalid key' );
|
||||
|
||||
}
|
||||
|
||||
return $this->$key;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the object has a key
|
||||
*
|
||||
* @param string $key The key to check.
|
||||
*
|
||||
* @return boolean True if the object has the key.
|
||||
*/
|
||||
public function has( $key ) {
|
||||
return property_exists( $this, $key );
|
||||
}
|
||||
|
||||
/**
|
||||
* Generic setter.
|
||||
*
|
||||
* @param string $key The key to set.
|
||||
* @param string $value The value to set.
|
||||
*
|
||||
* @return mixed The value.
|
||||
*/
|
||||
public function set( $key, $value ) {
|
||||
if ( ! $this->has( $key ) ) {
|
||||
return new WP_Error( 'invalid_key', 'Invalid key' );
|
||||
}
|
||||
|
||||
$this->$key = $value;
|
||||
|
||||
return $this->$key;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generic adder.
|
||||
*
|
||||
* @param string $key The key to set.
|
||||
* @param mixed $value The value to add.
|
||||
*
|
||||
* @return mixed The value.
|
||||
*/
|
||||
public function add( $key, $value ) {
|
||||
if ( ! $this->has( $key ) ) {
|
||||
return new WP_Error( 'invalid_key', 'Invalid key' );
|
||||
}
|
||||
|
||||
if ( ! isset( $this->$key ) ) {
|
||||
$this->$key = array();
|
||||
}
|
||||
|
||||
$attributes = $this->$key;
|
||||
$attributes[] = $value;
|
||||
|
||||
$this->$key = $attributes;
|
||||
|
||||
return $this->$key;
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert JSON input to an array.
|
||||
*
|
||||
* @return string The JSON string.
|
||||
*
|
||||
* @return array An Object built from the JSON string.
|
||||
*/
|
||||
public static function from_json( $json ) {
|
||||
$array = wp_json_decode( $json, true );
|
||||
|
||||
return self::from_array( $array );
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert JSON input to an array.
|
||||
*
|
||||
* @return string The object array.
|
||||
*
|
||||
* @return array An Object built from the JSON string.
|
||||
*/
|
||||
public static function from_array( $array ) {
|
||||
$object = new static();
|
||||
|
||||
foreach ( $array as $key => $value ) {
|
||||
$key = camel_to_snake_case( $key );
|
||||
$object->set( $key, $value );
|
||||
}
|
||||
|
||||
return $object;
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert Object to an array.
|
||||
*
|
||||
* It tries to get the object attributes if they exist
|
||||
* and falls back to the getters. Empty values are ignored.
|
||||
*
|
||||
* @return array An array built from the Object.
|
||||
*/
|
||||
public function to_array() {
|
||||
$array = array();
|
||||
$vars = get_object_vars( $this );
|
||||
|
||||
foreach ( $vars as $key => $value ) {
|
||||
// if value is empty, try to get it from a getter.
|
||||
if ( ! $value ) {
|
||||
$value = call_user_func( array( $this, 'get_' . $key ) );
|
||||
}
|
||||
|
||||
// if value is still empty, ignore it for the array and continue.
|
||||
if ( $value ) {
|
||||
$array[ snake_to_camel_case( $key ) ] = $value;
|
||||
}
|
||||
}
|
||||
|
||||
return $array;
|
||||
}
|
||||
}
|
20
includes/activity/class-person.php
Normal file
20
includes/activity/class-person.php
Normal file
|
@ -0,0 +1,20 @@
|
|||
<?php
|
||||
/**
|
||||
* Inspired by the PHP ActivityPub Library by @Landrok
|
||||
*
|
||||
* @link https://github.com/landrok/activitypub
|
||||
*/
|
||||
|
||||
namespace Activitypub\Activity;
|
||||
|
||||
/**
|
||||
* Represents an individual person.
|
||||
*
|
||||
* @see https://www.w3.org/TR/activitystreams-vocabulary/#dfn-person
|
||||
*/
|
||||
class Person extends Base_Object {
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $type = 'Person';
|
||||
}
|
|
@ -71,19 +71,19 @@ class Migration {
|
|||
foreach ( $followers as $actor ) {
|
||||
$meta = get_remote_metadata_by_actor( $actor );
|
||||
|
||||
$follower = new Follower( $actor );
|
||||
|
||||
if ( is_tombstone( $meta ) ) {
|
||||
continue;
|
||||
} if ( empty( $meta ) || ! is_array( $meta ) || is_wp_error( $meta ) ) {
|
||||
}
|
||||
|
||||
$follower = Follower::from_array( $meta );
|
||||
|
||||
if ( empty( $meta ) || ! is_array( $meta ) || is_wp_error( $meta ) ) {
|
||||
$follower->set_error( $meta );
|
||||
} else {
|
||||
$follower->from_meta( $meta );
|
||||
}
|
||||
|
||||
$follower->upsert();
|
||||
|
||||
add_post_meta( $follower->get_id(), 'user_id', $user_id );
|
||||
add_post_meta( $follower->get__id(), '_user_id', $user_id );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -100,7 +100,7 @@ class Scheduler {
|
|||
$followers = Followers::get_outdated_followers();
|
||||
|
||||
foreach ( $followers as $follower ) {
|
||||
$meta = get_remote_metadata_by_actor( $follower->get_actor(), true );
|
||||
$meta = get_remote_metadata_by_actor( $follower->get_url(), true );
|
||||
|
||||
if ( empty( $meta ) || ! is_array( $meta ) || is_wp_error( $meta ) ) {
|
||||
$follower->set_error( $meta );
|
||||
|
@ -121,7 +121,7 @@ class Scheduler {
|
|||
$followers = Followers::get_faulty_followers();
|
||||
|
||||
foreach ( $followers as $follower ) {
|
||||
$meta = get_remote_metadata_by_actor( $follower->get_actor(), true );
|
||||
$meta = get_remote_metadata_by_actor( $follower->get_url(), true );
|
||||
|
||||
if ( is_tombstone( $meta ) ) {
|
||||
$follower->delete();
|
||||
|
|
|
@ -62,19 +62,7 @@ class Followers {
|
|||
|
||||
register_post_meta(
|
||||
self::POST_TYPE,
|
||||
'name',
|
||||
array(
|
||||
'type' => 'string',
|
||||
'single' => true,
|
||||
'sanitize_callback' => function( $value ) {
|
||||
return sanitize_user( $value );
|
||||
},
|
||||
)
|
||||
);
|
||||
|
||||
register_post_meta(
|
||||
self::POST_TYPE,
|
||||
'username',
|
||||
'preferred_username',
|
||||
array(
|
||||
'type' => 'string',
|
||||
'single' => true,
|
||||
|
@ -86,11 +74,9 @@ class Followers {
|
|||
|
||||
register_post_meta(
|
||||
self::POST_TYPE,
|
||||
'avatar',
|
||||
'icon',
|
||||
array(
|
||||
'type' => 'string',
|
||||
'single' => true,
|
||||
'sanitize_callback' => array( self::class, 'sanitize_url' ),
|
||||
)
|
||||
);
|
||||
|
||||
|
@ -116,7 +102,7 @@ class Followers {
|
|||
|
||||
register_post_meta(
|
||||
self::POST_TYPE,
|
||||
'shared_inbox',
|
||||
'_shared_inbox',
|
||||
array(
|
||||
'type' => 'string',
|
||||
'single' => true,
|
||||
|
@ -126,23 +112,7 @@ class Followers {
|
|||
|
||||
register_post_meta(
|
||||
self::POST_TYPE,
|
||||
'updated_at',
|
||||
array(
|
||||
'type' => 'string',
|
||||
'single' => true,
|
||||
'sanitize_callback' => function( $value ) {
|
||||
if ( ! is_numeric( $value ) && (int) $value !== $value ) {
|
||||
$value = \time();
|
||||
}
|
||||
|
||||
return $value;
|
||||
},
|
||||
)
|
||||
);
|
||||
|
||||
register_post_meta(
|
||||
self::POST_TYPE,
|
||||
'errors',
|
||||
'_errors',
|
||||
array(
|
||||
'type' => 'string',
|
||||
'single' => false,
|
||||
|
@ -156,6 +126,18 @@ class Followers {
|
|||
)
|
||||
);
|
||||
|
||||
register_post_meta(
|
||||
self::POST_TYPE,
|
||||
'_actor',
|
||||
array(
|
||||
'type' => 'string',
|
||||
'single' => false,
|
||||
'sanitize_callback' => function( $value ) {
|
||||
return esc_sql( $value );
|
||||
},
|
||||
)
|
||||
);
|
||||
|
||||
do_action( 'activitypub_after_register_post_type' );
|
||||
}
|
||||
|
||||
|
@ -213,14 +195,13 @@ class Followers {
|
|||
return $meta;
|
||||
}
|
||||
|
||||
$follower = new Follower( $actor );
|
||||
$follower->from_meta( $meta );
|
||||
$follower = Follower::from_array( $meta );
|
||||
$follower->upsert();
|
||||
|
||||
$meta = get_post_meta( $follower->get_id(), 'user_id' );
|
||||
$meta = get_post_meta( $follower->get__id(), '_user_id' );
|
||||
|
||||
if ( is_array( $meta ) && ! in_array( $user_id, $meta, true ) ) {
|
||||
add_post_meta( $follower->get_id(), 'user_id', $user_id );
|
||||
add_post_meta( $follower->get__id(), '_user_id', $user_id );
|
||||
wp_cache_delete( sprintf( self::CACHE_KEY_INBOXES, $user_id ), 'activitypub' );
|
||||
}
|
||||
|
||||
|
@ -244,7 +225,7 @@ class Followers {
|
|||
return false;
|
||||
}
|
||||
|
||||
return delete_post_meta( $follower->get_id(), 'user_id', $user_id );
|
||||
return delete_post_meta( $follower->get__id(), '_user_id', $user_id );
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -260,7 +241,7 @@ class Followers {
|
|||
|
||||
$post_id = $wpdb->get_var(
|
||||
$wpdb->prepare(
|
||||
"SELECT p.ID FROM $wpdb->posts p INNER JOIN $wpdb->postmeta pm ON p.ID = pm.post_id WHERE p.post_type = %s AND pm.meta_key = 'user_id' AND pm.meta_value = %d AND p.guid = %s",
|
||||
"SELECT p.ID FROM $wpdb->posts p INNER JOIN $wpdb->postmeta pm ON p.ID = pm.post_id WHERE p.post_type = %s AND pm.meta_key = '_user_id' AND pm.meta_value = %d AND p.guid = %s",
|
||||
array(
|
||||
esc_sql( self::POST_TYPE ),
|
||||
esc_sql( $user_id ),
|
||||
|
@ -271,7 +252,7 @@ class Followers {
|
|||
|
||||
if ( $post_id ) {
|
||||
$post = get_post( $post_id );
|
||||
return new Follower( $post );
|
||||
return Follower::from_custom_post_type( $post );
|
||||
}
|
||||
|
||||
return null;
|
||||
|
@ -304,7 +285,7 @@ class Followers {
|
|||
|
||||
// send "Accept" activity
|
||||
$activity = new Activity( 'Accept' );
|
||||
$activity->set_object( $object );
|
||||
$activity->set_activity_object( $object );
|
||||
$activity->set_actor( \get_author_posts_url( $user_id ) );
|
||||
$activity->set_to( $actor );
|
||||
$activity->set_id( \get_author_posts_url( $user_id ) . '#follow-' . \preg_replace( '~^https?://~', '', $actor ) );
|
||||
|
@ -332,7 +313,7 @@ class Followers {
|
|||
'order' => 'DESC',
|
||||
'meta_query' => array(
|
||||
array(
|
||||
'key' => 'user_id',
|
||||
'key' => '_user_id',
|
||||
'value' => $user_id,
|
||||
),
|
||||
),
|
||||
|
@ -343,7 +324,7 @@ class Followers {
|
|||
$items = array();
|
||||
|
||||
foreach ( $query->get_posts() as $post ) {
|
||||
$items[] = new Follower( $post ); // phpcs:ignore
|
||||
$items[] = Follower::from_custom_post_type( $post ); // phpcs:ignore
|
||||
}
|
||||
|
||||
return $items;
|
||||
|
@ -377,7 +358,7 @@ class Followers {
|
|||
'fields' => 'ids',
|
||||
'meta_query' => array(
|
||||
array(
|
||||
'key' => 'user_id',
|
||||
'key' => '_user_id',
|
||||
'value' => $user_id,
|
||||
),
|
||||
),
|
||||
|
@ -409,11 +390,11 @@ class Followers {
|
|||
'fields' => 'ids',
|
||||
'meta_query' => array(
|
||||
array(
|
||||
'key' => 'inbox',
|
||||
'key' => '_shared_inbox',
|
||||
'compare' => 'EXISTS',
|
||||
),
|
||||
array(
|
||||
'key' => 'user_id',
|
||||
'key' => '_user_id',
|
||||
'value' => $user_id,
|
||||
),
|
||||
),
|
||||
|
@ -431,7 +412,7 @@ class Followers {
|
|||
$wpdb->prepare(
|
||||
"SELECT DISTINCT meta_value FROM {$wpdb->postmeta}
|
||||
WHERE post_id IN (" . implode( ', ', array_fill( 0, count( $posts ), '%d' ) ) . ")
|
||||
AND meta_key = 'shared_inbox'
|
||||
AND meta_key = '_shared_inbox'
|
||||
AND meta_value IS NOT NULL",
|
||||
$posts
|
||||
)
|
||||
|
@ -471,7 +452,7 @@ class Followers {
|
|||
$items = array();
|
||||
|
||||
foreach ( $posts->get_posts() as $follower ) {
|
||||
$items[] = new Follower( $follower ); // phpcs:ignore
|
||||
$items[] = Follower::from_custom_post_type( $follower ); // phpcs:ignore
|
||||
}
|
||||
|
||||
return $items;
|
||||
|
@ -501,7 +482,7 @@ class Followers {
|
|||
$items = array();
|
||||
|
||||
foreach ( $posts->get_posts() as $follower ) {
|
||||
$items[] = new Follower( $follower ); // phpcs:ignore
|
||||
$items[] = Follower::from_custom_post_type( $follower ); // phpcs:ignore
|
||||
}
|
||||
|
||||
return $items;
|
||||
|
|
|
@ -237,6 +237,28 @@ function get_rest_url_by_path( $path = '' ) {
|
|||
return \get_rest_url( null, $namespaced_path );
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert a string from camelCase to snake_case.
|
||||
*
|
||||
* @param string $string The string to convert.
|
||||
*
|
||||
* @return string The converted string.
|
||||
*/
|
||||
function camel_to_snake_case( $string ) {
|
||||
return strtolower( preg_replace( '/(?<!^)[A-Z]/', '_$0', $string ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert a string from snake_case to camelCase.
|
||||
*
|
||||
* @param string $string The string to convert.
|
||||
*
|
||||
* @return string The converted string.
|
||||
*/
|
||||
function snake_to_camel_case( $string ) {
|
||||
return lcfirst( str_replace( '_', '', ucwords( $string, '_' ) ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if a request is for an ActivityPub request.
|
||||
*
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
namespace Activitypub\Model;
|
||||
|
||||
use WP_Query;
|
||||
use Activitypub\Activity\Actor;
|
||||
use Activitypub\Collection\Followers;
|
||||
|
||||
/**
|
||||
|
@ -15,74 +16,27 @@ use Activitypub\Collection\Followers;
|
|||
*
|
||||
* @see https://www.w3.org/TR/activitypub/#follow-activity-inbox
|
||||
*/
|
||||
class Follower {
|
||||
class Follower extends Actor {
|
||||
/**
|
||||
* The Object ID
|
||||
* The complete Remote-Profile of the Follower
|
||||
*
|
||||
* @var int
|
||||
* @var array
|
||||
*/
|
||||
private $id;
|
||||
|
||||
/**
|
||||
* The Actor-URL of the Follower
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
private $actor;
|
||||
|
||||
/**
|
||||
* The Object Name
|
||||
*
|
||||
* This is the same as the Actor-URL
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
private $name;
|
||||
|
||||
/**
|
||||
* The Username
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
private $username;
|
||||
|
||||
/**
|
||||
* The Avatar URL
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
private $avatar;
|
||||
|
||||
/**
|
||||
* The URL to the Followers Inbox
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
private $inbox;
|
||||
|
||||
/**
|
||||
* The URL to the Servers Shared-Inbox
|
||||
*
|
||||
* If the Server does not support Shared-Inboxes,
|
||||
* the Inbox will be stored.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
private $shared_inbox;
|
||||
|
||||
/**
|
||||
* The date, the Follower was updated
|
||||
*
|
||||
* @var string untixtimestamp
|
||||
*/
|
||||
private $updated_at;
|
||||
protected $_id; // phpcs:ignore PSR2.Classes.PropertyDeclaration.Underscore
|
||||
|
||||
/**
|
||||
* The complete Remote-Profile of the Follower
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
private $meta;
|
||||
protected $_shared_inbox; // phpcs:ignore PSR2.Classes.PropertyDeclaration.Underscore
|
||||
|
||||
/**
|
||||
* The complete Remote-Profile of the Follower
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $_actor; // phpcs:ignore PSR2.Classes.PropertyDeclaration.Underscore
|
||||
|
||||
/**
|
||||
* The latest received error.
|
||||
|
@ -91,89 +45,14 @@ class Follower {
|
|||
*
|
||||
* @var string
|
||||
*/
|
||||
private $error;
|
||||
protected $_error; // phpcs:ignore PSR2.Classes.PropertyDeclaration.Underscore
|
||||
|
||||
/**
|
||||
* A list of errors
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
private $errors;
|
||||
|
||||
/**
|
||||
* The WordPress User ID, or 0 for whole site.
|
||||
* @var int
|
||||
*/
|
||||
private $user_id;
|
||||
|
||||
/**
|
||||
* Maps the meta fields to the local db fields
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
private $map_meta = array(
|
||||
'name' => 'name',
|
||||
'preferredUsername' => 'username',
|
||||
'inbox' => 'inbox',
|
||||
);
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* @param string|WP_Post $actor The Actor-URL or WP_Post Object.
|
||||
* @param int $user_id The WordPress User ID. 0 Represents the whole site.
|
||||
*/
|
||||
public function __construct( $actor ) {
|
||||
$post = null;
|
||||
|
||||
if ( \is_a( $actor, 'WP_Post' ) ) {
|
||||
$post = $actor;
|
||||
} else {
|
||||
global $wpdb;
|
||||
|
||||
$post_id = $wpdb->get_var(
|
||||
$wpdb->prepare(
|
||||
"SELECT ID FROM $wpdb->posts WHERE guid=%s",
|
||||
esc_sql( $actor )
|
||||
)
|
||||
);
|
||||
|
||||
if ( $post_id ) {
|
||||
$post = get_post( $post_id );
|
||||
} else {
|
||||
$this->actor = $actor;
|
||||
}
|
||||
}
|
||||
|
||||
if ( $post ) {
|
||||
$this->id = $post->ID;
|
||||
$this->actor = $post->guid;
|
||||
$this->updated_at = $post->post_modified;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Magic function to implement getter and setter
|
||||
*
|
||||
* @param string $method The method name.
|
||||
* @param string $params The method params.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function __call( $method, $params ) {
|
||||
$var = \strtolower( \substr( $method, 4 ) );
|
||||
|
||||
if ( \strncasecmp( $method, 'get', 3 ) === 0 ) {
|
||||
if ( empty( $this->$var ) ) {
|
||||
return $this->get( $var );
|
||||
}
|
||||
return $this->$var;
|
||||
}
|
||||
|
||||
if ( \strncasecmp( $method, 'set', 3 ) === 0 ) {
|
||||
$this->$var = $params[0];
|
||||
}
|
||||
}
|
||||
protected $_errors; // phpcs:ignore PSR2.Classes.PropertyDeclaration.Underscore
|
||||
|
||||
/**
|
||||
* Magic function to return the Actor-URL when the Object is used as a string
|
||||
|
@ -181,64 +60,7 @@ class Follower {
|
|||
* @return string
|
||||
*/
|
||||
public function __toString() {
|
||||
return $this->get_actor();
|
||||
}
|
||||
|
||||
/**
|
||||
* Prefill the Object with the meta data.
|
||||
*
|
||||
* @param array $meta The meta data.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function from_meta( $meta ) {
|
||||
$this->meta = $meta;
|
||||
|
||||
foreach ( $this->map_meta as $remote => $internal ) {
|
||||
if ( ! empty( $meta[ $remote ] ) ) {
|
||||
$this->$internal = $meta[ $remote ];
|
||||
}
|
||||
}
|
||||
|
||||
if ( ! empty( $meta['icon']['url'] ) ) {
|
||||
$this->avatar = $meta['icon']['url'];
|
||||
}
|
||||
|
||||
if ( ! empty( $meta['endpoints']['sharedInbox'] ) ) {
|
||||
$this->shared_inbox = $meta['endpoints']['sharedInbox'];
|
||||
} elseif ( ! empty( $meta['inbox'] ) ) {
|
||||
$this->shared_inbox = $meta['inbox'];
|
||||
}
|
||||
|
||||
$this->updated_at = \time();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the data by the given attribute
|
||||
*
|
||||
* @param string $attribute The attribute name.
|
||||
*
|
||||
* @return mixed The attribute value.
|
||||
*/
|
||||
public function get( $attribute ) {
|
||||
if ( ! is_null( $this->$attribute ) ) {
|
||||
return $this->$attribute;
|
||||
}
|
||||
|
||||
$attribute_value = get_post_meta( $this->id, $attribute, true );
|
||||
|
||||
if ( $attribute_value ) {
|
||||
$this->$attribute = $attribute_value;
|
||||
return $attribute_value;
|
||||
}
|
||||
|
||||
$attribute_value = $this->get_meta_by( $attribute );
|
||||
if ( $attribute_value ) {
|
||||
$this->$attribute = $attribute_value;
|
||||
return $attribute_value;
|
||||
}
|
||||
|
||||
return null;
|
||||
return $this->get_url();
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -249,8 +71,8 @@ class Follower {
|
|||
* @return void
|
||||
*/
|
||||
public function set_error( $error ) {
|
||||
$this->errors = array();
|
||||
$this->error = $error;
|
||||
$this->_errors = array();
|
||||
$this->_error = $error;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -259,12 +81,12 @@ class Follower {
|
|||
* @return mixed
|
||||
*/
|
||||
public function get_errors() {
|
||||
if ( $this->errors ) {
|
||||
return $this->errors;
|
||||
if ( $this->_errors ) {
|
||||
return $this->_errors;
|
||||
}
|
||||
|
||||
$this->errors = get_post_meta( $this->id, 'errors' );
|
||||
return $this->errors;
|
||||
$this->_errors = get_post_meta( $this->_id, 'errors' );
|
||||
return $this->_errors;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -273,7 +95,7 @@ class Follower {
|
|||
* @return void
|
||||
*/
|
||||
public function reset_errors() {
|
||||
delete_post_meta( $this->id, 'errors' );
|
||||
delete_post_meta( $this->_id, 'errors' );
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -282,7 +104,7 @@ class Follower {
|
|||
* @return int The number of errors.
|
||||
*/
|
||||
public function count_errors() {
|
||||
$errors = $this->get_errors();
|
||||
$errors = $this->get__errors();
|
||||
|
||||
if ( is_array( $errors ) && ! empty( $errors ) ) {
|
||||
return count( $errors );
|
||||
|
@ -291,13 +113,21 @@ class Follower {
|
|||
return 0;
|
||||
}
|
||||
|
||||
public function get_url() {
|
||||
if ( ! $this->url ) {
|
||||
return $this->id;
|
||||
}
|
||||
|
||||
return $this->url;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the latest error message.
|
||||
*
|
||||
* @return string The error message.
|
||||
*/
|
||||
public function get_latest_error_message() {
|
||||
$errors = $this->get_errors();
|
||||
$errors = $this->get__errors();
|
||||
|
||||
if ( is_array( $errors ) && ! empty( $errors ) ) {
|
||||
return reset( $errors );
|
||||
|
@ -306,48 +136,12 @@ class Follower {
|
|||
return '';
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the meta data by the given attribute.
|
||||
*
|
||||
* @param string $attribute The attribute name.
|
||||
*
|
||||
* @return mixed $attribute The attribute value.
|
||||
*/
|
||||
public function get_meta_by( $attribute ) {
|
||||
$meta = $this->get_meta();
|
||||
if ( ! is_array( $meta ) ) {
|
||||
return null;
|
||||
}
|
||||
// try mapped data (see $this->map_meta)
|
||||
foreach ( $this->map_meta as $remote => $local ) {
|
||||
if ( $attribute === $local && isset( $meta[ $remote ] ) ) {
|
||||
return $meta[ $remote ];
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the meta data.
|
||||
*
|
||||
* @return array $meta The meta data.
|
||||
*/
|
||||
public function get_meta() {
|
||||
if ( $this->meta ) {
|
||||
return $this->meta;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the current Follower-Object.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function update() {
|
||||
$this->updated_at = \time();
|
||||
$this->save();
|
||||
}
|
||||
|
||||
|
@ -358,18 +152,18 @@ class Follower {
|
|||
*/
|
||||
public function save() {
|
||||
$args = array(
|
||||
'ID' => $this->id,
|
||||
'guid' => $this->actor,
|
||||
'ID' => $this->get__id(),
|
||||
'guid' => $this->get_id(),
|
||||
'post_title' => $this->get_name(),
|
||||
'post_author' => 0,
|
||||
'post_type' => Followers::POST_TYPE,
|
||||
'post_content' => wp_json_encode( $this->meta ),
|
||||
'post_content' => $this->get_summary(),
|
||||
'post_status' => 'publish',
|
||||
'post_modified' => gmdate( 'Y-m-d H:i:s', $this->updated_at ),
|
||||
'meta_input' => $this->get_post_meta_input(),
|
||||
);
|
||||
|
||||
$post_id = wp_insert_post( $args );
|
||||
$this->id = $post_id;
|
||||
$this->_id = $post_id;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -378,7 +172,7 @@ class Follower {
|
|||
* @return void
|
||||
*/
|
||||
public function upsert() {
|
||||
if ( $this->id ) {
|
||||
if ( $this->_id ) {
|
||||
$this->update();
|
||||
} else {
|
||||
$this->save();
|
||||
|
@ -391,7 +185,7 @@ class Follower {
|
|||
* @return void
|
||||
*/
|
||||
public function delete() {
|
||||
wp_delete_post( $this->id );
|
||||
wp_delete_post( $this->_id );
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -400,7 +194,7 @@ class Follower {
|
|||
* @return void
|
||||
*/
|
||||
protected function get_post_meta_input() {
|
||||
$attributes = array( 'inbox', 'shared_inbox', 'avatar', 'name', 'username' );
|
||||
$attributes = array( 'inbox', '_shared_inbox', 'icon', 'preferred_username', '_actor', 'url' );
|
||||
|
||||
$meta_input = array();
|
||||
|
||||
|
@ -410,18 +204,94 @@ class Follower {
|
|||
}
|
||||
}
|
||||
|
||||
if ( $this->error ) {
|
||||
if ( is_string( $this->error ) ) {
|
||||
$error = $this->error;
|
||||
} elseif ( is_wp_error( $this->error ) ) {
|
||||
$error = $this->error->get_error_message();
|
||||
if ( $this->_error ) {
|
||||
if ( is_string( $this->_error ) ) {
|
||||
$_error = $this->_error;
|
||||
} elseif ( is_wp_error( $this->_error ) ) {
|
||||
$_error = $this->_error->get_error_message();
|
||||
} else {
|
||||
$error = __( 'Unknown Error or misconfigured Error-Message', 'activitypub' );
|
||||
$_error = __( 'Unknown Error or misconfigured Error-Message', 'activitypub' );
|
||||
}
|
||||
|
||||
$meta_input['errors'] = array( $error );
|
||||
$meta_input['_errors'] = $_error;
|
||||
}
|
||||
|
||||
return $meta_input;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the Icon URL (Avatar)
|
||||
*
|
||||
* @return string The URL to the Avatar.
|
||||
*/
|
||||
public function get_icon_url() {
|
||||
$icon = $this->get_icon();
|
||||
|
||||
if ( ! $icon ) {
|
||||
return '';
|
||||
}
|
||||
|
||||
if ( is_array( $icon ) ) {
|
||||
return $icon['url'];
|
||||
}
|
||||
|
||||
return $icon;
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts an ActivityPub Array to an Follower Object.
|
||||
*
|
||||
* @param array $array The ActivityPub Array.
|
||||
*
|
||||
* @return Activitypub\Model\Follower The Follower Object.
|
||||
*/
|
||||
public static function from_array( $array ) {
|
||||
$object = parent::from_array( $array );
|
||||
$object->set__actor( $array );
|
||||
|
||||
global $wpdb;
|
||||
|
||||
$post_id = $wpdb->get_var(
|
||||
$wpdb->prepare(
|
||||
"SELECT ID FROM $wpdb->posts WHERE guid=%s",
|
||||
esc_sql( $object->get_id() )
|
||||
)
|
||||
);
|
||||
|
||||
if ( $post_id ) {
|
||||
$post = get_post( $post_id );
|
||||
$object->set__id( $post->ID );
|
||||
}
|
||||
|
||||
if ( ! empty( $object->get_endpoints()['sharedInbox'] ) ) {
|
||||
$object->_shared_inbox = $object->get_endpoints()['sharedInbox'];
|
||||
} elseif ( ! empty( $object->get_inbox() ) ) {
|
||||
$object->_shared_inbox = $object->get_inbox();
|
||||
}
|
||||
|
||||
return $object;
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert a Custom-Post-Type input to an Activitypub\Model\Follower.
|
||||
*
|
||||
* @return string The JSON string.
|
||||
*
|
||||
* @return array Activitypub\Model\Follower
|
||||
*/
|
||||
public static function from_custom_post_type( $post ) {
|
||||
$object = new static();
|
||||
|
||||
$object->set__id( $post->ID );
|
||||
$object->set_id( $post->guid );
|
||||
$object->set_name( $post->post_title );
|
||||
$object->set_summary( $post->post_content );
|
||||
$object->set_url( get_post_meta( $post->ID, 'url', true ) );
|
||||
$object->set_icon( get_post_meta( $post->ID, 'icon', true ) );
|
||||
$object->set_preferred_username( get_post_meta( $post->ID, 'preferred_username', true ) );
|
||||
$object->set_published( gmdate( 'Y-m-d H:i:s', strtotime( $post->post_published ) ) );
|
||||
$object->set_updated( gmdate( 'Y-m-d H:i:s', strtotime( $post->post_modified ) ) );
|
||||
|
||||
return $object;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -86,7 +86,7 @@ class Followers {
|
|||
// phpcs:ignore
|
||||
$json->orderedItems = array_map(
|
||||
function( $item ) {
|
||||
return $item->get_actor();
|
||||
return $item->get_url();
|
||||
},
|
||||
FollowerCollection::get_followers( $user_id )
|
||||
);
|
||||
|
|
|
@ -16,9 +16,9 @@ class Followers extends WP_List_Table {
|
|||
'name' => \__( 'Name', 'activitypub' ),
|
||||
'username' => \__( 'Username', 'activitypub' ),
|
||||
'identifier' => \__( 'Identifier', 'activitypub' ),
|
||||
'updated_at' => \__( 'Last updated', 'activitypub' ),
|
||||
'errors' => \__( 'Errors', 'activitypub' ),
|
||||
'latest-error' => \__( 'Latest Error Message', 'activitypub' ),
|
||||
'updated' => \__( 'Last updated', 'activitypub' ),
|
||||
//'errors' => \__( 'Errors', 'activitypub' ),
|
||||
//'latest-error' => \__( 'Latest Error Message', 'activitypub' ),
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -50,11 +50,11 @@ class Followers extends WP_List_Table {
|
|||
|
||||
foreach ( $followers as $follower ) {
|
||||
$item = array(
|
||||
'avatar' => esc_attr( $follower->get_avatar() ),
|
||||
'icon' => esc_attr( $follower->get_icon_url() ),
|
||||
'name' => esc_attr( $follower->get_name() ),
|
||||
'username' => esc_attr( $follower->get_username() ),
|
||||
'identifier' => esc_attr( $follower->get_actor() ),
|
||||
'updated_at' => esc_attr( $follower->get_updated_at() ),
|
||||
'username' => esc_attr( $follower->get_preferred_username() ),
|
||||
'identifier' => esc_attr( $follower->get_url() ),
|
||||
'updated' => esc_attr( $follower->get_updated() ),
|
||||
'errors' => $follower->count_errors(),
|
||||
'latest-error' => $follower->get_latest_error_message(),
|
||||
);
|
||||
|
@ -79,7 +79,7 @@ class Followers extends WP_List_Table {
|
|||
public function column_avatar( $item ) {
|
||||
return sprintf(
|
||||
'<img src="%s" width="25px;" />',
|
||||
$item['avatar']
|
||||
$item['icon']
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
@ -1,16 +1,16 @@
|
|||
<div class="wrap">
|
||||
<h1><?php \esc_html_e( 'Followers', 'activitypub' ); ?></h1>
|
||||
|
||||
<?php Activitypub\Migration::maybe_migrate(); ?>
|
||||
<?php // translators: ?>
|
||||
<p><?php \printf( \esc_html__( 'You currently have %s followers.', 'activitypub' ), \esc_attr( \Activitypub\Collection\Followers::count_followers( \get_current_user_id() ) ) ); ?></p>
|
||||
|
||||
<?php $token_table = new \Activitypub\Table\Followers(); ?>
|
||||
<?php $table = new \Activitypub\Table\Followers(); ?>
|
||||
|
||||
<form method="get">
|
||||
<input type="hidden" name="page" value="activitypub-followers-list" />
|
||||
<?php
|
||||
$token_table->prepare_items();
|
||||
$token_table->display();
|
||||
$table->prepare_items();
|
||||
$table->display();
|
||||
?>
|
||||
<?php wp_nonce_field( 'activitypub-followers-list', '_apnonce' ); ?>
|
||||
</form>
|
||||
|
|
|
@ -2,12 +2,14 @@
|
|||
class Test_Activitypub_Activity_Dispatcher extends ActivityPub_TestCase_Cache_HTTP {
|
||||
public static $users = array(
|
||||
'username@example.org' => array(
|
||||
'id' => 'https://example.org/users/username',
|
||||
'url' => 'https://example.org/users/username',
|
||||
'inbox' => 'https://example.org/users/username/inbox',
|
||||
'name' => 'username',
|
||||
'prefferedUsername' => 'username',
|
||||
),
|
||||
'jon@example.com' => array(
|
||||
'id' => 'https://example.com/author/jon',
|
||||
'url' => 'https://example.com/author/jon',
|
||||
'inbox' => 'https://example.com/author/jon/inbox',
|
||||
'name' => 'jon',
|
||||
|
@ -56,6 +58,7 @@ class Test_Activitypub_Activity_Dispatcher extends ActivityPub_TestCase_Cache_HT
|
|||
);
|
||||
|
||||
self::$users['https://example.com/alex'] = array(
|
||||
'id' => 'https://example.com/alex',
|
||||
'url' => 'https://example.com/alex',
|
||||
'inbox' => 'https://example.com/alex/inbox',
|
||||
'name' => 'alex',
|
||||
|
|
|
@ -28,4 +28,17 @@ class Test_Activitypub_Activity extends WP_UnitTestCase {
|
|||
remove_all_filters( 'activitypub_extract_mentions' );
|
||||
\wp_trash_post( $post );
|
||||
}
|
||||
|
||||
public function test_object_transformation() {
|
||||
$test_array = array(
|
||||
'id' => 'https://example.com/post/123',
|
||||
'type' => 'Note',
|
||||
'content' => 'Hello world!',
|
||||
);
|
||||
|
||||
$object = \Activitypub\Activity\Base_Object::from_array( $test_array );
|
||||
|
||||
$this->assertEquals( 'Hello world!', $object->get_content() );
|
||||
$this->assertEquals( $test_array, $object->to_array() );
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
class Test_Activitypub_Mention extends ActivityPub_TestCase_Cache_HTTP {
|
||||
public static $users = array(
|
||||
'username@example.org' => array(
|
||||
'id' => 'https://example.org/users/username',
|
||||
'url' => 'https://example.org/users/username',
|
||||
'name' => 'username',
|
||||
),
|
||||
|
|
|
@ -2,36 +2,42 @@
|
|||
class Test_Db_Activitypub_Followers extends WP_UnitTestCase {
|
||||
public static $users = array(
|
||||
'username@example.org' => array(
|
||||
'id' => 'https://example.org/users/username',
|
||||
'url' => 'https://example.org/users/username',
|
||||
'inbox' => 'https://example.org/users/username/inbox',
|
||||
'name' => 'username',
|
||||
'prefferedUsername' => 'username',
|
||||
),
|
||||
'jon@example.com' => array(
|
||||
'id' => 'https://example.com/author/jon',
|
||||
'url' => 'https://example.com/author/jon',
|
||||
'inbox' => 'https://example.com/author/jon/inbox',
|
||||
'name' => 'jon',
|
||||
'prefferedUsername' => 'jon',
|
||||
),
|
||||
'doe@example.org' => array(
|
||||
'id' => 'https://example.org/author/doe',
|
||||
'url' => 'https://example.org/author/doe',
|
||||
'inbox' => 'https://example.org/author/doe/inbox',
|
||||
'name' => 'doe',
|
||||
'prefferedUsername' => 'doe',
|
||||
),
|
||||
'sally@example.org' => array(
|
||||
'id' => 'http://sally.example.org',
|
||||
'url' => 'http://sally.example.org',
|
||||
'inbox' => 'http://sally.example.org/inbox',
|
||||
'name' => 'jon',
|
||||
'prefferedUsername' => 'jon',
|
||||
),
|
||||
'12345@example.com' => array(
|
||||
'id' => 'https://12345.example.com',
|
||||
'url' => 'https://12345.example.com',
|
||||
'inbox' => 'https://12345.example.com/inbox',
|
||||
'name' => '12345',
|
||||
'prefferedUsername' => '12345',
|
||||
),
|
||||
'user2@example.com' => array(
|
||||
'id' => 'https://user2.example.com',
|
||||
'url' => 'https://user2.example.com',
|
||||
'inbox' => 'https://user2.example.com/inbox',
|
||||
'name' => 'user2',
|
||||
|
@ -66,7 +72,7 @@ class Test_Db_Activitypub_Followers extends WP_UnitTestCase {
|
|||
|
||||
$db_followers = array_map(
|
||||
function( $item ) {
|
||||
return $item->get_actor();
|
||||
return $item->get_url();
|
||||
},
|
||||
$db_followers
|
||||
);
|
||||
|
@ -107,7 +113,7 @@ class Test_Db_Activitypub_Followers extends WP_UnitTestCase {
|
|||
}
|
||||
|
||||
$follower = \Activitypub\Collection\Followers::get_follower( 1, 'https://example.com/author/jon' );
|
||||
$this->assertEquals( 'https://example.com/author/jon', $follower->get_actor() );
|
||||
$this->assertEquals( 'https://example.com/author/jon', $follower->get_url() );
|
||||
|
||||
$follower = \Activitypub\Collection\Followers::get_follower( 1, 'http://sally.example.org' );
|
||||
$this->assertNull( $follower );
|
||||
|
@ -116,10 +122,10 @@ class Test_Db_Activitypub_Followers extends WP_UnitTestCase {
|
|||
$this->assertNull( $follower );
|
||||
|
||||
$follower = \Activitypub\Collection\Followers::get_follower( 1, 'https://example.com/author/jon' );
|
||||
$this->assertEquals( 'https://example.com/author/jon', $follower->get_actor() );
|
||||
$this->assertEquals( 'https://example.com/author/jon', $follower->get_url() );
|
||||
|
||||
$follower2 = \Activitypub\Collection\Followers::get_follower( 2, 'https://user2.example.com' );
|
||||
$this->assertEquals( 'https://user2.example.com', $follower2->get_actor() );
|
||||
$this->assertEquals( 'https://user2.example.com', $follower2->get_url() );
|
||||
}
|
||||
|
||||
public function test_delete_follower() {
|
||||
|
@ -144,13 +150,13 @@ class Test_Db_Activitypub_Followers extends WP_UnitTestCase {
|
|||
}
|
||||
|
||||
$follower = \Activitypub\Collection\Followers::get_follower( 1, 'https://example.com/author/jon' );
|
||||
$this->assertEquals( 'https://example.com/author/jon', $follower->get_actor() );
|
||||
$this->assertEquals( 'https://example.com/author/jon', $follower->get_url() );
|
||||
|
||||
$followers = \Activitypub\Collection\Followers::get_followers( 1 );
|
||||
$this->assertEquals( 2, count( $followers ) );
|
||||
|
||||
$follower2 = \Activitypub\Collection\Followers::get_follower( 2, 'https://example.com/author/jon' );
|
||||
$this->assertEquals( 'https://example.com/author/jon', $follower2->get_actor() );
|
||||
$this->assertEquals( 'https://example.com/author/jon', $follower2->get_url() );
|
||||
|
||||
\Activitypub\Collection\Followers::remove_follower( 1, 'https://example.com/author/jon' );
|
||||
|
||||
|
@ -158,7 +164,7 @@ class Test_Db_Activitypub_Followers extends WP_UnitTestCase {
|
|||
$this->assertNull( $follower );
|
||||
|
||||
$follower2 = \Activitypub\Collection\Followers::get_follower( 2, 'https://example.com/author/jon' );
|
||||
$this->assertEquals( 'https://example.com/author/jon', $follower2->get_actor() );
|
||||
$this->assertEquals( 'https://example.com/author/jon', $follower2->get_url() );
|
||||
|
||||
$followers = \Activitypub\Collection\Followers::get_followers( 1 );
|
||||
$this->assertEquals( 1, count( $followers ) );
|
||||
|
@ -174,7 +180,7 @@ class Test_Db_Activitypub_Followers extends WP_UnitTestCase {
|
|||
\Activitypub\Collection\Followers::add_follower( 1, $follower );
|
||||
}
|
||||
|
||||
$follower = new \Activitypub\Model\Follower( 'https://example.com/author/jon' );
|
||||
$follower = \Activitypub\Collection\Followers::get_follower( 1, 'https://example.com/author/jon' );
|
||||
|
||||
global $wpdb;
|
||||
|
||||
|
@ -184,7 +190,7 @@ class Test_Db_Activitypub_Followers extends WP_UnitTestCase {
|
|||
|
||||
$post_modified = gmdate( $mysql_time_format, $time );
|
||||
$post_modified_gmt = gmdate( $mysql_time_format, ( $time + get_option( 'gmt_offset' ) * HOUR_IN_SECONDS ) );
|
||||
$post_id = $follower->get_id();
|
||||
$post_id = $follower->get__id();
|
||||
|
||||
$wpdb->query(
|
||||
$wpdb->prepare(
|
||||
|
@ -214,13 +220,13 @@ class Test_Db_Activitypub_Followers extends WP_UnitTestCase {
|
|||
\Activitypub\Collection\Followers::add_follower( 1, $follower );
|
||||
}
|
||||
|
||||
$follower = new \Activitypub\Model\Follower( 'http://sally.example.org' );
|
||||
$follower = \Activitypub\Collection\Followers::get_follower( 1, 'http://sally.example.org' );
|
||||
|
||||
for ( $i = 1; $i <= 15; $i++ ) {
|
||||
add_post_meta( $follower->get_id(), 'errors', 'error ' . $i );
|
||||
add_post_meta( $follower->get__id(), 'errors', 'error ' . $i );
|
||||
}
|
||||
|
||||
$follower = new \Activitypub\Model\Follower( 'http://sally.example.org' );
|
||||
$follower = \Activitypub\Collection\Followers::get_follower( 1, 'http://sally.example.org' );
|
||||
$count = $follower->count_errors();
|
||||
|
||||
$followers = \Activitypub\Collection\Followers::get_faulty_followers();
|
||||
|
@ -230,7 +236,7 @@ class Test_Db_Activitypub_Followers extends WP_UnitTestCase {
|
|||
|
||||
$follower->reset_errors();
|
||||
|
||||
$follower = new \Activitypub\Model\Follower( 'http://sally.example.org' );
|
||||
$follower = \Activitypub\Collection\Followers::get_follower( 1, 'http://sally.example.org' );
|
||||
$count = $follower->count_errors();
|
||||
|
||||
$followers = \Activitypub\Collection\Followers::get_faulty_followers();
|
||||
|
|
Loading…
Reference in a new issue