aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Zotlabs/Lib/Activity.php80
-rw-r--r--Zotlabs/Lib/ActivityStreams.php6
-rw-r--r--Zotlabs/Module/Activity.php175
-rw-r--r--Zotlabs/Module/Event.php76
-rw-r--r--Zotlabs/Module/Item.php2
-rw-r--r--Zotlabs/Module/Like.php9
-rw-r--r--include/channel.php17
-rw-r--r--include/event.php12
8 files changed, 344 insertions, 33 deletions
diff --git a/Zotlabs/Lib/Activity.php b/Zotlabs/Lib/Activity.php
index 08a8b8d03..a99bb36e4 100644
--- a/Zotlabs/Lib/Activity.php
+++ b/Zotlabs/Lib/Activity.php
@@ -168,6 +168,10 @@ class Activity {
if($r) {
xchan_query($r,true);
$r = fetch_post_tags($r,true);
+ if ($r[0]['verb'] === 'Create' && $r[0]['obj_type'] === ACTIVITY_OBJ_EVENT) {
+ $r[0]['verb'] = 'Invite';
+ return self::encode_activity($r[0]);
+ }
return self::encode_item($r[0]);
}
}
@@ -220,7 +224,7 @@ class Activity {
'startTime' => (($ev['adjust']) ? datetime_convert($ev['timezone'],'UTC',$ev['dtstart'], ATOM_TIME) : datetime_convert('UTC','UTC',$ev['dtstart'],'Y-m-d\\TH:i:s-00:00')),
'content' => bbcode($ev['description'], [ 'cache' => true ]),
'location' => [ 'type' => 'Place', 'content' => bbcode($ev['location'], [ 'cache' => true ]) ],
- 'source' => [ 'content' => format_event_bbcode($ev), 'mediaType' => 'text/bbcode' ],
+ 'source' => [ 'content' => format_event_bbcode($ev,true), 'mediaType' => 'text/bbcode' ],
'actor' => $actor,
];
if(! $ev['nofinish']) {
@@ -611,10 +615,10 @@ class Activity {
if($i['id'] != $i['parent']) {
$reply = true;
- // inReplyTo needs to be set in the activity for followup actiions (Like, Dislike, Attend, Announce, etc.),
- // but *not* for comments, where it should only be present in the object
-
- if (! in_array($ret['type'],[ 'Create','Update' ])) {
+ // inReplyTo needs to be set in the activity for followup actions (Like, Dislike, Announce, etc.),
+ // but *not* for comments and RSVPs, where it should only be present in the object
+
+ if (! in_array($ret['type'],[ 'Create','Update','Accept','Reject','TentativeAccept','TentativeReject' ])) {
$ret['inReplyTo'] = ((strpos($i['thr_parent'],'http') === 0) ? $i['thr_parent'] : z_root() . '/item/' . urlencode($i['thr_parent']));
}
@@ -672,6 +676,9 @@ class Activity {
return [];
}
+ if(array_path_exists('object/type',$ret) && $ret['object']['type'] === 'Event' && $ret['type'] === 'Create') {
+ $ret['type'] = 'Invite';
+ }
if($i['target']) {
if(! is_array($i['target'])) {
@@ -822,7 +829,8 @@ class Activity {
'http://activitystrea.ms/schema/1.0/unfollow' => 'Unfollow',
'http://purl.org/zot/activity/attendyes' => 'Accept',
'http://purl.org/zot/activity/attendno' => 'Reject',
- 'http://purl.org/zot/activity/attendmaybe' => 'TentativeAccept'
+ 'http://purl.org/zot/activity/attendmaybe' => 'TentativeAccept',
+ 'Invite' => 'Invite',
];
call_hooks('activity_mapper',$acts);
@@ -868,7 +876,8 @@ class Activity {
'http://activitystrea.ms/schema/1.0/unfollow' => 'Unfollow',
'http://purl.org/zot/activity/attendyes' => 'Accept',
'http://purl.org/zot/activity/attendno' => 'Reject',
- 'http://purl.org/zot/activity/attendmaybe' => 'TentativeAccept'
+ 'http://purl.org/zot/activity/attendmaybe' => 'TentativeAccept',
+ 'Invite' => 'Invite',
];
call_hooks('activity_decode_mapper',$acts);
@@ -902,6 +911,7 @@ class Activity {
'http://purl.org/zot/activity/thing' => 'Object',
'http://purl.org/zot/activity/file' => 'zot:File',
'http://purl.org/zot/activity/mood' => 'zot:Mood',
+ 'Invite' => 'Invite',
];
@@ -941,7 +951,7 @@ class Activity {
'http://purl.org/zot/activity/thing' => 'Object',
'http://purl.org/zot/activity/file' => 'zot:File',
'http://purl.org/zot/activity/mood' => 'zot:Mood',
-
+ 'Invite' => 'Invite',
];
call_hooks('activity_obj_mapper',$objs);
@@ -1664,15 +1674,23 @@ class Activity {
if($act->type === 'Dislike') {
$content['content'] = sprintf( t('Doesn\'t like %1$s\'s %2$s'),$mention,$act->obj['type']) . "\n\n" . $content['content'];
}
- if($act->type === 'Accept' && $act->obj['type'] === 'Event' ) {
- $content['content'] = sprintf( t('Will attend %1$s\'s %2$s'),$mention,$act->obj['type']) . "\n\n" . $content['content'];
- }
- if($act->type === 'Reject' && $act->obj['type'] === 'Event' ) {
- $content['content'] = sprintf( t('Will not attend %1$s\'s %2$s'),$mention,$act->obj['type']) . "\n\n" . $content['content'];
- }
- if($act->type === 'TentativeAccept' && $act->obj['type'] === 'Event' ) {
- $content['content'] = sprintf( t('May attend %1$s\'s %2$s'),$mention,$act->obj['type']) . "\n\n" . $content['content'];
+
+ // handle event RSVPs
+ if (($act->obj['type'] === 'Event') || ($act->obj['type'] === 'Invite' && array_path_exists('object/type',$act->obj) && $act->obj['object']['type'] === 'Event')) {
+ if ($act->type === 'Accept') {
+ $content['content'] = sprintf( t('Will attend %s\'s event'),$mention) . EOL . EOL . $content['content'];
+ }
+ if ($act->type === 'Reject') {
+ $content['content'] = sprintf( t('Will not attend %s\'s event'),$mention) . EOL . EOL . $content['content'];
+ }
+ if ($act->type === 'TentativeAccept') {
+ $content['content'] = sprintf( t('May attend %s\'s event'),$mention) . EOL . EOL . $content['content'];
+ }
+ if ($act->type === 'TentativeReject') {
+ $content['content'] = sprintf( t('May not attend %s\'s event'),$mention) . EOL . EOL . $content['content'];
+ }
}
+
if($act->type === 'Announce') {
$content['content'] = sprintf( t('🔁 Repeated %1$s\'s %2$s'), $mention, $act->obj['type']);
}
@@ -1703,28 +1721,42 @@ class Activity {
$s['obj_type'] = ACTIVITY_OBJ_COMMENT;
}
+ $eventptr = null;
+
+ if ($act->obj['type'] === 'Invite' && array_path_exists('object/type',$act->obj) && $act->obj['object']['type'] === 'Event') {
+ $eventptr = $act->obj['object'];
+ $s['mid'] = $s['parent_mid'] = $act->obj['id'];
+ }
+
if($act->obj['type'] === 'Event') {
+ if ($act->type === 'Invite') {
+ $s['mid'] = $s['parent_mid'] = $act->id;
+ }
+ $eventptr = $act->obj;
+ }
+
+ if ($eventptr) {
$s['obj'] = [];
- $s['obj']['asld'] = $act->obj;
+ $s['obj']['asld'] = $eventptr;
$s['obj']['type'] = ACTIVITY_OBJ_EVENT;
- $s['obj']['id'] = $act->obj['id'];
- $s['obj']['title'] = $act->obj['name'];
+ $s['obj']['id'] = $eventptr['id'];
+ $s['obj']['title'] = $eventptr['name'];
if(strpos($act->obj['startTime'],'Z'))
$s['obj']['adjust'] = true;
else
$s['obj']['adjust'] = false;
- $s['obj']['dtstart'] = datetime_convert('UTC','UTC',$act->obj['startTime']);
+ $s['obj']['dtstart'] = datetime_convert('UTC','UTC',$eventptr['startTime']);
if($act->obj['endTime'])
- $s['obj']['dtend'] = datetime_convert('UTC','UTC',$act->obj['endTime']);
+ $s['obj']['dtend'] = datetime_convert('UTC','UTC',$eventptr['endTime']);
else
$s['obj']['nofinish'] = true;
- $s['obj']['description'] = $act->obj['content'];
+ $s['obj']['description'] = $eventptr['content'];
- if(array_path_exists('location/content',$act->obj))
- $s['obj']['location'] = $act->obj['location']['content'];
+ if(array_path_exists('location/content',$eventptr))
+ $s['obj']['location'] = $eventptr['location']['content'];
}
else {
diff --git a/Zotlabs/Lib/ActivityStreams.php b/Zotlabs/Lib/ActivityStreams.php
index 006744aff..d8bd72943 100644
--- a/Zotlabs/Lib/ActivityStreams.php
+++ b/Zotlabs/Lib/ActivityStreams.php
@@ -101,7 +101,13 @@ class ActivityStreams {
$this->actor = $this->get_actor('attributedTo',$this->obj);
}
}
+
+ // fetch recursive or embedded activities
+ if ($this->obj && is_array($this->obj) && array_key_exists('object',$this->obj)) {
+ $this->obj['object'] = $this->get_compound_property($this->obj['object']);
+ }
+
if($this->obj && is_array($this->obj) && $this->obj['actor'])
$this->obj['actor'] = $this->get_actor('actor',$this->obj);
if($this->tgt && is_array($this->tgt) && $this->tgt['actor'])
diff --git a/Zotlabs/Module/Activity.php b/Zotlabs/Module/Activity.php
new file mode 100644
index 000000000..93b5a15fc
--- /dev/null
+++ b/Zotlabs/Module/Activity.php
@@ -0,0 +1,175 @@
+<?php
+namespace Zotlabs\Module;
+
+use Zotlabs\Lib\IConfig;
+use Zotlabs\Lib\Enotify;
+use Zotlabs\Web\Controller;
+use Zotlabs\Daemon\Master;
+use Zotlabs\Lib\Activity as ZlibActivity;
+use Zotlabs\Lib\ActivityStreams;
+use Zotlabs\Lib\LDSignatures;
+use Zotlabs\Web\HTTPSig;
+use Zotlabs\Lib\Libzot;
+use Zotlabs\Lib\ThreadListener;
+use App;
+
+
+class Activity extends Controller {
+
+ function init() {
+
+ if (Libzot::is_zot_request()) {
+
+ $item_id = argv(1);
+
+ if (! $item_id)
+ http_status_exit(404, 'Not found');
+
+ $portable_id = EMPTY_STR;
+
+ $item_normal = " and item.item_hidden = 0 and item.item_type = 0 and item.item_unpublished = 0 and item.item_delayed = 0 and item.item_blocked = 0 ";
+
+ $i = null;
+
+ // do we have the item (at all)?
+
+ $r = q("select * from item where mid = '%s' $item_normal limit 1",
+ dbesc(z_root() . '/activity/' . $item_id)
+ );
+
+ if (! $r) {
+ http_status_exit(404,'Not found');
+ }
+
+ // process an authenticated fetch
+
+ $sigdata = HTTPSig::verify(EMPTY_STR);
+ if($sigdata['portable_id'] && $sigdata['header_valid']) {
+ $portable_id = $sigdata['portable_id'];
+ observer_auth($portable_id);
+
+ // first see if we have a copy of this item's parent owned by the current signer
+ // include xchans for all zot-like networks - these will have the same guid and public key
+
+ $x = q("select * from xchan where xchan_hash = '%s'",
+ dbesc($sigdata['portable_id'])
+ );
+
+ if ($x) {
+ $xchans = q("select xchan_hash from xchan where xchan_hash = '%s' OR ( xchan_guid = '%s' AND xchan_pubkey = '%s' ) ",
+ dbesc($sigdata['portable_id']),
+ dbesc($x[0]['xchan_guid']),
+ dbesc($x[0]['xchan_pubkey'])
+ );
+
+ if ($xchans) {
+ $hashes = ids_to_querystr($xchans,'xchan_hash',true);
+ $i = q("select id as item_id from item where mid = '%s' $item_normal and owner_xchan in ( " . protect_sprintf($hashes) . " ) limit 1",
+ dbesc($r[0]['parent_mid'])
+ );
+ }
+ }
+ }
+
+ // if we don't have a parent id belonging to the signer see if we can obtain one as a visitor that we have permission to access
+ // with a bias towards those items owned by channels on this site (item_wall = 1)
+
+ $sql_extra = item_permissions_sql(0);
+
+ if (! $i) {
+ $i = q("select id as item_id from item where mid = '%s' $item_normal $sql_extra order by item_wall desc limit 1",
+ dbesc($r[0]['parent_mid'])
+ );
+ }
+
+ if(! $i) {
+ http_status_exit(403,'Forbidden');
+ }
+
+ $parents_str = ids_to_querystr($i,'item_id');
+
+ $items = q("SELECT item.*, item.id AS item_id FROM item WHERE item.parent IN ( %s ) $item_normal ",
+ dbesc($parents_str)
+ );
+
+ if(! $items) {
+ http_status_exit(404, 'Not found');
+ }
+
+ xchan_query($items,true);
+ $items = fetch_post_tags($items,true);
+
+ $observer = App::get_observer();
+ $parent = $items[0];
+ $recips = (($parent['owner']['xchan_network'] === 'activitypub') ? get_iconfig($parent['id'],'activitypub','recips', []) : []);
+ $to = (($recips && array_key_exists('to',$recips) && is_array($recips['to'])) ? $recips['to'] : null);
+ $nitems = [];
+ foreach($items as $i) {
+
+ $mids = [];
+
+ if(intval($i['item_private'])) {
+ if(! $observer) {
+ continue;
+ }
+ // ignore private reshare, possibly from hubzilla
+ if($i['verb'] === 'Announce') {
+ if(! in_array($i['thr_parent'],$mids)) {
+ $mids[] = $i['thr_parent'];
+ }
+ continue;
+ }
+ // also ignore any children of the private reshares
+ if(in_array($i['thr_parent'],$mids)) {
+ continue;
+ }
+
+ if((! $to) || (! in_array($observer['xchan_url'],$to))) {
+ continue;
+ }
+
+ }
+ $nitems[] = $i;
+ }
+
+ if(! $nitems)
+ http_status_exit(404, 'Not found');
+
+ $chan = channelx_by_n($nitems[0]['uid']);
+
+ if(! $chan)
+ http_status_exit(404, 'Not found');
+
+ if(! perm_is_allowed($chan['channel_id'],get_observer_hash(),'view_stream'))
+ http_status_exit(403, 'Forbidden');
+
+ $i = ZlibActivity::encode_item_collection($nitems,'conversation/' . $item_id,'OrderedCollection');
+ if($portable_id) {
+ ThreadListener::store(z_root() . '/activity/' . $item_id,$portable_id);
+ }
+
+ if(! $i)
+ http_status_exit(404, 'Not found');
+
+ $x = array_merge(['@context' => [
+ ACTIVITYSTREAMS_JSONLD_REV,
+ 'https://w3id.org/security/v1',
+ z_root() . ZOT_APSCHEMA_REV
+ ]], $i);
+
+ $headers = [];
+ $headers['Content-Type'] = 'application/x-zot+json' ;
+ $x['signature'] = LDSignatures::sign($x,$chan);
+ $ret = json_encode($x, JSON_UNESCAPED_SLASHES);
+ $headers['Digest'] = HTTPSig::generate_digest_header($ret);
+ $headers['(request-target)'] = strtolower($_SERVER['REQUEST_METHOD']) . ' ' . $_SERVER['REQUEST_URI'];
+ $h = HTTPSig::create_sig($headers,$chan['channel_prvkey'],channel_url($chan));
+ HTTPSig::set_headers($h);
+ echo $ret;
+ killme();
+
+ }
+
+ }
+
+}
diff --git a/Zotlabs/Module/Event.php b/Zotlabs/Module/Event.php
new file mode 100644
index 000000000..22a1341cc
--- /dev/null
+++ b/Zotlabs/Module/Event.php
@@ -0,0 +1,76 @@
+<?php
+namespace Zotlabs\Module;
+
+use Zotlabs\Web\Controller;
+use Zotlabs\Lib\ActivityStreams;
+use Zotlabs\Lib\Activity;
+use Zotlabs\Lib\LDSignatures;
+use Zotlabs\Web\HTTPSig;
+
+class Event extends Controller {
+
+ function init() {
+
+ if(ActivityStreams::is_as_request()) {
+ $item_id = argv(1);
+
+ if(! $item_id)
+ return;
+
+ $item_normal = " and item.item_hidden = 0 and item.item_type = 0 and item.item_unpublished = 0
+ and item.item_delayed = 0 and item.item_blocked = 0 ";
+
+ $sql_extra = item_permissions_sql(0);
+
+ $r = q("select * from item where mid like '%s' $item_normal $sql_extra limit 1",
+ dbesc(z_root() . '/activity/' . $item_id . '%')
+ );
+
+ if(! $r) {
+ $r = q("select * from item where mid like '%s' $item_normal limit 1",
+ dbesc(z_root() . '/activity/' . $item_id . '%')
+ );
+
+ if($r) {
+ http_status_exit(403, 'Forbidden');
+ }
+ http_status_exit(404, 'Not found');
+ }
+
+ xchan_query($r,true);
+ $items = fetch_post_tags($r,true);
+
+ $channel = channelx_by_n($items[0]['uid']);
+
+ if(! is_array($items[0]['obj'])) {
+ $obj = json_decode($items[0]['obj'],true);
+ }
+ else {
+ $obj = $items[0]['obj'];
+ }
+
+ $x = array_merge(['@context' => [
+ ACTIVITYSTREAMS_JSONLD_REV,
+ 'https://w3id.org/security/v1',
+ z_root() . ZOT_APSCHEMA_REV
+ ]], $obj );
+
+ $headers = [];
+ $headers['Content-Type'] = 'application/ld+json; profile="https://www.w3.org/ns/activitystreams"' ;
+ $x['signature'] = LDSignatures::sign($x,$channel);
+ $ret = json_encode($x, JSON_UNESCAPED_SLASHES);
+ $headers['Date'] = datetime_convert('UTC','UTC', 'now', 'D, d M Y H:i:s \\G\\M\\T');
+ $headers['Digest'] = HTTPSig::generate_digest_header($ret);
+ $headers['(request-target)'] = strtolower($_SERVER['REQUEST_METHOD']) . ' ' . $_SERVER['REQUEST_URI'];
+
+ $h = HTTPSig::create_sig($headers,$channel['channel_prvkey'],channel_url($channel));
+ HTTPSig::set_headers($h);
+
+ echo $ret;
+ killme();
+
+ }
+
+ }
+
+} \ No newline at end of file
diff --git a/Zotlabs/Module/Item.php b/Zotlabs/Module/Item.php
index 14881844d..1a25e54df 100644
--- a/Zotlabs/Module/Item.php
+++ b/Zotlabs/Module/Item.php
@@ -42,8 +42,6 @@ class Item extends Controller {
if (Libzot::is_zot_request()) {
- $conversation = false;
-
$item_id = argv(1);
if (! $item_id)
diff --git a/Zotlabs/Module/Like.php b/Zotlabs/Module/Like.php
index 052d51d43..ff7554b7e 100644
--- a/Zotlabs/Module/Like.php
+++ b/Zotlabs/Module/Like.php
@@ -75,7 +75,12 @@ class Like extends \Zotlabs\Web\Controller {
return EMPTY_STR;
}
-
+ $is_rsvp = false;
+ if (in_array($activity, [ ACTVITY_ATTEND, ACTIVITY_ATTENDNO, ACTIVITY_ATTENDMAYBE ])) {
+ $is_rsvp = true;
+ }
+
+
$extended_like = false;
$object = $target = null;
$post_type = EMPTY_STR;
@@ -381,7 +386,7 @@ class Like extends \Zotlabs\Web\Controller {
$arr = array();
$arr['uuid'] = $uuid;
- $arr['mid'] = z_root() . '/item/' . $uuid;
+ $arr['mid'] = z_root() . (($is_rsvp) ? '/activity/' : '/item/') . $uuid;
if($extended_like) {
$arr['item_thread_top'] = 1;
diff --git a/include/channel.php b/include/channel.php
index 32bd596fc..e7d119b1e 100644
--- a/include/channel.php
+++ b/include/channel.php
@@ -796,7 +796,7 @@ function get_default_export_sections() {
* @return array
* See function for details
*/
-function identity_basic_export($channel_id, $sections = null) {
+function identity_basic_export($channel_id, $sections = null, $zap_compat = false) {
/*
* basic channel export
@@ -812,12 +812,16 @@ function identity_basic_export($channel_id, $sections = null) {
// with a non-standard platform and version.
$ret['compatibility'] = [
- 'project' => PLATFORM_NAME,
+ 'project' => (($zap_compat) ? 'zap' : PLATFORM_NAME),
'version' => STD_VERSION,
'database' => DB_UPDATE_VERSION,
'server_role' => System::get_server_role()
];
+ if ($zap_compat) {
+ $ret['compatibility']['codebase'] = 'zap';
+ }
+
/*
* Process channel information regardless of it is one of the sections desired
* because we need the channel relocation information in all export files/streams.
@@ -834,6 +838,13 @@ function identity_basic_export($channel_id, $sections = null) {
unset($ret['channel']['channel_password']);
unset($ret['channel']['channel_salt']);
}
+ if ($zap_compat) {
+ $channel['channel_guid_sig'] = 'sha256.' . $channel['channel_guid_sig'];
+ $channel['channel_hash'] = $channel['channel_portable_id'];
+ unset($channel['channel_portable_id']);
+ }
+
+
}
if(in_array('channel',$sections) || in_array('profile',$sections)) {
@@ -853,7 +864,7 @@ function identity_basic_export($channel_id, $sections = null) {
$ret['photo'] = [
'type' => $r[0]['mimetype'],
'data' => (($r[0]['os_storage'])
- ? base64url_encode(file_get_contents($r[0]['content'])) : base64url_encode($r[0]['content']))
+ ? base64url_encode(file_get_contents($r[0]['content'])) : base64url_encode(dbunescbin($r[0]['content'])))
];
}
}
diff --git a/include/event.php b/include/event.php
index 9d76aabd6..59057e7d5 100644
--- a/include/event.php
+++ b/include/event.php
@@ -250,7 +250,7 @@ function format_ical_sourcetext($s) {
}
-function format_event_bbcode($ev) {
+function format_event_bbcode($ev, $utc = false) {
$o = '';
@@ -258,6 +258,14 @@ function format_event_bbcode($ev) {
$o .= '[event]' . $ev['event_vdata'] . '[/event]';
}
+ if ($utc && $ev['event-timezone'] !== 'UTC') {
+ $ev['dtstart'] = datetime_convert($ev['timezone'],'UTC',$ev['dtstart'],ATOM_TIME);
+ if ($ev['dtend'] && ! $ev['nofinish']) {
+ $ev['dtend'] = datetime_convert($ev['timezone'],'UTC',$ev['dtend'],ATOM_TIME);
+ }
+ $ev['timezone'] = 'UTC';
+ }
+
if($ev['summary'])
$o .= '[event-summary]' . $ev['summary'] . '[/event-summary]';
@@ -1194,7 +1202,7 @@ function event_store_item($arr, $event) {
if(! $arr['mid']) {
$arr['uuid'] = $event['event_hash'];
- $arr['mid'] = z_root() . '/event/' . $event['event_hash'];
+ $arr['mid'] = z_root() . '/activity/' . $event['event_hash'];
}
$item_arr['aid'] = $z[0]['channel_account_id'];