2023-06-23 14:54:29 +02:00
< ? php
/**
* Inspired by the PHP ActivityPub Library by @ Landrok
*
* @ link https :// github . com / landrok / activitypub
*/
namespace Activitypub\Activity ;
use WP_Error ;
2023-07-26 22:05:41 +02:00
use ReflectionClass ;
2023-06-23 14:54:29 +02:00
use function Activitypub\camel_to_snake_case ;
2023-06-26 11:08:04 +02:00
use function Activitypub\snake_to_camel_case ;
2023-06-29 14:54:45 +02:00
2023-06-23 14:54:29 +02:00
/**
2023-06-29 14:54:45 +02:00
* Base_Object is an implementation of one of the
2023-06-23 14:54:29 +02:00
* 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
2023-06-29 14:54:45 +02:00
* 'Base_' for this reason .
2023-06-23 14:54:29 +02:00
*
* @ see https :// www . w3 . org / TR / activitystreams - core / #object
*/
2023-06-26 11:08:04 +02:00
class Base_Object {
2023-12-11 19:44:44 +01:00
/**
* if this is set , the next setter call doesn ' t change anything
*
* @ var bool
*/
protected $_skip_next ;
2023-12-11 23:21:02 +01:00
/**
* if this is set , the language maps will be set automatically with this language if not set yet todo nicer description
*
* @ var string
*/
protected $_default_language_code_for_language_maps ;
2023-06-23 14:54:29 +02:00
/**
* 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 ) {
2023-07-26 22:05:41 +02:00
if ( ! $this -> has ( $var ) ) {
2023-09-21 16:26:17 +02:00
return new WP_Error ( 'invalid_key' , __ ( 'Invalid key' , 'activitypub' ), array ( 'status' => 404 ) );
2023-06-23 14:54:29 +02:00
}
2023-07-26 22:05:41 +02:00
2023-06-23 14:54:29 +02:00
return $this -> $var ;
}
if ( \strncasecmp ( $method , 'set' , 3 ) === 0 ) {
2023-11-29 23:46:13 +01:00
return $this -> set ( $var , $params [ 0 ] );
2023-06-23 14:54:29 +02:00
}
if ( \strncasecmp ( $method , 'add' , 3 ) === 0 ) {
2023-11-29 23:46:13 +01:00
return $this -> add ( $var , $params [ 0 ] );
2023-06-23 14:54:29 +02:00
}
}
2023-06-28 19:38:19 +02:00
/**
* Magic function , to transform the object to string .
*
* @ return string The object id .
*/
public function __toString () {
return $this -> to_string ();
}
/**
* Function to transform the object to string .
*
* @ return string The object id .
*/
public function to_string () {
return $this -> get_id ();
}
2023-06-23 14:54:29 +02:00
/**
* Generic getter .
*
* @ param string $key The key to get .
*
* @ return mixed The value .
*/
public function get ( $key ) {
if ( ! $this -> has ( $key ) ) {
2023-09-21 16:26:17 +02:00
return new WP_Error ( 'invalid_key' , __ ( 'Invalid key' , 'activitypub' ), array ( 'status' => 404 ) );
2023-06-23 14:54:29 +02:00
}
2023-07-26 22:05:41 +02:00
return call_user_func ( array ( $this , 'get_' . $key ) );
2023-06-23 14:54:29 +02:00
}
/**
* 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 ) ) {
2023-09-21 16:26:17 +02:00
return new WP_Error ( 'invalid_key' , __ ( 'Invalid key' , 'activitypub' ), array ( 'status' => 404 ) );
2023-06-23 14:54:29 +02:00
}
2023-12-11 19:44:44 +01:00
if ( $this -> _skip_next ) {
$this -> _skip_next = false ;
2023-12-09 02:01:13 +01:00
return $this ;
}
2023-12-11 23:21:02 +01:00
if ( 'content' === $key || 'summary' === $key || 'name' === $key ) { // todo nicer?
$this -> set_basic_language_map ( $key , $value ); // todo skip setter if language map is already set
}
2023-06-23 14:54:29 +02:00
$this -> $key = $value ;
2023-11-29 23:46:13 +01:00
return $this ;
2023-06-23 14:54:29 +02:00
}
2023-12-11 23:21:02 +01:00
/**
* Setter for the languageMap default language
*
* @ param string $languagecode languageocde for default
*
*/
public function set_default_language_for_maps ( $languagecode ) {
$this -> _default_language_code_for_language_maps = $languagecode ;
return $this ;
}
/**
* Generic languageMap setter .
*
* @ param string $key The key for the language map .
* @ param string $value The value to set .
*
*/
public function set_basic_language_map ( $key , $value ) {
// todo better name, implement usage for content, summary and name
$mapkey = $key . '_map' ;
if ( ! isset ( $this -> _default_language_code_for_language_maps ) ) {
return new WP_Error ( '_default_language_code_for_language_maps must be set before calling set_languageMap' , __ ( '_default_language_code_for_language_maps must be set before calling set_languageMap' , 'activitypub' ), array ( 'status' => 404 ) );
}
if ( ! $this -> has ( $mapkey ) ) {
return new WP_Error ( 'invalid_key' , __ ( 'Invalid key' , 'activitypub' ), array ( 'status' => 404 ) );
}
$this -> $mapkey = array (
$this -> _default_language_code_for_language_maps => $value ,
);
return $this ;
}
2023-06-23 14:54:29 +02:00
/**
* 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 ) ) {
2023-09-21 16:26:17 +02:00
return new WP_Error ( 'invalid_key' , __ ( 'Invalid key' , 'activitypub' ), array ( 'status' => 404 ) );
2023-06-23 14:54:29 +02:00
}
if ( ! isset ( $this -> $key ) ) {
$this -> $key = array ();
}
2023-06-23 14:57:46 +02:00
$attributes = $this -> $key ;
$attributes [] = $value ;
$this -> $key = $attributes ;
2023-06-23 14:54:29 +02:00
2023-11-29 23:46:13 +01:00
return $this ;
2023-06-23 14:54:29 +02:00
}
/**
* Convert JSON input to an array .
*
* @ return string The JSON string .
*
2023-07-06 14:42:18 +02:00
* @ return \Activitypub\Activity\Base_Object An Object built from the JSON string .
2023-06-23 14:54:29 +02:00
*/
2023-07-06 14:42:18 +02:00
public static function init_from_json ( $json ) {
$array = \json_decode ( $json , true );
2023-06-23 14:54:29 +02:00
2023-09-21 16:26:17 +02:00
if ( ! is_array ( $array ) ) {
$array = array ();
}
2023-07-06 14:42:18 +02:00
return self :: init_from_array ( $array );
2023-06-23 14:54:29 +02:00
}
/**
* Convert JSON input to an array .
*
* @ return string The object array .
*
2023-07-06 14:42:18 +02:00
* @ return \Activitypub\Activity\Base_Object An Object built from the JSON string .
2023-06-23 14:54:29 +02:00
*/
2023-07-06 14:42:18 +02:00
public static function init_from_array ( $array ) {
2023-09-21 16:26:17 +02:00
if ( ! is_array ( $array ) ) {
return new WP_Error ( 'invalid_array' , __ ( 'Invalid array' , 'activitypub' ), array ( 'status' => 404 ) );
}
2023-06-23 14:54:29 +02:00
$object = new static ();
foreach ( $array as $key => $value ) {
$key = camel_to_snake_case ( $key );
$object -> set ( $key , $value );
}
return $object ;
}
2023-06-26 11:08:04 +02:00
2023-07-06 14:42:18 +02:00
/**
* Convert JSON input to an array and pre - fill the object .
*
* @ param string $json The JSON string .
*/
public function from_json ( $json ) {
$array = \json_decode ( $json , true );
$this -> from_array ( $array );
}
/**
* Convert JSON input to an array and pre - fill the object .
*
* @ param array $array The array .
*/
public function from_array ( $array ) {
foreach ( $array as $key => $value ) {
2023-07-28 10:34:10 +02:00
if ( $value ) {
$key = camel_to_snake_case ( $key );
$this -> set ( $key , $value );
}
2023-07-06 14:42:18 +02:00
}
}
2023-06-26 11:08:04 +02:00
/**
* 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 ) {
2023-06-28 19:38:19 +02:00
// ignotre all _prefixed keys.
if ( '_' === substr ( $key , 0 , 1 ) ) {
continue ;
}
2023-06-26 11:08:04 +02:00
// if value is empty, try to get it from a getter.
2023-07-31 20:15:11 +02:00
if ( ! $value ) {
2023-06-26 11:08:04 +02:00
$value = call_user_func ( array ( $this , 'get_' . $key ) );
}
2023-11-29 23:46:13 +01:00
if ( is_object ( $value ) && is_subclass_of ( $value , self :: class ) ) {
2023-07-03 17:59:42 +02:00
$value = $value -> to_array ();
}
2023-06-26 11:08:04 +02:00
// if value is still empty, ignore it for the array and continue.
2023-09-22 09:38:59 +02:00
if ( isset ( $value ) ) {
2023-06-26 11:08:04 +02:00
$array [ snake_to_camel_case ( $key ) ] = $value ;
}
}
2023-06-28 16:42:20 +02:00
// replace 'context' key with '@context' and move it to the top.
if ( array_key_exists ( 'context' , $array ) ) {
$context = $array [ 'context' ];
unset ( $array [ 'context' ] );
$array = array_merge ( array ( '@context' => $context ), $array );
}
2023-07-26 22:05:41 +02:00
$class = new ReflectionClass ( $this );
2023-06-28 14:22:27 +02:00
$class = strtolower ( $class -> getShortName () );
$array = \apply_filters ( 'activitypub_activity_object_array' , $array , $class , $this -> id , $this );
$array = \apply_filters ( " activitypub_activity_ { $class } _object_array " , $array , $this -> id , $this );
2023-06-26 11:08:04 +02:00
return $array ;
}
2023-07-03 17:59:42 +02:00
/**
* Convert Object to JSON .
*
* @ return string The JSON string .
*/
public function to_json () {
$array = $this -> to_array ();
2023-07-07 13:43:12 +02:00
return \wp_json_encode ( $array , \JSON_HEX_TAG | \JSON_HEX_AMP | \JSON_HEX_QUOT );
2023-07-03 17:59:42 +02:00
}
2023-12-09 02:01:13 +01:00
2023-12-11 23:21:20 +01:00
/**
* skips next setter if ( false );
*
* @ param bool $b
*
*/
public function if ( $b ) {
2023-12-11 19:44:44 +01:00
$this -> _skip_next = ! $b ;
2023-12-09 02:01:13 +01:00
return $this ;
}
2023-12-11 23:22:19 +01:00
public function check_jsonld_completeness () {
// Just a debugging helper function
$vars = get_object_vars ( $this );
$total_var_count = count ( $vars );
2023-12-09 02:01:13 +01:00
2023-12-11 23:22:19 +01:00
$unset_var_array = array ();
foreach ( $vars as $key => $value ) {
// ignore all _prefixed keys.
if ( '_' === substr ( $key , 0 , 1 ) ) {
-- $total_var_count ;
continue ;
}
// 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 ( ! isset ( $value ) ) {
$unset_var_array [] = $key ;
}
}
$unset_var_count = count ( $unset_var_array );
return array (
'total_var_count' => $total_var_count ,
'unset_var_count' => $unset_var_count ,
'set_var_count' => $total_var_count - $unset_var_count ,
'unset_vars' => $unset_var_array ,
);
}
2023-06-23 14:54:29 +02:00
}