2018-05-30 04:08:52 +00:00
< ? php
namespace Zotlabs\Lib ;
2019-01-14 05:22:45 +00:00
use Zotlabs\Web\HTTPSig ;
2018-09-07 00:36:35 +00:00
use Zotlabs\Access\Permissions ;
use Zotlabs\Access\PermissionRoles ;
2019-04-02 02:17:28 +00:00
use Zotlabs\Access\PermissionLimits ;
2018-08-23 06:04:37 +00:00
use Zotlabs\Daemon\Master ;
2018-05-30 04:08:52 +00:00
class Activity {
2018-09-19 23:25:05 +00:00
static $ACTOR_CACHE_DAYS = 3 ;
2018-05-30 04:08:52 +00:00
static function encode_object ( $x ) {
2019-05-13 06:57:57 +00:00
if (( $x ) && ( ! is_array ( $x )) && ( substr ( trim ( $x ), 0 , 1 )) === '{' ) {
2018-05-30 04:08:52 +00:00
$x = json_decode ( $x , true );
}
2019-05-13 06:57:57 +00:00
if ( $x [ 'type' ] === ACTIVITY_OBJ_PERSON ) {
2018-05-30 04:08:52 +00:00
return self :: fetch_person ( $x );
}
2019-05-13 06:57:57 +00:00
if ( $x [ 'type' ] === ACTIVITY_OBJ_PROFILE ) {
2018-05-30 04:08:52 +00:00
return self :: fetch_profile ( $x );
}
2019-05-13 06:57:57 +00:00
if ( in_array ( $x [ 'type' ], [ ACTIVITY_OBJ_NOTE , ACTIVITY_OBJ_ARTICLE ] )) {
2019-09-10 04:03:33 +00:00
// Use Mastodon-specific note and media hacks if nomadic. Else HTML.
// Eventually this needs to be passed in much further up the stack
// and base the decision on whether or not we are encoding for ActivityPub or Zot6
return self :: fetch_item ( $x ,(( get_config ( 'system' , 'activitypub' )) ? true : false ));
2018-05-30 04:08:52 +00:00
}
2019-05-13 06:57:57 +00:00
if ( $x [ 'type' ] === ACTIVITY_OBJ_THING ) {
2018-05-30 04:08:52 +00:00
return self :: fetch_thing ( $x );
}
2018-07-17 02:08:10 +00:00
return $x ;
2018-05-30 04:08:52 +00:00
}
2019-01-14 05:22:45 +00:00
2019-02-19 05:07:48 +00:00
static function fetch ( $url , $channel = null , $hub = null ) {
2019-01-14 05:22:45 +00:00
$redirects = 0 ;
2019-05-13 06:57:57 +00:00
if ( ! check_siteallowed ( $url )) {
2019-01-14 05:22:45 +00:00
logger ( 'blacklisted: ' . $url );
return null ;
}
2019-05-13 06:57:57 +00:00
if ( ! $channel ) {
2019-01-14 05:22:45 +00:00
$channel = get_sys_channel ();
}
2019-02-18 22:40:46 +00:00
logger ( 'fetch: ' . $url , LOGGER_DEBUG );
2019-05-13 06:57:57 +00:00
if ( strpos ( $url , 'x-zot:' ) === 0 ) {
2019-02-19 05:07:48 +00:00
$x = ZotURL :: fetch ( $url , $channel , $hub );
2019-02-18 22:40:46 +00:00
}
else {
2019-01-14 05:22:45 +00:00
$m = parse_url ( $url );
2019-08-16 06:53:02 +00:00
// handle bearcaps
if ( $m [ 'scheme' ] === 'bear' && $m [ 'query' ]) {
$params = explode ( '&' , $m [ 'query' ]);
if ( $params ) {
foreach ( $params as $p ) {
if ( substr ( $p , 0 , 2 ) === 'u=' ) {
$url = substr ( $p , 2 );
}
if ( substr ( $p , 0 , 2 ) === 't=' ) {
$token = substr ( $p , 2 );
}
}
}
}
2019-01-14 05:22:45 +00:00
$headers = [
'Accept' => 'application/activity+json, application/ld+json; profile="https://www.w3.org/ns/activitystreams"' ,
'Host' => $m [ 'host' ],
'(request-target)' => 'get ' . get_request_string ( $url ),
'Date' => datetime_convert ( 'UTC' , 'UTC' , 'now' , 'D, d M Y H:i:s' ) . ' UTC'
];
2019-08-16 06:53:02 +00:00
if ( isset ( $token )) {
$headers [ 'Authorization' ] = 'Bearer ' . $token ;
}
2019-01-14 05:22:45 +00:00
$h = HTTPSig :: create_sig ( $headers , $channel [ 'channel_prvkey' ], channel_url ( $channel ), false );
2019-02-18 22:40:46 +00:00
$x = z_fetch_url ( $url , true , $redirects , [ 'headers' => $h ] );
2019-01-14 05:22:45 +00:00
}
2019-05-13 06:57:57 +00:00
if ( $x [ 'success' ]) {
2019-01-14 05:22:45 +00:00
$y = json_decode ( $x [ 'body' ], true );
logger ( 'returned: ' . json_encode ( $y , JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES ));
return json_decode ( $x [ 'body' ], true );
}
else {
logger ( 'fetch failed: ' . $url );
}
return null ;
}
2018-05-30 04:08:52 +00:00
static function fetch_person ( $x ) {
return self :: fetch_profile ( $x );
}
static function fetch_profile ( $x ) {
2019-06-17 03:35:47 +00:00
$r = q ( " select * from xchan left join hubloc on xchan_hash = hubloc_hash where hubloc_id_url = '%s' limit 1 " ,
dbesc ( $x [ 'id' ])
2018-05-30 04:08:52 +00:00
);
2019-05-13 06:57:57 +00:00
if ( ! $r ) {
2018-05-30 04:08:52 +00:00
$r = q ( " select * from xchan where xchan_hash = '%s' limit 1 " ,
dbesc ( $x [ 'id' ])
);
2019-06-17 03:35:47 +00:00
}
2019-05-13 06:57:57 +00:00
if ( ! $r ) {
2018-05-30 04:08:52 +00:00
return [];
2019-05-13 06:57:57 +00:00
}
2019-06-17 03:35:47 +00:00
2018-09-18 05:51:27 +00:00
return self :: encode_person ( $r [ 0 ], false );
2018-05-30 04:08:52 +00:00
}
static function fetch_thing ( $x ) {
$r = q ( " select * from obj where obj_type = %d and obj_obj = '%s' limit 1 " ,
intval ( TERM_OBJ_THING ),
dbesc ( $x [ 'id' ])
);
2019-05-13 06:57:57 +00:00
if ( ! $r ) {
2018-05-30 04:08:52 +00:00
return [];
2019-05-13 06:57:57 +00:00
}
2018-05-30 04:08:52 +00:00
$x = [
'type' => 'Object' ,
'id' => z_root () . '/thing/' . $r [ 0 ][ 'obj_obj' ],
'name' => $r [ 0 ][ 'obj_term' ]
];
2019-05-13 06:57:57 +00:00
if ( $r [ 0 ][ 'obj_image' ]) {
2018-05-30 04:08:52 +00:00
$x [ 'image' ] = $r [ 0 ][ 'obj_image' ];
2019-05-13 06:57:57 +00:00
}
2018-05-30 04:08:52 +00:00
return $x ;
}
2019-09-10 04:03:33 +00:00
static function fetch_item ( $x , $activitypub = false ) {
2018-05-30 04:08:52 +00:00
2018-07-17 02:08:10 +00:00
if ( array_key_exists ( 'source' , $x )) {
// This item is already processed and encoded
return $x ;
}
2018-05-30 04:08:52 +00:00
$r = q ( " select * from item where mid = '%s' limit 1 " ,
dbesc ( $x [ 'id' ])
);
2019-05-13 06:57:57 +00:00
if ( $r ) {
2018-05-30 04:08:52 +00:00
xchan_query ( $r , true );
$r = fetch_post_tags ( $r , true );
2019-09-10 04:03:33 +00:00
return self :: encode_item ( $r [ 0 ], $activitypub );
2018-05-30 04:08:52 +00:00
}
}
2018-08-20 03:39:23 +00:00
static function encode_item_collection ( $items , $id , $type , $activitypub = false ) {
2018-05-30 04:08:52 +00:00
$ret = [
'id' => z_root () . '/' . $id ,
'type' => $type ,
'totalItems' => count ( $items ),
];
2019-05-13 06:57:57 +00:00
if ( $items ) {
2018-05-30 04:08:52 +00:00
$x = [];
2019-05-13 06:57:57 +00:00
foreach ( $items as $i ) {
2018-09-10 03:55:51 +00:00
$m = get_iconfig ( $i [ 'id' ], 'activitypub' , 'rawmsg' );
2019-05-13 06:57:57 +00:00
if ( $m ) {
2018-09-10 03:55:51 +00:00
$t = json_decode ( $m , true );
}
else {
$t = self :: encode_activity ( $i , $activitypub );
}
2019-05-13 06:57:57 +00:00
if ( $t ) {
2018-05-30 04:08:52 +00:00
$x [] = $t ;
2019-05-13 06:57:57 +00:00
}
2018-05-30 04:08:52 +00:00
}
2019-05-13 06:57:57 +00:00
if ( $type === 'OrderedCollection' ) {
2018-05-30 04:08:52 +00:00
$ret [ 'orderedItems' ] = $x ;
2019-05-13 06:57:57 +00:00
}
else {
2018-05-30 04:08:52 +00:00
$ret [ 'items' ] = $x ;
2019-05-13 06:57:57 +00:00
}
2018-05-30 04:08:52 +00:00
}
return $ret ;
}
static function encode_follow_collection ( $items , $id , $type , $extra = null ) {
$ret = [
'id' => z_root () . '/' . $id ,
'type' => $type ,
'totalItems' => count ( $items ),
];
2019-05-13 06:57:57 +00:00
if ( $extra ) {
2018-05-30 04:08:52 +00:00
$ret = array_merge ( $ret , $extra );
2019-05-13 06:57:57 +00:00
}
2018-05-30 04:08:52 +00:00
2019-05-13 06:57:57 +00:00
if ( $items ) {
2018-05-30 04:08:52 +00:00
$x = [];
2019-05-13 06:57:57 +00:00
foreach ( $items as $i ) {
if ( $i [ 'xchan_url' ]) {
2018-05-30 04:08:52 +00:00
$x [] = $i [ 'xchan_url' ];
}
}
2019-05-13 06:57:57 +00:00
if ( $type === 'OrderedCollection' ) {
2018-05-30 04:08:52 +00:00
$ret [ 'orderedItems' ] = $x ;
2019-05-13 06:57:57 +00:00
}
else {
2018-05-30 04:08:52 +00:00
$ret [ 'items' ] = $x ;
2019-05-13 06:57:57 +00:00
}
2018-05-30 04:08:52 +00:00
}
return $ret ;
}
2018-08-20 03:39:23 +00:00
static function encode_item ( $i , $activitypub = false ) {
2018-05-30 04:08:52 +00:00
$ret = [];
2018-10-17 08:26:23 +00:00
$reply = false ;
$is_directmessage = false ;
2018-05-30 04:08:52 +00:00
2018-07-18 04:56:51 +00:00
$objtype = self :: activity_obj_mapper ( $i [ 'obj_type' ]);
2019-05-13 06:57:57 +00:00
if ( intval ( $i [ 'item_deleted' ])) {
2018-05-30 04:08:52 +00:00
$ret [ 'type' ] = 'Tombstone' ;
2018-07-18 04:56:51 +00:00
$ret [ 'formerType' ] = $objtype ;
2018-09-04 01:22:31 +00:00
$ret [ 'id' ] = $i [ 'mid' ];
2018-10-17 08:26:23 +00:00
$ret [ 'to' ] = [ ACTIVITY_PUBLIC_INBOX ];
2018-05-30 04:08:52 +00:00
return $ret ;
}
2018-07-18 04:56:51 +00:00
$ret [ 'type' ] = $objtype ;
2018-05-30 04:08:52 +00:00
2018-08-20 03:39:23 +00:00
/**
* If the destination is activitypub , see if the content needs conversion to
* Mastodon " quirks " mode . This will be the case if there is any markup beyond
* links or images OR if the number of images exceeds 1. This content may be
* purified into oblivion when using the Note type so turn it into an Article .
*/
$convert_to_article = false ;
$images = false ;
2019-05-13 06:57:57 +00:00
if ( $activitypub && $ret [ 'type' ] === 'Note' ) {
2018-10-04 09:19:08 +00:00
2018-08-20 03:39:23 +00:00
$bbtags = false ;
2018-12-05 23:27:07 +00:00
$num_bbtags = preg_match_all ( '/\[\/([a-z]+)\]/ism' , $i [ 'body' ], $bbtags , PREG_SET_ORDER );
2019-05-13 06:57:57 +00:00
if ( $num_bbtags ) {
2018-10-05 04:40:16 +00:00
2019-05-13 06:57:57 +00:00
foreach ( $bbtags as $t ) {
2018-10-05 04:40:16 +00:00
if (( ! $t [ 1 ]) || ( in_array ( $t [ 1 ],[ 'url' , 'zrl' , 'img' , 'zmg' ]))) {
2018-08-20 03:39:23 +00:00
continue ;
}
$convert_to_article = true ;
}
}
2018-10-04 09:19:08 +00:00
2018-08-20 03:39:23 +00:00
$has_images = preg_match_all ( '/\[[zi]mg(.*?)\](.*?)\[/ism' , $i [ 'body' ], $images , PREG_SET_ORDER );
2018-10-04 09:19:08 +00:00
2019-05-13 06:57:57 +00:00
if ( $has_images > 1 ) {
2018-08-20 03:39:23 +00:00
$convert_to_article = true ;
}
2019-05-13 06:57:57 +00:00
if ( $convert_to_article ) {
2018-08-20 03:39:23 +00:00
$ret [ 'type' ] = 'Article' ;
}
}
2018-09-04 01:22:31 +00:00
$ret [ 'id' ] = $i [ 'mid' ];
2018-05-30 04:08:52 +00:00
$ret [ 'published' ] = datetime_convert ( 'UTC' , 'UTC' , $i [ 'created' ], ATOM_TIME );
2019-05-13 06:57:57 +00:00
if ( $i [ 'created' ] !== $i [ 'edited' ]) {
2018-05-30 04:08:52 +00:00
$ret [ 'updated' ] = datetime_convert ( 'UTC' , 'UTC' , $i [ 'edited' ], ATOM_TIME );
2018-08-20 03:39:23 +00:00
}
2019-05-28 23:42:32 +00:00
if ( $i [ 'expires' ] <= NULL_DATE ) {
$ret [ 'expires' ] = datetime_convert ( 'UTC' , 'UTC' , $i [ 'expires' ], ATOM_TIME );
}
2019-05-13 06:57:57 +00:00
if ( $i [ 'app' ]) {
2019-05-29 04:10:32 +00:00
$ret [ 'generator' ] = [ 'type' => 'Application' , 'name' => $i [ 'app' ] ];
2018-05-30 04:08:52 +00:00
}
2019-05-13 06:57:57 +00:00
if ( $i [ 'location' ] || $i [ 'coord' ]) {
2018-05-30 04:08:52 +00:00
$ret [ 'location' ] = [ 'type' => 'Place' ];
2019-05-13 06:57:57 +00:00
if ( $i [ 'location' ]) {
2018-05-30 04:08:52 +00:00
$ret [ 'location' ][ 'name' ] = $i [ 'location' ];
}
2019-05-13 06:57:57 +00:00
if ( $i [ 'coord' ]) {
2018-05-30 04:08:52 +00:00
$l = explode ( ' ' , $i [ 'coord' ]);
$ret [ 'location' ][ 'latitude' ] = $l [ 0 ];
$ret [ 'location' ][ 'longitude' ] = $l [ 1 ];
}
}
2018-11-19 05:53:09 +00:00
$ret [ 'inheritPrivacy' ] = true ;
2019-05-13 06:57:57 +00:00
if ( intval ( $i [ 'item_wall' ]) && $i [ 'mid' ] === $i [ 'parent_mid' ]) {
2019-04-01 22:19:41 +00:00
$ret [ 'commentPolicy' ] = map_scope ( PermissionLimits :: Get ( $i [ 'uid' ], 'post_comments' ));
}
2019-06-19 02:21:31 +00:00
if ( intval ( $i [ 'item_private' ]) === 2 ) {
$ret [ 'directMessage' ] = true ;
}
2019-05-13 06:57:57 +00:00
if ( array_key_exists ( 'comments_closed' , $i ) && $i [ 'comments_closed' ] !== EMPTY_STR && $i [ 'comments_closed' ] !== NULL_DATE ) {
2019-04-01 22:19:41 +00:00
if ( $ret [ 'commentPolicy' ]) {
$ret [ 'commentPolicy' ] .= ' ' ;
}
$ret [ 'commentPolicy' ] .= 'until=' . datetime_convert ( 'UTC' , 'UTC' , $i [ 'comments_closed' ], ATOM_TIME );
}
2018-05-30 04:08:52 +00:00
$ret [ 'attributedTo' ] = $i [ 'author' ][ 'xchan_url' ];
2019-05-13 06:57:57 +00:00
if ( $i [ 'mid' ] !== $i [ 'parent_mid' ]) {
2018-12-02 22:39:58 +00:00
$ret [ 'inReplyTo' ] = $i [ 'thr_parent' ];
2018-08-30 06:54:13 +00:00
$cnv = get_iconfig ( $i [ 'parent' ], 'ostatus' , 'conversation' );
2019-05-13 06:57:57 +00:00
if ( ! $cnv ) {
2018-12-02 22:39:58 +00:00
$cnv = $ret [ 'parent_mid' ];
}
2018-10-17 08:26:23 +00:00
$reply = true ;
2019-05-13 06:57:57 +00:00
if ( $i [ 'item_private' ]) {
2018-10-17 08:26:23 +00:00
$d = q ( " select xchan_url, xchan_addr, xchan_name from item left join xchan on xchan_hash = author_xchan where id = %d limit 1 " ,
intval ( $i [ 'parent' ])
);
2019-05-13 06:57:57 +00:00
if ( $d ) {
2018-10-17 08:26:23 +00:00
$recips = get_iconfig ( $i [ 'parent' ], 'activitypub' , 'recips' );
2019-05-13 06:57:57 +00:00
if ( in_array ( $i [ 'author' ][ 'xchan_url' ], $recips [ 'to' ])) {
2018-10-17 08:26:23 +00:00
$reply_url = $d [ 0 ][ 'xchan_url' ];
$is_directmessage = true ;
}
2019-05-13 06:57:57 +00:00
else {
$reply_url = z_root () . '/followers/' . substr ( $i [ 'author' ][ 'xchan_addr' ], 0 , strpos ( $i [ 'author' ][ 'xchan_addr' ], '@' ));
2018-10-17 08:26:23 +00:00
}
$reply_addr = (( $d [ 0 ][ 'xchan_addr' ]) ? $d [ 0 ][ 'xchan_addr' ] : $d [ 0 ][ 'xchan_name' ]);
}
}
2018-08-30 06:54:13 +00:00
}
2019-05-13 06:57:57 +00:00
if ( ! $cnv ) {
2018-08-30 06:54:13 +00:00
$cnv = get_iconfig ( $i , 'ostatus' , 'conversation' );
}
2019-05-13 06:57:57 +00:00
if ( $cnv ) {
2018-08-30 06:54:13 +00:00
$ret [ 'conversation' ] = $cnv ;
2018-05-30 04:08:52 +00:00
}
2019-05-13 06:57:57 +00:00
if ( $i [ 'mimetype' ] === 'text/bbcode' ) {
if ( $i [ 'title' ]) {
2018-08-20 03:39:23 +00:00
$ret [ 'name' ] = $i [ 'title' ];
2019-05-13 06:57:57 +00:00
}
if ( $i [ 'summary' ]) {
2018-10-05 04:16:20 +00:00
$ret [ 'summary' ] = bbcode ( $i [ 'summary' ], [ 'export' => true ]);
2019-05-13 06:57:57 +00:00
}
2018-10-05 04:16:20 +00:00
$ret [ 'content' ] = bbcode ( $i [ 'body' ], [ 'export' => true ]);
2018-08-20 03:39:23 +00:00
$ret [ 'source' ] = [ 'content' => $i [ 'body' ], 'summary' => $i [ 'summary' ], 'mediaType' => 'text/bbcode' ];
2018-05-30 04:08:52 +00:00
}
2018-06-26 03:55:53 +00:00
$actor = self :: encode_person ( $i [ 'author' ], false );
2019-05-13 06:57:57 +00:00
if ( $actor ) {
2018-05-30 04:08:52 +00:00
$ret [ 'actor' ] = $actor ;
2019-05-13 06:57:57 +00:00
}
else {
2018-05-30 04:08:52 +00:00
return [];
2019-05-13 06:57:57 +00:00
}
2018-05-30 04:08:52 +00:00
$t = self :: encode_taxonomy ( $i );
2019-05-13 06:57:57 +00:00
if ( $t ) {
$ret [ 'tag' ] = $t ;
2018-05-30 04:08:52 +00:00
}
$a = self :: encode_attachment ( $i );
2019-05-13 06:57:57 +00:00
if ( $a ) {
2018-05-30 04:08:52 +00:00
$ret [ 'attachment' ] = $a ;
}
2019-05-13 06:57:57 +00:00
if ( $activitypub && $has_images && $ret [ 'type' ] === 'Note' ) {
2018-08-20 03:39:23 +00:00
$img = [];
2019-05-13 06:57:57 +00:00
foreach ( $images as $match ) {
2018-08-20 03:39:23 +00:00
$img [] = [ 'type' => 'Image' , 'url' => $match [ 2 ] ];
}
2019-05-13 06:57:57 +00:00
if ( ! $ret [ 'attachment' ]) {
2018-08-20 03:39:23 +00:00
$ret [ 'attachment' ] = [];
2019-05-13 06:57:57 +00:00
}
2018-08-20 03:39:23 +00:00
$ret [ 'attachment' ] = array_merge ( $img , $ret [ 'attachment' ]);
}
2019-05-13 06:57:57 +00:00
if ( $activitypub ) {
if ( $i [ 'item_private' ]) {
if ( $reply ) {
if ( $i [ 'author_xchan' ] === $i [ 'owner_xchan' ]) {
2018-10-17 08:26:23 +00:00
$m = self :: map_acl ( $i ,(( $i [ 'allow_gid' ]) ? false : true ));
$ret [ 'tag' ] = (( $ret [ 'tag' ]) ? array_merge ( $ret [ 'tag' ], $m ) : $m );
}
else {
2019-05-13 06:57:57 +00:00
if ( $is_directmessage ) {
2018-10-17 08:26:23 +00:00
$m = [
'type' => 'Mention' ,
'href' => $reply_url ,
'name' => '@' . $reply_addr
];
$ret [ 'tag' ] = (( $ret [ 'tag' ]) ? array_merge ( $ret [ 'tag' ], $m ) : $m );
}
else {
$ret [ 'to' ] = [ $reply_url ];
}
}
}
else {
/* Add mentions only if the targets are individuals */
$m = self :: map_acl ( $i ,(( $i [ 'allow_gid' ]) ? false : true ));
$ret [ 'tag' ] = (( $ret [ 'tag' ]) ? array_merge ( $ret [ 'tag' ], $m ) : $m );
}
}
else {
2019-05-13 06:57:57 +00:00
if ( $reply ) {
2018-10-17 08:26:23 +00:00
$ret [ 'to' ] = [ z_root () . '/followers/' . substr ( $i [ 'author' ][ 'xchan_addr' ], 0 , strpos ( $i [ 'author' ][ 'xchan_addr' ], '@' )) ];
$ret [ 'cc' ] = [ ACTIVITY_PUBLIC_INBOX ];
}
else {
$ret [ 'to' ] = [ ACTIVITY_PUBLIC_INBOX ];
$ret [ 'cc' ] = [ z_root () . '/followers/' . substr ( $i [ 'author' ][ 'xchan_addr' ], 0 , strpos ( $i [ 'author' ][ 'xchan_addr' ], '@' )) ];
}
}
$mentions = self :: map_mentions ( $i );
2019-05-13 06:57:57 +00:00
if ( count ( $mentions ) > 0 ) {
if ( ! $ret [ 'to' ]) {
2018-10-29 03:39:01 +00:00
$ret [ 'to' ] = $mentions ;
2018-10-17 08:26:23 +00:00
}
else {
2018-10-29 03:39:01 +00:00
$ret [ 'to' ] = array_merge ( $ret [ 'to' ], $mentions );
2018-10-17 08:26:23 +00:00
}
}
}
2018-05-30 04:08:52 +00:00
return $ret ;
}
static function decode_taxonomy ( $item ) {
$ret = [];
2019-05-13 06:57:57 +00:00
if ( $item [ 'tag' ] && is_array ( $item [ 'tag' ])) {
foreach ( $item [ 'tag' ] as $t ) {
if ( ! array_key_exists ( 'type' , $t ))
2018-05-30 04:08:52 +00:00
$t [ 'type' ] = 'Hashtag' ;
switch ( $t [ 'type' ]) {
case 'Hashtag' :
2018-06-05 06:19:16 +00:00
$ret [] = [ 'ttype' => TERM_HASHTAG , 'url' => $t [ 'href' ], 'term' => escape_tags (( substr ( $t [ 'name' ], 0 , 1 ) === '#' ) ? substr ( $t [ 'name' ], 1 ) : $t [ 'name' ]) ];
2018-05-30 04:08:52 +00:00
break ;
2019-04-03 23:22:03 +00:00
case 'topicalCollection' :
$ret [] = [ 'ttype' => TERM_PCATEGORY , 'url' => $t [ 'href' ], 'term' => escape_tags ( $t [ 'name' ]) ];
break ;
2018-05-30 04:08:52 +00:00
case 'Mention' :
2018-06-29 03:25:43 +00:00
$mention_type = substr ( $t [ 'name' ], 0 , 1 );
2019-05-13 06:57:57 +00:00
if ( $mention_type === '!' ) {
2018-06-29 03:25:43 +00:00
$ret [] = [ 'ttype' => TERM_FORUM , 'url' => $t [ 'href' ], 'term' => escape_tags ( substr ( $t [ 'name' ], 1 )) ];
}
else {
$ret [] = [ 'ttype' => TERM_MENTION , 'url' => $t [ 'href' ], 'term' => escape_tags (( substr ( $t [ 'name' ], 0 , 1 ) === '@' ) ? substr ( $t [ 'name' ], 1 ) : $t [ 'name' ]) ];
}
2018-05-30 04:08:52 +00:00
break ;
2018-09-06 23:40:13 +00:00
case 'Emoji' :
2018-09-07 05:52:48 +00:00
$ret [] = [ 'ttype' => TERM_EMOJI , 'url' => $t [ 'icon' ][ 'url' ], 'term' => escape_tags ( $t [ 'name' ]) ];
2018-09-06 23:40:13 +00:00
break ;
2018-05-30 04:08:52 +00:00
default :
break ;
}
}
}
return $ret ;
}
static function encode_taxonomy ( $item ) {
$ret = [];
2019-05-13 06:57:57 +00:00
if ( $item [ 'term' ]) {
foreach ( $item [ 'term' ] as $t ) {
2018-05-30 04:08:52 +00:00
switch ( $t [ 'ttype' ]) {
case TERM_HASHTAG :
// An id is required so if we don't have a url in the taxonomy, ignore it and keep going.
2019-05-13 06:57:57 +00:00
if ( $t [ 'url' ]) {
2018-05-30 04:08:52 +00:00
$ret [] = [ 'id' => $t [ 'url' ], 'name' => '#' . $t [ 'term' ] ];
}
break ;
2019-04-03 23:22:03 +00:00
case TERM_PCATEGORY :
2019-05-13 06:57:57 +00:00
if ( $t [ 'url' ] && $t [ 'term' ]) {
2019-04-03 23:22:03 +00:00
$ret [] = [ 'type' => 'topicalCollection' , 'href' => $t [ 'url' ], 'name' => $t [ 'term' ] ];
}
break ;
2018-06-29 03:25:43 +00:00
case TERM_FORUM :
2018-11-08 02:24:22 +00:00
$term = self :: lookup_term_addr ( $t [ 'url' ], $t [ 'term' ]);
$ret [] = [ 'type' => 'Mention' , 'href' => $t [ 'url' ], 'name' => '!' . (( $term ) ? $term : $t [ 'term' ]) ];
2018-06-29 03:25:43 +00:00
break ;
2018-05-30 04:08:52 +00:00
case TERM_MENTION :
2018-11-08 02:24:22 +00:00
$term = self :: lookup_term_addr ( $t [ 'url' ], $t [ 'term' ]);
$ret [] = [ 'type' => 'Mention' , 'href' => $t [ 'url' ], 'name' => '@' . (( $term ) ? $term : $t [ 'term' ]) ];
2018-05-30 04:08:52 +00:00
break ;
default :
break ;
}
}
}
return $ret ;
}
2018-11-08 02:24:22 +00:00
static function lookup_term_addr ( $url , $name ) {
// The visible mention in our activities is always the full name.
// In the object taxonomy change this to the webfinger handle in case
// platforms expect the Mastodon form in order to generate notifications
// Try a couple of different things in case the url provided isn't the canonical id.
// If all else fails, try to match the name.
$r = false ;
2019-05-13 06:57:57 +00:00
if ( $url ) {
2018-11-08 02:24:22 +00:00
$r = q ( " select xchan_addr from xchan where ( xchan_url = '%s' OR xchan_hash = '%s' ) limit 1 " ,
dbesc ( $url ),
dbesc ( $url )
);
2019-05-13 06:57:57 +00:00
if ( $r ) {
2018-11-08 02:24:22 +00:00
return $r [ 0 ][ 'xchan_addr' ];
}
}
2019-05-13 06:57:57 +00:00
if ( $name ) {
2018-11-08 02:24:22 +00:00
$r = q ( " select xchan_addr from xchan where xchan_name = '%s' limit 1 " ,
dbesc ( $name )
);
2019-05-13 06:57:57 +00:00
if ( $r ) {
2018-11-08 02:24:22 +00:00
return $r [ 0 ][ 'xchan_addr' ];
}
}
return EMPTY_STR ;
}
static function lookup_term_url ( $url ) {
// The xchan_url for mastodon is a text/html rendering. This is called from map_mentions where we need
// to convert the mention url to an ActivityPub id. If this fails for any reason, return the url we have
2018-11-12 03:16:22 +00:00
$r = q ( " select hubloc_id_url from hubloc left join xchan on hubloc_hash = xchan_hash where xchan_url = '%s' and hubloc_primary = 1 limit 1 " ,
2018-11-08 02:24:22 +00:00
dbesc ( $url )
);
2019-05-13 06:57:57 +00:00
if ( $r ) {
2018-11-08 02:24:22 +00:00
return $r [ 0 ][ 'hubloc_id_url' ];
}
return EMPTY_STR ;
}
2018-05-30 04:08:52 +00:00
static function encode_attachment ( $item ) {
$ret = [];
2019-05-13 06:57:57 +00:00
if ( $item [ 'attach' ]) {
2019-04-11 11:10:59 +00:00
$atts = (( is_array ( $item [ 'attach' ])) ? $item [ 'attach' ] : json_decode ( $item [ 'attach' ], true ));
2019-05-13 06:57:57 +00:00
if ( $atts ) {
foreach ( $atts as $att ) {
if ( strpos ( $att [ 'type' ], 'image' )) {
2018-05-30 04:08:52 +00:00
$ret [] = [ 'type' => 'Image' , 'url' => $att [ 'href' ] ];
}
else {
$ret [] = [ 'type' => 'Link' , 'mediaType' => $att [ 'type' ], 'href' => $att [ 'href' ] ];
}
}
}
}
return $ret ;
}
static function decode_attachment ( $item ) {
$ret = [];
2019-05-13 06:57:57 +00:00
if ( $item [ 'attachment' ]) {
foreach ( $item [ 'attachment' ] as $att ) {
2018-05-30 04:08:52 +00:00
$entry = [];
2019-05-13 06:57:57 +00:00
if ( $att [ 'href' ])
2018-05-30 04:08:52 +00:00
$entry [ 'href' ] = $att [ 'href' ];
2019-05-13 06:57:57 +00:00
elseif ( $att [ 'url' ])
2018-05-30 04:08:52 +00:00
$entry [ 'href' ] = $att [ 'url' ];
2019-05-13 06:57:57 +00:00
if ( $att [ 'mediaType' ])
2018-05-30 04:08:52 +00:00
$entry [ 'type' ] = $att [ 'mediaType' ];
2019-05-13 06:57:57 +00:00
elseif ( $att [ 'type' ] === 'Image' )
2018-05-30 04:08:52 +00:00
$entry [ 'type' ] = 'image/jpeg' ;
2019-05-13 06:57:57 +00:00
if ( $entry )
2018-05-30 04:08:52 +00:00
$ret [] = $entry ;
}
}
return $ret ;
}
2018-08-20 03:39:23 +00:00
static function encode_activity ( $i , $activitypub = false ) {
2018-05-30 04:08:52 +00:00
$ret = [];
$reply = false ;
2019-05-13 06:57:57 +00:00
if ( intval ( $i [ 'item_deleted' ])) {
2018-10-11 01:53:26 +00:00
$ret [ 'type' ] = 'Delete' ;
$ret [ 'id' ] = str_replace ( '/item/' , '/activity/' , $i [ 'mid' ]) . '#delete' ;
$actor = self :: encode_person ( $i [ 'author' ], false );
2019-05-13 06:57:57 +00:00
if ( $actor )
2018-10-11 01:53:26 +00:00
$ret [ 'actor' ] = $actor ;
else
return [];
2019-05-13 06:57:57 +00:00
if ( $i [ 'obj' ]) {
if ( ! is_array ( $i [ 'obj' ])) {
2018-10-11 01:53:26 +00:00
$i [ 'obj' ] = json_decode ( $i [ 'obj' ], true );
}
$obj = self :: encode_object ( $i [ 'obj' ]);
2019-05-13 06:57:57 +00:00
if ( $obj )
2018-10-11 01:53:26 +00:00
$ret [ 'object' ] = $obj ;
else
return [];
}
else {
$obj = self :: encode_item ( $i , $activitypub );
2019-05-13 06:57:57 +00:00
if ( $obj )
2018-10-11 01:53:26 +00:00
$ret [ 'object' ] = $obj ;
else
return [];
}
$ret [ 'to' ] = [ ACTIVITY_PUBLIC_INBOX ];
2018-05-30 04:08:52 +00:00
return $ret ;
2018-10-11 01:53:26 +00:00
2018-05-30 04:08:52 +00:00
}
2018-05-30 06:30:18 +00:00
$ret [ 'type' ] = self :: activity_mapper ( $i [ 'verb' ]);
2018-09-03 06:12:53 +00:00
2019-05-13 06:57:57 +00:00
if ( strpos ( $i [ 'mid' ], z_root () . '/item/' ) !== false ) {
2018-09-04 01:22:31 +00:00
$ret [ 'id' ] = str_replace ( '/item/' , '/activity/' , $i [ 'mid' ]);
}
2019-05-13 06:57:57 +00:00
elseif ( strpos ( $i [ 'mid' ], z_root () . '/event/' ) !== false ) {
2018-10-16 20:56:46 +00:00
$ret [ 'id' ] = str_replace ( '/event/' , '/activity/' , $i [ 'mid' ]);
}
2018-09-04 01:22:31 +00:00
else {
$ret [ 'id' ] = $i [ 'mid' ];
2018-09-03 06:12:53 +00:00
}
2018-05-30 04:08:52 +00:00
2019-05-13 06:57:57 +00:00
if ( $i [ 'title' ]) {
2018-08-20 03:39:23 +00:00
$ret [ 'name' ] = $i [ 'title' ];
}
2018-07-17 02:08:10 +00:00
2019-05-13 06:57:57 +00:00
if ( $i [ 'summary' ]) {
2018-10-05 04:16:20 +00:00
$ret [ 'summary' ] = bbcode ( $i [ 'summary' ], [ 'export' => true ]);
2018-08-20 03:39:23 +00:00
}
2018-05-30 04:08:52 +00:00
2019-05-13 06:57:57 +00:00
if ( $ret [ 'type' ] === 'Announce' ) {
2018-09-03 03:16:33 +00:00
$tmp = $i [ 'body' ];
2018-10-05 04:16:20 +00:00
$ret [ 'content' ] = bbcode ( $tmp , [ 'export' => true ]);
2018-08-01 02:39:50 +00:00
$ret [ 'source' ] = [
'content' => $i [ 'body' ],
'mediaType' => 'text/bbcode'
];
}
2018-05-30 04:08:52 +00:00
$ret [ 'published' ] = datetime_convert ( 'UTC' , 'UTC' , $i [ 'created' ], ATOM_TIME );
2019-05-13 06:57:57 +00:00
if ( $i [ 'created' ] !== $i [ 'edited' ])
2018-05-30 04:08:52 +00:00
$ret [ 'updated' ] = datetime_convert ( 'UTC' , 'UTC' , $i [ 'edited' ], ATOM_TIME );
2019-05-13 06:57:57 +00:00
if ( $i [ 'app' ]) {
2019-05-29 04:10:32 +00:00
$ret [ 'generator' ] = [ 'type' => 'Application' , 'name' => $i [ 'app' ] ];
2018-05-30 04:08:52 +00:00
}
2019-05-13 06:57:57 +00:00
if ( $i [ 'location' ] || $i [ 'coord' ]) {
2018-05-30 04:08:52 +00:00
$ret [ 'location' ] = [ 'type' => 'Place' ];
2019-05-13 06:57:57 +00:00
if ( $i [ 'location' ]) {
2018-05-30 04:08:52 +00:00
$ret [ 'location' ][ 'name' ] = $i [ 'location' ];
}
2019-05-13 06:57:57 +00:00
if ( $i [ 'coord' ]) {
2018-05-30 04:08:52 +00:00
$l = explode ( ' ' , $i [ 'coord' ]);
$ret [ 'location' ][ 'latitude' ] = $l [ 0 ];
$ret [ 'location' ][ 'longitude' ] = $l [ 1 ];
}
}
2019-05-13 06:57:57 +00:00
if ( $i [ 'mid' ] != $i [ 'parent_mid' ]) {
2018-12-02 22:39:58 +00:00
$ret [ 'inReplyTo' ] = $i [ 'thr_parent' ];
$cnv = get_iconfig ( $i [ 'parent' ], 'ostatus' , 'conversation' );
2019-05-13 06:57:57 +00:00
if ( ! $cnv ) {
2018-12-02 22:39:58 +00:00
$cnv = $ret [ 'parent_mid' ];
}
2018-05-30 04:08:52 +00:00
$reply = true ;
2019-05-13 06:57:57 +00:00
if ( $i [ 'item_private' ]) {
2018-05-30 04:08:52 +00:00
$d = q ( " select xchan_url, xchan_addr, xchan_name from item left join xchan on xchan_hash = author_xchan where id = %d limit 1 " ,
intval ( $i [ 'parent' ])
);
2019-05-13 06:57:57 +00:00
if ( $d ) {
2018-05-30 04:08:52 +00:00
$is_directmessage = false ;
$recips = get_iconfig ( $i [ 'parent' ], 'activitypub' , 'recips' );
2019-05-13 06:57:57 +00:00
if ( $recips && is_array ( $recips ) and array_key_exists ( 'to' , $recips ) && is_array ( $recips [ 'to' ])
2019-02-06 03:01:17 +00:00
&& in_array ( $i [ 'author' ][ 'xchan_url' ], $recips [ 'to' ])) {
2018-05-30 04:08:52 +00:00
$reply_url = $d [ 0 ][ 'xchan_url' ];
$is_directmessage = true ;
}
else {
$reply_url = z_root () . '/followers/' . substr ( $i [ 'author' ][ 'xchan_addr' ], 0 , strpos ( $i [ 'author' ][ 'xchan_addr' ], '@' ));
}
$reply_addr = (( $d [ 0 ][ 'xchan_addr' ]) ? $d [ 0 ][ 'xchan_addr' ] : $d [ 0 ][ 'xchan_name' ]);
}
}
}
2019-05-13 06:57:57 +00:00
if ( ! $cnv ) {
2018-12-02 22:39:58 +00:00
$cnv = get_iconfig ( $i , 'ostatus' , 'conversation' );
}
2019-05-13 06:57:57 +00:00
if ( $cnv ) {
2018-12-02 22:39:58 +00:00
$ret [ 'conversation' ] = $cnv ;
}
2018-11-19 05:53:09 +00:00
$ret [ 'inheritPrivacy' ] = true ;
2018-06-26 03:55:53 +00:00
$actor = self :: encode_person ( $i [ 'author' ], false );
2019-05-13 06:57:57 +00:00
if ( $actor )
2018-05-30 04:08:52 +00:00
$ret [ 'actor' ] = $actor ;
else
return [];
2019-05-13 06:57:57 +00:00
if ( $i [ 'obj' ]) {
if ( ! is_array ( $i [ 'obj' ])) {
2018-07-12 01:02:25 +00:00
$i [ 'obj' ] = json_decode ( $i [ 'obj' ], true );
}
2018-05-30 04:08:52 +00:00
$obj = self :: encode_object ( $i [ 'obj' ]);
2019-05-13 06:57:57 +00:00
if ( $obj )
2018-05-30 04:08:52 +00:00
$ret [ 'object' ] = $obj ;
else
return [];
}
else {
2018-10-04 02:10:52 +00:00
$obj = self :: encode_item ( $i , $activitypub );
2019-05-13 06:57:57 +00:00
if ( $obj )
2018-05-30 04:08:52 +00:00
$ret [ 'object' ] = $obj ;
else
return [];
}
2019-05-13 06:57:57 +00:00
if ( $i [ 'target' ]) {
if ( ! is_array ( $i [ 'target' ])) {
2018-07-12 01:02:25 +00:00
$i [ 'target' ] = json_decode ( $i [ 'target' ], true );
}
2018-05-30 04:08:52 +00:00
$tgt = self :: encode_object ( $i [ 'target' ]);
2019-05-13 06:57:57 +00:00
if ( $tgt ) {
2018-05-30 04:08:52 +00:00
$ret [ 'target' ] = $tgt ;
2019-05-01 06:18:39 +00:00
}
2018-05-30 04:08:52 +00:00
}
2019-05-13 06:57:57 +00:00
if ( $activitypub ) {
if ( $i [ 'item_private' ]) {
if ( $reply ) {
if ( $i [ 'author_xchan' ] === $i [ 'owner_xchan' ]) {
2018-08-20 03:39:23 +00:00
$m = self :: map_acl ( $i ,(( $i [ 'allow_gid' ]) ? false : true ));
$ret [ 'tag' ] = (( $ret [ 'tag' ]) ? array_merge ( $ret [ 'tag' ], $m ) : $m );
}
else {
2019-05-13 06:57:57 +00:00
if ( $is_directmessage ) {
2018-08-20 03:39:23 +00:00
$m = [
'type' => 'Mention' ,
'href' => $reply_url ,
'name' => '@' . $reply_addr
];
$ret [ 'tag' ] = (( $ret [ 'tag' ]) ? array_merge ( $ret [ 'tag' ], $m ) : $m );
}
else {
$ret [ 'to' ] = [ $reply_url ];
}
}
}
else {
/* Add mentions only if the targets are individuals */
$m = self :: map_acl ( $i ,(( $i [ 'allow_gid' ]) ? false : true ));
$ret [ 'tag' ] = (( $ret [ 'tag' ]) ? array_merge ( $ret [ 'tag' ], $m ) : $m );
}
}
else {
2019-05-13 06:57:57 +00:00
if ( $reply ) {
2018-08-20 03:39:23 +00:00
$ret [ 'to' ] = [ z_root () . '/followers/' . substr ( $i [ 'author' ][ 'xchan_addr' ], 0 , strpos ( $i [ 'author' ][ 'xchan_addr' ], '@' )) ];
$ret [ 'cc' ] = [ ACTIVITY_PUBLIC_INBOX ];
}
else {
$ret [ 'to' ] = [ ACTIVITY_PUBLIC_INBOX ];
$ret [ 'cc' ] = [ z_root () . '/followers/' . substr ( $i [ 'author' ][ 'xchan_addr' ], 0 , strpos ( $i [ 'author' ][ 'xchan_addr' ], '@' )) ];
}
}
$mentions = self :: map_mentions ( $i );
2019-05-13 06:57:57 +00:00
if ( count ( $mentions ) > 0 ) {
if ( ! $ret [ 'to' ]) {
2018-10-29 03:39:01 +00:00
$ret [ 'to' ] = $mentions ;
2018-08-20 03:39:23 +00:00
}
else {
2018-10-29 03:39:01 +00:00
$ret [ 'to' ] = array_merge ( $ret [ 'to' ], $mentions );
2018-08-20 03:39:23 +00:00
}
}
}
2018-05-30 04:08:52 +00:00
return $ret ;
}
static function map_mentions ( $i ) {
2019-05-13 06:57:57 +00:00
if ( ! $i [ 'term' ]) {
2018-05-30 04:08:52 +00:00
return [];
}
$list = [];
foreach ( $i [ 'term' ] as $t ) {
2019-05-13 06:57:57 +00:00
if ( $t [ 'ttype' ] == TERM_MENTION ) {
2018-11-08 02:24:22 +00:00
$url = self :: lookup_term_url ( $t [ 'url' ]);
$list [] = (( $url ) ? $url : $t [ 'url' ]);
2018-05-30 04:08:52 +00:00
}
}
return $list ;
}
static function map_acl ( $i , $mentions = false ) {
$private = false ;
$list = [];
$x = collect_recipients ( $i , $private );
2018-08-20 03:39:23 +00:00
2019-05-13 06:57:57 +00:00
if ( $x ) {
2018-05-30 04:08:52 +00:00
stringify_array_elms ( $x );
2019-05-13 06:57:57 +00:00
if ( ! $x )
2018-05-30 04:08:52 +00:00
return ;
$strict = (( $mentions ) ? true : get_config ( 'activitypub' , 'compliance' ));
$sql_extra = (( $strict ) ? " and xchan_network = 'activitypub' " : '' );
$details = q ( " select xchan_url, xchan_addr, xchan_name from xchan where xchan_hash in ( " . implode ( ',' , $x ) . " ) $sql_extra " );
2019-05-13 06:57:57 +00:00
if ( $details ) {
foreach ( $details as $d ) {
if ( $mentions ) {
2018-05-30 04:08:52 +00:00
$list [] = [ 'type' => 'Mention' , 'href' => $d [ 'xchan_url' ], 'name' => '@' . (( $d [ 'xchan_addr' ]) ? $d [ 'xchan_addr' ] : $d [ 'xchan_name' ]) ];
}
else {
$list [] = $d [ 'xchan_url' ];
}
}
}
}
return $list ;
}
2018-08-20 03:39:23 +00:00
static function encode_person ( $p , $extended = true , $activitypub = false ) {
2018-06-26 03:55:53 +00:00
2018-10-05 01:40:57 +00:00
$ret = [];
2019-05-13 06:57:57 +00:00
if ( ! $p [ 'xchan_url' ])
2018-10-05 01:40:57 +00:00
return $ret ;
2018-05-30 04:08:52 +00:00
2019-05-13 06:57:57 +00:00
if ( ! $extended ) {
2018-06-26 03:55:53 +00:00
return $p [ 'xchan_url' ];
}
2018-10-05 01:40:57 +00:00
$c = (( array_key_exists ( 'channel_id' , $p )) ? $p : channelx_by_hash ( $p [ 'xchan_hash' ]));
2018-05-30 04:08:52 +00:00
$ret [ 'type' ] = 'Person' ;
2018-10-05 01:40:57 +00:00
2019-05-13 06:57:57 +00:00
if ( $c ) {
2018-12-12 23:39:17 +00:00
$role = PConfig :: Get ( $c [ 'channel_id' ], 'system' , 'permissions_role' );
2019-05-13 06:57:57 +00:00
if ( strpos ( $role , 'forum' ) !== false ) {
2018-10-05 01:40:57 +00:00
$ret [ 'type' ] = 'Group' ;
}
}
2019-08-15 04:21:40 +00:00
if ( $c ) {
$ret [ 'id' ] = channel_url ( $c );
}
else {
$ret [ 'id' ] = (( strpos ( $p [ 'xchan_hash' ], 'http' ) === 0 ) ? $p [ 'xchan_hash' ] : $p [ 'xchan_url' ]);
}
2019-05-13 06:57:57 +00:00
if ( $p [ 'xchan_addr' ] && strpos ( $p [ 'xchan_addr' ], '@' ))
2018-05-30 04:08:52 +00:00
$ret [ 'preferredUsername' ] = substr ( $p [ 'xchan_addr' ], 0 , strpos ( $p [ 'xchan_addr' ], '@' ));
$ret [ 'name' ] = $p [ 'xchan_name' ];
2018-08-14 00:27:46 +00:00
$ret [ 'updated' ] = datetime_convert ( 'UTC' , 'UTC' , $p [ 'xchan_name_date' ], ATOM_TIME );
2018-05-30 04:08:52 +00:00
$ret [ 'icon' ] = [
'type' => 'Image' ,
'mediaType' => (( $p [ 'xchan_photo_mimetype' ]) ? $p [ 'xchan_photo_mimetype' ] : 'image/png' ),
2018-08-14 00:27:46 +00:00
'updated' => datetime_convert ( 'UTC' , 'UTC' , $p [ 'xchan_photo_date' ], ATOM_TIME ),
2018-05-30 04:08:52 +00:00
'url' => $p [ 'xchan_photo_l' ],
'height' => 300 ,
'width' => 300 ,
2018-06-25 01:54:29 +00:00
];
2019-08-21 02:25:36 +00:00
$ret [ 'url' ] = $p [ 'xchan_url' ];
2018-08-29 09:19:32 +00:00
2019-09-10 04:03:33 +00:00
if ( $activitypub && $feature_complete ) {
2018-08-29 09:19:32 +00:00
2019-05-13 06:57:57 +00:00
if ( $c ) {
2018-08-20 03:39:23 +00:00
$ret [ 'inbox' ] = z_root () . '/inbox/' . $c [ 'channel_address' ];
$ret [ 'outbox' ] = z_root () . '/outbox/' . $c [ 'channel_address' ];
$ret [ 'followers' ] = z_root () . '/followers/' . $c [ 'channel_address' ];
$ret [ 'following' ] = z_root () . '/following/' . $c [ 'channel_address' ];
$ret [ 'endpoints' ] = [ 'sharedInbox' => z_root () . '/inbox' ];
$ret [ 'publicKey' ] = [
2019-07-19 03:38:36 +00:00
'id' => $p [ 'xchan_url' ],
2018-08-20 03:39:23 +00:00
'owner' => $p [ 'xchan_url' ],
'publicKeyPem' => $p [ 'xchan_pubkey' ]
];
2018-12-04 05:45:35 +00:00
$cp = get_cover_photo ( $c [ 'channel_id' ], 'array' );
2019-05-13 06:57:57 +00:00
if ( $cp ) {
2018-12-04 05:45:35 +00:00
$ret [ 'image' ] = [
'type' => 'Image' ,
'mediaType' => $cp [ 'type' ],
'url' => $cp [ 'url' ]
];
}
$dp = q ( " select about from profile where uid = %d and is_default = 1 " ,
intval ( $c [ 'channel_id' ])
);
2019-05-13 06:57:57 +00:00
if ( $dp && $dp [ 0 ][ 'about' ]) {
2018-12-04 05:45:35 +00:00
$ret [ 'summary' ] = bbcode ( $dp [ 0 ][ 'about' ],[ 'export' => true ]);
}
2018-08-20 03:39:23 +00:00
}
else {
2018-11-22 03:04:42 +00:00
$collections = get_xconfig ( $p [ 'xchan_hash' ], 'activitypub' , 'collections' ,[]);
2019-05-13 06:57:57 +00:00
if ( $collections ) {
2018-08-20 03:39:23 +00:00
$ret = array_merge ( $ret , $collections );
}
else {
$ret [ 'inbox' ] = null ;
$ret [ 'outbox' ] = null ;
}
}
}
2018-08-29 09:19:32 +00:00
else {
$ret [ 'inbox' ] = z_root () . '/nullbox' ;
2019-05-13 05:29:16 +00:00
if ( $c ) {
$ret [ 'outbox' ] = z_root () . '/outbox/' . $c [ 'channel_address' ];
}
else {
$ret [ 'outbox' ] = z_root () . '/nullbox' ;
}
2018-08-29 09:19:32 +00:00
$ret [ 'publicKey' ] = [
2019-08-26 00:35:14 +00:00
'id' => $p [ 'xchan_url' ],
2018-08-29 09:19:32 +00:00
'owner' => $p [ 'xchan_url' ],
'publicKeyPem' => $p [ 'xchan_pubkey' ]
];
}
2018-08-20 03:39:23 +00:00
$arr = [ 'xchan' => $p , 'encoded' => $ret , 'activitypub' => $activitypub ];
2018-08-06 01:41:33 +00:00
call_hooks ( 'encode_person' , $arr );
$ret = $arr [ 'encoded' ];
2018-05-30 04:08:52 +00:00
return $ret ;
}
static function activity_mapper ( $verb ) {
2019-05-13 06:57:57 +00:00
if ( strpos ( $verb , '/' ) === false ) {
2018-05-30 04:08:52 +00:00
return $verb ;
}
$acts = [
'http://activitystrea.ms/schema/1.0/post' => 'Create' ,
'http://activitystrea.ms/schema/1.0/share' => 'Announce' ,
'http://activitystrea.ms/schema/1.0/update' => 'Update' ,
'http://activitystrea.ms/schema/1.0/like' => 'Like' ,
'http://activitystrea.ms/schema/1.0/favorite' => 'Like' ,
'http://purl.org/zot/activity/dislike' => 'Dislike' ,
'http://activitystrea.ms/schema/1.0/tag' => 'Add' ,
'http://activitystrea.ms/schema/1.0/follow' => 'Follow' ,
'http://activitystrea.ms/schema/1.0/unfollow' => 'Unfollow' ,
];
2019-05-13 06:57:57 +00:00
if ( array_key_exists ( $verb , $acts ) && $acts [ $verb ]) {
2018-05-30 04:08:52 +00:00
return $acts [ $verb ];
}
// Reactions will just map to normal activities
2019-05-13 06:57:57 +00:00
if ( strpos ( $verb , ACTIVITY_REACT ) !== false )
2018-05-30 04:08:52 +00:00
return 'Create' ;
2019-05-13 06:57:57 +00:00
if ( strpos ( $verb , ACTIVITY_MOOD ) !== false )
2018-05-30 04:08:52 +00:00
return 'Create' ;
2019-05-13 06:57:57 +00:00
if ( strpos ( $verb , ACTIVITY_POKE ) !== false )
2018-05-30 04:08:52 +00:00
return 'Activity' ;
// We should return false, however this will trigger an uncaught execption and crash
// the delivery system if encountered by the JSON-LDSignature library
logger ( 'Unmapped activity: ' . $verb );
return 'Create' ;
2019-05-15 05:16:49 +00:00
// return false;
}
2018-05-30 04:08:52 +00:00
static function activity_obj_mapper ( $obj ) {
2019-05-13 06:57:57 +00:00
if ( strpos ( $obj , '/' ) === false ) {
2018-05-30 04:08:52 +00:00
return $obj ;
}
$objs = [
'http://activitystrea.ms/schema/1.0/note' => 'Note' ,
'http://activitystrea.ms/schema/1.0/comment' => 'Note' ,
'http://activitystrea.ms/schema/1.0/person' => 'Person' ,
'http://purl.org/zot/activity/profile' => 'Profile' ,
'http://activitystrea.ms/schema/1.0/photo' => 'Image' ,
'http://activitystrea.ms/schema/1.0/profile-photo' => 'Icon' ,
'http://activitystrea.ms/schema/1.0/event' => 'Event' ,
'http://activitystrea.ms/schema/1.0/wiki' => 'Document' ,
'http://purl.org/zot/activity/location' => 'Place' ,
'http://purl.org/zot/activity/chessgame' => 'Game' ,
'http://purl.org/zot/activity/tagterm' => 'zot:Tag' ,
'http://purl.org/zot/activity/thing' => 'Object' ,
'http://purl.org/zot/activity/file' => 'zot:File' ,
'http://purl.org/zot/activity/mood' => 'zot:Mood' ,
];
2019-05-13 06:57:57 +00:00
if ( array_key_exists ( $obj , $objs )) {
2018-05-30 04:08:52 +00:00
return $objs [ $obj ];
}
logger ( 'Unmapped activity object: ' . $obj );
return 'Note' ;
// return false;
}
static function follow ( $channel , $act ) {
$contact = null ;
$their_follow_id = null ;
/*
*
* if $act -> type === 'Follow' , actor is now following $channel
* if $act -> type === 'Accept' , actor has approved a follow request from $channel
*
*/
$person_obj = $act -> actor ;
2019-05-13 06:57:57 +00:00
if ( $act -> type === 'Follow' ) {
2018-05-30 04:08:52 +00:00
$their_follow_id = $act -> id ;
}
2019-05-13 06:57:57 +00:00
elseif ( $act -> type === 'Accept' ) {
2018-05-30 04:08:52 +00:00
$my_follow_id = z_root () . '/follow/' . $contact [ 'id' ];
}
2019-05-13 06:57:57 +00:00
if ( is_array ( $person_obj )) {
2018-05-30 04:08:52 +00:00
// store their xchan and hubloc
self :: actor_store ( $person_obj [ 'id' ], $person_obj );
// Find any existing abook record
$r = q ( " select * from abook left join xchan on abook_xchan = xchan_hash where abook_xchan = '%s' and abook_channel = %d limit 1 " ,
dbesc ( $person_obj [ 'id' ]),
intval ( $channel [ 'channel_id' ])
);
2019-05-13 06:57:57 +00:00
if ( $r ) {
2018-05-30 04:08:52 +00:00
$contact = $r [ 0 ];
}
}
2018-09-07 00:36:35 +00:00
$x = PermissionRoles :: role_perms ( 'social' );
$p = Permissions :: FilledPerms ( $x [ 'perms_connect' ]);
2018-10-25 05:43:00 +00:00
// add tag_deliver permissions to remote groups
2019-05-13 06:57:57 +00:00
if ( is_array ( $person_obj ) && $person_obj [ 'type' ] === 'Group' ) {
2018-10-25 05:43:00 +00:00
$p [ 'tag_deliver' ] = 1 ;
}
2018-09-07 00:36:35 +00:00
$their_perms = Permissions :: serialise ( $p );
2018-05-30 04:08:52 +00:00
2018-10-25 05:43:00 +00:00
2019-05-13 06:57:57 +00:00
if ( $contact && $contact [ 'abook_id' ]) {
2018-05-30 04:08:52 +00:00
// A relationship of some form already exists on this site.
switch ( $act -> type ) {
case 'Follow' :
// A second Follow request, but we haven't approved the first one
2019-05-13 06:57:57 +00:00
if ( $contact [ 'abook_pending' ]) {
2018-05-30 04:08:52 +00:00
return ;
}
// We've already approved them or followed them first
// Send an Accept back to them
2018-09-20 06:53:26 +00:00
set_abconfig ( $channel [ 'channel_id' ], $person_obj [ 'id' ], 'activitypub' , 'their_follow_id' , $their_follow_id );
2018-09-07 00:36:35 +00:00
Master :: Summon ([ 'Notifier' , 'permissions_accept' , $contact [ 'abook_id' ] ]);
2018-05-30 04:08:52 +00:00
return ;
case 'Accept' :
// They accepted our Follow request - set default permissions
2018-06-08 02:44:09 +00:00
set_abconfig ( $channel [ 'channel_id' ], $contact [ 'abook_xchan' ], 'system' , 'their_perms' , $their_perms );
2018-05-30 04:08:52 +00:00
$abook_instance = $contact [ 'abook_instance' ];
2019-05-13 06:57:57 +00:00
if ( strpos ( $abook_instance , z_root ()) === false ) {
if ( $abook_instance )
2018-05-30 04:08:52 +00:00
$abook_instance .= ',' ;
$abook_instance .= z_root ();
$r = q ( " update abook set abook_instance = '%s', abook_not_here = 0
where abook_id = % d and abook_channel = % d " ,
dbesc ( $abook_instance ),
intval ( $contact [ 'abook_id' ]),
intval ( $channel [ 'channel_id' ])
);
}
return ;
default :
return ;
}
}
// No previous relationship exists.
2019-05-13 06:57:57 +00:00
if ( $act -> type === 'Accept' ) {
2018-05-30 04:08:52 +00:00
// This should not happen unless we deleted the connection before it was accepted.
return ;
}
// From here on out we assume a Follow activity to somebody we have no existing relationship with
2018-09-20 06:53:26 +00:00
set_abconfig ( $channel [ 'channel_id' ], $person_obj [ 'id' ], 'activitypub' , 'their_follow_id' , $their_follow_id );
2018-05-30 04:08:52 +00:00
// The xchan should have been created by actor_store() above
$r = q ( " select * from xchan where xchan_hash = '%s' and xchan_network = 'activitypub' limit 1 " ,
dbesc ( $person_obj [ 'id' ])
);
2019-05-13 06:57:57 +00:00
if ( ! $r ) {
2018-05-30 04:08:52 +00:00
logger ( 'xchan not found for ' . $person_obj [ 'id' ]);
return ;
}
$ret = $r [ 0 ];
2018-09-07 00:36:35 +00:00
$p = Permissions :: connect_perms ( $channel [ 'channel_id' ]);
$my_perms = Permissions :: serialise ( $p [ 'perms' ]);
2018-05-30 04:08:52 +00:00
$automatic = $p [ 'automatic' ];
2018-12-12 23:39:17 +00:00
$closeness = PConfig :: Get ( $channel [ 'channel_id' ], 'system' , 'new_abook_closeness' , 80 );
2018-05-30 04:08:52 +00:00
$r = abook_store_lowlevel (
[
'abook_account' => intval ( $channel [ 'channel_account_id' ]),
'abook_channel' => intval ( $channel [ 'channel_id' ]),
'abook_xchan' => $ret [ 'xchan_hash' ],
'abook_closeness' => intval ( $closeness ),
'abook_created' => datetime_convert (),
'abook_updated' => datetime_convert (),
'abook_connected' => datetime_convert (),
'abook_dob' => NULL_DATE ,
'abook_pending' => intval (( $automatic ) ? 0 : 1 ),
'abook_instance' => z_root ()
]
);
2019-05-13 06:57:57 +00:00
if ( $my_perms )
2018-12-13 13:57:00 +00:00
AbConfig :: Set ( $channel [ 'channel_id' ], $ret [ 'xchan_hash' ], 'system' , 'my_perms' , $my_perms );
2018-05-30 04:08:52 +00:00
2019-05-13 06:57:57 +00:00
if ( $their_perms )
2018-12-12 23:39:17 +00:00
AbConfig :: Set ( $channel [ 'channel_id' ], $ret [ 'xchan_hash' ], 'system' , 'their_perms' , $their_perms );
2018-05-30 04:08:52 +00:00
2019-05-13 06:57:57 +00:00
if ( $r ) {
2018-05-30 04:08:52 +00:00
logger ( " New ActivityPub follower for { $channel [ 'channel_name' ] } " );
$new_connection = q ( " select * from abook left join xchan on abook_xchan = xchan_hash left join hubloc on hubloc_hash = xchan_hash where abook_channel = %d and abook_xchan = '%s' order by abook_created desc limit 1 " ,
intval ( $channel [ 'channel_id' ]),
dbesc ( $ret [ 'xchan_hash' ])
);
2019-05-13 06:57:57 +00:00
if ( $new_connection ) {
2018-09-07 00:36:35 +00:00
Enotify :: submit (
2018-05-30 04:08:52 +00:00
[
'type' => NOTIFY_INTRO ,
'from_xchan' => $ret [ 'xchan_hash' ],
'to_xchan' => $channel [ 'channel_hash' ],
'link' => z_root () . '/connedit/' . $new_connection [ 0 ][ 'abook_id' ],
]
);
2019-05-13 06:57:57 +00:00
if ( $my_perms && $automatic ) {
2018-05-30 04:08:52 +00:00
// send an Accept for this Follow activity
2018-09-07 00:36:35 +00:00
Master :: Summon ([ 'Notifier' , 'permissions_accept' , $new_connection [ 0 ][ 'abook_id' ] ]);
2018-05-30 04:08:52 +00:00
// Send back a Follow notification to them
2018-09-07 00:36:35 +00:00
Master :: Summon ([ 'Notifier' , 'permissions_create' , $new_connection [ 0 ][ 'abook_id' ] ]);
2018-05-30 04:08:52 +00:00
}
$clone = array ();
2019-05-13 06:57:57 +00:00
foreach ( $new_connection [ 0 ] as $k => $v ) {
if ( strpos ( $k , 'abook_' ) === 0 ) {
2018-05-30 04:08:52 +00:00
$clone [ $k ] = $v ;
}
}
unset ( $clone [ 'abook_id' ]);
unset ( $clone [ 'abook_account' ]);
unset ( $clone [ 'abook_channel' ]);
$abconfig = load_abconfig ( $channel [ 'channel_id' ], $clone [ 'abook_xchan' ]);
2019-05-13 06:57:57 +00:00
if ( $abconfig )
2018-05-30 04:08:52 +00:00
$clone [ 'abconfig' ] = $abconfig ;
2018-06-05 01:40:11 +00:00
Libsync :: build_sync_packet ( $channel [ 'channel_id' ], [ 'abook' => array ( $clone ) ] );
2018-05-30 04:08:52 +00:00
}
}
/* If there is a default group for this channel and permissions are automatic, add this member to it */
2019-05-13 06:57:57 +00:00
if ( $channel [ 'channel_default_group' ] && $automatic ) {
2019-02-14 23:26:37 +00:00
$g = AccessList :: rec_byhash ( $channel [ 'channel_id' ], $channel [ 'channel_default_group' ]);
2019-05-13 06:57:57 +00:00
if ( $g )
2019-02-14 23:26:37 +00:00
AccessList :: member_add ( $channel [ 'channel_id' ], '' , $ret [ 'xchan_hash' ], $g [ 'id' ]);
2018-05-30 04:08:52 +00:00
}
return ;
}
static function unfollow ( $channel , $act ) {
$contact = null ;
/* @FIXME This really needs to be a signed request. */
/* actor is unfollowing $channel */
$person_obj = $act -> actor ;
2019-05-13 06:57:57 +00:00
if ( is_array ( $person_obj )) {
2018-05-30 04:08:52 +00:00
$r = q ( " select * from abook left join xchan on abook_xchan = xchan_hash where abook_xchan = '%s' and abook_channel = %d limit 1 " ,
dbesc ( $person_obj [ 'id' ]),
intval ( $channel [ 'channel_id' ])
);
2019-05-13 06:57:57 +00:00
if ( $r ) {
2018-05-30 04:08:52 +00:00
// remove all permissions they provided
2018-06-08 02:44:09 +00:00
del_abconfig ( $channel [ 'channel_id' ], $r [ 0 ][ 'xchan_hash' ], 'system' , 'their_perms' , EMPTY_STR );
2018-05-30 04:08:52 +00:00
}
}
return ;
}
static function actor_store ( $url , $person_obj ) {
2019-05-13 06:57:57 +00:00
if ( ! is_array ( $person_obj ))
2018-05-30 04:08:52 +00:00
return ;
2018-10-09 04:27:35 +00:00
// logger('person_obj: ' . print_r($person_obj,true));
2018-09-19 23:25:05 +00:00
// We may have been passed a cached entry. If it is, and the cache duration has expired
// fetch a fresh copy before continuing.
2019-05-13 06:57:57 +00:00
if ( array_key_exists ( 'cached' , $person_obj )) {
if ( array_key_exists ( 'updated' , $person_obj ) && datetime_convert ( 'UTC' , 'UTC' , $person_obj [ 'updated' ]) < datetime_convert ( 'UTC' , 'UTC' , 'now - ' . self :: $ACTOR_CACHE_DAYS . ' days' )) {
2019-01-14 05:22:45 +00:00
$person_obj = self :: fetch ( $url );
2018-11-08 23:00:33 +00:00
}
else {
return ;
}
2018-09-19 23:25:05 +00:00
}
2018-11-09 01:02:04 +00:00
$url = $person_obj [ 'id' ];
2018-09-19 23:25:05 +00:00
2019-05-13 06:57:57 +00:00
if ( ! $url ) {
2018-11-14 01:23:09 +00:00
return ;
}
2018-05-30 04:08:52 +00:00
$name = $person_obj [ 'name' ];
2019-05-13 06:57:57 +00:00
if ( ! $name )
2018-05-30 04:08:52 +00:00
$name = $person_obj [ 'preferredUsername' ];
2019-05-13 06:57:57 +00:00
if ( ! $name )
2018-05-30 04:08:52 +00:00
$name = t ( 'Unknown' );
2018-09-17 04:41:24 +00:00
$username = $person_obj [ 'preferredUsername' ];
$h = parse_url ( $url );
2019-05-13 06:57:57 +00:00
if ( $h && $h [ 'host' ]) {
2018-09-17 04:41:24 +00:00
$username .= '@' . $h [ 'host' ];
}
2019-05-13 06:57:57 +00:00
if ( $person_obj [ 'icon' ]) {
if ( is_array ( $person_obj [ 'icon' ])) {
if ( array_key_exists ( 'url' , $person_obj [ 'icon' ]))
2018-05-30 04:08:52 +00:00
$icon = $person_obj [ 'icon' ][ 'url' ];
else
$icon = $person_obj [ 'icon' ][ 0 ][ 'url' ];
}
else
$icon = $person_obj [ 'icon' ];
}
2019-05-13 06:57:57 +00:00
if ( ! $icon ) {
2018-09-05 02:06:19 +00:00
$icon = z_root () . '/' . get_default_profile_photo ();
}
2018-05-30 04:08:52 +00:00
2018-09-24 00:03:10 +00:00
$links = false ;
$profile = false ;
2019-05-13 06:57:57 +00:00
if ( is_array ( $person_obj [ 'url' ])) {
if ( ! array_key_exists ( 0 , $person_obj [ 'url' ])) {
2018-09-24 00:03:10 +00:00
$links = [ $person_obj [ 'url' ] ];
}
else {
$links = $person_obj [ 'url' ];
}
}
2019-05-13 06:57:57 +00:00
if ( $links ) {
foreach ( $links as $link ) {
if ( array_key_exists ( 'mediaType' , $link ) && $link [ 'mediaType' ] === 'text/html' ) {
2018-09-24 00:03:10 +00:00
$profile = $link [ 'href' ];
}
}
2019-05-13 06:57:57 +00:00
if ( ! $profile ) {
2018-09-24 00:03:10 +00:00
$profile = $links [ 0 ][ 'href' ];
}
}
2019-05-13 06:57:57 +00:00
elseif ( isset ( $person_obj [ 'url' ]) && is_string ( $person_obj [ 'url' ])) {
2018-09-24 00:03:10 +00:00
$profile = $person_obj [ 'url' ];
}
2019-05-13 06:57:57 +00:00
if ( ! $profile ) {
2018-09-24 00:03:10 +00:00
$profile = $url ;
}
2018-05-30 04:08:52 +00:00
$inbox = $person_obj [ 'inbox' ];
2018-11-08 23:00:33 +00:00
// either an invalid identity or a cached entry of some kind which didn't get caught above
2019-05-13 06:57:57 +00:00
if (( ! $inbox ) || strpos ( $inbox , z_root ()) !== false ) {
2018-11-08 23:00:33 +00:00
return ;
}
2018-05-30 04:08:52 +00:00
$collections = [];
2019-05-13 06:57:57 +00:00
if ( $inbox ) {
2018-05-30 04:08:52 +00:00
$collections [ 'inbox' ] = $inbox ;
2019-05-13 06:57:57 +00:00
if ( $person_obj [ 'outbox' ])
2018-05-30 04:08:52 +00:00
$collections [ 'outbox' ] = $person_obj [ 'outbox' ];
2019-05-13 06:57:57 +00:00
if ( $person_obj [ 'followers' ])
2018-05-30 04:08:52 +00:00
$collections [ 'followers' ] = $person_obj [ 'followers' ];
2019-05-13 06:57:57 +00:00
if ( $person_obj [ 'following' ])
2018-05-30 04:08:52 +00:00
$collections [ 'following' ] = $person_obj [ 'following' ];
2019-05-13 06:57:57 +00:00
if ( $person_obj [ 'endpoints' ] && is_array ( $person_obj [ 'endpoints' ]) && $person_obj [ 'endpoints' ][ 'sharedInbox' ])
2018-05-30 04:08:52 +00:00
$collections [ 'sharedInbox' ] = $person_obj [ 'endpoints' ][ 'sharedInbox' ];
}
2019-05-13 06:57:57 +00:00
if ( isset ( $person_obj [ 'publicKey' ][ 'publicKeyPem' ])) {
if ( $person_obj [ 'id' ] === $person_obj [ 'publicKey' ][ 'owner' ]) {
2018-05-30 04:08:52 +00:00
$pubkey = $person_obj [ 'publicKey' ][ 'publicKeyPem' ];
2019-05-13 06:57:57 +00:00
if ( strstr ( $pubkey , 'RSA ' )) {
2018-12-14 20:46:03 +00:00
$pubkey = Keyutils :: rsatopem ( $pubkey );
2018-05-30 04:08:52 +00:00
}
}
}
$r = q ( " select * from xchan where xchan_hash = '%s' limit 1 " ,
dbesc ( $url )
);
2019-05-13 06:57:57 +00:00
if ( ! $r ) {
2018-05-30 04:08:52 +00:00
// create a new record
$r = xchan_store_lowlevel (
[
2018-09-05 06:42:06 +00:00
'xchan_hash' => $url ,
'xchan_guid' => $url ,
'xchan_pubkey' => $pubkey ,
2018-09-17 04:41:24 +00:00
'xchan_addr' => (( strpos ( $username , '@' )) ? $username : '' ),
2018-09-05 06:42:06 +00:00
'xchan_url' => $profile ,
'xchan_name' => $name ,
'xchan_name_date' => datetime_convert (),
'xchan_network' => 'activitypub' ,
'xchan_photo_date' => datetime_convert ( 'UTC' , 'UTC' , '1968-01-01' ),
2018-09-06 23:59:19 +00:00
'xchan_photo_l' => z_root () . '/' . get_default_profile_photo (),
'xchan_photo_m' => z_root () . '/' . get_default_profile_photo ( 80 ),
'xchan_photo_s' => z_root () . '/' . get_default_profile_photo ( 48 ),
2018-09-05 06:42:06 +00:00
'xchan_photo_mimetype' => 'image/png' ,
2018-05-30 04:08:52 +00:00
]
);
}
else {
2018-09-19 23:25:05 +00:00
// Record exists. Cache existing records for a set number of days
2018-05-30 04:08:52 +00:00
// then refetch to catch updated profile photos, names, etc.
2019-05-13 06:57:57 +00:00
if ( $r [ 0 ][ 'xchan_name_date' ] >= datetime_convert ( 'UTC' , 'UTC' , 'now - ' . self :: $ACTOR_CACHE_DAYS . ' days' )) {
2018-05-30 04:08:52 +00:00
return ;
2018-09-19 23:25:05 +00:00
}
2018-05-30 04:08:52 +00:00
// update existing record
2018-11-19 05:53:09 +00:00
$u = q ( " update xchan set xchan_name = '%s', xchan_pubkey = '%s', xchan_network = '%s', xchan_name_date = '%s' where xchan_hash = '%s' " ,
2018-05-30 04:08:52 +00:00
dbesc ( $name ),
dbesc ( $pubkey ),
dbesc ( 'activitypub' ),
dbesc ( datetime_convert ()),
dbesc ( $url )
);
2018-11-14 01:23:09 +00:00
2019-05-13 06:57:57 +00:00
if ( strpos ( $username , '@' ) && ( $r [ 0 ][ 'xchan_addr' ] !== $username )) {
2018-11-14 01:23:09 +00:00
$r = q ( " update xchan set xchan_addr = '%s' where xchan_hash = '%s' " ,
dbesc ( $username ),
dbesc ( $url )
);
}
2018-05-30 04:08:52 +00:00
}
2019-05-13 06:57:57 +00:00
if ( $collections ) {
2018-05-30 04:08:52 +00:00
set_xconfig ( $url , 'activitypub' , 'collections' , $collections );
}
2018-12-05 03:57:28 +00:00
$h = q ( " select * from hubloc where hubloc_hash = '%s' limit 1 " ,
2018-05-30 04:08:52 +00:00
dbesc ( $url )
);
$m = parse_url ( $url );
2019-05-13 06:57:57 +00:00
if ( $m ) {
2018-05-30 04:08:52 +00:00
$hostname = $m [ 'host' ];
$baseurl = $m [ 'scheme' ] . '://' . $m [ 'host' ] . (( $m [ 'port' ]) ? ':' . $m [ 'port' ] : '' );
}
2019-05-13 06:57:57 +00:00
if ( ! $h ) {
2018-05-30 04:08:52 +00:00
$r = hubloc_store_lowlevel (
[
'hubloc_guid' => $url ,
'hubloc_hash' => $url ,
2018-11-08 02:24:22 +00:00
'hubloc_id_url' => $url ,
2018-09-17 04:41:24 +00:00
'hubloc_addr' => (( strpos ( $username , '@' )) ? $username : '' ),
2018-05-30 04:08:52 +00:00
'hubloc_network' => 'activitypub' ,
'hubloc_url' => $baseurl ,
'hubloc_host' => $hostname ,
'hubloc_callback' => $inbox ,
'hubloc_updated' => datetime_convert (),
'hubloc_primary' => 1
]
);
}
2018-11-14 01:23:09 +00:00
else {
2019-05-13 06:57:57 +00:00
if ( strpos ( $username , '@' ) && ( $h [ 0 ][ 'hubloc_addr' ] !== $username )) {
2018-11-14 01:23:09 +00:00
$r = q ( " update hubloc set hubloc_addr = '%s' where hubloc_hash = '%s' " ,
dbesc ( $username ),
dbesc ( $url )
);
}
2019-05-13 06:57:57 +00:00
if ( $inbox !== $h [ 0 ][ 'hubloc_callback' ]) {
2018-12-04 11:24:41 +00:00
$r = q ( " update hubloc set hubloc_callback = '%s' where hubloc_hash = '%s' " ,
dbesc ( $inbox ),
dbesc ( $url )
);
}
2018-11-21 05:50:41 +00:00
$r = q ( " update hubloc set hubloc_updated = '%s' where hubloc_hash = '%s' " ,
dbesc ( datetime_convert ()),
dbesc ( $url )
);
2018-11-14 01:23:09 +00:00
}
2018-05-30 04:08:52 +00:00
2019-05-13 06:57:57 +00:00
if ( ! $icon )
2018-05-30 04:08:52 +00:00
$icon = z_root () . '/' . get_default_profile_photo ( 300 );
2018-09-07 00:36:35 +00:00
Master :: Summon ( [ 'Xchan_photo' , bin2hex ( $icon ), bin2hex ( $url ) ] );
2018-05-30 04:08:52 +00:00
}
2018-10-11 01:53:26 +00:00
static function drop ( $channel , $observer , $act ) {
$r = q ( " select * from item where mid = '%s' and uid = %d limit 1 " ,
$act -> obj [ 'id' ],
$channel [ 'channel_id' ]
);
2019-05-13 06:57:57 +00:00
if ( ! $r ) {
2018-10-11 01:53:26 +00:00
return ;
}
2019-05-13 06:57:57 +00:00
if ( in_array ( $observer ,[ $r [ 0 ][ 'author_xchan' ], $r [ 0 ][ 'owner_xchan' ] ])) {
2018-10-11 01:53:26 +00:00
drop_item ( $r [ 0 ][ 'id' ], false );
}
2019-05-13 06:57:57 +00:00
elseif ( in_array ( $act -> actor [ 'id' ],[ $r [ 0 ][ 'author_xchan' ], $r [ 0 ][ 'owner_xchan' ] ])) {
2018-10-11 01:53:26 +00:00
drop_item ( $r [ 0 ][ 'id' ], false );
}
}
2018-05-30 04:08:52 +00:00
static function create_action ( $channel , $observer_hash , $act ) {
2019-05-13 06:57:57 +00:00
if ( in_array ( $act -> obj [ 'type' ], [ 'Note' , 'Article' , 'Video' , 'Audio' , 'Image' ])) {
2018-05-30 04:08:52 +00:00
self :: create_note ( $channel , $observer_hash , $act );
}
}
static function announce_action ( $channel , $observer_hash , $act ) {
2019-05-13 06:57:57 +00:00
if ( in_array ( $act -> type , [ 'Announce' ])) {
2018-05-30 04:08:52 +00:00
self :: announce_note ( $channel , $observer_hash , $act );
}
}
static function like_action ( $channel , $observer_hash , $act ) {
2019-05-13 06:57:57 +00:00
if ( in_array ( $act -> obj [ 'type' ], [ 'Note' , 'Article' , 'Video' , 'Audio' , 'Image' ])) {
2018-05-30 04:08:52 +00:00
self :: like_note ( $channel , $observer_hash , $act );
}
}
// sort function width decreasing
2018-09-17 05:04:11 +00:00
static function vid_sort ( $a , $b ) {
2019-05-13 06:57:57 +00:00
if ( $a [ 'width' ] === $b [ 'width' ])
2018-05-30 04:08:52 +00:00
return 0 ;
return (( $a [ 'width' ] > $b [ 'width' ]) ? - 1 : 1 );
}
static function create_note ( $channel , $observer_hash , $act ) {
$s = [];
// Mastodon only allows visibility in public timelines if the public inbox is listed in the 'to' field.
// They are hidden in the public timeline if the public inbox is listed in the 'cc' field.
// This is not part of the activitypub protocol - we might change this to show all public posts in pubstream at some point.
$pubstream = (( is_array ( $act -> obj ) && array_key_exists ( 'to' , $act -> obj ) && in_array ( ACTIVITY_PUBLIC_INBOX , $act -> obj [ 'to' ])) ? true : false );
$is_sys_channel = is_sys_channel ( $channel [ 'channel_id' ]);
$parent = (( array_key_exists ( 'inReplyTo' , $act -> obj )) ? urldecode ( $act -> obj [ 'inReplyTo' ]) : '' );
2019-05-13 06:57:57 +00:00
if ( $parent ) {
2018-05-30 04:08:52 +00:00
$r = q ( " select * from item where uid = %d and ( mid = '%s' or mid = '%s' ) limit 1 " ,
intval ( $channel [ 'channel_id' ]),
dbesc ( $parent ),
dbesc ( basename ( $parent ))
);
2019-05-13 06:57:57 +00:00
if ( ! $r ) {
2018-05-30 04:08:52 +00:00
logger ( 'parent not found.' );
return ;
}
2019-05-13 06:57:57 +00:00
if ( $r [ 0 ][ 'owner_xchan' ] === $channel [ 'channel_hash' ]) {
if ( ! perm_is_allowed ( $channel [ 'channel_id' ], $observer_hash , 'send_stream' ) && ! ( $is_sys_channel && $pubstream )) {
2018-05-30 04:08:52 +00:00
logger ( 'no comment permission.' );
return ;
}
}
$s [ 'parent_mid' ] = $r [ 0 ][ 'mid' ];
$s [ 'owner_xchan' ] = $r [ 0 ][ 'owner_xchan' ];
$s [ 'author_xchan' ] = $observer_hash ;
}
else {
2019-05-13 06:57:57 +00:00
if ( ! perm_is_allowed ( $channel [ 'channel_id' ], $observer_hash , 'send_stream' ) && ! ( $is_sys_channel && $pubstream )) {
2018-05-30 04:08:52 +00:00
logger ( 'no permission' );
return ;
}
$s [ 'owner_xchan' ] = $s [ 'author_xchan' ] = $observer_hash ;
}
$abook = q ( " select * from abook where abook_xchan = '%s' and abook_channel = %d limit 1 " ,
dbesc ( $observer_hash ),
intval ( $channel [ 'channel_id' ])
);
2019-05-13 06:57:57 +00:00
if ( is_array ( $act -> obj )) {
2018-09-01 22:52:28 +00:00
$content = self :: get_content ( $act -> obj );
}
2018-05-30 04:08:52 +00:00
2019-05-13 06:57:57 +00:00
if ( ! $content ) {
2018-05-30 04:08:52 +00:00
logger ( 'no content' );
return ;
}
$s [ 'aid' ] = $channel [ 'channel_account_id' ];
$s [ 'uid' ] = $channel [ 'channel_id' ];
$s [ 'mid' ] = urldecode ( $act -> obj [ 'id' ]);
2018-06-04 00:49:48 +00:00
$s [ 'plink' ] = urldecode ( $act -> obj [ 'id' ]);
2018-05-30 04:08:52 +00:00
2019-05-13 06:57:57 +00:00
if ( $act -> data [ 'published' ]) {
2018-05-30 04:08:52 +00:00
$s [ 'created' ] = datetime_convert ( 'UTC' , 'UTC' , $act -> data [ 'published' ]);
}
2019-05-13 06:57:57 +00:00
elseif ( $act -> obj [ 'published' ]) {
2018-05-30 04:08:52 +00:00
$s [ 'created' ] = datetime_convert ( 'UTC' , 'UTC' , $act -> obj [ 'published' ]);
}
2019-05-13 06:57:57 +00:00
if ( $act -> data [ 'updated' ]) {
2018-05-30 04:08:52 +00:00
$s [ 'edited' ] = datetime_convert ( 'UTC' , 'UTC' , $act -> data [ 'updated' ]);
}
2019-05-13 06:57:57 +00:00
elseif ( $act -> obj [ 'updated' ]) {
2018-05-30 04:08:52 +00:00
$s [ 'edited' ] = datetime_convert ( 'UTC' , 'UTC' , $act -> obj [ 'updated' ]);
}
2019-05-13 06:57:57 +00:00
if ( ! $s [ 'created' ])
2018-05-30 04:08:52 +00:00
$s [ 'created' ] = datetime_convert ();
2019-05-13 06:57:57 +00:00
if ( ! $s [ 'edited' ])
2018-05-30 04:08:52 +00:00
$s [ 'edited' ] = $s [ 'created' ];
2019-05-13 06:57:57 +00:00
if ( ! $s [ 'parent_mid' ])
2018-05-30 04:08:52 +00:00
$s [ 'parent_mid' ] = $s [ 'mid' ];
$s [ 'title' ] = self :: bb_content ( $content , 'name' );
2018-07-18 04:56:51 +00:00
$s [ 'summary' ] = self :: bb_content ( $content , 'summary' );
$s [ 'body' ] = self :: bb_content ( $content , 'content' );
2018-05-30 04:08:52 +00:00
$s [ 'verb' ] = ACTIVITY_POST ;
$s [ 'obj_type' ] = ACTIVITY_OBJ_NOTE ;
2019-05-29 04:10:32 +00:00
$generator = $act -> get_property_obj ( 'generator' );
if ( ! $generator )
$generator = $act -> get_property_obj ( 'generator' , $act -> obj );
2018-05-31 03:32:59 +00:00
2019-05-29 04:10:32 +00:00
if ( $generator && array_key_exists ( 'type' , $generator )
&& in_array ( $generator [ 'type' ], [ 'Application' , 'Service' ] ) && array_key_exists ( 'name' , $generator )) {
$s [ 'app' ] = escape_tags ( $generator [ 'name' ]);
2018-05-31 03:32:59 +00:00
}
2018-05-30 04:08:52 +00:00
2019-05-13 06:57:57 +00:00
if ( $channel [ 'channel_system' ]) {
if ( ! MessageFilter :: evaluate ( $s , get_config ( 'system' , 'pubstream_incl' ), get_config ( 'system' , 'pubstream_excl' ))) {
2018-05-30 04:08:52 +00:00
logger ( 'post is filtered' );
return ;
}
}
2019-05-13 06:57:57 +00:00
if ( ! post_is_importable ( $channel [ 'channel_id' ], $s , $abook [ 0 ])) {
2018-12-11 23:57:15 +00:00
logger ( 'post is filtered' );
return ;
2018-05-30 04:08:52 +00:00
}
2019-05-13 06:57:57 +00:00
if ( $act -> obj [ 'conversation' ]) {
2018-05-30 04:08:52 +00:00
set_iconfig ( $s , 'ostatus' , 'conversation' , $act -> obj [ 'conversation' ], 1 );
}
$a = self :: decode_taxonomy ( $act -> obj );
2019-05-13 06:57:57 +00:00
if ( $a ) {
2018-05-30 04:08:52 +00:00
$s [ 'term' ] = $a ;
}
$a = self :: decode_attachment ( $act -> obj );
2019-05-13 06:57:57 +00:00
if ( $a ) {
2018-05-30 04:08:52 +00:00
$s [ 'attach' ] = $a ;
}
2019-05-13 06:57:57 +00:00
if ( $act -> obj [ 'type' ] === 'Note' && $s [ 'attach' ]) {
2018-10-11 04:36:55 +00:00
$s [ 'body' ] .= self :: bb_attach ( $s [ 'attach' ], $s [ 'body' ]);
2018-05-30 04:08:52 +00:00
}
// we will need a hook here to extract magnet links e.g. peertube
// right now just link to the largest mp4 we find that will fit in our
// standard content region
2019-05-13 06:57:57 +00:00
if ( $act -> obj [ 'type' ] === 'Video' ) {
2018-05-30 04:08:52 +00:00
$vtypes = [
'video/mp4' ,
'video/ogg' ,
'video/webm'
];
$mps = [];
2019-05-13 06:57:57 +00:00
if ( array_key_exists ( 'url' , $act -> obj ) && is_array ( $act -> obj [ 'url' ])) {
foreach ( $act -> obj [ 'url' ] as $vurl ) {
if ( in_array ( $vurl [ 'mimeType' ], $vtypes )) {
if ( ! array_key_exists ( 'width' , $vurl )) {
2018-05-30 04:08:52 +00:00
$vurl [ 'width' ] = 0 ;
}
$mps [] = $vurl ;
}
}
}
2019-05-13 06:57:57 +00:00
if ( $mps ) {
2018-09-17 05:04:11 +00:00
usort ( $mps ,[ __CLASS__ , 'vid_sort' ]);
2019-05-13 06:57:57 +00:00
foreach ( $mps as $m ) {
if ( intval ( $m [ 'width' ]) < 500 ) {
2018-05-30 04:08:52 +00:00
$s [ 'body' ] .= " \n \n " . '[video]' . $m [ 'href' ] . '[/video]' ;
break ;
}
}
}
}
2019-05-13 06:57:57 +00:00
if ( $act -> recips && ( ! in_array ( ACTIVITY_PUBLIC_INBOX , $act -> recips )))
2018-05-30 04:08:52 +00:00
$s [ 'item_private' ] = 1 ;
set_iconfig ( $s , 'activitypub' , 'recips' , $act -> raw_recips );
2019-05-13 06:57:57 +00:00
if ( $parent ) {
2018-05-30 04:08:52 +00:00
set_iconfig ( $s , 'activitypub' , 'rawmsg' , $act -> raw , 1 );
}
$x = null ;
$r = q ( " select created, edited from item where mid = '%s' and uid = %d limit 1 " ,
dbesc ( $s [ 'mid' ]),
intval ( $s [ 'uid' ])
);
2019-05-13 06:57:57 +00:00
if ( $r ) {
if ( $s [ 'edited' ] > $r [ 0 ][ 'edited' ]) {
2018-05-30 04:08:52 +00:00
$x = item_store_update ( $s );
}
else {
return ;
}
}
else {
$x = item_store ( $s );
}
2019-05-13 06:57:57 +00:00
if ( is_array ( $x ) && $x [ 'item_id' ]) {
if ( $parent ) {
if ( $s [ 'owner_xchan' ] === $channel [ 'channel_hash' ]) {
2018-05-30 04:08:52 +00:00
// We are the owner of this conversation, so send all received comments back downstream
2018-09-07 00:36:35 +00:00
Master :: Summon ( array ( 'Notifier' , 'comment-import' , $x [ 'item_id' ]));
2018-05-30 04:08:52 +00:00
}
$r = q ( " select * from item where id = %d limit 1 " ,
intval ( $x [ 'item_id' ])
);
2019-05-13 06:57:57 +00:00
if ( $r ) {
2018-05-30 04:08:52 +00:00
send_status_notifications ( $x [ 'item_id' ], $r [ 0 ]);
}
}
sync_an_item ( $channel [ 'channel_id' ], $x [ 'item_id' ]);
}
}
2018-05-30 06:30:18 +00:00
2018-08-31 02:19:32 +00:00
static function share_bb ( $obj ) {
// @fixme - error check and set defaults
$name = urlencode ( $obj [ 'actor' ][ 'name' ]);
$profile = $obj [ 'actor' ][ 'id' ];
$photo = $obj [ 'icon' ][ 'url' ];
$s = " \r \n [share author=' " . $name .
" ' profile=' " . $profile .
" ' avatar=' " . $photo .
" ' link=' " . $act -> obj [ 'id' ] .
" ' auth=' " . (( is_matrix_url ( $act -> obj [ 'id' ])) ? 'true' : 'false' ) .
" ' posted=' " . $act -> obj [ 'published' ] .
" ' message_id=' " . $act -> obj [ 'id' ] .
" '] " ;
return $s ;
}
2018-10-14 22:47:57 +00:00
static function get_actor_bbmention ( $id ) {
$x = q ( " select * from hubloc left join xchan on hubloc_hash = xchan_hash where hubloc_hash = '%s' or hubloc_id_url = '%s' limit 1 " ,
dbesc ( $id ),
dbesc ( $id )
);
2019-05-13 06:57:57 +00:00
if ( $x ) {
2018-10-14 22:47:57 +00:00
return sprintf ( '@[zrl=%s]%s[/zrl]' , $x [ 0 ][ 'xchan_url' ], $x [ 0 ][ 'xchan_name' ]);
}
return '@{' . $id . '}' ;
}
2018-05-30 06:30:18 +00:00
static function decode_note ( $act ) {
2018-10-14 22:47:57 +00:00
$response_activity = false ;
2018-05-30 06:30:18 +00:00
$s = [];
2019-05-13 06:57:57 +00:00
if ( is_array ( $act -> obj )) {
2018-09-01 22:52:28 +00:00
$content = self :: get_content ( $act -> obj );
}
2018-09-04 04:30:39 +00:00
2018-06-28 01:43:39 +00:00
$s [ 'owner_xchan' ] = $act -> actor [ 'id' ];
2018-05-30 06:30:18 +00:00
$s [ 'author_xchan' ] = $act -> actor [ 'id' ];
2019-02-03 19:45:29 +00:00
// ensure we store the original actor
self :: actor_store ( $act -> actor [ 'id' ], $act -> actor );
2018-09-03 03:16:33 +00:00
$s [ 'mid' ] = $act -> obj [ 'id' ];
2018-06-28 01:43:39 +00:00
$s [ 'parent_mid' ] = $act -> parent_id ;
2018-05-30 06:30:18 +00:00
2019-05-13 06:57:57 +00:00
if ( $act -> data [ 'published' ]) {
2018-05-30 06:30:18 +00:00
$s [ 'created' ] = datetime_convert ( 'UTC' , 'UTC' , $act -> data [ 'published' ]);
}
2019-05-13 06:57:57 +00:00
elseif ( $act -> obj [ 'published' ]) {
2018-05-30 06:30:18 +00:00
$s [ 'created' ] = datetime_convert ( 'UTC' , 'UTC' , $act -> obj [ 'published' ]);
}
2019-05-13 06:57:57 +00:00
if ( $act -> data [ 'updated' ]) {
2018-05-30 06:30:18 +00:00
$s [ 'edited' ] = datetime_convert ( 'UTC' , 'UTC' , $act -> data [ 'updated' ]);
}
2019-05-13 06:57:57 +00:00
elseif ( $act -> obj [ 'updated' ]) {
2018-05-30 06:30:18 +00:00
$s [ 'edited' ] = datetime_convert ( 'UTC' , 'UTC' , $act -> obj [ 'updated' ]);
}
2019-05-28 23:42:32 +00:00
if ( $act -> data [ 'expires' ]) {
$s [ 'expires' ] = datetime_convert ( 'UTC' , 'UTC' , $act -> data [ 'expires' ]);
}
elseif ( $act -> obj [ 'expires' ]) {
$s [ 'expires' ] = datetime_convert ( 'UTC' , 'UTC' , $act -> obj [ 'expires' ]);
}
2018-05-30 06:30:18 +00:00
2019-05-29 04:10:32 +00:00
if ( in_array ( $act -> type , [ 'Like' , 'Dislike' , 'Flag' , 'Block' , 'Announce' , 'Accept' , 'Reject' ,
'TentativeAccept' , 'TentativeReject' , 'emojiReaction' ])) {
2018-10-14 22:47:57 +00:00
$response_activity = true ;
2018-09-26 00:50:12 +00:00
$s [ 'mid' ] = $act -> id ;
$s [ 'parent_mid' ] = $act -> obj [ 'id' ];
2019-09-10 04:03:33 +00:00
$s [ 'replyto' ] = $act -> replyto ;
2018-09-26 00:50:12 +00:00
// over-ride the object timestamp with the activity
2019-05-13 06:57:57 +00:00
if ( $act -> data [ 'published' ]) {
2018-09-26 00:50:12 +00:00
$s [ 'created' ] = datetime_convert ( 'UTC' , 'UTC' , $act -> data [ 'published' ]);
}
2019-05-13 06:57:57 +00:00
if ( $act -> data [ 'updated' ]) {
2018-10-14 22:47:57 +00:00
$s [ 'edited' ] = datetime_convert ( 'UTC' , 'UTC' , $act -> data [ 'updated' ]);
}
2018-10-10 05:32:20 +00:00
$obj_actor = (( isset ( $act -> obj [ 'actor' ])) ? $act -> obj [ 'actor' ] : $act -> get_actor ( 'attributedTo' , $act -> obj ));
2019-06-17 03:35:47 +00:00
// if the object is an actor it is not really a response activity, reset a couple of things
if ( ActivityStreams :: is_an_actor ( $act -> obj [ 'type' ])) {
$obj_actor = $act -> actor ;
$s [ 'parent_mid' ] = $s [ 'mid' ];
}
2018-10-14 22:47:57 +00:00
// ensure we store the original actor
self :: actor_store ( $obj_actor [ 'id' ], $obj_actor );
$mention = self :: get_actor_bbmention ( $obj_actor [ 'id' ]);
2018-10-10 05:32:20 +00:00
2019-05-13 06:57:57 +00:00
if ( $act -> type === 'Like' ) {
2019-06-17 03:35:47 +00:00
$content [ 'content' ] = sprintf ( t ( 'Likes %1$s\'s %2$s' ), $mention , (( ActivityStreams :: is_an_actor ( $act -> obj [ 'type' ])) ? t ( 'Profile' ) : $act -> obj [ 'type' ])) . EOL . EOL . $content [ 'content' ];
2018-09-26 00:50:12 +00:00
}
2019-05-13 06:57:57 +00:00
if ( $act -> type === 'Dislike' ) {
2019-06-17 03:35:47 +00:00
$content [ 'content' ] = sprintf ( t ( 'Doesn\'t like %1$s\'s %2$s' ), $mention , (( ActivityStreams :: is_an_actor ( $act -> obj [ 'type' ])) ? t ( 'Profile' ) : $act -> obj [ 'type' ])) . EOL . EOL . $content [ 'content' ];
2018-09-26 00:50:12 +00:00
}
2019-05-13 06:57:57 +00:00
if ( $act -> type === 'Accept' && $act -> obj [ 'type' ] === 'Event' ) {
2019-02-19 20:31:39 +00:00
$content [ 'content' ] = sprintf ( t ( 'Will attend %1$s\'s %2$s' ), $mention , $act -> obj [ 'type' ]) . EOL . EOL . $content [ 'content' ];
2018-10-16 20:56:46 +00:00
}
2019-05-13 06:57:57 +00:00
if ( $act -> type === 'Reject' && $act -> obj [ 'type' ] === 'Event' ) {
2019-02-19 20:31:39 +00:00
$content [ 'content' ] = sprintf ( t ( 'Will not attend %1$s\'s %2$s' ), $mention , $act -> obj [ 'type' ]) . EOL . EOL . $content [ 'content' ];
2018-10-16 20:56:46 +00:00
}
2019-05-13 06:57:57 +00:00
if ( $act -> type === 'TentativeAccept' && $act -> obj [ 'type' ] === 'Event' ) {
2019-02-19 20:31:39 +00:00
$content [ 'content' ] = sprintf ( t ( 'May attend %1$s\'s %2$s' ), $mention , $act -> obj [ 'type' ]) . EOL . EOL . $content [ 'content' ];
2018-10-16 20:56:46 +00:00
}
2019-05-29 04:10:32 +00:00
if ( $act -> type === 'TentativeReject' && $act -> obj [ 'type' ] === 'Event' ) {
$content [ 'content' ] = sprintf ( t ( 'May not attend %1$s\'s %2$s' ), $mention , $act -> obj [ 'type' ]) . EOL . EOL . $content [ 'content' ];
}
2019-05-13 06:57:57 +00:00
if ( $act -> type === 'Announce' ) {
2018-10-15 02:02:15 +00:00
$content [ 'content' ] = sprintf ( t ( '🔁 Repeated %1$s\'s %2$s' ), $mention , $act -> obj [ 'type' ]);
2018-08-01 03:15:00 +00:00
}
2019-05-03 04:21:10 +00:00
if ( $act -> type === 'emojiReaction' ) {
$content [ 'content' ] = (( $act -> tgt && $act -> tgt [ 'type' ] === 'Image' ) ? '[img=32x32]' . $act -> tgt [ 'url' ] . '[/img]' : '&#x' . $act -> tgt [ 'name' ] . ';' );
}
2018-08-01 02:50:39 +00:00
}
2018-09-03 03:16:33 +00:00
2019-05-13 06:57:57 +00:00
if ( ! $s [ 'created' ])
2018-09-26 00:50:12 +00:00
$s [ 'created' ] = datetime_convert ();
2019-05-13 06:57:57 +00:00
if ( ! $s [ 'edited' ])
2018-09-26 00:50:12 +00:00
$s [ 'edited' ] = $s [ 'created' ];
2018-12-12 05:03:50 +00:00
$s [ 'title' ] = (( $response_activity ) ? EMPTY_STR : self :: bb_content ( $content , 'name' ));
2018-09-03 03:16:33 +00:00
$s [ 'summary' ] = self :: bb_content ( $content , 'summary' );
2018-10-15 08:21:47 +00:00
$s [ 'body' ] = (( self :: bb_content ( $content , 'bbcode' ) && ( ! $response_activity )) ? self :: bb_content ( $content , 'bbcode' ) : self :: bb_content ( $content , 'content' ));
2018-08-01 02:50:39 +00:00
2019-05-15 05:16:49 +00:00
// handle some of the more widely used of the numerous and varied ways of deleting something
if ( $act -> type === 'Tombstone' ) {
$s [ 'item_deleted' ] = 1 ;
}
if ( $act -> type === 'Create' && $act -> obj [ 'type' ] === 'Tombstone' ) {
$s [ 'item_deleted' ] = 1 ;
}
2019-06-12 03:16:38 +00:00
if ( $act -> type === 'Delete' ) {
2018-08-04 02:26:16 +00:00
$s [ 'item_deleted' ] = 1 ;
}
2019-06-19 02:21:31 +00:00
2019-06-11 04:19:19 +00:00
2019-02-01 20:47:34 +00:00
$s [ 'verb' ] = self :: activity_mapper ( $act -> type );
2018-06-28 01:43:39 +00:00
$s [ 'obj_type' ] = self :: activity_obj_mapper ( $act -> obj [ 'type' ]);
$s [ 'obj' ] = $act -> obj ;
2019-05-13 06:57:57 +00:00
if ( is_array ( $obj ) && array_path_exists ( 'actor/id' , $s [ 'obj' ])) {
2018-12-26 23:33:01 +00:00
$s [ 'obj' ][ 'actor' ] = $s [ 'obj' ][ 'actor' ][ 'id' ];
}
// @todo add target if present
2018-05-30 06:30:18 +00:00
2019-05-29 04:10:32 +00:00
$generator = $act -> get_property_obj ( 'generator' );
if (( ! $generator ) && ( ! $response_activity )) {
$generator = $act -> get_property_obj ( 'generator' , $act -> obj );
2018-10-14 22:47:57 +00:00
}
2018-06-05 06:19:16 +00:00
2019-05-29 04:10:32 +00:00
if ( $generator && array_key_exists ( 'type' , $generator )
&& in_array ( $generator [ 'type' ], [ 'Application' , 'Service' ] ) && array_key_exists ( 'name' , $generator )) {
$s [ 'app' ] = escape_tags ( $generator [ 'name' ]);
2018-06-05 06:19:16 +00:00
}
2018-10-14 22:47:57 +00:00
2019-05-13 06:57:57 +00:00
if ( ! $response_activity ) {
2018-10-14 22:47:57 +00:00
$a = self :: decode_taxonomy ( $act -> obj );
2019-05-13 06:57:57 +00:00
if ( $a ) {
2018-10-14 22:47:57 +00:00
$s [ 'term' ] = $a ;
2019-05-13 06:57:57 +00:00
foreach ( $a as $b ) {
if ( $b [ 'ttype' ] === TERM_EMOJI ) {
2018-10-14 22:47:57 +00:00
$s [ 'title' ] = str_replace ( $b [ 'term' ], '[img=16x16]' . $b [ 'url' ] . '[/img]' , $s [ 'title' ]);
$s [ 'summary' ] = str_replace ( $b [ 'term' ], '[img=16x16]' . $b [ 'url' ] . '[/img]' , $s [ 'summary' ]);
$s [ 'body' ] = str_replace ( $b [ 'term' ], '[img=16x16]' . $b [ 'url' ] . '[/img]' , $s [ 'body' ]);
}
2018-09-06 23:40:13 +00:00
}
}
2018-05-30 06:30:18 +00:00
2018-10-14 22:47:57 +00:00
$a = self :: decode_attachment ( $act -> obj );
2019-05-13 06:57:57 +00:00
if ( $a ) {
2018-10-14 22:47:57 +00:00
$s [ 'attach' ] = $a ;
}
2018-05-30 06:30:18 +00:00
}
2019-05-13 06:57:57 +00:00
if ( $act -> obj [ 'type' ] === 'Note' && $s [ 'attach' ]) {
2018-10-11 04:36:55 +00:00
$s [ 'body' ] .= self :: bb_attach ( $s [ 'attach' ], $s [ 'body' ]);
2018-09-01 22:51:44 +00:00
}
2018-05-30 06:30:18 +00:00
// we will need a hook here to extract magnet links e.g. peertube
// right now just link to the largest mp4 we find that will fit in our
// standard content region
2019-05-13 06:57:57 +00:00
if ( ! $response_activity ) {
if ( $act -> obj [ 'type' ] === 'Video' ) {
2018-05-30 06:30:18 +00:00
2018-10-14 22:47:57 +00:00
$vtypes = [
'video/mp4' ,
'video/ogg' ,
'video/webm'
];
2018-05-30 06:30:18 +00:00
2018-10-14 22:47:57 +00:00
$mps = [];
$ptr = null ;
2018-09-26 06:50:35 +00:00
2019-05-13 06:57:57 +00:00
if ( array_key_exists ( 'url' , $act -> obj )) {
if ( is_array ( $act -> obj [ 'url' ])) {
if ( array_key_exists ( 0 , $act -> obj [ 'url' ])) {
2018-10-14 22:47:57 +00:00
$ptr = $act -> obj [ 'url' ];
}
else {
$ptr = [ $act -> obj [ 'url' ] ];
}
2019-05-13 06:57:57 +00:00
foreach ( $ptr as $vurl ) {
2018-10-16 01:27:03 +00:00
// peertube uses the non-standard element name 'mimeType' here
2019-05-13 06:57:57 +00:00
if ( array_key_exists ( 'mimeType' , $vurl )) {
if ( in_array ( $vurl [ 'mimeType' ], $vtypes )) {
if ( ! array_key_exists ( 'width' , $vurl )) {
2018-10-16 01:27:03 +00:00
$vurl [ 'width' ] = 0 ;
}
$mps [] = $vurl ;
}
}
2019-05-13 06:57:57 +00:00
elseif ( array_key_exists ( 'mediaType' , $vurl )) {
if ( in_array ( $vurl [ 'mediaType' ], $vtypes )) {
if ( ! array_key_exists ( 'width' , $vurl )) {
2018-10-16 01:27:03 +00:00
$vurl [ 'width' ] = 0 ;
}
$mps [] = $vurl ;
2018-10-14 22:47:57 +00:00
}
2018-09-26 06:50:35 +00:00
}
2018-05-30 06:30:18 +00:00
}
}
2019-05-13 06:57:57 +00:00
if ( $mps ) {
2018-10-14 22:47:57 +00:00
usort ( $mps ,[ __CLASS__ , 'vid_sort' ]);
2019-05-13 06:57:57 +00:00
foreach ( $mps as $m ) {
if ( intval ( $m [ 'width' ]) < 500 && self :: media_not_in_body ( $m [ 'href' ], $s [ 'body' ])) {
2018-10-14 22:47:57 +00:00
$s [ 'body' ] .= " \n \n " . '[video]' . $m [ 'href' ] . '[/video]' ;
break ;
}
2018-09-26 06:50:35 +00:00
}
2018-05-30 06:30:18 +00:00
}
2019-05-13 06:57:57 +00:00
elseif ( is_string ( $act -> obj [ 'url' ]) && self :: media_not_in_body ( $act -> obj [ 'url' ], $s [ 'body' ])) {
2018-10-14 22:47:57 +00:00
$s [ 'body' ] .= " \n \n " . '[video]' . $act -> obj [ 'url' ] . '[/video]' ;
}
2018-09-26 06:50:35 +00:00
}
2018-05-30 06:30:18 +00:00
}
2019-05-13 06:57:57 +00:00
if ( $act -> obj [ 'type' ] === 'Audio' ) {
2018-09-20 05:44:22 +00:00
2018-10-14 22:47:57 +00:00
$atypes = [
'audio/mpeg' ,
'audio/ogg' ,
'audio/wav'
];
2018-09-20 05:44:22 +00:00
2018-10-14 22:47:57 +00:00
$ptr = null ;
2018-09-26 06:50:35 +00:00
2019-05-13 06:57:57 +00:00
if ( array_key_exists ( 'url' , $act -> obj )) {
if ( is_array ( $act -> obj [ 'url' ])) {
if ( array_key_exists ( 0 , $act -> obj [ 'url' ])) {
2018-10-14 22:47:57 +00:00
$ptr = $act -> obj [ 'url' ];
}
else {
$ptr = [ $act -> obj [ 'url' ] ];
}
2019-05-13 06:57:57 +00:00
foreach ( $ptr as $vurl ) {
if ( in_array ( $vurl [ 'mediaType' ], $atypes ) && self :: media_not_in_body ( $vurl [ 'href' ], $s [ 'body' ])) {
2018-10-14 22:47:57 +00:00
$s [ 'body' ] .= " \n \n " . '[audio]' . $vurl [ 'href' ] . '[/audio]' ;
break ;
}
2018-09-26 06:50:35 +00:00
}
}
2019-05-13 06:57:57 +00:00
elseif ( is_string ( $act -> obj [ 'url' ]) && self :: media_not_in_body ( $act -> obj [ 'url' ], $s [ 'body' ])) {
2018-10-14 22:47:57 +00:00
$s [ 'body' ] .= " \n \n " . '[audio]' . $act -> obj [ 'url' ] . '[/audio]' ;
}
2018-09-26 06:50:35 +00:00
}
2018-10-14 22:47:57 +00:00
}
2018-09-20 05:44:22 +00:00
2019-05-13 06:57:57 +00:00
if ( $act -> obj [ 'type' ] === 'Image' && strpos ( $s [ 'body' ], 'zrl=' ) === false ) {
2018-09-26 06:50:35 +00:00
2018-10-14 22:47:57 +00:00
$ptr = null ;
2018-09-26 06:50:35 +00:00
2019-05-13 06:57:57 +00:00
if ( array_key_exists ( 'url' , $act -> obj )) {
if ( is_array ( $act -> obj [ 'url' ])) {
if ( array_key_exists ( 0 , $act -> obj [ 'url' ])) {
2018-10-14 22:47:57 +00:00
$ptr = $act -> obj [ 'url' ];
}
else {
$ptr = [ $act -> obj [ 'url' ] ];
}
2019-05-13 06:57:57 +00:00
foreach ( $ptr as $vurl ) {
if ( strpos ( $s [ 'body' ], $vurl [ 'href' ]) === false ) {
2018-10-14 22:47:57 +00:00
$s [ 'body' ] .= " \n \n " . '[zmg]' . $vurl [ 'href' ] . '[/zmg]' ;
break ;
}
2018-09-26 06:50:35 +00:00
}
}
2019-05-13 06:57:57 +00:00
elseif ( is_string ( $act -> obj [ 'url' ])) {
if ( strpos ( $s [ 'body' ], $act -> obj [ 'url' ]) === false ) {
2018-10-14 22:47:57 +00:00
$s [ 'body' ] .= " \n \n " . '[zmg]' . $act -> obj [ 'url' ] . '[/zmg]' ;
}
2018-09-20 05:44:22 +00:00
}
}
}
2018-11-01 23:53:45 +00:00
2019-05-13 06:57:57 +00:00
if ( $act -> obj [ 'type' ] === 'Page' && ! $s [ 'body' ]) {
2018-11-01 23:53:45 +00:00
2018-11-14 01:23:09 +00:00
$ptr = null ;
$purl = EMPTY_STR ;
2018-11-01 23:53:45 +00:00
2019-05-13 06:57:57 +00:00
if ( array_key_exists ( 'url' , $act -> obj )) {
if ( is_array ( $act -> obj [ 'url' ])) {
if ( array_key_exists ( 0 , $act -> obj [ 'url' ])) {
2018-11-01 23:53:45 +00:00
$ptr = $act -> obj [ 'url' ];
}
else {
$ptr = [ $act -> obj [ 'url' ] ];
}
2019-05-13 06:57:57 +00:00
foreach ( $ptr as $vurl ) {
if ( array_key_exists ( 'mediaType' , $vurl ) && $vurl [ 'mediaType' ] === 'text/html' ) {
2018-11-14 01:23:09 +00:00
$purl = $vurl [ 'href' ];
2018-11-01 23:53:45 +00:00
break ;
}
2019-05-13 06:57:57 +00:00
elseif ( array_key_exists ( 'mimeType' , $vurl ) && $vurl [ 'mimeType' ] === 'text/html' ) {
2018-11-14 01:23:09 +00:00
$purl = $vurl [ 'href' ];
2018-11-13 03:38:21 +00:00
break ;
}
2018-11-01 23:53:45 +00:00
}
}
2019-05-13 06:57:57 +00:00
elseif ( is_string ( $act -> obj [ 'url' ])) {
2018-11-14 01:23:09 +00:00
$purl = $act -> obj [ 'url' ];
}
2019-05-13 06:57:57 +00:00
if ( $purl ) {
2018-11-14 01:23:09 +00:00
$li = z_fetch_url ( z_root () . '/linkinfo?binurl=' . bin2hex ( $purl ));
2019-05-13 06:57:57 +00:00
if ( $li [ 'success' ] && $li [ 'body' ]) {
2018-11-14 01:23:09 +00:00
$s [ 'body' ] .= " \n " . $li [ 'body' ];
}
else {
$s [ 'body' ] .= " \n \n " . $purl ;
}
2018-11-01 23:53:45 +00:00
}
}
}
2018-09-20 05:44:22 +00:00
}
2018-10-14 22:47:57 +00:00
2019-05-13 06:57:57 +00:00
if ( in_array ( $act -> obj [ 'type' ],[ 'Note' , 'Article' , 'Page' ])) {
2018-09-26 06:50:35 +00:00
$ptr = null ;
2019-05-13 06:57:57 +00:00
if ( array_key_exists ( 'url' , $act -> obj )) {
if ( is_array ( $act -> obj [ 'url' ])) {
if ( array_key_exists ( 0 , $act -> obj [ 'url' ])) {
2018-09-26 06:50:35 +00:00
$ptr = $act -> obj [ 'url' ];
}
else {
$ptr = [ $act -> obj [ 'url' ] ];
}
2019-05-13 06:57:57 +00:00
foreach ( $ptr as $vurl ) {
if ( array_key_exists ( 'mediaType' , $vurl ) && $vurl [ 'mediaType' ] === 'text/html' ) {
2018-09-26 06:50:35 +00:00
$s [ 'plink' ] = $vurl [ 'href' ];
break ;
}
}
}
2019-05-13 06:57:57 +00:00
elseif ( is_string ( $act -> obj [ 'url' ])) {
2018-09-26 06:50:35 +00:00
$s [ 'plink' ] = $act -> obj [ 'url' ];
}
}
}
2018-09-20 05:44:22 +00:00
2019-05-13 06:57:57 +00:00
if ( ! $s [ 'plink' ]) {
2018-09-26 06:50:35 +00:00
$s [ 'plink' ] = $s [ 'mid' ];
}
2018-09-20 05:44:22 +00:00
2019-05-13 06:57:57 +00:00
if ( $act -> recips && ( ! in_array ( ACTIVITY_PUBLIC_INBOX , $act -> recips )))
2018-05-30 06:30:18 +00:00
$s [ 'item_private' ] = 1 ;
2019-06-19 02:21:31 +00:00
2019-06-21 06:17:04 +00:00
if ( array_key_exists ( 'directMessage' , $act -> obj ) && intval ( $act -> obj [ 'directMessage' ])) {
2019-06-19 02:21:31 +00:00
$s [ 'item_private' ] = 2 ;
}
2018-05-30 06:30:18 +00:00
set_iconfig ( $s , 'activitypub' , 'recips' , $act -> raw_recips );
2019-05-13 06:57:57 +00:00
if ( $parent ) {
2018-05-30 06:30:18 +00:00
set_iconfig ( $s , 'activitypub' , 'rawmsg' , $act -> raw , 1 );
}
return $s ;
}
2018-09-01 22:52:28 +00:00
static function store ( $channel , $observer_hash , $act , $item , $fetch_parents = true ) {
2018-08-20 03:39:23 +00:00
$is_sys_channel = is_sys_channel ( $channel [ 'channel_id' ]);
2018-11-19 05:53:09 +00:00
$is_child_node = false ;
2018-08-20 03:39:23 +00:00
// Mastodon only allows visibility in public timelines if the public inbox is listed in the 'to' field.
// They are hidden in the public timeline if the public inbox is listed in the 'cc' field.
// This is not part of the activitypub protocol - we might change this to show all public posts in pubstream at some point.
2018-11-06 02:47:21 +00:00
$pubstream = (( is_array ( $act -> obj ) && array_key_exists ( 'to' , $act -> obj ) && is_array ( $act -> obj [ 'to' ]) && in_array ( ACTIVITY_PUBLIC_INBOX , $act -> obj [ 'to' ])) ? true : false );
2018-08-20 03:39:23 +00:00
2019-05-13 06:57:57 +00:00
if ( $item [ 'parent_mid' ] && $item [ 'parent_mid' ] !== $item [ 'mid' ]) {
2018-11-19 05:53:09 +00:00
$is_child_node = true ;
}
$allowed = false ;
if ( $is_child_node ) {
2018-11-20 01:30:22 +00:00
// in ActivityPub, anybody can post comments
$allowed = true ;
2018-11-19 05:53:09 +00:00
}
elseif ( perm_is_allowed ( $channel [ 'channel_id' ], $observer_hash , 'send_stream' ) || ( $is_sys_channel && $pubstream )) {
$allowed = true ;
}
2018-11-22 01:46:08 +00:00
if ( tgroup_check ( $channel [ 'channel_id' ], $item ) && ( ! $is_child_node )) {
// for forum deliveries, make sure we keep a copy of the signed original
set_iconfig ( $item , 'activitypub' , 'rawmsg' , $act -> raw , 1 );
$allowed = true ;
}
2019-05-13 06:57:57 +00:00
if ( intval ( $channel [ 'channel_system' ])) {
2019-01-05 22:47:21 +00:00
2019-05-13 06:57:57 +00:00
if ( ! check_pubstream_channelallowed ( $observer_hash )) {
2019-01-05 22:47:21 +00:00
$allowed = false ;
}
// don't allow pubstream posts if the sender even has a clone on a pubstream blacklisted site
$h = q ( " select hubloc_url from hubloc where hubloc_hash = '%s' " ,
dbesc ( $observer_hash )
);
2019-05-13 06:57:57 +00:00
if ( $h ) {
foreach ( $h as $hub ) {
if ( ! check_pubstream_siteallowed ( $hub [ 'hubloc_url' ])) {
2019-01-05 22:47:21 +00:00
$allowed = false ;
break ;
}
}
}
}
2018-11-22 01:46:08 +00:00
2019-05-13 06:57:57 +00:00
if ( ! $allowed ) {
2018-08-20 03:39:23 +00:00
logger ( 'no permission' );
return ;
}
2019-05-13 06:57:57 +00:00
if ( is_array ( $act -> obj )) {
2018-09-01 22:52:28 +00:00
$content = self :: get_content ( $act -> obj );
}
2019-05-13 06:57:57 +00:00
if ( ! $content ) {
2018-08-20 03:39:23 +00:00
logger ( 'no content' );
return ;
}
$item [ 'aid' ] = $channel [ 'channel_account_id' ];
$item [ 'uid' ] = $channel [ 'channel_id' ];
2018-12-12 04:30:47 +00:00
// Some authors may be zot6 authors in which case we want to store their nomadic identity
// instead of their ActivityPub identity
$item [ 'author_xchan' ] = self :: find_best_identity ( $item [ 'author_xchan' ]);
$item [ 'owner_xchan' ] = self :: find_best_identity ( $item [ 'owner_xchan' ]);
2019-05-13 06:57:57 +00:00
if ( ! ( $item [ 'author_xchan' ] && $item [ 'owner_xchan' ])) {
2018-09-04 04:30:39 +00:00
logger ( 'owner or author missing.' );
return ;
}
2018-12-12 04:30:47 +00:00
2019-05-13 06:57:57 +00:00
if ( $channel [ 'channel_system' ]) {
if ( ! MessageFilter :: evaluate ( $item , get_config ( 'system' , 'pubstream_incl' ), get_config ( 'system' , 'pubstream_excl' ))) {
2018-08-20 03:39:23 +00:00
logger ( 'post is filtered' );
return ;
}
}
$abook = q ( " select * from abook where abook_xchan = '%s' and abook_channel = %d limit 1 " ,
dbesc ( $observer_hash ),
intval ( $channel [ 'channel_id' ])
);
2019-05-13 06:57:57 +00:00
if ( ! post_is_importable ( $channel [ 'channel_id' ], $item , $abook [ 0 ])) {
2018-12-11 23:57:15 +00:00
logger ( 'post is filtered' );
return ;
}
2018-08-31 01:50:21 +00:00
2019-05-13 06:57:57 +00:00
if ( $act -> obj [ 'conversation' ]) {
2018-08-20 03:39:23 +00:00
set_iconfig ( $item , 'ostatus' , 'conversation' , $act -> obj [ 'conversation' ], 1 );
}
2018-09-05 23:30:46 +00:00
// This isn't perfect but the best we can do for now.
$item [ 'comment_policy' ] = 'authenticated' ;
2018-08-20 03:39:23 +00:00
set_iconfig ( $item , 'activitypub' , 'recips' , $act -> raw_recips );
2019-05-13 06:57:57 +00:00
if ( ! ( isset ( $act -> data [ 'inheritPrivacy' ]) && $act -> data [ 'inheritPrivacy' ])) {
if ( $item [ 'item_private' ]) {
2018-11-21 02:55:33 +00:00
$item [ 'item_restrict' ] = $item [ 'item_restrict' ] & 1 ;
2019-05-13 06:57:57 +00:00
if ( $is_child_node ) {
2018-11-20 01:30:22 +00:00
$item [ 'allow_cid' ] = '<' . $channel [ 'channel_hash' ] . '>' ;
$item [ 'allow_gid' ] = $item [ 'deny_cid' ] = $item [ 'deny_gid' ] = '' ;
}
2018-11-19 05:53:09 +00:00
logger ( 'restricted' );
}
}
2019-05-13 06:57:57 +00:00
if ( intval ( $act -> sigok )) {
2018-11-27 00:16:07 +00:00
$item [ 'item_verified' ] = 1 ;
}
2019-05-13 06:57:57 +00:00
if ( $is_child_node ) {
2018-11-19 05:53:09 +00:00
2018-08-31 01:50:21 +00:00
$p = q ( " select parent_mid from item where mid = '%s' and uid = %d limit 1 " ,
dbesc ( $item [ 'parent_mid' ]),
intval ( $item [ 'uid' ])
);
2019-05-13 06:57:57 +00:00
if ( ! $p ) {
2019-09-10 04:03:33 +00:00
if ( ! get_config ( 'system' , 'activitypub' )) {
2019-06-18 02:10:30 +00:00
return ;
}
else {
$a = false ;
if ( PConfig :: Get ( $channel [ 'channel_id' ], 'system' , 'hyperdrive' , true ) || $act -> type === 'Announce' ) {
$a = (( $fetch_parents ) ? self :: fetch_and_store_parents ( $channel , $observer_hash , $act , $item ) : false );
}
if ( $a ) {
$p = q ( " select parent_mid from item where mid = '%s' and uid = %d limit 1 " ,
dbesc ( $item [ 'parent_mid' ]),
intval ( $item [ 'uid' ])
);
}
else {
// if no parent was fetched, turn into a top-level post
// @TODO we maybe could accept these is we formatted the body correctly with share_bb()
// or at least provided a link to the object
if ( in_array ( $act -> type ,[ 'Like' , 'Dislike' , 'Announce' ])) {
return ;
}
// turn into a top level post
$item [ 'parent_mid' ] = $item [ 'mid' ];
$item [ 'thr_parent' ] = $item [ 'mid' ];
}
}
2018-08-31 01:50:21 +00:00
}
2019-06-18 02:04:06 +00:00
2019-05-13 06:57:57 +00:00
if ( $p [ 0 ][ 'parent_mid' ] !== $item [ 'parent_mid' ]) {
2018-08-31 01:50:21 +00:00
$item [ 'thr_parent' ] = $item [ 'parent_mid' ];
}
else {
$item [ 'thr_parent' ] = $p [ 0 ][ 'parent_mid' ];
}
$item [ 'parent_mid' ] = $p [ 0 ][ 'parent_mid' ];
}
2018-09-04 11:06:50 +00:00
$r = q ( " select id, created, edited from item where mid = '%s' and uid = %d limit 1 " ,
2018-08-20 03:39:23 +00:00
dbesc ( $item [ 'mid' ]),
intval ( $item [ 'uid' ])
);
2019-05-13 06:57:57 +00:00
if ( $r ) {
if ( $item [ 'edited' ] > $r [ 0 ][ 'edited' ]) {
2018-09-04 11:06:50 +00:00
$item [ 'id' ] = $r [ 0 ][ 'id' ];
2018-08-20 03:39:23 +00:00
$x = item_store_update ( $item );
}
else {
return ;
}
}
else {
$x = item_store ( $item );
}
2019-05-13 06:57:57 +00:00
if ( is_array ( $x ) && $x [ 'item_id' ]) {
if ( $is_child_node ) {
if ( $item [ 'owner_xchan' ] === $channel [ 'channel_hash' ]) {
2018-08-20 03:39:23 +00:00
// We are the owner of this conversation, so send all received comments back downstream
2018-09-07 00:36:35 +00:00
Master :: Summon ( array ( 'Notifier' , 'comment-import' , $x [ 'item_id' ]));
2018-08-20 03:39:23 +00:00
}
$r = q ( " select * from item where id = %d limit 1 " ,
intval ( $x [ 'item_id' ])
);
2019-05-13 06:57:57 +00:00
if ( $r ) {
2018-08-20 03:39:23 +00:00
send_status_notifications ( $x [ 'item_id' ], $r [ 0 ]);
}
}
sync_an_item ( $channel [ 'channel_id' ], $x [ 'item_id' ]);
}
}
2018-12-12 04:30:47 +00:00
static public function find_best_identity ( $xchan ) {
$r = q ( " select hubloc_hash from hubloc where hubloc_id_url = '%s' limit 1 " ,
dbesc ( $xchan )
);
2019-05-13 06:57:57 +00:00
if ( $r ) {
2018-12-12 04:30:47 +00:00
return $r [ 0 ][ 'hubloc_hash' ];
}
return $xchan ;
}
2018-09-01 22:52:28 +00:00
static public function fetch_and_store_parents ( $channel , $observer_hash , $act , $item ) {
2018-09-04 01:22:31 +00:00
logger ( 'fetching parents' );
2018-09-01 22:52:28 +00:00
$p = [];
$current_act = $act ;
$current_item = $item ;
2018-08-20 03:39:23 +00:00
2018-09-01 22:52:28 +00:00
while ( $current_item [ 'parent_mid' ] !== $current_item [ 'mid' ]) {
2019-01-14 05:22:45 +00:00
$n = self :: fetch ( $current_item [ 'parent_mid' ]);
2019-05-13 06:57:57 +00:00
if ( ! $n ) {
2018-09-01 22:52:28 +00:00
break ;
}
$a = new ActivityStreams ( $n );
2018-09-04 01:22:31 +00:00
logger ( $a -> debug ());
2019-05-13 06:57:57 +00:00
if ( ! $a -> is_valid ()) {
2018-09-01 22:52:28 +00:00
break ;
}
2019-05-13 06:57:57 +00:00
if ( is_array ( $a -> actor ) && array_key_exists ( 'id' , $a -> actor )) {
2018-09-01 22:52:28 +00:00
Activity :: actor_store ( $a -> actor [ 'id' ], $a -> actor );
}
$item = null ;
switch ( $a -> type ) {
case 'Create' :
case 'Update' :
case 'Like' :
case 'Dislike' :
case 'Announce' :
$item = Activity :: decode_note ( $a );
break ;
default :
break ;
}
2019-05-13 06:57:57 +00:00
if ( ! $item ) {
2018-09-01 22:52:28 +00:00
break ;
}
2018-09-03 03:16:33 +00:00
array_unshift ( $p ,[ $a , $item ]);
2018-09-01 22:52:28 +00:00
2019-05-13 06:57:57 +00:00
if ( $item [ 'parent_mid' ] === $item [ 'mid' ] || count ( $p ) > 30 ) {
2018-09-01 22:52:28 +00:00
break ;
}
$current_act = $a ;
$current_item = $item ;
}
2019-05-13 06:57:57 +00:00
if ( $p ) {
foreach ( $p as $pv ) {
if ( $pv [ 0 ] -> is_valid ()) {
2018-12-03 00:46:52 +00:00
Activity :: store ( $channel , $observer_hash , $pv [ 0 ], $pv [ 1 ], false );
}
2018-09-01 22:52:28 +00:00
}
return true ;
}
return false ;
}
2018-08-20 03:39:23 +00:00
2018-05-30 06:30:18 +00:00
2018-05-30 04:08:52 +00:00
static function announce_note ( $channel , $observer_hash , $act ) {
$s = [];
$is_sys_channel = is_sys_channel ( $channel [ 'channel_id' ]);
// Mastodon only allows visibility in public timelines if the public inbox is listed in the 'to' field.
// They are hidden in the public timeline if the public inbox is listed in the 'cc' field.
// This is not part of the activitypub protocol - we might change this to show all public posts in pubstream at some point.
$pubstream = (( is_array ( $act -> obj ) && array_key_exists ( 'to' , $act -> obj ) && in_array ( ACTIVITY_PUBLIC_INBOX , $act -> obj [ 'to' ])) ? true : false );
2019-05-13 06:57:57 +00:00
if ( ! perm_is_allowed ( $channel [ 'channel_id' ], $observer_hash , 'send_stream' ) && ! ( $is_sys_channel && $pubstream )) {
2018-05-30 04:08:52 +00:00
logger ( 'no permission' );
return ;
}
2019-05-13 06:57:57 +00:00
if ( is_array ( $act -> obj )) {
2018-09-01 22:52:28 +00:00
$content = self :: get_content ( $act -> obj );
}
2019-05-13 06:57:57 +00:00
if ( ! $content ) {
2018-05-30 04:08:52 +00:00
logger ( 'no content' );
return ;
}
$s [ 'owner_xchan' ] = $s [ 'author_xchan' ] = $observer_hash ;
$s [ 'aid' ] = $channel [ 'channel_account_id' ];
$s [ 'uid' ] = $channel [ 'channel_id' ];
$s [ 'mid' ] = urldecode ( $act -> obj [ 'id' ]);
2018-06-04 00:49:48 +00:00
$s [ 'plink' ] = urldecode ( $act -> obj [ 'id' ]);
2018-05-30 04:08:52 +00:00
2019-05-13 06:57:57 +00:00
if ( ! $s [ 'created' ])
2018-05-30 04:08:52 +00:00
$s [ 'created' ] = datetime_convert ();
2019-05-13 06:57:57 +00:00
if ( ! $s [ 'edited' ])
2018-05-30 04:08:52 +00:00
$s [ 'edited' ] = $s [ 'created' ];
$s [ 'parent_mid' ] = $s [ 'mid' ];
$s [ 'verb' ] = ACTIVITY_POST ;
$s [ 'obj_type' ] = ACTIVITY_OBJ_NOTE ;
$s [ 'app' ] = t ( 'ActivityPub' );
2019-05-13 06:57:57 +00:00
if ( $channel [ 'channel_system' ]) {
if ( ! MessageFilter :: evaluate ( $s , get_config ( 'system' , 'pubstream_incl' ), get_config ( 'system' , 'pubstream_excl' ))) {
2018-05-30 04:08:52 +00:00
logger ( 'post is filtered' );
return ;
}
}
$abook = q ( " select * from abook where abook_xchan = '%s' and abook_channel = %d limit 1 " ,
dbesc ( $observer_hash ),
intval ( $channel [ 'channel_id' ])
);
2019-05-13 06:57:57 +00:00
if ( ! post_is_importable ( $channel [ 'channel_id' ], $s , $abook [ 0 ])) {
2018-12-11 23:57:15 +00:00
logger ( 'post is filtered' );
return ;
2018-05-30 04:08:52 +00:00
}
2019-05-13 06:57:57 +00:00
if ( $act -> obj [ 'conversation' ]) {
2018-05-30 04:08:52 +00:00
set_iconfig ( $s , 'ostatus' , 'conversation' , $act -> obj [ 'conversation' ], 1 );
}
$a = self :: decode_taxonomy ( $act -> obj );
2019-05-13 06:57:57 +00:00
if ( $a ) {
2018-05-30 04:08:52 +00:00
$s [ 'term' ] = $a ;
}
$a = self :: decode_attachment ( $act -> obj );
2019-05-13 06:57:57 +00:00
if ( $a ) {
2018-05-30 04:08:52 +00:00
$s [ 'attach' ] = $a ;
}
$body = " [share author=' " . urlencode ( $act -> sharee [ 'name' ]) .
" ' profile=' " . $act -> sharee [ 'url' ] .
" ' avatar=' " . $act -> sharee [ 'photo_s' ] .
" ' link=' " . (( is_array ( $act -> obj [ 'url' ])) ? $act -> obj [ 'url' ][ 'href' ] : $act -> obj [ 'url' ]) .
" ' auth=' " . (( is_matrix_url ( $act -> obj [ 'url' ])) ? 'true' : 'false' ) .
" ' posted=' " . $act -> obj [ 'published' ] .
" ' message_id=' " . $act -> obj [ 'id' ] .
" '] " ;
2019-05-13 06:57:57 +00:00
if ( $content [ 'name' ])
2018-05-30 04:08:52 +00:00
$body .= self :: bb_content ( $content , 'name' ) . " \r \n " ;
$body .= self :: bb_content ( $content , 'content' );
2019-05-13 06:57:57 +00:00
if ( $act -> obj [ 'type' ] === 'Note' && $s [ 'attach' ]) {
2018-10-11 04:36:55 +00:00
$body .= self :: bb_attach ( $s [ 'attach' ], body );
2018-05-30 04:08:52 +00:00
}
$body .= " [/share] " ;
$s [ 'title' ] = self :: bb_content ( $content , 'name' );
$s [ 'body' ] = $body ;
2019-05-13 06:57:57 +00:00
if ( $act -> recips && ( ! in_array ( ACTIVITY_PUBLIC_INBOX , $act -> recips )))
2018-05-30 04:08:52 +00:00
$s [ 'item_private' ] = 1 ;
set_iconfig ( $s , 'activitypub' , 'recips' , $act -> raw_recips );
$r = q ( " select created, edited from item where mid = '%s' and uid = %d limit 1 " ,
dbesc ( $s [ 'mid' ]),
intval ( $s [ 'uid' ])
);
2019-05-13 06:57:57 +00:00
if ( $r ) {
if ( $s [ 'edited' ] > $r [ 0 ][ 'edited' ]) {
2018-05-30 04:08:52 +00:00
$x = item_store_update ( $s );
}
else {
return ;
}
}
else {
$x = item_store ( $s );
}
2019-05-13 06:57:57 +00:00
if ( is_array ( $x ) && $x [ 'item_id' ]) {
if ( $parent ) {
if ( $s [ 'owner_xchan' ] === $channel [ 'channel_hash' ]) {
2018-05-30 04:08:52 +00:00
// We are the owner of this conversation, so send all received comments back downstream
2018-09-07 00:36:35 +00:00
Master :: Summon ( array ( 'Notifier' , 'comment-import' , $x [ 'item_id' ]));
2018-05-30 04:08:52 +00:00
}
$r = q ( " select * from item where id = %d limit 1 " ,
intval ( $x [ 'item_id' ])
);
2019-05-13 06:57:57 +00:00
if ( $r ) {
2018-05-30 04:08:52 +00:00
send_status_notifications ( $x [ 'item_id' ], $r [ 0 ]);
}
}
sync_an_item ( $channel [ 'channel_id' ], $x [ 'item_id' ]);
}
}
static function like_note ( $channel , $observer_hash , $act ) {
$s = [];
$parent = $act -> obj [ 'id' ];
2019-05-13 06:57:57 +00:00
if ( $act -> type === 'Like' )
2018-05-30 04:08:52 +00:00
$s [ 'verb' ] = ACTIVITY_LIKE ;
2019-05-13 06:57:57 +00:00
if ( $act -> type === 'Dislike' )
2018-05-30 04:08:52 +00:00
$s [ 'verb' ] = ACTIVITY_DISLIKE ;
2019-05-13 06:57:57 +00:00
if ( ! $parent )
2018-05-30 04:08:52 +00:00
return ;
$r = q ( " select * from item where uid = %d and ( mid = '%s' or mid = '%s' ) limit 1 " ,
intval ( $channel [ 'channel_id' ]),
dbesc ( $parent ),
dbesc ( urldecode ( basename ( $parent )))
);
2019-05-13 06:57:57 +00:00
if ( ! $r ) {
2018-05-30 04:08:52 +00:00
logger ( 'parent not found.' );
return ;
}
xchan_query ( $r );
$parent_item = $r [ 0 ];
2019-05-13 06:57:57 +00:00
if ( $parent_item [ 'owner_xchan' ] === $channel [ 'channel_hash' ]) {
if ( ! perm_is_allowed ( $channel [ 'channel_id' ], $observer_hash , 'post_comments' )) {
2018-05-30 04:08:52 +00:00
logger ( 'no comment permission.' );
return ;
}
}
2019-05-13 06:57:57 +00:00
if ( $parent_item [ 'mid' ] === $parent_item [ 'parent_mid' ]) {
2018-05-30 04:08:52 +00:00
$s [ 'parent_mid' ] = $parent_item [ 'mid' ];
}
else {
$s [ 'thr_parent' ] = $parent_item [ 'mid' ];
$s [ 'parent_mid' ] = $parent_item [ 'parent_mid' ];
}
$s [ 'owner_xchan' ] = $parent_item [ 'owner_xchan' ];
$s [ 'author_xchan' ] = $observer_hash ;
$s [ 'aid' ] = $channel [ 'channel_account_id' ];
$s [ 'uid' ] = $channel [ 'channel_id' ];
$s [ 'mid' ] = $act -> id ;
2019-05-13 06:57:57 +00:00
if ( ! $s [ 'parent_mid' ])
2018-05-30 04:08:52 +00:00
$s [ 'parent_mid' ] = $s [ 'mid' ];
$post_type = (( $parent_item [ 'resource_type' ] === 'photo' ) ? t ( 'photo' ) : t ( 'status' ));
$links = array ( array ( 'rel' => 'alternate' , 'type' => 'text/html' , 'href' => $parent_item [ 'plink' ]));
$objtype = (( $parent_item [ 'resource_type' ] === 'photo' ) ? ACTIVITY_OBJ_PHOTO : ACTIVITY_OBJ_NOTE );
$body = $parent_item [ 'body' ];
$z = q ( " select * from xchan where xchan_hash = '%s' limit 1 " ,
dbesc ( $parent_item [ 'author_xchan' ])
);
2019-05-13 06:57:57 +00:00
if ( $z )
2018-05-30 04:08:52 +00:00
$item_author = $z [ 0 ];
$object = json_encode ( array (
'type' => $post_type ,
'id' => $parent_item [ 'mid' ],
'parent' => (( $parent_item [ 'thr_parent' ]) ? $parent_item [ 'thr_parent' ] : $parent_item [ 'parent_mid' ]),
'link' => $links ,
'title' => $parent_item [ 'title' ],
'content' => $parent_item [ 'body' ],
'created' => $parent_item [ 'created' ],
'edited' => $parent_item [ 'edited' ],
'author' => array (
'name' => $item_author [ 'xchan_name' ],
'address' => $item_author [ 'xchan_addr' ],
'guid' => $item_author [ 'xchan_guid' ],
'guid_sig' => $item_author [ 'xchan_guid_sig' ],
'link' => array (
array ( 'rel' => 'alternate' , 'type' => 'text/html' , 'href' => $item_author [ 'xchan_url' ]),
array ( 'rel' => 'photo' , 'type' => $item_author [ 'xchan_photo_mimetype' ], 'href' => $item_author [ 'xchan_photo_m' ])),
),
), JSON_UNESCAPED_SLASHES
);
2019-05-13 06:57:57 +00:00
if ( $act -> type === 'Like' )
2018-05-30 04:08:52 +00:00
$bodyverb = t ( '%1$s likes %2$s\'s %3$s' );
2019-05-13 06:57:57 +00:00
if ( $act -> type === 'Dislike' )
2018-05-30 04:08:52 +00:00
$bodyverb = t ( '%1$s doesn\'t like %2$s\'s %3$s' );
$ulink = '[url=' . $item_author [ 'xchan_url' ] . ']' . $item_author [ 'xchan_name' ] . '[/url]' ;
$alink = '[url=' . $parent_item [ 'author' ][ 'xchan_url' ] . ']' . $parent_item [ 'author' ][ 'xchan_name' ] . '[/url]' ;
$plink = '[url=' . z_root () . '/display/' . urlencode ( $act -> id ) . ']' . $post_type . '[/url]' ;
$s [ 'body' ] = sprintf ( $bodyverb , $ulink , $alink , $plink );
$s [ 'app' ] = t ( 'ActivityPub' );
// set the route to that of the parent so downstream hubs won't reject it.
$s [ 'route' ] = $parent_item [ 'route' ];
$s [ 'item_private' ] = $parent_item [ 'item_private' ];
$s [ 'obj_type' ] = $objtype ;
$s [ 'obj' ] = $object ;
2019-05-13 06:57:57 +00:00
if ( $act -> obj [ 'conversation' ]) {
2018-05-30 04:08:52 +00:00
set_iconfig ( $s , 'ostatus' , 'conversation' , $act -> obj [ 'conversation' ], 1 );
}
2019-05-13 06:57:57 +00:00
if ( $act -> recips && ( ! in_array ( ACTIVITY_PUBLIC_INBOX , $act -> recips )))
2018-05-30 04:08:52 +00:00
$s [ 'item_private' ] = 1 ;
set_iconfig ( $s , 'activitypub' , 'recips' , $act -> raw_recips );
$result = item_store ( $s );
2019-05-13 06:57:57 +00:00
if ( $result [ 'success' ]) {
2018-05-30 04:08:52 +00:00
// if the message isn't already being relayed, notify others
2019-05-13 06:57:57 +00:00
if ( intval ( $parent_item [ 'item_origin' ]))
2018-09-07 00:36:35 +00:00
Master :: Summon ( array ( 'Notifier' , 'comment-import' , $result [ 'item_id' ]));
sync_an_item ( $channel [ 'channel_id' ], $result [ 'item_id' ]);
2018-05-30 04:08:52 +00:00
}
return ;
}
2018-10-11 04:36:55 +00:00
static function bb_attach ( $attach , $body ) {
2018-05-30 04:08:52 +00:00
$ret = false ;
2019-05-13 06:57:57 +00:00
foreach ( $attach as $a ) {
if ( strpos ( $a [ 'type' ], 'image' ) !== false ) {
if ( self :: media_not_in_body ( $a [ 'href' ], $body )) {
2018-10-11 04:36:55 +00:00
$ret .= " \n \n " . '[img]' . $a [ 'href' ] . '[/img]' ;
}
2018-05-30 04:08:52 +00:00
}
2019-05-13 06:57:57 +00:00
if ( array_key_exists ( 'type' , $a ) && strpos ( $a [ 'type' ], 'video' ) === 0 ) {
if ( self :: media_not_in_body ( $a [ 'href' ], $body )) {
2018-10-11 04:36:55 +00:00
$ret .= " \n \n " . '[video]' . $a [ 'href' ] . '[/video]' ;
}
2018-05-30 04:08:52 +00:00
}
2019-05-13 06:57:57 +00:00
if ( array_key_exists ( 'type' , $a ) && strpos ( $a [ 'type' ], 'audio' ) === 0 ) {
if ( self :: media_not_in_body ( $a [ 'href' ], $body )) {
2018-10-11 04:36:55 +00:00
$ret .= " \n \n " . '[audio]' . $a [ 'href' ] . '[/audio]' ;
}
2018-05-30 04:08:52 +00:00
}
}
return $ret ;
}
2018-10-11 23:54:31 +00:00
// check for the existence of existing media link in body
static function media_not_in_body ( $s , $body ) {
2019-05-13 06:57:57 +00:00
if (( strpos ( $body , ']' . $s . '[/img]' ) === false ) &&
2018-10-20 09:03:31 +00:00
( strpos ( $body , ']' . $s . '[/zmg]' ) === false ) &&
2018-10-11 23:54:31 +00:00
( strpos ( $body , ']' . $s . '[/video]' ) === false ) &&
( strpos ( $body , ']' . $s . '[/audio]' ) === false )) {
return true ;
}
return false ;
}
2018-05-30 04:08:52 +00:00
static function bb_content ( $content , $field ) {
require_once ( 'include/html2bbcode.php' );
2018-08-23 01:45:53 +00:00
require_once ( 'include/event.php' );
2018-05-30 04:08:52 +00:00
$ret = false ;
2019-05-13 06:57:57 +00:00
if ( is_array ( $content [ $field ])) {
foreach ( $content [ $field ] as $k => $v ) {
2018-10-23 22:36:48 +00:00
$ret .= html2bbcode ( $v );
// save this for auto-translate or dynamic filtering
// $ret .= '[language=' . $k . ']' . html2bbcode($v) . '[/language]';
2018-05-30 04:08:52 +00:00
}
}
else {
2019-05-13 06:57:57 +00:00
if ( $field === 'bbcode' && array_key_exists ( 'bbcode' , $content )) {
2018-07-09 05:28:15 +00:00
$ret = $content [ $field ];
}
else {
$ret = html2bbcode ( $content [ $field ]);
}
2018-05-30 04:08:52 +00:00
}
2019-05-13 06:57:57 +00:00
if ( $field === 'content' && $content [ 'event' ] && ( ! strpos ( $ret , '[event' ))) {
2018-08-23 01:45:53 +00:00
$ret .= format_event_bbcode ( $content [ 'event' ]);
}
2018-05-30 04:08:52 +00:00
return $ret ;
}
static function get_content ( $act ) {
$content = [];
2018-08-23 01:45:53 +00:00
$event = null ;
2018-09-01 22:52:28 +00:00
if (( ! $act ) || ( ! is_array ( $act ))) {
2018-07-18 04:56:51 +00:00
return $content ;
}
2018-05-30 04:08:52 +00:00
2019-05-13 06:57:57 +00:00
if ( $act [ 'type' ] === 'Event' ) {
2019-04-11 06:45:24 +00:00
$adjust = false ;
$event = [];
$event [ 'event_hash' ] = $act [ 'id' ];
2019-05-13 06:57:57 +00:00
if ( array_key_exists ( 'startTime' , $act ) && strpos ( $act [ 'startTime' ], - 1 , 1 ) === 'Z' ) {
2019-04-11 06:45:24 +00:00
$adjust = true ;
$event [ 'adjust' ] = 1 ;
$event [ 'dtstart' ] = datetime_convert ( 'UTC' , 'UTC' , $event [ 'startTime' ] . (( $adjust ) ? '' : 'Z' ));
}
2019-05-13 06:57:57 +00:00
if ( array_key_exists ( 'endTime' , $act )) {
2019-04-11 06:45:24 +00:00
$event [ 'dtend' ] = datetime_convert ( 'UTC' , 'UTC' , $event [ 'endTime' ] . (( $adjust ) ? '' : 'Z' ));
}
else {
$event [ 'nofinish' ] = true ;
}
2019-05-13 06:57:57 +00:00
if ( array_key_exists ( 'eventRepeat' , $act )) {
2019-04-11 06:45:24 +00:00
$event [ 'event_repeat' ] = $act [ 'eventRepeat' ];
}
2018-08-23 01:45:53 +00:00
}
2018-07-18 04:56:51 +00:00
foreach ([ 'name' , 'summary' , 'content' ] as $a ) {
if (( $x = self :: get_textfield ( $act , $a )) !== false ) {
2018-05-30 04:08:52 +00:00
$content [ $a ] = $x ;
}
}
2018-08-23 01:45:53 +00:00
2019-05-13 06:57:57 +00:00
if ( $event ) {
2018-08-23 01:45:53 +00:00
$event [ 'summary' ] = html2bbcode ( $content [ 'summary' ]);
2019-09-12 03:41:16 +00:00
if ( ! $event [ 'summary' ]) {
if ( $content [ 'name' ]) {
$event [ 'summary' ] = html2bbcode ( $content [ 'name' ]);
}
}
2018-08-23 01:45:53 +00:00
$event [ 'description' ] = html2bbcode ( $content [ 'content' ]);
2019-05-13 06:57:57 +00:00
if ( $event [ 'summary' ] && $event [ 'dtstart' ]) {
2018-08-23 01:45:53 +00:00
$content [ 'event' ] = $event ;
}
}
2018-07-18 04:56:51 +00:00
if ( array_key_exists ( 'source' , $act ) && array_key_exists ( 'mediaType' , $act [ 'source' ])) {
if ( $act [ 'source' ][ 'mediaType' ] === 'text/bbcode' ) {
2018-07-09 05:28:15 +00:00
$content [ 'bbcode' ] = purify_html ( $act [ 'source' ][ 'content' ]);
}
}
2018-05-30 04:08:52 +00:00
return $content ;
}
static function get_textfield ( $act , $field ) {
$content = false ;
2019-05-13 06:57:57 +00:00
if ( array_key_exists ( $field , $act ) && $act [ $field ])
2018-05-30 04:08:52 +00:00
$content = purify_html ( $act [ $field ]);
2019-05-13 06:57:57 +00:00
elseif ( array_key_exists ( $field . 'Map' , $act ) && $act [ $field . 'Map' ]) {
foreach ( $act [ $field . 'Map' ] as $k => $v ) {
2018-05-30 04:08:52 +00:00
$content [ escape_tags ( $k )] = purify_html ( $v );
}
}
return $content ;
}
2018-08-23 06:04:37 +00:00
2018-11-03 14:20:18 +00:00
}