aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Zotlabs/Lib/Activity.php216
-rw-r--r--Zotlabs/Lib/Libzot.php6
-rw-r--r--Zotlabs/Web/HTTPSig.php54
-rw-r--r--boot.php2
-rw-r--r--include/event.php182
-rw-r--r--include/html2bbcode.php2
6 files changed, 368 insertions, 94 deletions
diff --git a/Zotlabs/Lib/Activity.php b/Zotlabs/Lib/Activity.php
index b83de6bb6..0c25605e7 100644
--- a/Zotlabs/Lib/Activity.php
+++ b/Zotlabs/Lib/Activity.php
@@ -501,7 +501,7 @@ class Activity {
$ret['attributedTo'] = $i['author']['xchan_url'];
- if ($i['id'] != $i['parent']) {
+ if ($i['mid'] !== $i['parent_mid']) {
$ret['inReplyTo'] = ((strpos($i['thr_parent'], 'http') === 0) ? $i['thr_parent'] : z_root() . '/item/' . urlencode($i['thr_parent']));
}
@@ -875,7 +875,7 @@ class Activity {
}
}
- if ($i['id'] != $i['parent']) {
+ if ($i['mid'] !== $i['parent_mid']) {
$reply = true;
// inReplyTo needs to be set in the activity for followup actions (Like, Dislike, Announce, etc.),
@@ -1154,9 +1154,10 @@ class Activity {
$ret['url'] = $p['xchan_url'];
$ret['publicKey'] = [
- 'id' => $p['xchan_url'],
- 'owner' => $p['xchan_url'],
- 'publicKeyPem' => $p['xchan_pubkey']
+ 'id' => $p['xchan_url'],
+ 'owner' => $p['xchan_url'],
+ 'signatureAlgorithm' => 'http://www.w3.org/2001/04/xmldsig-more#rsa-sha256',
+ 'publicKeyPem' => $p['xchan_pubkey']
];
if ($c) {
@@ -1599,6 +1600,25 @@ class Activity {
return;
}
+ public static function drop($channel, $observer, $act) {
+ $r = q(
+ "select * from item where mid = '%s' and uid = %d limit 1",
+ dbesc((is_array($act->obj)) ? $act->obj['id'] : $act->obj),
+ intval($channel['channel_id'])
+ );
+
+ if (!$r) {
+ return;
+ }
+
+ if (in_array($observer, [$r[0]['author_xchan'], $r[0]['owner_xchan']])) {
+ drop_item($r[0]['id'], false);
+ } elseif (in_array($act->actor['id'], [$r[0]['author_xchan'], $r[0]['owner_xchan']])) {
+ drop_item($r[0]['id'], false);
+ }
+ }
+
+
static function actor_store($url, $person_obj, $force = false) {
if (!is_array($person_obj)) {
@@ -2003,7 +2023,10 @@ class Activity {
}
if ($channel['channel_system']) {
- if (!MessageFilter::evaluate($s, get_config('system', 'pubstream_incl'), get_config('system', 'pubstream_excl'))) {
+ $incl = get_config('system','pubstream_incl');
+ $excl = get_config('system','pubstream_excl');
+
+ if(($incl || $excl) && !MessageFilter::evaluate($s, $incl, $excl)) {
logger('post is filtered');
return;
}
@@ -2225,6 +2248,21 @@ class Activity {
static function decode_note($act) {
+ $response_activity = false;
+
+ $s = [];
+
+ // These activities should have been handled separately in the Inbox module and should not be turned into posts
+
+ if (
+ in_array($act->type, ['Follow', 'Accept', 'Reject', 'Create', 'Update']) &&
+ is_array($act->obj) &&
+ array_key_exists('type', $act->obj) &&
+ ($act->obj['type'] === 'Follow' || ActivityStreams::is_an_actor($act->obj['type']))
+ ) {
+ return false;
+ }
+
// Within our family of projects, Follow/Unfollow of a thread is an internal activity which should not be transmitted,
// hence if we receive it - ignore or reject it.
// Unfollow is not defined by ActivityStreams, which prefers Undo->Follow.
@@ -2234,22 +2272,31 @@ class Activity {
return false;
}
- $response_activity = false;
+ if (!isset($act->actor['id'])) {
+ logger('No actor!');
+ return false;
+ }
- $s = [];
+ // ensure we store the original actor
+ self::actor_store($act->actor['id'], $act->actor);
+
+ $s['owner_xchan'] = $act->actor['id'];
+ $s['author_xchan'] = $act->actor['id'];
if (is_array($act->obj)) {
$content = self::get_content($act->obj);
}
- $s['owner_xchan'] = $act->actor['id'];
- $s['author_xchan'] = $act->actor['id'];
+ $s['mid'] = ((is_array($act->obj) && isset($act->obj['id'])) ? $act->obj['id'] : $act->obj);
- // ensure we store the original actor
- self::actor_store($act->actor['id'], $act->actor);
+ if (!$s['mid']) {
+ return false;
+ }
+
+ // Friendica sends the diaspora guid in a nonstandard field via AP
+ // If no uuid is provided we will create an uuid v5 from the mid
+ $s['uuid'] = ((is_array($act->obj) && isset($act->obj['diaspora:guid'])) ? $act->obj['diaspora:guid'] : uuid_from_url($s['mid']));
- $s['mid'] = $act->obj['id'];
- $s['uuid'] = $act->obj['diaspora:guid'];
$s['parent_mid'] = $act->parent_id;
if (array_key_exists('published', $act->data)) {
@@ -2273,13 +2320,18 @@ class Activity {
$s['expires'] = datetime_convert('UTC', 'UTC', $act->obj['expires']);
}
+ if ($act->type === 'Invite' && is_array($act->obj) && array_key_exists('type', $act->obj) && $act->obj['type'] === 'Event') {
+ $s['mid'] = $s['parent_mid'] = $act->id;
+ }
+
if (ActivityStreams::is_response_activity($act->type)) {
$response_activity = true;
$s['mid'] = $act->id;
- // $s['parent_mid'] = $act->obj['id'];
- $s['uuid'] = $act->data['diaspora:guid'];
+ $s['uuid'] = ((is_array($act->data) && isset($act->data['diaspora:guid'])) ? $act->data['diaspora:guid'] : uuid_from_url($s['mid']));
+
+ $s['parent_mid'] = ((is_array($act->obj) && isset($act->obj['id'])) ? $act->obj['id'] : $act->obj);
// over-ride the object timestamp with the activity
@@ -2292,8 +2344,8 @@ class Activity {
}
$obj_actor = ((isset($act->obj['actor'])) ? $act->obj['actor'] : $act->get_actor('attributedTo', $act->obj));
- // ensure we store the original actor
+ // ensure we store the original actor
self::actor_store($obj_actor['id'], $obj_actor);
$mention = self::get_actor_bbmention($obj_actor['id']);
@@ -2322,13 +2374,41 @@ class Activity {
}
if ($act->type === 'Announce') {
- $content['content'] = sprintf(t('🔁 Repeated %1$s\'s %2$s'), $mention, $act->obj['type']);
+ $s['author_xchan'] = $obj_actor['id'];
+ $s['mid'] = $act->obj['id'];
+ $s['parent_mid'] = $act->obj['id'];
}
if ($act->type === 'emojiReaction') {
$content['content'] = (($act->tgt && $act->tgt['type'] === 'Image') ? '[img=32x32]' . $act->tgt['url'] . '[/img]' : '&#x' . $act->tgt['name'] . ';');
}
}
+ $s['item_thread_top'] = 0;
+ $s['comment_policy'] = 'authenticated';
+
+ if ($s['mid'] === $s['parent_mid']) {
+ $s['item_thread_top'] = 1;
+
+ // it is a parent node - decode the comment policy info if present
+ if (isset($act->obj['commentPolicy'])) {
+ $until = strpos($act->obj['commentPolicy'], 'until=');
+ if ($until !== false) {
+ $s['comments_closed'] = datetime_convert('UTC', 'UTC', substr($act->obj['commentPolicy'], $until + 6));
+ if ($s['comments_closed'] < datetime_convert()) {
+ $s['nocomment'] = true;
+ }
+ }
+
+ $remainder = substr($act->obj['commentPolicy'], 0, (($until) ? $until : strlen($act->obj['commentPolicy'])));
+ if ($remainder) {
+ $s['comment_policy'] = $remainder;
+ }
+ if (!(isset($item['comment_policy']) && strlen($item['comment_policy']))) {
+ $s['comment_policy'] = 'contacts';
+ }
+ }
+ }
+
if (!array_key_exists('created', $s))
$s['created'] = datetime_convert();
@@ -2365,6 +2445,12 @@ class Activity {
$s['obj_type'] = ACTIVITY_OBJ_COMMENT;
}
+ $s['obj'] = $act->obj;
+ if (is_array($s['obj']) && array_path_exists('actor/id', $s['obj'])) {
+ $s['obj']['actor'] = $s['obj']['actor']['id'];
+ }
+
+/*
$eventptr = null;
if ($act->obj['type'] === 'Invite' && array_path_exists('object/type', $act->obj) && $act->obj['object']['type'] === 'Event') {
@@ -2385,19 +2471,19 @@ class Activity {
$s['obj']['asld'] = $eventptr;
$s['obj']['type'] = ACTIVITY_OBJ_EVENT;
$s['obj']['id'] = $eventptr['id'];
- $s['obj']['title'] = $eventptr['name'];
+ $s['obj']['title'] = html2plain($eventptr['name']);
if (strpos($act->obj['startTime'], 'Z'))
$s['obj']['adjust'] = true;
else
- $s['obj']['adjust'] = false;
+ $s['obj']['adjust'] = true;
$s['obj']['dtstart'] = datetime_convert('UTC', 'UTC', $eventptr['startTime']);
if ($act->obj['endTime'])
$s['obj']['dtend'] = datetime_convert('UTC', 'UTC', $eventptr['endTime']);
else
$s['obj']['nofinish'] = true;
- $s['obj']['description'] = $eventptr['content'];
+ $s['obj']['description'] = html2bbcode($eventptr['content']);
if (array_path_exists('location/content', $eventptr))
$s['obj']['location'] = $eventptr['location']['content'];
@@ -2406,6 +2492,7 @@ class Activity {
else {
$s['obj'] = $act->obj;
}
+*/
$generator = $act->get_property_obj('generator');
if ((!$generator) && (!$response_activity)) {
@@ -2445,7 +2532,7 @@ class Activity {
if (array_key_exists('type', $act->obj)) {
if ($act->obj['type'] === 'Note' && $s['attach']) {
- $s['body'] .= self::bb_attach($s['attach'], $s['body']);
+ $s['body'] = self::bb_attach($s['attach'], $s['body']) . $s['body'];
}
if ($act->obj['type'] === 'Question' && in_array($act->type, ['Create', 'Update'])) {
@@ -2534,13 +2621,13 @@ class Activity {
usort($mps,[ '\Zotlabs\Lib\Activity', 'vid_sort' ]);
foreach ($mps as $m) {
if (intval($m['height']) < 500 && Activity::media_not_in_body($m['href'],$s['body'])) {
- $s['body'] .= "\n\n" . $tag . $m['href'] . '[/video]';
+ $s['body'] = $tag . $m['href'] . '[/video]' . "\n\n" . $s['body'];
break;
}
}
}
elseif (is_string($act->obj['url']) && Activity::media_not_in_body($act->obj['url'],$s['body'])) {
- $s['body'] .= "\n\n" . $tag . $act->obj['url'] . '[/video]';
+ $s['body'] = $tag . $act->obj['url'] . '[/video]' . "\n\n" . $s['body'];
}
}
@@ -2566,13 +2653,13 @@ class Activity {
}
foreach ($ptr as $vurl) {
if (in_array($vurl['mediaType'], $atypes) && self::media_not_in_body($vurl['href'], $s['body'])) {
- $s['body'] .= "\n\n" . '[audio]' . $vurl['href'] . '[/audio]';
+ $s['body'] = '[audio]' . $vurl['href'] . '[/audio]' . "\n\n" . $s['body'];
break;
}
}
}
elseif (is_string($act->obj['url']) && self::media_not_in_body($act->obj['url'], $s['body'])) {
- $s['body'] .= "\n\n" . '[audio]' . $act->obj['url'] . '[/audio]';
+ $s['body'] = '[audio]' . $act->obj['url'] . '[/audio]' . "\n\n" . $s['body'];
}
}
@@ -2647,7 +2734,6 @@ class Activity {
}
}
-
if (in_array($act->obj['type'], ['Note', 'Article', 'Page'])) {
$ptr = null;
@@ -2785,12 +2871,6 @@ class Activity {
return;
}*/
- // TODO: this his handled in pubcrawl atm.
- // very unpleasant and imperfect way of determining a Mastodon DM
- /*if ($act->raw_recips && array_key_exists('to',$act->raw_recips) && is_array($act->raw_recips['to']) && count($act->raw_recips['to']) === 1 && $act->raw_recips['to'][0] === channel_url($channel) && ! $act->raw_recips['cc']) {
- $item['item_private'] = 2;
- }*/
-
if ($item['parent_mid'] && $item['parent_mid'] !== $item['mid']) {
$is_child_node = true;
}
@@ -2955,7 +3035,10 @@ class Activity {
return;
if ($channel['channel_system']) {
- if (!MessageFilter::evaluate($item, get_config('system', 'pubstream_incl'), get_config('system', 'pubstream_excl'))) {
+ $incl = get_config('system','pubstream_incl');
+ $excl = get_config('system','pubstream_excl');
+
+ if(($incl || $excl) && !MessageFilter::evaluate($item, $incl, $excl)) {
logger('post is filtered');
return;
}
@@ -3038,6 +3121,19 @@ class Activity {
$item['thr_parent'] = $parent[0]['parent_mid'];
}
$item['parent_mid'] = $parent[0]['parent_mid'];
+ //$item['item_private'] = $parent[0]['item_private'];
+
+ }
+
+ // An ugly and imperfect way to recognise a mastodon direct message
+ if (
+ $item['item_private'] === 1 &&
+ !isset($act->raw_recips['cc']) &&
+ is_array($act->raw_recips['to']) &&
+ in_array(channel_url($channel), $act->raw_recips['to']) &&
+ !in_array($act->actor['followers'], $act->raw_recips['to'])
+ ) {
+ $item['item_private'] = 2;
}
// TODO: not implemented
@@ -3048,6 +3144,12 @@ class Activity {
intval($item['uid'])
);
if ($r) {
+
+ // If we already have the item, dismiss its announce
+ if ($act->type === 'Announce') {
+ return;
+ }
+
if ($item['edited'] > $r[0]['edited']) {
$item['id'] = $r[0]['id'];
$x = item_store_update($item);
@@ -3064,12 +3166,12 @@ class Activity {
logger('topfetch', LOGGER_DEBUG);
// if the thread owner is a connnection, we will already receive any additional comments to their posts
// but if they are not we can try to fetch others in the background
- $x = q("SELECT abook.*, xchan.* FROM abook left join xchan on abook_xchan = xchan_hash
+ $connected = q("SELECT abook.*, xchan.* FROM abook left join xchan on abook_xchan = xchan_hash
WHERE abook_channel = %d and abook_xchan = '%s' LIMIT 1",
intval($channel['channel_id']),
dbesc($parent[0]['owner_xchan'])
);
- if (!$x) {
+ if (!$connected) {
// determine if the top-level post provides a replies collection
if ($parent[0]['obj']) {
$parent[0]['obj'] = json_decode($parent[0]['obj'], true);
@@ -3673,7 +3775,49 @@ class Activity {
$event['nofinish'] = true;
}
}
+/*
+ $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'] = $eventptr;
+ $s['obj']['type'] = ACTIVITY_OBJ_EVENT;
+ $s['obj']['id'] = $eventptr['id'];
+ $s['obj']['title'] = html2plain($eventptr['name']);
+ if (strpos($act->obj['startTime'], 'Z'))
+ $s['obj']['adjust'] = true;
+ else
+ $s['obj']['adjust'] = true;
+
+ $s['obj']['dtstart'] = datetime_convert('UTC', 'UTC', $eventptr['startTime']);
+ if ($act->obj['endTime'])
+ $s['obj']['dtend'] = datetime_convert('UTC', 'UTC', $eventptr['endTime']);
+ else
+ $s['obj']['nofinish'] = true;
+ $s['obj']['description'] = html2bbcode($eventptr['content']);
+
+ if (array_path_exists('location/content', $eventptr))
+ $s['obj']['location'] = $eventptr['location']['content'];
+
+ }
+ else {
+ $s['obj'] = $act->obj;
+ }
+*/
foreach (['name', 'summary', 'content'] as $a) {
if (($x = self::get_textfield($act, $a)) !== false) {
$content[$a] = $x;
diff --git a/Zotlabs/Lib/Libzot.php b/Zotlabs/Lib/Libzot.php
index fdeb7a3b0..40422a7d8 100644
--- a/Zotlabs/Lib/Libzot.php
+++ b/Zotlabs/Lib/Libzot.php
@@ -1578,7 +1578,11 @@ class Libzot {
$local_public = false;
continue;
}
- if (!MessageFilter::evaluate($arr, get_config('system', 'pubstream_incl'), get_config('system', 'pubstream_excl'))) {
+
+ $incl = get_config('system','pubstream_incl');
+ $excl = get_config('system','pubstream_excl');
+
+ if(($incl || $excl) && !MessageFilter::evaluate($arr, $incl, $excl)) {
$local_public = false;
continue;
}
diff --git a/Zotlabs/Web/HTTPSig.php b/Zotlabs/Web/HTTPSig.php
index 660fdc4ce..4177477a1 100644
--- a/Zotlabs/Web/HTTPSig.php
+++ b/Zotlabs/Web/HTTPSig.php
@@ -127,6 +127,7 @@ class HTTPSig {
if (array_key_exists($h, $headers)) {
$signed_data .= $h . ': ' . $headers[$h] . "\n";
}
+
if ($h === 'date') {
$d = new DateTime($headers[$h]);
$d->setTimeZone(new DateTimeZone('UTC'));
@@ -142,20 +143,34 @@ class HTTPSig {
$signed_data = rtrim($signed_data, "\n");
$algorithm = null;
+
if ($sig_block['algorithm'] === 'rsa-sha256') {
$algorithm = 'sha256';
}
+
if ($sig_block['algorithm'] === 'rsa-sha512') {
$algorithm = 'sha512';
}
- if (!array_key_exists('keyId', $sig_block))
+ if (!array_key_exists('keyId', $sig_block)) {
return $result;
+ }
$result['signer'] = $sig_block['keyId'];
$cached_key = self::get_key($key, $keytype, $result['signer']);
+ if ($sig_block['algorithm'] === 'hs2019') {
+ if (isset($cached_key['algorithm'])) {
+ if (strpos($cached_key['algorithm'], 'rsa-sha256') !== false) {
+ $algorithm = 'sha256';
+ }
+
+ if (strpos($cached_key['algorithm'], 'rsa-sha512') !== false) {
+ $algorithm = 'sha512';
+ }
+ }
+ }
if (!($cached_key && $cached_key['public_key'])) {
return $result;
@@ -296,7 +311,7 @@ class HTTPSig {
$best = Libzot::zot_record_preferred($x);
}
if ($best && $best['xchan_pubkey']) {
- return ['portable_id' => $best['xchan_hash'], 'public_key' => $best['xchan_pubkey'], 'hubloc' => $best];
+ return ['portable_id' => $best['xchan_hash'], 'public_key' => $best['xchan_pubkey'], 'algorithm' => get_xconfig($best['xchan_hash'], 'system', 'signing_algorithm'), 'hubloc' => $best];
}
}
@@ -308,12 +323,38 @@ class HTTPSig {
// The record wasn't in cache. Fetch it now.
$r = ActivityStreams::fetch($id);
+ $signatureAlgorithm = EMPTY_STR;
if ($r) {
if (array_key_exists('publicKey', $r) && array_key_exists('publicKeyPem', $r['publicKey']) && array_key_exists('id', $r['publicKey'])) {
if ($r['publicKey']['id'] === $id || $r['id'] === $id) {
$portable_id = ((array_key_exists('owner', $r['publicKey'])) ? $r['publicKey']['owner'] : EMPTY_STR);
- return ['public_key' => self::convertKey($r['publicKey']['publicKeyPem']), 'portable_id' => $portable_id, 'hubloc' => []];
+
+ // the w3c sec context has conflicting names and no defined values for this property except
+ // "http://www.w3.org/2000/09/xmldsig#rsa-sha1"
+
+ // Since the names conflict, it could mess up LD-signatures but we will accept both, and at this
+ // time we will only look for the substrings 'rsa-sha256' and 'rsa-sha512' within those properties.
+ // We will also accept a toplevel 'sigAlgorithm' regardless of namespace with the same constraints.
+ // Default to rsa-sha256 if we can't figure out. If they're sending 'hs2019' we have to
+ // look for something.
+
+ if (isset($r['publicKey']['signingAlgorithm'])) {
+ $signatureAlgorithm = $r['publicKey']['signingAlgorithm'];
+ set_xconfig($portable_id, 'system', 'signing_algorithm', $signatureAlgorithm);
+ }
+
+ if (isset($r['publicKey']['signatureAlgorithm'])) {
+ $signatureAlgorithm = $r['publicKey']['signatureAlgorithm'];
+ set_xconfig($portable_id, 'system', 'signing_algorithm', $signatureAlgorithm);
+ }
+
+ if (isset($r['sigAlgorithm'])) {
+ $signatureAlgorithm = $r['sigAlgorithm'];
+ set_xconfig($portable_id, 'system', 'signing_algorithm', $signatureAlgorithm);
+ }
+
+ return ['public_key' => self::convertKey($r['publicKey']['publicKeyPem']), 'portable_id' => $portable_id, 'algorithm' => (($signatureAlgorithm) ? $signatureAlgorithm : 'rsa-sha256'), 'hubloc' => []];
}
}
}
@@ -343,7 +384,7 @@ class HTTPSig {
}
if ($best && $best['xchan_pubkey']) {
- return ['portable_id' => $best['xchan_hash'], 'public_key' => $best['xchan_pubkey'], 'hubloc' => $best];
+ return ['portable_id' => $best['xchan_hash'], 'public_key' => $best['xchan_pubkey'], 'algorithm' => get_xconfig($best['xchan_hash'], 'system', 'signing_algorithm'), 'hubloc' => $best];
}
}
@@ -389,7 +430,7 @@ class HTTPSig {
}
if ($best && $best['xchan_pubkey']) {
- return ['portable_id' => $best['xchan_hash'], 'public_key' => $best['xchan_pubkey'], 'hubloc' => $best];
+ return ['portable_id' => $best['xchan_hash'], 'public_key' => $best['xchan_pubkey'], 'algorithm' => get_xconfig($best['xchan_hash'], 'system', 'signing_algorithm'), 'hubloc' => $best];
}
}
@@ -461,6 +502,9 @@ class HTTPSig {
$x = self::sign($head, $prvkey, $alg);
+ // TODO: should we default to hs2019?
+ // $headerval = 'keyId="' . $keyid . '",algorithm="' . (($algorithm === 'rsa-sha256') ? 'hs2019' : $algorithm) . '",headers="' . $x['headers'] . '",signature="' . $x['signature'] . '"';
+
$headerval = 'keyId="' . $keyid . '",algorithm="' . $algorithm . '",headers="' . $x['headers'] . '",signature="' . $x['signature'] . '"';
if ($encryption) {
diff --git a/boot.php b/boot.php
index 42b162506..ff73c0146 100644
--- a/boot.php
+++ b/boot.php
@@ -60,7 +60,7 @@ require_once('include/bbcode.php');
require_once('include/items.php');
define('PLATFORM_NAME', 'hubzilla');
-define('STD_VERSION', '7.1.4');
+define('STD_VERSION', '7.1.6');
define('ZOT_REVISION', '6.0');
define('DB_UPDATE_VERSION', 1252);
diff --git a/include/event.php b/include/event.php
index 440f559da..3d3dda035 100644
--- a/include/event.php
+++ b/include/event.php
@@ -70,8 +70,87 @@ function format_event_html($ev) {
}
function format_event_obj($jobject) {
+
$event = [];
+ $object = json_decode($jobject, true);
+
+/*******
+ This is our encoded format
+
+ $x = [
+ 'type' => 'Event',
+ 'id' => z_root() . '/event/' . $r[0]['resource_id'],
+ 'summary' => bbcode($arr['summary']),
+ // RFC3339 Section 4.3
+ 'startTime' => (($arr['adjust']) ? datetime_convert('UTC','UTC',$arr['dtstart'], ATOM_TIME) : datetime_convert('UTC','UTC',$arr['dtstart'],'Y-m-d\\TH:i:s-00:00')),
+ 'content' => bbcode($arr['description']),
+ 'location' => [ 'type' => 'Place', 'content' => $arr['location'] ],
+ 'source' => [ 'content' => format_event_bbcode($arr), 'mediaType' => 'text/x-multicode' ],
+ 'url' => [ [ 'mediaType' => 'text/calendar', 'href' => z_root() . '/events/ical/' . $event['event_hash'] ] ],
+ 'actor' => Activity::encode_person($r[0],false),
+ ];
+ if(! $arr['nofinish']) {
+ $x['endTime'] = (($arr['adjust']) ? datetime_convert('UTC','UTC',$arr['dtend'], ATOM_TIME) : datetime_convert('UTC','UTC',$arr['dtend'],'Y-m-d\\TH:i:s-00:00'));
+ }
+
+******/
+
+ if (is_array($object) && (array_key_exists('summary', $object) || array_key_exists('name', $object))) {
+
+ $dtend = ((array_key_exists('endTime', $object)) ? $object['endTime'] : NULL_DATE);
+ $title = ((isset($object['summary']) && $object['summary']) ? zidify_links(smilies(bbcode($object['summary']))) : $object['name']);
+
+ // mobilizon sets a timezone in the object
+ // we will assume that events with an timezone should be adjusted
+ $tz = $object['timezone'] ?? '';
+
+ // friendica has its own flag for adjust
+ $dfrn_adjust = $object['dfrn:adjust'] ?? '';
+
+ $adjust = ((strpos($object['startTime'], 'Z') !== false) || $tz || $dfrn_adjust);
+
+ $allday = (($adjust) ? false : true);
+
+ $dtstart = new DateTime($object['startTime']);
+ $dtend_obj = new DateTime($dtend);
+
+ $dtdiff = $dtstart->diff($dtend_obj);
+
+ if($allday && ($dtdiff->days < 2))
+ $oneday = true;
+
+ if($allday && !$oneday) {
+ // Subtract one day from the end date so we can use the "first day - last day" format for display.
+ $dtend_obj->modify('-1 day');
+ $dtend = datetime_convert('UTC', 'UTC', $dtend_obj->format('Y-m-d H:i:s'));
+ }
+
+ $bd_format = (($allday) ? t('l F d, Y') : t('l F d, Y \@ g:i A')); // Friday January 18, 2011 @ 8:01 AM or Friday January 18, 2011 for allday events
+
+ $event['header'] = replace_macros(get_markup_template('event_item_header.tpl'), array(
+ '$title' => $title,
+ '$dtstart_label' => t('Start:'),
+ '$dtstart_title' => datetime_convert('UTC', 'UTC', $object['startTime'], ((strpos($object['startTime'], 'Z')) ? ATOM_TIME : 'Y-m-d\TH:i:s' )),
+ '$dtstart_dt' => (($adjust) ? day_translate(datetime_convert('UTC', date_default_timezone_get(), $object['startTime'], $bd_format)) : day_translate(datetime_convert('UTC', 'UTC', $object['startTime'], $bd_format))),
+ '$finish' => ((array_key_exists('endTime', $object)) ? true : false),
+ '$dtend_label' => t('End:'),
+ '$dtend_title' => datetime_convert('UTC', 'UTC', $dtend, ((strpos($object['startTime'], 'Z')) ? ATOM_TIME : 'Y-m-d\TH:i:s' )),
+ '$dtend_dt' => (($adjust) ? day_translate(datetime_convert('UTC', date_default_timezone_get(), $dtend, $bd_format)) : day_translate(datetime_convert('UTC', 'UTC', $dtend, $bd_format))),
+ '$allday' => $allday,
+ '$oneday' => $oneday,
+ '$event_tz' => ['label' => t('Timezone'), 'value' => (($tz === date_default_timezone_get()) ? '' : $tz)]
+ ));
+ $event['content'] = replace_macros(get_markup_template('event_item_content.tpl'), array(
+ '$description' => $object['content'],
+ '$location_label' => t('Location:'),
+ '$location' => ((array_path_exists('location/name', $object)) ? zidify_links(smilies(bbcode($object['location']['name']))) : EMPTY_STR)
+ ));
+ }
+
+ return $event;
+/*
+ $event = [];
$object = json_decode($jobject,true);
$event_tz = '';
@@ -136,6 +215,7 @@ function format_event_obj($jobject) {
));
return $event;
+*/
}
function ical_wrapper($ev) {
@@ -1122,34 +1202,35 @@ function event_store_item($arr, $event) {
if($r) {
- set_iconfig($r[0]['id'], 'event', 'timezone', $arr['timezone'], true);
- xchan_query($r);
- $r = fetch_post_tags($r,true);
-
- $object = json_encode(array(
- 'type' => ACTIVITY_OBJ_EVENT,
- 'id' => z_root() . '/event/' . $r[0]['resource_id'],
- 'title' => $arr['summary'],
- 'timezone' => $arr['timezone'],
- 'dtstart' => $arr['dtstart'],
- 'dtend' => $arr['dtend'],
- 'nofinish' => $arr['nofinish'],
- 'description' => $arr['description'],
- 'location' => $arr['location'],
- 'adjust' => $arr['adjust'],
- 'content' => format_event_bbcode($arr),
+ //set_iconfig($r[0]['id'], 'event', 'timezone', $arr['timezone'], true);
+ //xchan_query($r);
+ //$r = fetch_post_tags($r,true);
+
+ $x = [
+ 'type' => 'Event',
+ 'id' => z_root() . '/event/' . $r[0]['resource_id'],
+ 'name' => $arr['summary'],
+// 'summary' => bbcode($arr['summary']),
+ // RFC3339 Section 4.3
+ 'startTime' => (($arr['adjust']) ? datetime_convert('UTC', 'UTC', $arr['dtstart'], ATOM_TIME) : datetime_convert('UTC', 'UTC', $arr['dtstart'], 'Y-m-d\\TH:i:s-00:00')),
+ 'content' => bbcode($arr['description']),
+ 'location' => [ 'type' => 'Place', 'name' => $arr['location'] ],
+ 'source' => [ 'content' => format_event_bbcode($arr), 'mediaType' => 'text/x-multicode' ],
+ 'url' => [ [ 'mediaType' => 'text/calendar', 'href' => z_root() . '/events/ical/' . $event['event_hash'] ] ],
+ 'actor' => Activity::encode_person($r[0], false),
'attachment' => Activity::encode_attachment($r[0]),
- 'author' => array(
- 'name' => $r[0]['author']['xchan_name'],
- 'address' => $r[0]['author']['xchan_addr'],
- 'guid' => $r[0]['author']['xchan_guid'],
- 'guid_sig' => $r[0]['author']['xchan_guid_sig'],
- 'link' => array(
- array('rel' => 'alternate', 'type' => 'text/html', 'href' => $r[0]['author']['xchan_url']),
- array('rel' => 'photo', 'type' => $r[0]['author']['xchan_photo_mimetype'], 'href' => $r[0]['author']['xchan_photo_m'])
- ),
- ),
- ));
+ 'tag' => Activity::encode_taxonomy($r[0])
+ ];
+
+ if (! $arr['nofinish']) {
+ $x['endTime'] = (($arr['adjust']) ? datetime_convert('UTC', 'UTC', $arr['dtend'], ATOM_TIME) : datetime_convert('UTC', 'UTC', $arr['dtend'], 'Y-m-d\\TH:i:s-00:00'));
+ }
+
+ if ($event['event_repeat']) {
+ $x['eventRepeat'] = $event['event_repeat'];
+ }
+
+ $object = json_encode($x);
$private = (($arr['allow_cid'] || $arr['allow_gid'] || $arr['deny_cid'] || $arr['deny_gid']) ? 1 : 0);
@@ -1285,29 +1366,30 @@ function event_store_item($arr, $event) {
dbesc($arr['event_xchan'])
);
if($x) {
- $item_arr['obj'] = json_encode(array(
- 'type' => ACTIVITY_OBJ_EVENT,
- 'id' => z_root() . '/event/' . $event['event_hash'],
- 'title' => $arr['summary'],
- 'timezone' => $arr['timezone'],
- 'dtstart' => $arr['dtstart'],
- 'dtend' => $arr['dtend'],
- 'nofinish' => $arr['nofinish'],
- 'description' => $arr['description'],
- 'location' => $arr['location'],
- 'adjust' => $arr['adjust'],
- 'content' => format_event_bbcode($arr),
- 'attachment' => Activity::encode_attachment($item_arr),
- 'author' => array(
- 'name' => $x[0]['xchan_name'],
- 'address' => $x[0]['xchan_addr'],
- 'guid' => $x[0]['xchan_guid'],
- 'guid_sig' => $x[0]['xchan_guid_sig'],
- 'link' => array(
- array('rel' => 'alternate', 'type' => 'text/html', 'href' => $x[0]['xchan_url']),
- array('rel' => 'photo', 'type' => $x[0]['xchan_photo_mimetype'], 'href' => $x[0]['xchan_photo_m'])),
- ),
- ));
+ $y = [
+ 'type' => 'Event',
+ 'id' => z_root() . '/event/' . $event['event_hash'],
+ 'name' => $arr['summary'],
+// 'summary' => bbcode($arr['summary']),
+ // RFC3339 Section 4.3
+ 'startTime' => (($arr['adjust']) ? datetime_convert('UTC', 'UTC', $arr['dtstart'], ATOM_TIME) : datetime_convert('UTC', 'UTC', $arr['dtstart'], 'Y-m-d\\TH:i:s-00:00')),
+ 'content' => bbcode($arr['description']),
+ 'location' => [ 'type' => 'Place', 'name' => bbcode($arr['location']) ],
+ 'source' => [ 'content' => format_event_bbcode($arr), 'mediaType' => 'text/x-multicode' ],
+ 'url' => [ [ 'mediaType' => 'text/calendar', 'href' => z_root() . '/events/ical/' . $event['event_hash'] ] ],
+ 'actor' => Activity::encode_person($z, false),
+ 'attachment' => Activity::encode_attachment($item_arr),
+ 'tag' => Activity::encode_taxonomy($item_arr)
+ ];
+
+ if (! $arr['nofinish']) {
+ $y['endTime'] = (($arr['adjust']) ? datetime_convert('UTC', 'UTC', $arr['dtend'], ATOM_TIME) : datetime_convert('UTC', 'UTC', $arr['dtend'], 'Y-m-d\\TH:i:s-00:00'));
+ }
+ if ($arr['event_repeat']) {
+ $y['eventRepeat'] = $arr['event_repeat'];
+ }
+
+ $item_arr['obj'] = json_encode($y);
}
// propagate the event resource_id so that posts containing it are easily searchable in downstream copies
diff --git a/include/html2bbcode.php b/include/html2bbcode.php
index 173ea63bd..cc67a5666 100644
--- a/include/html2bbcode.php
+++ b/include/html2bbcode.php
@@ -87,7 +87,7 @@ function deletenode(&$doc, $node)
function html2bbcode($message)
{
- if(!$message)
+ if(!is_string($message) && !$message)
return;
$message = str_replace("\r", "", $message);