aboutsummaryrefslogtreecommitdiffstats
path: root/Zotlabs
diff options
context:
space:
mode:
authorMario <mario@mariovavti.com>2024-03-22 08:37:29 +0000
committerMario <mario@mariovavti.com>2024-03-22 08:37:29 +0000
commit1aeb05628b6a2a069c46980efbe628362c9e3e74 (patch)
treee9aed15d0cd74e0c23dcb05c7be8fe9541efdf36 /Zotlabs
parent5b7387459cf4de8f7354d81cb0392c4225714d94 (diff)
parentb464fae3bf22585888c5f3def8eded76fd48ed16 (diff)
downloadvolse-hubzilla-9.0.tar.gz
volse-hubzilla-9.0.tar.bz2
volse-hubzilla-9.0.zip
Merge branch '9.0RC'9.0
Diffstat (limited to 'Zotlabs')
-rw-r--r--Zotlabs/Daemon/Cron.php4
-rw-r--r--Zotlabs/Daemon/Cron_daily.php4
-rw-r--r--Zotlabs/Daemon/Expire.php4
-rw-r--r--Zotlabs/Daemon/Fetchparents.php42
-rw-r--r--Zotlabs/Daemon/Notifier.php18
-rw-r--r--Zotlabs/Daemon/Zotconvo.php17
-rw-r--r--Zotlabs/Lib/ASCache.php33
-rw-r--r--Zotlabs/Lib/Activity.php1393
-rw-r--r--Zotlabs/Lib/ActivityStreams.php128
-rw-r--r--Zotlabs/Lib/Apps.php2
-rw-r--r--Zotlabs/Lib/Cache.php16
-rw-r--r--Zotlabs/Lib/Config.php3
-rw-r--r--Zotlabs/Lib/DReport.php53
-rw-r--r--Zotlabs/Lib/Enotify.php59
-rw-r--r--Zotlabs/Lib/JcsEddsa2022.php92
-rw-r--r--Zotlabs/Lib/Libsync.php14
-rw-r--r--Zotlabs/Lib/Libzot.php362
-rw-r--r--Zotlabs/Lib/Multibase.php34
-rw-r--r--Zotlabs/Lib/Share.php2
-rw-r--r--Zotlabs/Lib/Text.php24
-rw-r--r--Zotlabs/Lib/ThreadItem.php90
-rw-r--r--Zotlabs/Lib/ThreadStream.php6
-rw-r--r--Zotlabs/Module/Activity.php42
-rw-r--r--Zotlabs/Module/Admin/Site.php19
-rw-r--r--Zotlabs/Module/Api.php54
-rw-r--r--Zotlabs/Module/Apschema.php63
-rw-r--r--Zotlabs/Module/Channel.php50
-rw-r--r--Zotlabs/Module/Cloud.php2
-rw-r--r--Zotlabs/Module/Contactedit.php48
-rw-r--r--Zotlabs/Module/Conversation.php2
-rw-r--r--Zotlabs/Module/Cover_photo.php47
-rw-r--r--Zotlabs/Module/Display.php22
-rw-r--r--Zotlabs/Module/Dreport.php9
-rw-r--r--Zotlabs/Module/Emoji.php57
-rw-r--r--Zotlabs/Module/Event.php26
-rw-r--r--Zotlabs/Module/Follow.php23
-rw-r--r--Zotlabs/Module/Hq.php20
-rw-r--r--Zotlabs/Module/Item.php73
-rw-r--r--Zotlabs/Module/Like.php83
-rw-r--r--Zotlabs/Module/Linkinfo.php12
-rw-r--r--Zotlabs/Module/Mood.php163
-rw-r--r--Zotlabs/Module/Network.php2
-rw-r--r--Zotlabs/Module/Owa.php4
-rw-r--r--Zotlabs/Module/Photos.php4
-rw-r--r--Zotlabs/Module/Pin.php2
-rw-r--r--Zotlabs/Module/Poke.php207
-rw-r--r--Zotlabs/Module/Profile_photo.php3
-rw-r--r--Zotlabs/Module/Profiles.php6
-rw-r--r--Zotlabs/Module/Pubstream.php19
-rw-r--r--Zotlabs/Module/React.php122
-rw-r--r--Zotlabs/Module/Search.php8
-rw-r--r--Zotlabs/Module/Setup.php18
-rw-r--r--Zotlabs/Module/Share.php34
-rw-r--r--Zotlabs/Module/Sharedwithme.php4
-rw-r--r--Zotlabs/Module/Smilies.php16
-rw-r--r--Zotlabs/Module/Sse_bs.php63
-rw-r--r--Zotlabs/Module/Subthread.php15
-rw-r--r--Zotlabs/Module/Tagger.php8
-rw-r--r--Zotlabs/Module/Thing.php122
-rw-r--r--Zotlabs/Module/Vote.php2
-rw-r--r--Zotlabs/Render/Theme.php33
-rw-r--r--Zotlabs/Storage/BasicAuth.php13
-rw-r--r--Zotlabs/Storage/Directory.php18
-rw-r--r--Zotlabs/Storage/File.php2
-rw-r--r--Zotlabs/Update/_1260.php58
-rw-r--r--Zotlabs/Update/_1261.php58
-rw-r--r--Zotlabs/Update/_1262.php30
-rw-r--r--Zotlabs/Update/_1263.php26
-rw-r--r--Zotlabs/Web/HTTPSig.php46
-rw-r--r--Zotlabs/Widget/Messages.php31
-rw-r--r--Zotlabs/Widget/Pinned.php40
71 files changed, 1900 insertions, 2329 deletions
diff --git a/Zotlabs/Daemon/Cron.php b/Zotlabs/Daemon/Cron.php
index caae0ce53..e0fa2d629 100644
--- a/Zotlabs/Daemon/Cron.php
+++ b/Zotlabs/Daemon/Cron.php
@@ -124,13 +124,13 @@ class Cron {
$r = q("SELECT DISTINCT xchan, content FROM photo WHERE photo_usage = %d AND expires < %s - INTERVAL %s",
intval(PHOTO_CACHE),
db_utcnow(),
- db_quoteinterval(get_config('system', 'active_expire_days', '30') . ' DAY')
+ db_quoteinterval(get_config('system', 'cache_expire_days', 7) . ' DAY')
);
if ($r) {
q("DELETE FROM photo WHERE photo_usage = %d AND expires < %s - INTERVAL %s",
intval(PHOTO_CACHE),
db_utcnow(),
- db_quoteinterval(get_config('system', 'active_expire_days', '30') . ' DAY')
+ db_quoteinterval(get_config('system', 'cache_expire_days', 7) . ' DAY')
);
foreach ($r as $rr) {
$file = dbunescbin($rr['content']);
diff --git a/Zotlabs/Daemon/Cron_daily.php b/Zotlabs/Daemon/Cron_daily.php
index 850d38229..98379be1b 100644
--- a/Zotlabs/Daemon/Cron_daily.php
+++ b/Zotlabs/Daemon/Cron_daily.php
@@ -65,10 +65,10 @@ class Cron_daily {
}
}
- // Clean up emdedded content cache
+ // Clean up cache
q("DELETE FROM cache WHERE updated < %s - INTERVAL %s",
db_utcnow(),
- db_quoteinterval(get_config('system', 'active_expire_days', '30') . ' DAY')
+ db_quoteinterval(get_config('system', 'cache_expire_days', 7) . ' DAY')
);
//update statistics in config
diff --git a/Zotlabs/Daemon/Expire.php b/Zotlabs/Daemon/Expire.php
index 84a606dc2..6ab67150f 100644
--- a/Zotlabs/Daemon/Expire.php
+++ b/Zotlabs/Daemon/Expire.php
@@ -43,8 +43,8 @@ class Expire {
logger('expire: start with pid ' . $pid, LOGGER_DEBUG);
- $site_expire = intval(get_config('system', 'default_expire_days'));
- $commented_days = intval(get_config('system', 'active_expire_days'));
+ $site_expire = intval(get_config('system', 'default_expire_days', 30));
+ $commented_days = intval(get_config('system', 'active_expire_days', 7));
logger('site_expire: ' . $site_expire);
diff --git a/Zotlabs/Daemon/Fetchparents.php b/Zotlabs/Daemon/Fetchparents.php
new file mode 100644
index 000000000..b00acdfbf
--- /dev/null
+++ b/Zotlabs/Daemon/Fetchparents.php
@@ -0,0 +1,42 @@
+<?php
+
+namespace Zotlabs\Daemon;
+
+use Zotlabs\Lib\Activity;
+
+class Fetchparents {
+
+ static public function run($argc, $argv) {
+
+ logger('Fetchparents invoked: ' . print_r($argv, true));
+
+ if ($argc < 4) {
+ return;
+ }
+
+ $channels = explode(',', $argv[1]);
+ if (!$channels) {
+ return;
+ }
+
+ $observer_hash = $argv[2];
+ if (!$observer_hash) {
+ return;
+ }
+
+ $mid = $argv[3];
+ if (!$mid) {
+ return;
+ }
+
+ $force = $argv[4] ?? false;
+
+ foreach ($channels as $channel_id) {
+ $channel = channelx_by_n($channel_id);
+ Activity::fetch_and_store_parents($channel, $observer_hash, $mid, null, $force);
+ }
+
+ return;
+
+ }
+}
diff --git a/Zotlabs/Daemon/Notifier.php b/Zotlabs/Daemon/Notifier.php
index 4b74a7ba9..4e7ca3911 100644
--- a/Zotlabs/Daemon/Notifier.php
+++ b/Zotlabs/Daemon/Notifier.php
@@ -5,7 +5,6 @@ namespace Zotlabs\Daemon;
use Zotlabs\Lib\Libzot;
use Zotlabs\Lib\Activity;
use Zotlabs\Lib\Queue;
-use Zotlabs\Lib\LDSignatures;
require_once('include/html2plain.php');
require_once('include/conversation.php');
@@ -271,14 +270,13 @@ class Notifier {
// Check for non published items, but allow an exclusion for transmitting hidden file activities
if (intval($target_item['item_unpublished']) || intval($target_item['item_delayed']) ||
- intval($target_item['item_blocked']) ||
- (intval($target_item['item_hidden']) && ($target_item['obj_type'] !== ACTIVITY_OBJ_FILE))) {
+ intval($target_item['item_blocked']) || intval($target_item['item_hidden'])) {
logger('notifier: target item not published, so not forwardable', LOGGER_DEBUG);
return;
}
// follow/unfollow is for internal use only
- if (in_array($target_item['verb'], [ACTIVITY_FOLLOW, ACTIVITY_UNFOLLOW])) {
+ if (in_array($target_item['verb'], ['Follow', 'Ignore', ACTIVITY_FOLLOW, ACTIVITY_UNFOLLOW])) {
logger('not fowarding follow/unfollow note activity');
return;
}
@@ -342,14 +340,7 @@ class Notifier {
self::$encoded_item = json_decode($m, true);
}
else {
-
- self::$encoded_item = array_merge(['@context' => [
- ACTIVITYSTREAMS_JSONLD_REV,
- 'https://w3id.org/security/v1',
- z_root() . ZOT_APSCHEMA_REV
- ]], Activity::encode_activity($target_item)
- );
- self::$encoded_item['signature'] = LDSignatures::sign(self::$encoded_item, self::$channel);
+ self::$encoded_item = Activity::build_packet(Activity::encode_activity($target_item), self::$channel, false);
}
logger('target_item: ' . print_r($target_item, true), LOGGER_DEBUG);
@@ -382,7 +373,8 @@ class Notifier {
if (($relay_to_owner || $uplink) && ($cmd !== 'relay')) {
logger('notifier: followup relay', LOGGER_DEBUG);
- $sendto = (($uplink) ? $parent_item['source_xchan'] : (($parent_item['verb'] === ACTIVITY_SHARE) ? $parent_item['author_xchan'] : $parent_item['owner_xchan']));
+ // If the Parent item is an Announce the real owner is the parent author
+ $sendto = (($uplink) ? $parent_item['source_xchan'] : $parent_item['owner_xchan']);
self::$recipients = [$sendto];
self::$private = true;
$upstream = true;
diff --git a/Zotlabs/Daemon/Zotconvo.php b/Zotlabs/Daemon/Zotconvo.php
index 16e7f113f..188956d26 100644
--- a/Zotlabs/Daemon/Zotconvo.php
+++ b/Zotlabs/Daemon/Zotconvo.php
@@ -10,21 +10,26 @@ class Zotconvo {
logger('Zotconvo invoked: ' . print_r($argv, true));
- if ($argc != 3) {
+ if ($argc < 3) {
return;
}
- $mid = $argv[2];
- if (!$mid) {
+ $channels = explode(',', $argv[1]);
+ if (!$channels) {
return;
}
- $channel = channelx_by_n(intval($argv[1]));
- if (!$channel) {
+ $mid = $argv[2];
+ if (!$mid) {
return;
}
- Libzot::fetch_conversation($channel, $mid);
+ $force = $argv[3] ?? false;
+
+ foreach ($channels as $channel_id) {
+ $channel = channelx_by_n($channel_id);
+ Libzot::fetch_conversation($channel, $mid, $force);
+ }
return;
diff --git a/Zotlabs/Lib/ASCache.php b/Zotlabs/Lib/ASCache.php
new file mode 100644
index 000000000..4904a1d8a
--- /dev/null
+++ b/Zotlabs/Lib/ASCache.php
@@ -0,0 +1,33 @@
+<?php /** @file */
+
+namespace Zotlabs\Lib;
+
+ /**
+ * A wrapper for the cache api
+ */
+
+class ASCache {
+ public static function isEnabled() {
+ return Config::Get('system', 'as_object_cache_enabled', true);
+ }
+
+ public static function getAge() {
+ return Config::Get('system', 'as_object_cache_time', '10 MINUTE');
+ }
+
+ public static function Get($key) {
+ if (!self::isEnabled()) {
+ return;
+ }
+
+ return Cache::get($key, self::getAge());
+ }
+
+ public static function Set($key, $value) {
+ if (!self::isEnabled()) {
+ return;
+ }
+
+ Cache::set($key, $value);
+ }
+}
diff --git a/Zotlabs/Lib/Activity.php b/Zotlabs/Lib/Activity.php
index 4fbc051bf..75d2ffbe9 100644
--- a/Zotlabs/Lib/Activity.php
+++ b/Zotlabs/Lib/Activity.php
@@ -27,19 +27,23 @@ class Activity {
return $x['asld'];
}
- if ($x['type'] === ACTIVITY_OBJ_PERSON) {
+ if (in_array($x['type'], ['Person', ACTIVITY_OBJ_PERSON])) {
return self::fetch_person($x);
}
- if ($x['type'] === ACTIVITY_OBJ_PROFILE) {
+
+ if (in_array($x['type'], ['Profile', ACTIVITY_OBJ_PROFILE])) {
return self::fetch_profile($x);
}
- if (in_array($x['type'], [ACTIVITY_OBJ_NOTE, ACTIVITY_OBJ_ARTICLE])) {
+
+ if (in_array($x['type'], ['Note', 'Article', ACTIVITY_OBJ_NOTE, ACTIVITY_OBJ_ARTICLE])) {
return self::fetch_item($x);
}
+
if ($x['type'] === ACTIVITY_OBJ_THING) {
return self::fetch_thing($x);
}
- if ($x['type'] === ACTIVITY_OBJ_EVENT) {
+
+ if (in_array($x['type'], ['Event', ACTIVITY_OBJ_EVENT])) {
return self::fetch_event($x);
}
@@ -59,13 +63,16 @@ class Activity {
"select *, id as item_id from item where mid = '%s' and item_wall = 1 $item_normal $sql_extra",
dbesc($url)
);
+
if ($j) {
xchan_query($j, true);
$items = fetch_post_tags($j);
}
+
if ($items) {
return self::encode_item(array_shift($items), true);
}
+
return null;
}
@@ -150,6 +157,7 @@ class Activity {
}
else {
logger('logger_stats_data cmd:Activity_fetch' . ' start:' . $start_timestamp . ' ' . 'end:' . microtime(true) . ' meta:' . $url . '#' . random_string(16));
+ btlogger('activity fetch');
}
return json_decode($x['body'], true);
@@ -164,10 +172,6 @@ class Activity {
}
static function fetch_person($x) {
- return self::fetch_profile($x);
- }
-
- static function fetch_profile($x) {
$r = q("select * from xchan where xchan_url = '%s' limit 1",
dbesc($x['id'])
);
@@ -181,7 +185,14 @@ class Activity {
return [];
return self::encode_person($r[0]);
+ }
+
+ static function fetch_profile($x) {
+ if (isset($x['describes'])) {
+ return $x;
+ }
+ return [];
}
static function fetch_thing($x) {
@@ -194,14 +205,23 @@ class Activity {
if (!$r)
return [];
+ $channel = channelx_by_n($r[0]['obj_channel']);
+
$x = [
- 'type' => 'Object',
+ 'type' => 'Page',
'id' => z_root() . '/thing/' . $r[0]['obj_obj'],
- 'name' => $r[0]['obj_term']
+ 'name' => $channel['channel_name'] . ' ' . $r[0]['obj_verb'] . ' ' . $r[0]['obj_term'],
+ 'content' => $r[0]['obj_url'],
+ 'url' => $r[0]['obj_url']
];
- if ($r[0]['obj_image'])
- $x['image'] = $r[0]['obj_image'];
+ if ($r[0]['obj_imgurl']) {
+ $x['content'] = '<a href="' . $r[0]['obj_url'] . '"><img src="' . $r[0]['obj_imgurl'] . '" alt="' . $r[0]['obj_term'] . '"></a>';
+ $x['icon'] = [
+ 'type' => 'Image',
+ 'url' => $r[0]['obj_imgurl']
+ ];
+ }
return $x;
@@ -220,7 +240,7 @@ class Activity {
if ($r) {
xchan_query($r, true);
$r = fetch_post_tags($r);
- if (in_array($r[0]['verb'], ['Create', 'Invite']) && $r[0]['obj_type'] === ACTIVITY_OBJ_EVENT) {
+ if (in_array($r[0]['verb'], ['Create', 'Invite']) && in_array($r[0]['obj_type'], ['Event', ACTIVITY_OBJ_EVENT])) {
$r[0]['verb'] = 'Invite';
return self::encode_activity($r[0]);
}
@@ -360,7 +380,7 @@ class Activity {
if ($items) {
$x = [];
foreach ($items as $i) {
- $m = get_iconfig($i['id'], 'activitypub', 'rawmsg');
+ $m = IConfig::Get($i['id'], 'activitypub', 'rawmsg');
if ($m) {
if (is_string($m))
$t = json_decode($m, true);
@@ -440,13 +460,7 @@ class Activity {
$ret = [];
- if ($i['verb'] === ACTIVITY_FRIEND) {
- // Hubzilla 'make-friend' activity, no direct mapping from AS1 to AS2 - make it a note
- $objtype = 'Note';
- }
- else {
- $objtype = self::activity_obj_mapper($i['obj_type']);
- }
+ $objtype = self::activity_obj_mapper($i['obj_type']);
if (intval($i['item_deleted'])) {
$ret['type'] = 'Tombstone';
@@ -495,7 +509,7 @@ class Activity {
// set this for descendants even if the current item is not private
// because it may have been relayed from a private item.
- $token = get_iconfig($i, 'ocap', 'relay');
+ $token = IConfig::Get($i, 'ocap', 'relay');
if ($token && $has_images) {
$matches_processed = [];
for ($n = 0; $n < count($images); $n++) {
@@ -513,7 +527,7 @@ class Activity {
}
if ($i['title'])
- $ret['name'] = $i['title'];
+ $ret['name'] = unescape_tags($i['title']);
$ret['published'] = datetime_convert('UTC', 'UTC', $i['created'], ATOM_TIME);
if ($i['created'] !== $i['edited'])
@@ -537,7 +551,7 @@ class Activity {
}
}
- if (intval($i['item_wall']) && $i['mid'] === $i['parent_mid']) {
+ if (intval($i['item_wall'])) {
$ret['commentPolicy'] = map_scope(PermissionLimits::Get($i['uid'], 'post_comments'));
}
@@ -560,11 +574,11 @@ class Activity {
if ($i['mimetype'] === 'text/bbcode') {
if ($i['title'])
- $ret['name'] = bbcode($i['title'], ['cache' => true]);
+ $ret['name'] = unescape_tags($i['title']);
if ($i['summary'])
- $ret['summary'] = bbcode($i['summary'], ['cache' => true]);
- $ret['content'] = bbcode($i['body'], ['cache' => true]);
- $ret['source'] = ['content' => $i['body'], 'mediaType' => 'text/bbcode'];
+ $ret['summary'] = unescape_tags($i['summary']);
+ $ret['content'] = bbcode(unescape_tags($i['body']), ['cache' => true]);
+ $ret['source'] = ['content' => unescape_tags($i['body']), 'mediaType' => 'text/bbcode'];
}
$actor = self::encode_person($i['author'], false);
@@ -594,9 +608,9 @@ class Activity {
call_hooks('encode_item', $hookinfo);
-
return $hookinfo['encoded'];
+
}
static function decode_taxonomy($item) {
@@ -608,11 +622,12 @@ class Activity {
if (!array_key_exists(0, $ptr)) {
$ptr = [$ptr];
}
+
foreach ($ptr as $t) {
if (is_array($t) && !array_key_exists('type', $t))
$t['type'] = 'Hashtag';
- if (is_array($t) && array_key_exists('href', $t) && array_key_exists('name', $t)) {
+ if (is_array($t) && (array_key_exists('href', $t) || array_key_exists('id', $t)) && array_key_exists('name', $t)) {
switch ($t['type']) {
case 'Hashtag':
$ret[] = ['ttype' => TERM_HASHTAG, 'url' => $t['href'], 'term' => escape_tags((substr($t['name'], 0, 1) === '#') ? substr($t['name'], 1) : $t['name'])];
@@ -626,6 +641,10 @@ class Activity {
$ret[] = ['ttype' => TERM_BOOKMARK, 'url' => $t['href'], 'term' => escape_tags($t['name'])];
break;
+ case 'Emoji':
+ $ret[] = ['ttype' => TERM_EMOJI, 'url' => $t['id'], 'term' => escape_tags($t['name']), 'imgurl' => $t['icon']['url']];
+ break;
+
default:
break;
}
@@ -658,6 +677,10 @@ class Activity {
$ret[] = ['type' => 'Bookmark', 'href' => $t['url'], 'name' => $t['term']];
break;
+ case TERM_EMOJI:
+ $ret[] = ['type' => 'Emoji', 'id' => $t['url'], 'name' => $t['term'], 'icon' => ['type' => 'Image', 'url' => $t['imgurl']]];
+ break;
+
default:
break;
}
@@ -761,7 +784,7 @@ class Activity {
$entry['image'] = $att['image'];
}
if ($entry) {
- $ret[] = $entry;
+ array_unshift($ret, $entry);
}
}
} elseif (isset($item['attachment']) && is_string($item['attachment'])) {
@@ -776,11 +799,6 @@ class Activity {
$ret = [];
$reply = false;
- if ($i['verb'] === ACTIVITY_FRIEND) {
- // Hubzilla 'make-friend' activity, no direct mapping from AS1 to AS2 - make it a note
- $ret['obj'] = [];
- }
-
$ret['type'] = self::activity_mapper($i['verb']);
if ((isset($i['item_deleted']) && intval($i['item_deleted'])) && !$recurse) {
@@ -927,7 +945,7 @@ class Activity {
if (!is_array($i['obj'])) {
$i['obj'] = json_decode($i['obj'], true);
}
- if ($i['obj']['type'] === ACTIVITY_OBJ_PHOTO) {
+ if (in_array($i['obj']['type'], ['Image', ACTIVITY_OBJ_PHOTO])) {
$i['obj']['id'] = $i['mid'];
}
@@ -962,10 +980,12 @@ class Activity {
return [];
}
+/* this should not be needed
$t = self::encode_taxonomy($i);
if ($t) {
$ret['tag'] = $t;
}
+*/
$a = self::encode_attachment($i, true);
if ($a) {
@@ -976,12 +996,14 @@ class Activity {
$ret['to'] = [ACTIVITY_PUBLIC_INBOX];
}
+
$hookinfo = [
'item' => $i,
'encoded' => $ret
];
call_hooks('encode_activity', $hookinfo);
+
return $hookinfo['encoded'];
}
@@ -1080,11 +1102,11 @@ class Activity {
$ret['type'] = 'Person';
if ($c) {
- if (get_pconfig($c['channel_id'], 'system', 'group_actor')) {
+ if (PConfig::Get($c['channel_id'], 'system', 'group_actor')) {
$ret['type'] = 'Group';
}
- $ret['manuallyApprovesFollowers'] = ((get_pconfig($c['channel_id'], 'system', 'autoperms')) ? false : true);
+ $ret['manuallyApprovesFollowers'] = ((PConfig::Get($c['channel_id'], 'system', 'autoperms')) ? false : true);
}
$ret['id'] = $id;
@@ -1165,7 +1187,7 @@ class Activity {
if (!is_array($item[$elm])) {
$item[$elm] = json_decode($item[$elm], true);
}
- if ($item[$elm]['type'] === ACTIVITY_OBJ_PHOTO) {
+ if (in_array($item[$elm]['type'], ['Image', ACTIVITY_OBJ_PHOTO])) {
$item[$elm]['id'] = $item['mid'];
}
@@ -1194,21 +1216,18 @@ class Activity {
$acts = [
'http://activitystrea.ms/schema/1.0/post' => 'Create',
- 'http://activitystrea.ms/schema/1.0/share' => 'Announce',
+ //'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/tag' => 'Add',
'http://activitystrea.ms/schema/1.0/follow' => 'Follow',
'http://activitystrea.ms/schema/1.0/unfollow' => 'Unfollow',
'http://activitystrea.ms/schema/1.0/stop-following' => 'Unfollow',
'http://purl.org/zot/activity/attendyes' => 'Accept',
'http://purl.org/zot/activity/attendno' => 'Reject',
'http://purl.org/zot/activity/attendmaybe' => 'TentativeAccept',
- 'Invite' => 'Invite',
- 'Delete' => 'Delete',
- 'Undo' => 'Undo'
];
call_hooks('activity_mapper', $acts);
@@ -1217,19 +1236,6 @@ class Activity {
return $acts[$verb];
}
- // Reactions will just map to normal activities
-
- if (strpos($verb, ACTIVITY_REACT) !== false)
- return 'emojiReaction';
- if (strpos($verb, ACTIVITY_MOOD) !== false)
- return 'Create';
-
- if (strpos($verb, ACTIVITY_FRIEND) !== false)
- return 'Create';
-
- if (strpos($verb, ACTIVITY_POKE) !== false)
- 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
@@ -1242,21 +1248,24 @@ class Activity {
$acts = [
'http://activitystrea.ms/schema/1.0/post' => 'Create',
- 'http://activitystrea.ms/schema/1.0/share' => 'Announce',
+ // '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/tag' => 'Add',
'http://activitystrea.ms/schema/1.0/follow' => 'Follow',
'http://activitystrea.ms/schema/1.0/unfollow' => 'Unfollow',
'http://activitystrea.ms/schema/1.0/stop-following' => 'Unfollow',
'http://purl.org/zot/activity/attendyes' => 'Accept',
'http://purl.org/zot/activity/attendno' => 'Reject',
'http://purl.org/zot/activity/attendmaybe' => 'TentativeAccept',
+ 'Announce' => 'Announce',
'Invite' => 'Invite',
'Delete' => 'Delete',
- 'Undo' => 'Undo'
+ 'Undo' => 'Undo',
+ 'Add' => 'Add',
+ 'Remove' => 'Remove'
];
call_hooks('activity_decode_mapper', $acts);
@@ -1325,13 +1334,7 @@ class Activity {
'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',
- 'Invite' => 'Invite',
- 'Question' => 'Question',
- 'Audio' => 'Audio',
- 'Video' => 'Video',
- 'Delete' => 'Delete',
- 'Undo' => 'Undo'
+ 'http://purl.org/zot/activity/mood' => 'zot:Mood'
];
call_hooks('activity_obj_mapper', $objs);
@@ -1391,7 +1394,7 @@ class Activity {
}
}
- $role = get_pconfig($channel['channel_id'], 'system', 'permissions_role', 'personal');
+ $role = PConfig::Get($channel['channel_id'], 'system', 'permissions_role', 'personal');
$x = PermissionRoles::role_perms($role);
$their_perms = Permissions::FilledPerms($x['perms_connect']);
@@ -1412,7 +1415,7 @@ class Activity {
// We've already approved them or followed them first
// Send an Accept back to them
- set_abconfig($channel['channel_id'], $person_obj['id'], 'pubcrawl', 'their_follow_id', $their_follow_id);
+ AbConfig::Set($channel['channel_id'], $person_obj['id'], 'pubcrawl', 'their_follow_id', $their_follow_id);
Master::Summon(['Notifier', 'permission_accept', $contact['abook_id']]);
return;
@@ -1424,7 +1427,7 @@ class Activity {
if(in_array($k, ['send_stream', 'post_wall'])) {
continue; // Those will be set once we accept their follow request
}
- set_abconfig($channel['channel_id'], $contact['abook_xchan'], 'their_perms', $k, $v);
+ AbConfig::Set($channel['channel_id'], $contact['abook_xchan'], 'their_perms', $k, $v);
}
$abook_instance = $contact['abook_instance'];
@@ -1458,7 +1461,7 @@ class Activity {
// From here on out we assume a Follow activity to somebody we have no existing relationship with
- set_abconfig($channel['channel_id'], $person_obj['id'], 'pubcrawl', 'their_follow_id', $their_follow_id);
+ AbConfig::Set($channel['channel_id'], $person_obj['id'], 'pubcrawl', 'their_follow_id', $their_follow_id);
// The xchan should have been created by actor_store() above
@@ -1476,7 +1479,7 @@ class Activity {
$my_perms = $p['perms'];
$automatic = $p['automatic'];
- $closeness = get_pconfig($channel['channel_id'], 'system', 'new_abook_closeness', 80);
+ $closeness = PConfig::Get($channel['channel_id'], 'system', 'new_abook_closeness', 80);
$r = abook_store_lowlevel(
[
@@ -1495,11 +1498,11 @@ class Activity {
if ($my_perms)
foreach ($my_perms as $k => $v)
- set_abconfig($channel['channel_id'], $ret['xchan_hash'], 'my_perms', $k, $v);
+ AbConfig::Set($channel['channel_id'], $ret['xchan_hash'], 'my_perms', $k, $v);
if ($their_perms)
foreach ($their_perms as $k => $v)
- set_abconfig($channel['channel_id'], $ret['xchan_hash'], 'their_perms', $k, $v);
+ AbConfig::Set($channel['channel_id'], $ret['xchan_hash'], 'their_perms', $k, $v);
if ($r) {
logger("New ActivityPub follower for {$channel['channel_name']}");
@@ -1535,7 +1538,7 @@ class Activity {
unset($clone['abook_account']);
unset($clone['abook_channel']);
- $abconfig = load_abconfig($channel['channel_id'], $clone['abook_xchan']);
+ $abconfig = AbConfig::Load($channel['channel_id'], $clone['abook_xchan']);
if ($abconfig)
$clone['abconfig'] = $abconfig;
@@ -1576,7 +1579,7 @@ class Activity {
);
if ($r) {
// remove all permissions they provided
- del_abconfig($channel['channel_id'], $r[0]['xchan_hash'], 'system', 'their_perms');
+ AbConfig::Delete($channel['channel_id'], $r[0]['xchan_hash'], 'system', 'their_perms');
}
}
@@ -1584,8 +1587,7 @@ class Activity {
}
public static function drop($channel, $observer, $act) {
- $r = q(
- "select * from item where mid = '%s' and uid = %d limit 1",
+ $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'])
);
@@ -1605,7 +1607,6 @@ class Activity {
if ($r[0]['item_wall']) {
Master::Summon(['Notifier', 'drop', $r[0]['id']]);
}
-
}
@@ -1651,7 +1652,7 @@ class Activity {
if ($ap_hubloc) {
// we already have a stored record. Determine if it needs updating.
if ($ap_hubloc['hubloc_updated'] < datetime_convert('UTC', 'UTC', ' now - 3 days') || $force) {
- $person_obj = self::get_cached_actor($url);
+ $person_obj = self::get_actor($url, $force);
}
else {
return;
@@ -1674,7 +1675,7 @@ class Activity {
$name = t('Unknown');
}
- $webfinger_addr = '';
+ $webfinger_addr = ((isset($person_obj['webfinger'])) ? str_replace('acct:', '', $person_obj['webfinger']) : '');
$hostname = '';
$baseurl = '';
$site_url = '';
@@ -1686,7 +1687,7 @@ class Activity {
$site_url = $m['scheme'] . '://' . $m['host'];
}
- if (!empty($person_obj['preferredUsername']) && $hostname) {
+ if (!$webfinger_addr && !empty($person_obj['preferredUsername']) && $hostname) {
$webfinger_addr = escape_tags($person_obj['preferredUsername']) . '@' . $hostname;
}
@@ -1740,6 +1741,7 @@ class Activity {
$profile = $url;
}
+ $pubkey = '';
if (array_key_exists('publicKey', $person_obj) && array_key_exists('publicKeyPem', $person_obj['publicKey'])) {
if ($person_obj['id'] === $person_obj['publicKey']['owner']) {
$pubkey = $person_obj['publicKey']['publicKeyPem'];
@@ -1749,6 +1751,22 @@ class Activity {
}
}
+ $epubkey = '';
+ if (isset($person_obj['assertionMethod'])) {
+ if (!isset($person_obj['assertionMethod'][0])) {
+ $person_obj['assertionMethod'] = [$person_obj['assertionMethod']];
+ }
+
+ foreach($person_obj['assertionMethod'] as $am) {
+ if ($person_obj['id'] === $am['controller'] &&
+ $am['type'] === 'Multikey' &&
+ str_starts_with($am['publicKeyMultibase'], 'z6Mk')
+ ) {
+ $epubkey = $am['publicKeyMultibase'];
+ }
+ }
+ }
+
$group_actor = ($person_obj['type'] === 'Group');
$r = q("select * from xchan join hubloc on xchan_hash = hubloc_hash where xchan_hash = '%s'",
@@ -1769,10 +1787,11 @@ class Activity {
);
// update existing xchan record
- q("update xchan set xchan_name = '%s', xchan_pubkey = '%s', xchan_addr = '%s', xchan_network = 'activitypub', xchan_name_date = '%s', xchan_pubforum = %d where xchan_hash = '%s'",
- dbesc(escape_tags($name)),
- dbesc(escape_tags($pubkey)),
- dbesc(escape_tags($webfinger_addr)),
+ q("update xchan set xchan_name = '%s', xchan_pubkey = '%s', xchan_epubkey = '%s', xchan_addr = '%s', xchan_network = 'activitypub', xchan_name_date = '%s', xchan_pubforum = %d where xchan_hash = '%s'",
+ dbesc($name),
+ dbesc($pubkey),
+ dbesc($epubkey),
+ dbesc($webfinger_addr),
dbescdate(datetime_convert()),
intval($group_actor),
dbesc($url)
@@ -1780,7 +1799,7 @@ class Activity {
// update existing hubloc record
q("update hubloc set hubloc_addr = '%s', hubloc_network = 'activitypub', hubloc_url = '%s', hubloc_host = '%s', hubloc_callback = '%s', hubloc_updated = '%s', hubloc_id_url = '%s' where hubloc_hash = '%s'",
- dbesc(escape_tags($webfinger_addr)),
+ dbesc($webfinger_addr),
dbesc($baseurl),
dbesc($hostname),
dbesc($inbox),
@@ -1796,13 +1815,14 @@ class Activity {
[
'xchan_hash' => $url,
'xchan_guid' => $url,
- 'xchan_pubkey' => escape_tags($pubkey),
+ 'xchan_pubkey' => $pubkey,
+ 'xchan_epubkey' => $epubkey,
'xchan_addr' => $webfinger_addr,
'xchan_url' => $profile,
- 'xchan_name' => escape_tags($name),
- '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),
+ 'xchan_name' => $name,
+ '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),
'xchan_name_date' => datetime_convert(),
'xchan_network' => 'activitypub',
'xchan_pubforum' => intval($group_actor)
@@ -1847,31 +1867,6 @@ class Activity {
}
}
- static function create_action($channel, $observer_hash, $act) {
-
- if (in_array($act->obj['type'], ['Note', 'Article', 'Video'])) {
- self::create_note($channel, $observer_hash, $act);
- }
-
-
- }
-
- static function announce_action($channel, $observer_hash, $act) {
-
- if (in_array($act->type, ['Announce'])) {
- self::announce_note($channel, $observer_hash, $act);
- }
-
- }
-
- static function like_action($channel, $observer_hash, $act) {
-
- if (in_array($act->obj['type'], ['Note', 'Article', 'Video'])) {
- self::like_note($channel, $observer_hash, $act);
- }
-
-
- }
// sort function width decreasing
static function vid_sort($a, $b) {
@@ -1884,251 +1879,6 @@ class Activity {
return (($a_width > $b_width) ? -1 : 1);
}
- static function create_note($channel, $observer_hash, $act) {
-
- $s = [];
- $is_sys_channel = is_sys_channel($channel['channel_id']);
- $parent = ((array_key_exists('inReplyTo', $act->obj)) ? urldecode($act->obj['inReplyTo']) : '');
-
- if ($parent) {
-
- $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))
- );
-
- if (!$r) {
- logger('parent not found.');
- return;
- }
-
- if ($r[0]['owner_xchan'] === $channel['channel_hash']) {
- if (!perm_is_allowed($channel['channel_id'], $observer_hash, 'send_stream') && !$is_sys_channel) {
- logger('no comment permission.');
- return;
- }
- }
-
- $s['parent_mid'] = $r[0]['mid'];
- $s['owner_xchan'] = $r[0]['owner_xchan'];
- $s['author_xchan'] = $observer_hash;
-
- }
- else {
- if (!perm_is_allowed($channel['channel_id'], $observer_hash, 'send_stream') && !$is_sys_channel) {
- logger('no send_stream permission');
- return;
- }
- $s['owner_xchan'] = $s['author_xchan'] = $observer_hash;
- }
-
- if ($act->recips && (!in_array(ACTIVITY_PUBLIC_INBOX, $act->recips)))
- $s['item_private'] = 1;
-
-
- if (array_key_exists('directMessage', $act->obj) && intval($act->obj['directMessage'])) {
- $s['item_private'] = 2;
- }
-
- if (intval($s['item_private']) === 2) {
- if (!perm_is_allowed($channel['channel_id'], $observer_hash, 'post_mail')) {
- logger('no post_mail permission');
- return;
- }
- }
-
- $content = self::get_content($act->obj);
-
- if (!$content) {
- logger('no content');
- return;
- }
-
- $s['aid'] = $channel['channel_account_id'];
- $s['uid'] = $channel['channel_id'];
-
- // Make sure we use the zot6 identity where applicable
-
- $s['author_xchan'] = self::find_best_identity($s['author_xchan']);
- $s['owner_xchan'] = self::find_best_identity($s['owner_xchan']);
-
- if (!$s['author_xchan']) {
- logger('No author: ' . print_r($act, true));
- }
-
- if (!$s['owner_xchan']) {
- logger('No owner: ' . print_r($act, true));
- }
-
- if (!$s['author_xchan'] || !$s['owner_xchan'])
- return;
-
- $s['mid'] = urldecode($act->obj['id']);
- $s['uuid'] = $act->obj['diaspora:guid'];
- $s['plink'] = urldecode($act->obj['id']);
-
-
- if ($act->data['published']) {
- $s['created'] = datetime_convert('UTC', 'UTC', $act->data['published']);
- }
- elseif ($act->obj['published']) {
- $s['created'] = datetime_convert('UTC', 'UTC', $act->obj['published']);
- }
- if ($act->data['updated']) {
- $s['edited'] = datetime_convert('UTC', 'UTC', $act->data['updated']);
- }
- elseif ($act->obj['updated']) {
- $s['edited'] = datetime_convert('UTC', 'UTC', $act->obj['updated']);
- }
- 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']);
- }
-
- if (!$s['created'])
- $s['created'] = datetime_convert();
-
- if (!$s['edited'])
- $s['edited'] = $s['created'];
-
-
- if (!$s['parent_mid'])
- $s['parent_mid'] = $s['mid'];
-
-
- $s['title'] = self::bb_content($content, 'name');
- $s['summary'] = self::bb_content($content, 'summary');
- $s['body'] = self::bb_content($content, 'content');
- $s['verb'] = ACTIVITY_POST;
- $s['obj_type'] = ACTIVITY_OBJ_NOTE;
-
- $generator = $act->get_property_obj('generator');
- if (!$generator)
- $generator = $act->get_property_obj('generator', $act->obj);
-
- if ($generator && array_key_exists('type', $generator)
- && in_array($generator['type'], ['Application', 'Service']) && array_key_exists('name', $generator)) {
- $s['app'] = escape_tags($generator['name']);
- }
-
- if ($channel['channel_system']) {
- $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;
- }
- }
-
- $abook = q("select * from abook where (abook_xchan = '%s' OR abook_xchan = '%s') and abook_channel = %d ",
- dbesc($s['author_xchan']),
- dbesc($s['owner_xchan']),
- intval($channel['channel_id'])
- );
-
- if ($abook) {
- if (!post_is_importable($channel['channel_id'], $s, $abook)) {
- logger('post is filtered');
- return;
- }
- }
-
- if ($act->obj['conversation']) {
- set_iconfig($s, 'ostatus', 'conversation', $act->obj['conversation'], 1);
- }
-
- $a = self::decode_taxonomy($act->obj);
- if ($a) {
- $s['term'] = $a;
- }
-
- $a = self::decode_attachment($act->obj);
- if ($a) {
- $s['attach'] = $a;
- }
-
- if ($act->obj['type'] === 'Note' && $s['attach']) {
- $s['body'] .= self::bb_attach($s['attach'], $s['body']);
- }
-
- // 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
-
- if ($act->obj['type'] === 'Video') {
-
- $vtypes = [
- 'video/mp4',
- 'video/ogg',
- 'video/webm'
- ];
-
- $mps = [];
- 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)) {
- $vurl['width'] = 0;
- }
- $mps[] = $vurl;
- }
- }
- }
- if ($mps) {
- usort($mps, [__CLASS__, 'vid_sort']);
- foreach ($mps as $m) {
- if (intval($m['width']) < 500) {
- $s['body'] .= "\n\n" . '[video]' . $m['href'] . '[/video]';
- break;
- }
- }
- }
- }
-
- set_iconfig($s, 'activitypub', 'recips', $act->raw_recips);
- if ($parent) {
- 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'])
- );
- if ($r) {
- if ($s['edited'] > $r[0]['edited']) {
- $x = item_store_update($s);
- }
- else {
- return;
- }
- }
- else {
- $x = item_store($s);
- }
-
- if (is_array($x) && $x['item_id']) {
- if ($parent) {
- if ($s['owner_xchan'] === $channel['channel_hash']) {
- // We are the owner of this conversation, so send all received comments back downstream
- Master::Summon(['Notifier', 'comment-import', $x['item_id']]);
- }
- $r = q("select * from item where id = %d limit 1",
- intval($x['item_id'])
- );
- if ($r) {
- send_status_notifications($x['item_id'], $r[0]);
- }
- }
- sync_an_item($channel['channel_id'], $x['item_id']);
- }
-
- }
static function get_actor_bbmention($id) {
@@ -2308,7 +2058,20 @@ class Activity {
$content = self::get_content($act->obj);
}
- $s['mid'] = $act->objprop('id') ?: $act->obj;
+ $s['mid'] = $act->objprop('id');
+
+ if (!$s['mid'] && is_string($act->obj)) {
+ $s['mid'] = $act->obj;
+ }
+
+ // pleroma fetched activities
+ if (!$s['mid'] && isset($act->obj['data']['id'])) {
+ $s['mid'] = $act->obj['data']['id'];
+ }
+
+ if ($act->objprop('type') === 'Profile') {
+ $s['mid'] = $act->id;
+ }
if (!$s['mid']) {
return false;
@@ -2410,15 +2173,30 @@ class Activity {
}
}
+ if ($act->type === 'Announce') {
+ $content['content'] = sprintf(t('&#x1f501; Repeated %1$s\'s %2$s'), $mention, $act->obj['type']);
+ }
+
+ // TODO: Deprecated
if ($act->type === 'emojiReaction') {
$content['content'] = (($act->tgt && $act->tgt['type'] === 'Image') ? '[img=32x32]' . $act->tgt['url'] . '[/img]' : '&#x' . $act->tgt['name'] . ';');
}
- if (in_array($act->type, ['EmojiReaction', 'EmojiReact'])) {
+ if (in_array($act->type, ['EmojiReact'])) {
// Pleroma reactions
$t = trim(self::get_textfield($act->data, 'content'));
- if (mb_strlen($t) === 1) {
- $content['content'] = $t;
+
+ $content['content'] = $t;
+
+ // Unicode emojis
+ if (grapheme_strlen($t) === 1) {
+ $content['content'] = '<h1>' . $t . '</h1>';
+ }
+
+ $a = self::decode_taxonomy($act->data);
+
+ if ($a) {
+ $s['term'] = $a;
}
}
}
@@ -2466,7 +2244,7 @@ class Activity {
$s['body'] .= $quote_bbcode;
}
- $s['verb'] = self::activity_decode_mapper($act->type);
+ $s['verb'] = self::activity_mapper($act->type);
// Mastodon does not provide update timestamps when updating poll tallies which means race conditions may occur here.
if ($act->type === 'Update' && $act->objprop('type') === 'Question' && $s['edited'] === $s['created']) {
@@ -2477,63 +2255,16 @@ class Activity {
$s['item_deleted'] = 1;
}
- if (isset($act->obj['type'])) {
- $s['obj_type'] = self::activity_obj_decode_mapper($act->obj['type']);
- }
-
- if ($s['obj_type'] === ACTIVITY_OBJ_NOTE && $s['mid'] !== $s['parent_mid']) {
- $s['obj_type'] = ACTIVITY_OBJ_COMMENT;
+ if ($act->objprop('type')) {
+ $s['obj_type'] = self::activity_obj_mapper($act->obj['type']);
}
$s['obj'] = $act->obj;
+
if (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') {
- $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;
- }
-*/
-
$generator = $act->get_property_obj('generator');
if ((!$generator) && (!$response_activity)) {
$generator = $act->get_property_obj('generator', $act->obj);
@@ -2546,15 +2277,9 @@ class Activity {
if (is_array($act->obj) && !$response_activity) {
$a = self::decode_taxonomy($act->obj);
+
if ($a) {
$s['term'] = $a;
- foreach ($a as $b) {
- if ($b['ttype'] === TERM_EMOJI) {
- $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']);
- }
- }
}
$a = self::decode_attachment($act->obj);
@@ -2587,16 +2312,13 @@ class Activity {
}
if (!$response_activity) {
- if ($act->type === 'Announce') {
- $s['author_xchan'] = self::get_attributed_to_actor_url($act);
- $s['mid'] = $act->objprop('id') ?: $act->obj;
- // Do not force new thread if the announce is from a group actor
- if ($act->actor['type'] !== 'Group') {
- $s['parent_mid'] = $act->objprop('id') ?: $act->obj;
- }
+ if ($act->objprop('type') === 'Profile') {
+ $s['parent_mid'] = $s['mid'];
+ $s['item_thread_top'] = 1;
}
+
// 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
@@ -2669,7 +2391,7 @@ class Activity {
if ($mps) {
usort($mps,[ '\Zotlabs\Lib\Activity', 'vid_sort' ]);
foreach ($mps as $m) {
- if (intval($m['height']) < 500 && self::media_not_in_body($m['href'],$s['body'])) {
+ if (intval($m['height']) <= 720 && self::media_not_in_body($m['href'],$s['body'])) {
$s['body'] = $tag . $m['href'] . '[/video]' . "\r\n" . $s['body'];
break;
}
@@ -2742,7 +2464,6 @@ class Activity {
}
}
-
if ($act->objprop('type') === 'Page' && !$s['body']) {
$ptr = null;
@@ -2783,7 +2504,7 @@ class Activity {
}
}
- if (in_array($act->objprop('type', ''), ['Note', 'Article', 'Page'])) {
+ if (in_array($act->objprop('type'), ['Note', 'Article', 'Page'])) {
$ptr = null;
if (array_key_exists('url', $act->obj)) {
@@ -2849,55 +2570,28 @@ class Activity {
}
}
- // old style: can be removed after most hubs are on 7.0.2
- elseif (array_key_exists('signed', $raw_arr) && is_array($act->obj) && isset($act->data['attachment']) && is_array($act->obj['attachment'])) {
- foreach($act->obj['attachment'] as $a) {
- if (
- isset($a['type']) && $a['type'] === 'PropertyValue' &&
- isset($a['name']) && $a['name'] === 'zot.activitypub.rawmsg' &&
- isset($a['value'])
- ) {
- $ap_rawmsg = $a['value'];
- }
-
- if (
- isset($a['type']) && $a['type'] === 'PropertyValue' &&
- isset($a['name']) && $a['name'] === 'zot.diaspora.fields' &&
- isset($a['value'])
- ) {
- $diaspora_rawmsg = $a['value'];
- }
- }
- }
-
- // catch the likes
- if (!$ap_rawmsg && $response_activity) {
- $ap_rawmsg = json_encode($act->data, JSON_UNESCAPED_SLASHES);
- }
- // end old style
-
if (!$ap_rawmsg && array_key_exists('signed', $raw_arr)) {
// zap
$ap_rawmsg = json_encode($act->data, JSON_UNESCAPED_SLASHES);
}
if ($ap_rawmsg) {
- set_iconfig($s, 'activitypub', 'rawmsg', $ap_rawmsg, 1);
+ IConfig::Set($s, 'activitypub', 'rawmsg', $ap_rawmsg, 1);
}
elseif (!array_key_exists('signed', $raw_arr)) {
- set_iconfig($s, 'activitypub', 'rawmsg', $act->raw, 1);
+ IConfig::Set($s, 'activitypub', 'rawmsg', $act->raw, 1);
}
if ($diaspora_rawmsg) {
- set_iconfig($s, 'diaspora', 'fields', $diaspora_rawmsg, 1);
+ IConfig::Set($s, 'diaspora', 'fields', $diaspora_rawmsg, 1);
}
if ($act->raw_recips) {
- set_iconfig($s, 'activitypub', 'recips', $act->raw_recips);
+ IConfig::Set($s, 'activitypub', 'recips', $act->raw_recips);
}
if ($act->objprop('type') === 'Event' && $act->objprop('timezone')) {
- set_iconfig($s, 'event', 'timezone', $act->objprop('timezone'), true);
+ IConfig::Set($s, 'event', 'timezone', $act->objprop('timezone'), true);
}
$hookinfo = [
@@ -2929,6 +2623,17 @@ class Activity {
$item['owner_xchan'] = $observer_hash;
}
+ // An ugly and imperfect way to recognise a mastodon or friendica 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;
+ }
+
$allowed = false;
$permit_mentions = intval(PConfig::Get($channel['channel_id'], 'system','permit_all_mentions') && i_am_mentioned($channel, $item));
@@ -2939,15 +2644,30 @@ class Activity {
intval($channel['channel_id'])
);
- // TODO: if we do not have a parent stop here and move the fetch to background?
+ if (!$parent) {
+ if (perm_is_allowed($channel['channel_id'], $observer_hash, 'send_stream') || $is_sys_channel) {
+ if ($item['verb'] === 'Announce') {
+ $force = true;
+ }
+
+ if ($fetch_parents) {
+ App::$cache['as_fetch_objects'][$item['mid']]['channels'][] = $channel['channel_id'];
+ App::$cache['as_fetch_objects'][$item['mid']]['force'] = intval($force);
+ return;
+ }
+ }
+
+ logger('no parent');
+ return;
+ }
- if ($parent && $parent[0]['obj_type'] === 'Question') {
- if ($item['obj_type'] === ACTIVITY_OBJ_COMMENT && $item['title'] && (!$item['body'])) {
+ if ($parent[0]['obj_type'] === 'Question') {
+ if (in_array($item['obj_type'], ['Note', ACTIVITY_OBJ_COMMENT]) && $item['title'] && (!$item['body'])) {
$item['obj_type'] = 'Answer';
}
}
- if ($parent && $parent[0]['item_wall']) {
+ if ($parent[0]['item_wall']) {
// set the owner to the owner of the parent
$item['owner_xchan'] = $parent[0]['owner_xchan'];
@@ -2979,7 +2699,7 @@ class Activity {
}*/
if (!$allowed) {
- if (get_pconfig($channel['channel_id'], 'system', 'moderate_unsolicited_comments') && $item['obj_type'] !== 'Answer') {
+ if (PConfig::Get($channel['channel_id'], 'system', 'moderate_unsolicited_comments') && $item['obj_type'] !== 'Answer') {
$item['item_blocked'] = ITEM_MODERATED;
$allowed = true;
}
@@ -3013,7 +2733,7 @@ class Activity {
// The $item['item_fetched'] flag is set in fetch_and_store_parents().
// In this case we should check against author permissions because sender is not owner.
- if (perm_is_allowed($channel['channel_id'], ((!empty($item['item_fetched'])) ? $item['author_xchan'] : $observer_hash), 'send_stream') || $is_sys_channel) {
+ if (perm_is_allowed($channel['channel_id'], ((empty($item['item_fetched'])) ? $observer_hash : $item['author_xchan']), 'send_stream') || $is_sys_channel) {
$allowed = true;
}
@@ -3024,13 +2744,13 @@ class Activity {
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);
+ IConfig::Set($item, 'activitypub', 'rawmsg', $act->raw, 1);
$allowed = true;
}
if (intval($item['item_private']) === 2) {
- if (!perm_is_allowed($channel['channel_id'], $observer_hash, 'post_mail')) {
- $allowed = false;
+ if (perm_is_allowed($channel['channel_id'], $observer_hash, 'post_mail')) {
+ $allowed = true;
}
}
@@ -3084,6 +2804,7 @@ class Activity {
$item['author_xchan'] = self::find_best_identity($item['author_xchan']);
$item['owner_xchan'] = self::find_best_identity($item['owner_xchan']);
+ $item['source_xchan'] = ((!empty($item['source_xchan'])) ? self::find_best_identity($item['source_xchan']) : '');
if (!$item['author_xchan']) {
logger('No author: ' . print_r($act, true));
@@ -3097,8 +2818,8 @@ class Activity {
return;
if ($channel['channel_system']) {
- $incl = get_config('system','pubstream_incl');
- $excl = get_config('system','pubstream_excl');
+ $incl = Config::Get('system','pubstream_incl');
+ $excl = Config::Get('system','pubstream_excl');
if(($incl || $excl) && !MessageFilter::evaluate($item, $incl, $excl)) {
logger('post is filtered');
@@ -3106,9 +2827,10 @@ class Activity {
}
}
- $abook = q("select * from abook where ( abook_xchan = '%s' OR abook_xchan = '%s') and abook_channel = %d ",
+ $abook = q("select * from abook where ( abook_xchan = '%s' OR abook_xchan = '%s' OR abook_xchan = '%s') and abook_channel = %d ",
dbesc($item['author_xchan']),
dbesc($item['owner_xchan']),
+ dbesc($item['source_xchan']),
intval($channel['channel_id'])
);
@@ -3120,43 +2842,21 @@ class Activity {
}
if (array_key_exists('conversation', $act->obj)) {
- set_iconfig($item, 'ostatus', 'conversation', $act->obj['conversation'], 1);
+ IConfig::Set($item, 'ostatus', 'conversation', $act->obj['conversation'], 1);
}
// This isn't perfect but the best we can do for now.
$item['comment_policy'] = ((isset($act->data['commentPolicy'])) ? $act->data['commentPolicy'] : 'authenticated');
- set_iconfig($item, 'activitypub', 'recips', $act->raw_recips);
+ IConfig::Set($item, 'activitypub', 'recips', $act->raw_recips);
if (intval($act->sigok)) {
$item['item_verified'] = 1;
}
if ($is_child_node) {
- if (!$parent) {
- if (!plugin_is_installed('pubcrawl')) {
- return;
- }
-
- $fetch = false;
-
- if (perm_is_allowed($channel['channel_id'], $observer_hash, 'send_stream') || $is_sys_channel) {
- $fetch = (($fetch_parents) ? self::fetch_and_store_parents($channel, $observer_hash, $item, $force) : false);
- }
-
- if ($fetch) {
- $parent = q("select * from item where mid = '%s' and uid = %d",
- dbesc($item['parent_mid']),
- intval($item['uid'])
- );
- }
- }
-
- if (!$parent) {
- logger('no parent');
- return;
- }
+ $item['owner_xchan'] = $parent[0]['owner_xchan'];
if ($parent[0]['parent_mid'] !== $item['parent_mid']) {
$item['thr_parent'] = $item['parent_mid'];
@@ -3197,30 +2897,28 @@ class Activity {
}
}
- // 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;
+ if (isset($item['term']) && !PConfig::Get($channel['channel_id'], 'system', 'no_smilies')) {
+ foreach ($item['term'] as $t) {
+ if ($t['ttype'] === TERM_EMOJI) {
+ $class = 'emoji';
+ $shortname = ':' . trim($t['term'], ':') . ':';
+ if (is_solo_string($shortname, $item['body'])) {
+ $class .= ' single-emoji';
+ }
+
+ $item['body'] = str_replace($shortname, '[img class="' . $class . '" alt="' . $t['term'] . '" title="' . $t['term'] . '"]' . ($t['imgurl'] ?: $t['url']) . '[/img]', $item['body']);
+ }
+ }
}
// TODO: not implemented
// self::rewrite_mentions($item);
+
$r = q("select id, created, edited from item where mid = '%s' and uid = %d limit 1",
dbesc($item['mid']),
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);
@@ -3281,18 +2979,59 @@ class Activity {
}
- static public function fetch_and_store_parents($channel, $observer_hash, $item, $force = false) {
+ /**
+ * @brief fetch a thread upwards by either providing a message id or an item/activity pair
+ *
+ * @param array $channel
+ * @param array $observer_hash
+ * @param array $item string|array
+ * @param object $act activitystreams object (optional) default null
+ * @param bool $force disregard permissions and force storage (optional) default false
+ * @return bool
+ */
+
+ static public function fetch_and_store_parents($channel, $observer_hash, $item, $act = null, $force = false) {
logger('fetching parents');
+ if (!$item) {
+ return false;
+ }
+
$p = [];
+ $announce_init = false;
+ $group_announce_init = false;
+
+ if (is_object($act) && is_array($item)) {
+ $p[] = [$act, $item];
+ $announce_init = ($item['verb'] === 'Announce');
+ $group_announce_init = ($announce_init && $act->actor['type'] === 'Group');
+ }
+
+ if (is_string($item)) {
+ $mid = $item;
+ $item = [
+ 'parent_mid' => $mid,
+ 'mid' => ''
+ ];
+ }
$current_item = $item;
- while ($current_item['parent_mid'] !== $current_item['mid']) {
- $n = self::fetch($current_item['parent_mid'], $channel);
+ $i = 0;
- if (!$n) {
- break;
+ while ($current_item['parent_mid'] !== $current_item['mid']) {
+ $cached = ASCache::Get($current_item['parent_mid']);
+ if ($cached) {
+ // logger('cached: ' . $current_item['parent_mid']);
+ $n = unserialise($cached);
+ }
+ else {
+ // logger('fetching: ' . $current_item['parent_mid']);
+ $n = self::fetch($current_item['parent_mid'], $channel);
+ if (!$n) {
+ break;
+ }
+ ASCache::Set($current_item['parent_mid'], serialise($n));
}
$a = new ActivityStreams($n);
@@ -3327,8 +3066,32 @@ class Activity {
$item = $hookinfo['item'];
if ($item) {
+
$item['item_fetched'] = true;
+ if ($announce_init) {
+ // Store the sender of the initial announce
+ $item['source_xchan'] = $observer_hash;
+ // WARNING: the presence of both source_xchan and non-zero item_uplink here will cause a delivery loop
+ $item['item_uplink'] = 0;
+
+ if ($item['item_thread_top']) {
+ $item['verb'] = 'Announce';
+ }
+
+ if (!$group_announce_init) {
+ // Force a new thread if the announce init actor is not a group
+ $item['verb'] = 'Announce';
+ $item['parent_mid'] = $item['thr_parent'] = $item['mid'];
+ $item['item_thread_top'] = 1;
+ }
+
+ }
+ else {
+ $announce_init = ($i === 0 && $item['verb'] === 'Announce');
+ $group_announce_init = ($announce_init && $a->actor['type'] === 'Group');
+ }
+
if (intval($channel['channel_system']) && intval($item['item_private'])) {
$p = [];
break;
@@ -3344,9 +3107,11 @@ class Activity {
if ($item['parent_mid'] === $item['mid']) {
break;
}
+
}
$current_item = $item;
+ $i++;
}
if ($p) {
@@ -3357,407 +3122,9 @@ class Activity {
}
return true;
}
-
return false;
}
- /*
- static public function fetch_and_store_parents($channel, $item) {
-
- logger('fetching parents');
-
- $p = [];
-
- $current_item = $item;
-
- while ($current_item['parent_mid'] !== $current_item['mid']) {
- $n = self::fetch($current_item['parent_mid'], $channel);
- if (!$n) {
- break;
- }
- $a = new ActivityStreams($n);
-
- //logger($a->debug());
-
- if (!$a->is_valid()) {
- break;
- }
-
- if (is_array($a->actor) && array_key_exists('id', $a->actor)) {
- self::actor_store($a->actor);
- }
-
- $replies = null;
- if (isset($a->obj['replies']['first']['items'])) {
- $replies = $a->obj['replies']['first']['items'];
- // we already have this one
- array_diff($replies, [$current_item['mid']]);
- }
-
- $item = null;
-
- switch ($a->type) {
- case 'Create':
- case 'Update':
- //case 'Like':
- //case 'Dislike':
- case 'Announce':
- $item = self::decode_note($a);
- break;
- default:
- break;
-
- }
-
- $hookinfo = [
- 'a' => $a,
- 'item' => $item
- ];
-
- call_hooks('fetch_and_store', $hookinfo);
-
- $item = $hookinfo['item'];
-
- if ($item) {
-
- array_unshift($p, [$a, $item, $replies]);
-
- if ($item['parent_mid'] === $item['mid'] || count($p) > 20) {
- break;
- }
-
- }
- $current_item = $item;
- }
-
- if ($p) {
- foreach ($p as $pv) {
- self::store($channel, $pv[0]->actor['id'], $pv[0], $pv[1], false);
- if ($pv[2])
- self::fetch_and_store_replies($channel, $pv[2]);
- }
- return true;
- }
-
- return false;
- }
-
- static public function fetch_and_store_replies($channel, $arr) {
-
- logger('fetching replies');
- logger(print_r($arr, true));
-
- $p = [];
-
- foreach ($arr as $url) {
-
- $n = self::fetch($url, $channel);
- if (!$n) {
- break;
- }
-
- $a = new ActivityStreams($n);
-
- if (!$a->is_valid()) {
- break;
- }
-
- $item = null;
-
- switch ($a->type) {
- case 'Create':
- case 'Update':
- case 'Like':
- case 'Dislike':
- case 'Announce':
- $item = self::decode_note($a);
- break;
- default:
- break;
- }
-
- $hookinfo = [
- 'a' => $a,
- 'item' => $item
- ];
-
- call_hooks('fetch_and_store', $hookinfo);
-
- $item = $hookinfo['item'];
-
- if ($item) {
- array_unshift($p, [$a, $item]);
- }
-
- }
-
- if ($p) {
- foreach ($p as $pv) {
- self::store($channel, $pv[0]->actor['id'], $pv[0], $pv[1], false);
- }
- }
-
- }
-*/
-
-/* this is deprecated and not used anymore
- static function announce_note($channel, $observer_hash, $act) {
-
- $s = [];
- $is_sys_channel = is_sys_channel($channel['channel_id']);
-
- if (!perm_is_allowed($channel['channel_id'], $observer_hash, 'send_stream') && !$is_sys_channel) {
- logger('no permission');
- return;
- }
-
- $content = self::get_content($act->obj);
-
- if (!$content) {
- 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']);
- $s['plink'] = urldecode($act->obj['id']);
-
- if (!$s['created'])
- $s['created'] = datetime_convert();
-
- if (!$s['edited'])
- $s['edited'] = $s['created'];
-
-
- $s['parent_mid'] = $s['mid'];
-
- $s['verb'] = ACTIVITY_POST;
- $s['obj_type'] = ACTIVITY_OBJ_NOTE;
- $s['app'] = t('ActivityPub');
-
- if ($channel['channel_system']) {
- if (!MessageFilter::evaluate($s, get_config('system', 'pubstream_incl'), get_config('system', 'pubstream_excl'))) {
- 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'])
- );
-
- if ($abook) {
- if (!post_is_importable($s, $abook[0])) {
- logger('post is filtered');
- return;
- }
- }
-
- if ($act->obj['conversation']) {
- set_iconfig($s, 'ostatus', 'conversation', $act->obj['conversation'], 1);
- }
-
- $a = self::decode_taxonomy($act->obj);
- if ($a) {
- $s['term'] = $a;
- }
-
- $a = self::decode_attachment($act->obj);
- if ($a) {
- $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'] .
- "']";
-
- if ($content['name'])
- $body .= self::bb_content($content, 'name') . "\r\n";
-
- $body .= self::bb_content($content, 'content');
-
- if ($act->obj['type'] === 'Note' && $s['attach']) {
- $body .= self::bb_attach($s['attach'], $body);
- }
-
- $body .= "[/share]";
-
- $s['title'] = self::bb_content($content, 'name');
- $s['body'] = $body;
-
- if ($act->recips && (!in_array(ACTIVITY_PUBLIC_INBOX, $act->recips)))
- $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'])
- );
- if ($r) {
- if ($s['edited'] > $r[0]['edited']) {
- $x = item_store_update($s);
- }
- else {
- return;
- }
- }
- else {
- $x = item_store($s);
- }
-
- if (is_array($x) && $x['item_id']) {
- if ($s['owner_xchan'] === $channel['channel_hash']) {
- // We are the owner of this conversation, so send all received comments back downstream
- Master::Summon(['Notifier', 'comment-import', $x['item_id']]);
- }
- $r = q("select * from item where id = %d limit 1",
- intval($x['item_id'])
- );
- if ($r) {
- 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'];
-
- if ($act->type === 'Like')
- $s['verb'] = ACTIVITY_LIKE;
- if ($act->type === 'Dislike')
- $s['verb'] = ACTIVITY_DISLIKE;
-
- if (!$parent)
- 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)))
- );
-
- if (!$r) {
- logger('parent not found.');
- return;
- }
-
- xchan_query($r);
- $parent_item = $r[0];
-
- if ($parent_item['owner_xchan'] === $channel['channel_hash']) {
- if (!perm_is_allowed($channel['channel_id'], $observer_hash, 'post_comments')) {
- logger('no comment permission.');
- return;
- }
- }
-
- if ($parent_item['mid'] === $parent_item['parent_mid']) {
- $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;
-
- if (!$s['parent_mid'])
- $s['parent_mid'] = $s['mid'];
-
-
- $post_type = (($parent_item['resource_type'] === 'photo') ? t('photo') : t('post'));
-
- $links = [['rel' => 'alternate', 'type' => 'text/html', 'href' => $parent_item['plink']]];
- $objtype = (($parent_item['resource_type'] === 'photo') ? ACTIVITY_OBJ_PHOTO : ACTIVITY_OBJ_NOTE);
-
- $z = q("select * from xchan where xchan_hash = '%s' limit 1",
- dbesc($parent_item['author_xchan'])
- );
- if ($z)
- $item_author = $z[0];
-
- $object = json_encode([
- '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' => [
- 'name' => $item_author['xchan_name'],
- 'address' => $item_author['xchan_addr'],
- 'guid' => $item_author['xchan_guid'],
- 'guid_sig' => $item_author['xchan_guid_sig'],
- 'link' => [
- ['rel' => 'alternate', 'type' => 'text/html', 'href' => $item_author['xchan_url']],
- ['rel' => 'photo', 'type' => $item_author['xchan_photo_mimetype'], 'href' => $item_author['xchan_photo_m']]],
- ],
- ], JSON_UNESCAPED_SLASHES
- );
-
- if ($act->type === 'Like')
- $bodyverb = t('%1$s likes %2$s\'s %3$s');
- if ($act->type === 'Dislike')
- $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;
-
- if ($act->obj['conversation']) {
- set_iconfig($s, 'ostatus', 'conversation', $act->obj['conversation'], 1);
- }
-
- if ($act->recips && (!in_array(ACTIVITY_PUBLIC_INBOX, $act->recips)))
- $s['item_private'] = 1;
-
- set_iconfig($s, 'activitypub', 'recips', $act->raw_recips);
-
- $result = item_store($s);
-
- if ($result['success']) {
- // if the message isn't already being relayed, notify others
- if (intval($parent_item['item_origin']))
- Master::Summon(['Notifier', 'comment-import', $result['item_id']]);
- sync_an_item($channel['channel_id'], $result['item_id']);
- }
-
- return;
- }
public static function bb_attach($item) {
@@ -3823,6 +3190,10 @@ class Activity {
public static function media_not_in_body($s, $body) {
+ if (empty($body)) {
+ return true;
+ }
+
$s_alt = htmlspecialchars($s, ENT_QUOTES, 'UTF-8');
if (
@@ -3904,49 +3275,7 @@ 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;
@@ -3975,17 +3304,18 @@ class Activity {
return $content;
}
- static function get_textfield($act, $field) {
+ static function get_textfield($act, $field): null|string|array {
+ $content = null;
- $content = false;
-
- if (array_key_exists($field, $act) && $act[$field])
+ if (array_key_exists($field, $act) && $act[$field]) {
$content = purify_html($act[$field]);
+ }
elseif (array_key_exists($field . 'Map', $act) && $act[$field . 'Map']) {
foreach ($act[$field . 'Map'] as $k => $v) {
$content[escape_tags($k)] = purify_html($v);
}
}
+
return $content;
}
@@ -4051,11 +3381,11 @@ class Activity {
return $hookdata['actor'];
}
- static function get_actor($actor_id) {
+ static function get_actor($actor_id, $force = false) {
// remove fragment
$actor_id = ((strpos($actor_id, '#')) ? substr($actor_id, 0, strpos($actor_id, '#')) : $actor_id);
- $actor = self::get_cached_actor($actor_id);
+ $actor = ((!$force) ? self::get_cached_actor($actor_id) : null);
if ($actor) {
return $actor;
@@ -4085,6 +3415,8 @@ class Activity {
static function get_actor_hublocs($url, $options = 'all') {
+ $url = ((strpos($url, '#')) ? substr($url, 0, strpos($url, '#')) : $url);
+
switch ($options) {
case 'activitypub':
$hublocs = q("select * from hubloc left join xchan on hubloc_hash = xchan_hash where hubloc_hash = '%s' and hubloc_deleted = 0 order by hubloc_id desc",
@@ -4205,4 +3537,125 @@ class Activity {
}
+ public static function ap_context($contextType = null): array {
+ return ['@context' => [
+ ACTIVITYSTREAMS_JSONLD_REV,
+ 'https://w3id.org/security/v1',
+ // 'https://www.w3.org/ns/did/v1',
+ // 'https://w3id.org/security/multikey/v1',
+ // 'https://w3id.org/security/data-integrity/v1',
+ 'https://purl.archive.org/socialweb/webfinger',
+ self::ap_schema($contextType)
+ ]];
+ }
+
+ public static function ap_schema($contextType = null): array {
+ // $contextType is reserved for future use so that the caller can specify
+ // a limited subset of the entire schema definition for particular activities.
+
+ return [
+ 'zot' => z_root() . '/apschema#',
+ 'schema' => 'http://schema.org#',
+ 'ostatus' => 'http://ostatus.org#',
+ 'diaspora' => 'https://diasporafoundation.org/ns/',
+ 'litepub' => 'http://litepub.social/ns#',
+ 'toot' => 'http://joinmastodon.org/ns#',
+
+ 'commentPolicy' => 'zot:commentPolicy',
+ 'Bookmark' => 'zot:Bookmark',
+ 'Category' => 'zot:Category',
+ 'Emoji' => 'toot:Emoji',
+
+ 'directMessage' => 'litepub:directMessage',
+
+ 'PropertyValue' => 'schema:PropertyValue',
+ 'value' => 'schema:value',
+ 'uuid' => 'schema:identifier',
+
+ 'conversation' => 'ostatus:conversation',
+
+ 'guid' => 'diaspora:guid',
+
+ 'manuallyApprovesFollowers' => 'as:manuallyApprovesFollowers',
+ 'Hashtag' => 'as:Hashtag'
+
+ ];
+
+ }
+
+ /**
+ * @brief Builds the activity packet and signs it if $channel is provided.
+ *
+ * @param array $obj
+ * @param array $channel (optional) default []
+ * @param bool $json_encode (optional) default true
+ * @return string|array
+ */
+
+ public static function build_packet(array $obj, array $channel = [], bool $json_encode = true): string|array {
+ $arr = array_merge(Activity::ap_context(), $obj);
+
+ if ($channel) {
+ $proof = (new JcsEddsa2022)->sign($arr, $channel);
+ $arr['proof'] = $proof;
+
+ $signature = LDSignatures::sign($arr, $channel);
+ $arr['signature'] = $signature;
+ }
+
+ if ($json_encode) {
+ return json_encode($arr, JSON_UNESCAPED_SLASHES);
+ }
+
+ return $arr;
+ }
+
+ /**
+ * @brief Prepares the arguments and inititates the Fetchparents or Zotconvo daemon.
+ * @param string $observer
+ *
+ */
+
+ public static function init_background_fetch(string $observer_hash = '') {
+ if (isset(App::$cache['zot_fetch_objects'])) {
+ $channels_str = '';
+
+ foreach (App::$cache['zot_fetch_objects'] as $mid => $info) {
+ $force = $info['force'];
+
+ foreach ($info['channels'] as $c) {
+ if ($channels_str) {
+ $channels_str .= ',';
+ }
+ $channels_str .= $c;
+ }
+
+ Master::Summon(['Zotconvo', $channels_str, $mid, $force]);
+ }
+ }
+
+ if (isset(App::$cache['as_fetch_objects'])) {
+ if (!$observer_hash) {
+ logger('Attempt to initiate Fetchparents daemon without observer');
+ return;
+ }
+
+ $channels_str = '';
+
+ foreach (App::$cache['as_fetch_objects'] as $mid => $info) {
+ $force = $info['force'];
+
+ foreach ($info['channels'] as $c) {
+ if ($channels_str) {
+ $channels_str .= ',';
+ }
+ $channels_str .= $c;
+ }
+
+ Master::Summon(['Fetchparents', $channels_str, $observer_hash, $mid, $force]);
+ }
+ }
+ }
+
+
}
diff --git a/Zotlabs/Lib/ActivityStreams.php b/Zotlabs/Lib/ActivityStreams.php
index 4c3e3d8f8..9f028bb46 100644
--- a/Zotlabs/Lib/ActivityStreams.php
+++ b/Zotlabs/Lib/ActivityStreams.php
@@ -24,10 +24,11 @@ class ActivityStreams {
public $origin = null;
public $owner = null;
public $signer = null;
- public $ldsig = null;
+ public $sig = null;
public $sigok = false;
public $recips = null;
public $raw_recips = null;
+ public $saved_recips = null;
/**
* @brief Constructor for ActivityStreams.
@@ -88,7 +89,16 @@ class ActivityStreams {
// Attempt to assemble an Activity from what we were given.
if ($this->is_valid()) {
- $this->id = $this->get_property_obj('id');
+ $this->id = $this->get_property_obj('id');
+
+ if (!$this->id) {
+ logger('Data with mmissing id: ' . print_r($this->data, true));
+ return;
+ }
+
+ // cache for future use
+ ASCache::Set($this->id, 'json:' . $this->raw);
+
$this->type = $this->get_primary_type();
$this->actor = $this->get_actor('actor', '', '');
$this->obj = $this->get_compound_property('object');
@@ -96,11 +106,19 @@ class ActivityStreams {
$this->origin = $this->get_compound_property('origin');
$this->recips = $this->collect_recips();
- $this->ldsig = $this->get_compound_property('signature');
- if ($this->ldsig) {
- $this->signer = $this->get_actor('creator', $this->ldsig);
- if ($this->signer && is_array($this->signer) && array_key_exists('publicKey', $this->signer) && is_array($this->signer['publicKey']) && $this->signer['publicKey']['publicKeyPem']) {
- $this->sigok = LDSignatures::verify($this->data, $this->signer['publicKey']['publicKeyPem']);
+ $this->sig = $this->get_compound_property('proof');
+ if ($this->sig) {
+ $this->checkEddsaSignature(); // will set signer and sigok if everything works out
+ }
+
+ // Try LDSignatures if edsig failed
+ if (!$this->sigok) {
+ $this->sig = $this->get_compound_property('signature');
+ if ($this->sig) {
+ $this->signer = $this->get_actor('creator', $this->sig);
+ if ($this->signer && is_array($this->signer) && array_key_exists('publicKey', $this->signer) && is_array($this->signer['publicKey']) && $this->signer['publicKey']['publicKeyPem']) {
+ $this->sigok = LDSignatures::verify($this->data, $this->signer['publicKey']['publicKeyPem']);
+ }
}
}
@@ -112,24 +130,31 @@ class ActivityStreams {
}
}
- // fetch recursive or embedded activities
+ // 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('object', $this->obj);
}
- if ($this->obj && is_array($this->obj) && isset($this->obj['actor']))
+ // Enumerate and store actors in referenced objects
+
+ if ($this->obj && is_array($this->obj) && isset($this->obj['actor'])) {
$this->obj['actor'] = $this->get_actor('actor', $this->obj);
- if ($this->tgt && is_array($this->tgt) && isset($this->tgt['actor']))
+ }
+
+ if ($this->tgt && is_array($this->tgt) && isset($this->tgt['actor'])) {
$this->tgt['actor'] = $this->get_actor('actor', $this->tgt);
+ }
+
+ // Determine if this is a followup or response activity
$this->parent_id = $this->get_property_obj('inReplyTo');
- if (!$this->parent_id && is_array($this->obj) && isset($this->obj['inReplyTo'])) {
- $this->parent_id = $this->obj['inReplyTo'];
+ if (!$this->parent_id && isset($this->obj['inReplyTo'])) {
+ $this->parent_id = ((is_array($this->obj['inReplyTo'])) ? $this->obj['inReplyTo']['id'] : $this->obj['inReplyTo']);
}
- if (!$this->parent_id && is_array($this->obj) && isset($this->obj['id'])) {
+ if (!$this->parent_id && isset($this->obj['id'])) {
$this->parent_id = $this->obj['id'];
}
@@ -318,9 +343,10 @@ class ActivityStreams {
if ($x === null && strpos($url, '/channel/')) {
// look for other nomadic channels which might be alive
$zf = Zotfinger::exec($url, $channel);
-
- $url = $zf['signature']['signer'];
- $x = Activity::fetch($url, $channel);
+ if ($zf) {
+ $url = $zf['signature']['signer'];
+ $x = Activity::fetch($url, $channel);
+ }
}
}
@@ -335,7 +361,7 @@ class ActivityStreams {
if (!$s) {
return false;
}
- return (in_array($s, ['Like', 'Dislike', 'Flag', 'Block', 'Accept', 'Reject', 'TentativeAccept', 'TentativeReject', 'emojiReaction', 'EmojiReaction', 'EmojiReact']));
+ return (in_array($s, ['Announce', 'Like', 'Dislike', 'Flag', 'Block', 'Accept', 'Reject', 'TentativeAccept', 'TentativeReject', 'emojiReaction', 'EmojiReaction', 'EmojiReact']));
}
/**
@@ -384,12 +410,24 @@ class ActivityStreams {
$x = $this->get_property_obj($property, $base, $namespace);
if ($this->is_url($x)) {
- $y = $this->fetch_property($x);
+ $cached = ASCache::Get($x);
+ if ($cached) {
+ // logger('AS cached: ' . $x);
+ $y = unserialise($cached);
+ }
+ else {
+ // logger('AS fetching: ' . $x);
+ $y = $this->fetch_property($x);
+ if ($y) {
+ ASCache::Set($x, serialise($y));
+ }
+ }
if (is_array($y)) {
$x = $y;
}
}
+
// verify and unpack JSalmon signature if present
if (is_array($x) && array_key_exists('signed', $x)) {
@@ -489,4 +527,58 @@ class ActivityStreams {
}
+ public function checkEddsaSignature() {
+ $signer = $this->get_property_obj('verificationMethod', $this->sig);
+
+ $parseUrl = parse_url($signer);
+
+ if (isset($parseUrl['fragment'])) {
+ if (str_starts_with($parseUrl['fragment'], 'z6Mk')) {
+ $publicKey = $parseUrl['fragment'];
+ }
+ unset($parseUrl['fragment']);
+ }
+
+ if (isset($parseUrl['query'])) {
+ unset($parseUrl['query']);
+ }
+
+ $url = unparse_url($parseUrl);
+
+ $hublocs = Activity::get_actor_hublocs($url);
+
+ $hasStoredKey = false;
+ if ($hublocs) {
+ foreach ($hublocs as $hubloc) {
+ if ($publicKey && $hubloc['xchan_epubkey'] === $publicKey) {
+ $hasStoredKey = true;
+ break;
+ }
+ }
+ }
+
+ if (!$hasStoredKey) {
+ $this->signer = Activity::get_actor($url);
+
+ if (isset($this->signer['assertionMethod'])) {
+ if (!isset($this->signer['assertionMethod'][0])) {
+ $this->signer['assertionMethod'] = [$this->signer['assertionMethod']];
+ }
+
+ foreach($this->signer['assertionMethod'] as $am) {
+ if ($url === $am['controller'] &&
+ $am['type'] === 'Multikey' &&
+ str_starts_with($am['publicKeyMultibase'], 'z6Mk')
+ ) {
+ $publicKey = $am['publicKeyMultibase'];
+ }
+ }
+ }
+ }
+
+ if ($publicKey) {
+ $this->sigok = (new JcsEddsa2022)->verify($this->data, $publicKey);
+ }
+ }
+
}
diff --git a/Zotlabs/Lib/Apps.php b/Zotlabs/Lib/Apps.php
index 00e65479e..1c05d69b1 100644
--- a/Zotlabs/Lib/Apps.php
+++ b/Zotlabs/Lib/Apps.php
@@ -352,8 +352,6 @@ class Apps {
'Directory' => t('Directory'),
'Help' => t('Help'),
'Mail' => t('Mail'),
- 'Mood' => t('Mood'),
- 'Poke' => t('Poke'),
'Chat' => t('Chat'),
'Search' => t('Search'),
'Probe' => t('Probe'),
diff --git a/Zotlabs/Lib/Cache.php b/Zotlabs/Lib/Cache.php
index 60bf64611..f3f520496 100644
--- a/Zotlabs/Lib/Cache.php
+++ b/Zotlabs/Lib/Cache.php
@@ -17,8 +17,8 @@ class Cache {
*/
public static function get($key, $age = '') {
-
- $hash = hash('whirlpool',$key);
+// $hash = hash('whirlpool',$key);
+ $hash = uuid_from_url($key);
$r = q("SELECT v FROM cache WHERE k = '%s' AND updated > %s - INTERVAL %s LIMIT 1",
dbesc($hash),
@@ -32,23 +32,25 @@ class Cache {
}
public static function set($key,$value) {
+// $hash = hash('whirlpool',$key);
+ $hash = uuid_from_url($key);
- $hash = hash('whirlpool',$key);
-
- $r = q("SELECT * FROM cache WHERE k = '%s' limit 1",
+ $r = q("SELECT * FROM cache WHERE k = '%s' LIMIT 1",
dbesc($hash)
);
if($r) {
q("UPDATE cache SET v = '%s', updated = '%s' WHERE k = '%s'",
dbesc($value),
dbesc(datetime_convert()),
- dbesc($hash));
+ dbesc($hash)
+ );
}
else {
q("INSERT INTO cache (k, v, updated) VALUES ('%s', '%s', '%s')",
dbesc($hash),
dbesc($value),
- dbesc(datetime_convert()));
+ dbesc(datetime_convert())
+ );
}
}
}
diff --git a/Zotlabs/Lib/Config.php b/Zotlabs/Lib/Config.php
index 933f4bff3..95df8ed6f 100644
--- a/Zotlabs/Lib/Config.php
+++ b/Zotlabs/Lib/Config.php
@@ -143,6 +143,9 @@ class Config {
return $value;
}
}
+ else {
+ return $value;
+ }
}
return $default;
diff --git a/Zotlabs/Lib/DReport.php b/Zotlabs/Lib/DReport.php
index e22ed65be..71e39a9d7 100644
--- a/Zotlabs/Lib/DReport.php
+++ b/Zotlabs/Lib/DReport.php
@@ -6,23 +6,26 @@ class DReport {
private $location;
private $sender;
private $recipient;
+ private $name;
private $message_id;
+ private $message_uuid;
private $status;
private $date;
- function __construct($location,$sender,$recipient,$message_id,$status = 'deliver') {
- $this->location = $location;
- $this->sender = $sender;
- $this->recipient = $recipient;
- $this->name = EMPTY_STR;
- $this->message_id = $message_id;
- $this->status = $status;
- $this->date = datetime_convert();
+ function __construct($location, $sender, $recipient, $message_id, $message_uuid = '', $status = 'deliver') {
+ $this->location = $location;
+ $this->sender = $sender;
+ $this->recipient = $recipient;
+ $this->name = EMPTY_STR;
+ $this->message_id = $message_id;
+ $this->message_uuid = $message_uuid;
+ $this->status = $status;
+ $this->date = datetime_convert();
}
function update($status) {
- $this->status = $status;
- $this->date = datetime_convert();
+ $this->status = $status;
+ $this->date = datetime_convert();
}
function set_name($name) {
@@ -35,24 +38,26 @@ class DReport {
function set($arr) {
- $this->location = $arr['location'];
- $this->sender = $arr['sender'];
- $this->recipient = $arr['recipient'];
- $this->name = $arr['name'];
- $this->message_id = $arr['message_id'];
- $this->status = $arr['status'];
- $this->date = $arr['date'];
+ $this->location = $arr['location'];
+ $this->sender = $arr['sender'];
+ $this->recipient = $arr['recipient'];
+ $this->name = $arr['name'];
+ $this->message_id = $arr['message_id'];
+ $this->message_uuid = $arr['message_uuid'] ?? '';
+ $this->status = $arr['status'];
+ $this->date = $arr['date'];
}
function get() {
return array(
- 'location' => $this->location,
- 'sender' => $this->sender,
- 'recipient' => $this->recipient,
- 'name' => $this->name,
- 'message_id' => $this->message_id,
- 'status' => $this->status,
- 'date' => $this->date
+ 'location' => $this->location,
+ 'sender' => $this->sender,
+ 'recipient' => $this->recipient,
+ 'name' => $this->name,
+ 'message_id' => $this->message_id,
+ 'message_uuid' => $this->message_uuid,
+ 'status' => $this->status,
+ 'date' => $this->date
);
}
diff --git a/Zotlabs/Lib/Enotify.php b/Zotlabs/Lib/Enotify.php
index c3f96e103..48a255e95 100644
--- a/Zotlabs/Lib/Enotify.php
+++ b/Zotlabs/Lib/Enotify.php
@@ -149,7 +149,7 @@ class Enotify {
if(array_key_exists('item',$params)) {
- if(in_array($params['item']['verb'], [ACTIVITY_LIKE, ACTIVITY_DISLIKE])) {
+ if(in_array($params['item']['verb'], ['Like', 'Dislike', ACTIVITY_LIKE, ACTIVITY_DISLIKE, ACTIVITY_SHARE])) {
if(! $always_show_in_notices || !($vnotify & VNOTIFY_LIKE)) {
logger('notification: not a visible activity. Ignoring.');
@@ -157,12 +157,15 @@ class Enotify {
return;
}
- if(activity_match($params['verb'], ACTIVITY_LIKE))
+ if(activity_match($params['verb'], ['Like', ACTIVITY_LIKE]))
$action = (($moderated) ? t('requested to like') : t('liked'));
- if(activity_match($params['verb'], ACTIVITY_DISLIKE))
+ if(activity_match($params['verb'], ['Dislike', ACTIVITY_DISLIKE]))
$action = (($moderated) ? t('requested to dislike') : t('disliked'));
+ if(activity_match($params['verb'], ACTIVITY_SHARE))
+ $action = t('repeated');
+
}
if($params['item']['obj_type'] === 'Answer')
@@ -259,7 +262,7 @@ class Enotify {
$itemlink = $params['link'];
- if (array_key_exists('item',$params) && (activity_match($params['item']['verb'], ACTIVITY_LIKE) || activity_match($params['item']['verb'], ACTIVITY_DISLIKE))) {
+ if (array_key_exists('item',$params) && (activity_match($params['item']['verb'], ['Like', 'Dislike', ACTIVITY_LIKE, ACTIVITY_DISLIKE]))) {
if(! $always_show_in_notices || !($vnotify & VNOTIFY_LIKE) || !feature_enabled($recip['channel_id'], 'dislike')) {
logger('notification: not a visible activity. Ignoring.');
pop_lang();
@@ -310,10 +313,10 @@ class Enotify {
//$verb = ((activity_match($params['item']['verb'], ACTIVITY_DISLIKE)) ? t('disliked') : t('liked'));
$moderated = (($params['item']['item_blocked'] == ITEM_MODERATED) ? true : false);
- if(activity_match($params['item']['verb'], ACTIVITY_LIKE))
+ if(activity_match($params['item']['verb'], ['Like', ACTIVITY_LIKE]))
$verb = (($moderated) ? t('requested to like') : t('liked'));
- if(activity_match($params['item']['verb'], ACTIVITY_DISLIKE))
+ if(activity_match($params['item']['verb'], ['Dislike', ACTIVITY_DISLIKE]))
$verb = (($moderated) ? t('requested to dislike') : t('disliked'));
// "your post"
@@ -483,6 +486,8 @@ class Enotify {
require_once('include/html2bbcode.php');
+/*
+
do {
$dups = false;
$hash = random_string();
@@ -491,10 +496,12 @@ class Enotify {
if ($r)
$dups = true;
} while ($dups === true);
+*/
+
$datarray = [];
- $datarray['hash'] = $hash;
+ $datarray['hash'] = $params['item']['uuid'] ?? new_uuid();
$datarray['sender_hash'] = $sender['xchan_hash'];
$datarray['xname'] = $sender['xchan_name'];
$datarray['url'] = $sender['xchan_url'];
@@ -505,7 +512,7 @@ class Enotify {
$datarray['link'] = $itemlink;
$datarray['parent'] = $parent_mid;
$datarray['parent_item'] = $parent_item;
- $datarray['ntype'] = $params['type'] ?? '';
+ $datarray['ntype'] = $params['type'] ?? 0;
$datarray['verb'] = $params['verb'] ?? '';
$datarray['otype'] = $params['otype'] ?? '';
$datarray['abort'] = false;
@@ -553,8 +560,9 @@ class Enotify {
dbesc($datarray['otype'])
);
- $r = q("select id from notify where hash = '%s' and uid = %d limit 1",
- dbesc($hash),
+ $r = q("select id from notify where hash = '%s' and ntype = %d and uid = %d limit 1",
+ dbesc($datarray['hash']),
+ intval($datarray['ntype']),
intval($recip['channel_id'])
);
if ($r) {
@@ -835,18 +843,6 @@ class Enotify {
: (($item['obj_type'] === 'Answer') ? sprintf( t('voted on %s\'s poll'), '[bdi]' . $item['owner']['xchan_name'] . '[/bdi]') : sprintf( t('commented on %s\'s post'), '[bdi]' . $item['owner']['xchan_name'] . '[/bdi]'))
);
- if($item['verb'] === ACTIVITY_SHARE && empty($item['owner']['xchan_pubforum'])) {
- $itemem_text = sprintf( t('repeated %s\'s post'), '[bdi]' . $item['author']['xchan_name'] . '[/bdi]');
- }
-
- if($item['verb'] === ACTIVITY_LIKE) {
- $itemem_text = sprintf( t('liked %s\'s post'), '[bdi]' . $item['author']['xchan_name'] . '[/bdi]');
- }
-
- if($item['verb'] === ACTIVITY_DISLIKE) {
- $itemem_text = sprintf( t('disliked %s\'s post'), '[bdi]' . $item['author']['xchan_name'] . '[/bdi]');
- }
-
if(in_array($item['obj_type'], ['Document', 'Video', 'Audio', 'Image'])) {
$itemem_text = t('shared a file with you');
}
@@ -867,7 +863,6 @@ class Enotify {
// convert this logic into a json array just like the system notifications
- $who = (($item['verb'] === ACTIVITY_SHARE && empty($item['owner']['xchan_pubforum'])) ? 'owner' : 'author');
$body = html2plain(bbcode($item['body'], ['drop_media' => true, 'tryoembed' => false]), 75, true);
if ($body) {
$body = htmlentities($body, ENT_QUOTES, 'UTF-8', false);
@@ -875,19 +870,20 @@ class Enotify {
$x = array(
'notify_link' => $item['llink'],
- 'name' => $item[$who]['xchan_name'],
- 'addr' => $item[$who]['xchan_addr'] ? $item[$who]['xchan_addr'] : $item[$who]['xchan_url'],
- 'url' => $item[$who]['xchan_url'],
- 'photo' => $item[$who]['xchan_photo_s'],
+ 'name' => $item['author']['xchan_name'],
+ 'addr' => $item['author']['xchan_addr'] ? $item['author']['xchan_addr'] : $item['author']['xchan_url'],
+ 'url' => $item['author']['xchan_url'],
+ 'photo' => $item['author']['xchan_photo_s'],
'when' => (($edit) ? datetime_convert('UTC', date_default_timezone_get(), $item['edited']) : datetime_convert('UTC', date_default_timezone_get(), $item['created'])),
'class' => (intval($item['item_unseen']) ? 'notify-unseen' : 'notify-seen'),
- 'b64mid' => (($item['mid']) ? gen_link_id($item['mid']) : ''),
+ // 'b64mid' => (($item['mid']) ? gen_link_id($item['mid']) : ''),
+ 'b64mid' => (($item['uuid']) ? $item['uuid'] : ''),
//'b64mid' => ((in_array($item['verb'], [ACTIVITY_LIKE, ACTIVITY_DISLIKE])) ? gen_link_id($item['thr_parent']) : gen_link_id($item['mid'])),
'thread_top' => (($item['item_thread_top']) ? true : false),
'message' => bbcode(escape_tags($itemem_text)),
'body' => $body,
// these are for the superblock addon
- 'hash' => $item[$who]['xchan_hash'],
+ 'hash' => $item['author']['xchan_hash'],
'uid' => $item['uid'],
'display' => true
);
@@ -907,9 +903,6 @@ class Enotify {
if(strpos($message, $tt['xname']) === 0)
$message = substr($message, strlen($tt['xname']) + 1);
- $mid = basename($tt['link']);
-
- $b64mid = gen_link_id($mid);
$x = [
'notify_link' => (($tt['ntype'] === NOTIFY_MAIL) ? $tt['link'] : z_root() . '/notify/view/' . $tt['id']),
'name' => $tt['xname'],
@@ -917,7 +910,7 @@ class Enotify {
'photo' => $tt['photo'],
'when' => datetime_convert('UTC', date_default_timezone_get(), $tt['created']),
'hclass' => (($tt['seen']) ? 'notify-seen' : 'notify-unseen'),
- 'b64mid' => (($tt['otype'] == 'item') ? $b64mid : ''),
+ 'b64mid' => (($tt['otype'] == 'item') ? $tt['hash'] : ''),
'notify_id' => (($tt['otype'] == 'item') ? $tt['id'] : ''),
'message' => $message
];
diff --git a/Zotlabs/Lib/JcsEddsa2022.php b/Zotlabs/Lib/JcsEddsa2022.php
new file mode 100644
index 000000000..14f16c94b
--- /dev/null
+++ b/Zotlabs/Lib/JcsEddsa2022.php
@@ -0,0 +1,92 @@
+<?php
+
+namespace Zotlabs\Lib;
+
+use Mmccook\JsonCanonicalizator\JsonCanonicalizatorFactory;
+use StephenHill\Base58;
+
+class JcsEddsa2022 {
+
+ public function __construct() {
+ return $this;
+ }
+
+ public function sign($data, $channel): array {
+ $base58 = new Base58();
+ $pubkey = (new Multibase())->publicKey($channel['channel_epubkey']);
+ $options = [
+ 'type' => 'DataIntegrityProof',
+ 'cryptosuite' => 'eddsa-jcs-2022',
+ 'created' => datetime_convert('UTC', 'UTC', 'now', ATOM_TIME),
+ 'verificationMethod' => channel_url($channel) . '#' . $pubkey,
+ 'proofPurpose' => 'assertionMethod',
+ ];
+
+ $optionsHash = $this->hash($this->signableOptions($options), true);
+ $dataHash = $this->hash($this->signableData($data), true);
+
+ $options['proofValue'] = 'z' . $base58->encode(sodium_crypto_sign_detached($optionsHash . $dataHash,
+ sodium_base642bin($channel['channel_eprvkey'], SODIUM_BASE64_VARIANT_ORIGINAL_NO_PADDING)));
+
+ return $options;
+ }
+
+ public function verify($data, $publicKey) {
+ $base58 = new Base58();
+ $encodedSignature = $data['proof']['proofValue'] ?? '';
+ if (!str_starts_with($encodedSignature,'z')) {
+ return false;
+ }
+
+ $encodedSignature = substr($encodedSignature, 1);
+ $optionsHash = $this->hash($this->signableOptions($data['proof']), true);
+ $dataHash = $this->hash($this->signableData($data),true);
+
+ try {
+ $result = sodium_crypto_sign_verify_detached($base58->decode($encodedSignature), $optionsHash . $dataHash,
+ (new Multibase())->decode($publicKey, true));
+ }
+ catch (\Exception $e) {
+ logger('verify exception:' . $e->getMessage());
+ }
+
+ logger('SignatureVerify (eddsa-jcs-2022) ' . (($result) ? 'true' : 'false'));
+
+ return $result;
+ }
+
+ public function signableData($data) {
+ $signableData = [];
+ if ($data) {
+ foreach ($data as $k => $v) {
+ if (!in_array($k, ['proof', 'signature'])) {
+ $signableData[$k] = $v;
+ }
+ }
+ }
+ return $signableData;
+ }
+
+ public function signableOptions($options) {
+ $signableOptions = [];
+
+ if ($options) {
+ foreach ($options as $k => $v) {
+ if ($k !== 'proofValue') {
+ $signableOptions[$k] = $v;
+ }
+ }
+ }
+ return $signableOptions;
+ }
+
+ public function hash($obj, $binary = false) {
+ return hash('sha256', $this->canonicalize($obj), $binary);
+ }
+
+ public function canonicalize($data) {
+ $canonicalization = JsonCanonicalizatorFactory::getInstance();
+ return $canonicalization->canonicalize($data);
+ }
+
+}
diff --git a/Zotlabs/Lib/Libsync.php b/Zotlabs/Lib/Libsync.php
index 80c447672..3130290f7 100644
--- a/Zotlabs/Lib/Libsync.php
+++ b/Zotlabs/Lib/Libsync.php
@@ -325,9 +325,6 @@ class Libsync {
if (array_key_exists('channel', $arr) && is_array($arr['channel']) && count($arr['channel'])) {
- $remote_channel = $arr['channel'];
- $remote_channel['channel_id'] = $channel['channel_id'];
-
if (array_key_exists('channel_pageflags', $arr['channel'])) {
// Several pageflags are site-specific and cannot be sync'd.
@@ -351,16 +348,21 @@ class Libsync {
'channel_a_delegate'
];
+ if (empty($channel['channel_epubkey']) && empty($channel['channel_eprvkey'])) {
+ $eckey = sodium_crypto_sign_keypair();
+ $channel['channel_epubkey'] = sodium_bin2base64(sodium_crypto_sign_publickey($eckey), SODIUM_BASE64_VARIANT_ORIGINAL_NO_PADDING);
+ $channel['channel_eprvkey'] = sodium_bin2base64(sodium_crypto_sign_secretkey($eckey), SODIUM_BASE64_VARIANT_ORIGINAL_NO_PADDING);
+ }
+
foreach ($arr['channel'] as $k => $v) {
if (in_array($k, $disallowed)) {
continue;
}
-
if (!in_array($k, $columns)) {
continue;
}
-
- dbq("UPDATE channel set " . dbesc($k) . " = '" . dbesc($v) . "' where channel_id = " . intval($channel['channel_id']));
+ $r = dbq("UPDATE channel set " . dbesc($k) . " = '" . dbesc($v)
+ . "' where channel_id = " . intval($channel['channel_id']));
}
}
diff --git a/Zotlabs/Lib/Libzot.php b/Zotlabs/Lib/Libzot.php
index 3f8685397..3495ede06 100644
--- a/Zotlabs/Lib/Libzot.php
+++ b/Zotlabs/Lib/Libzot.php
@@ -2,6 +2,7 @@
namespace Zotlabs\Lib;
+use App;
use Zotlabs\Web\HTTPSig;
use Zotlabs\Access\Permissions;
use Zotlabs\Access\PermissionLimits;
@@ -759,12 +760,13 @@ class Libzot {
|| ($r[0]['xchan_connurl'] != $arr['primary_location']['connections_url'])
|| ($r[0]['xchan_addr'] != $arr['primary_location']['address'])
|| ($r[0]['xchan_follow'] != $arr['primary_location']['follow_url'])
+ || (isset($arr['ed25519_key']) && $r[0]['xchan_epubkey'] != $arr['ed25519_key'])
|| ($r[0]['xchan_connpage'] != $arr['connect_url'])
|| ($r[0]['xchan_url'] != $arr['primary_location']['url'])
|| $hidden_changed || $adult_changed || $deleted_changed || $pubforum_changed) {
$rup = q("update xchan set xchan_name = '%s', xchan_name_date = '%s', xchan_connurl = '%s', xchan_follow = '%s',
xchan_connpage = '%s', xchan_hidden = %d, xchan_selfcensored = %d, xchan_deleted = %d, xchan_pubforum = %d,
- xchan_addr = '%s', xchan_url = '%s' where xchan_hash = '%s'",
+ xchan_addr = '%s', xchan_url = '%s', xchan_epubkey = '%s' where xchan_hash = '%s'",
dbesc(($arr['name']) ? escape_tags($arr['name']) : '-'),
dbesc($arr['name_updated']),
dbesc($arr['primary_location']['connections_url']),
@@ -776,6 +778,7 @@ class Libzot {
intval($arr['public_forum']),
dbesc(escape_tags($arr['primary_location']['address'])),
dbesc(escape_tags($arr['primary_location']['url'])),
+ dbesc($arr['ed25519_key'] ?? ''),
dbesc($xchan_hash)
);
@@ -799,6 +802,7 @@ class Libzot {
'xchan_guid' => $arr['id'],
'xchan_guid_sig' => $arr['id_sig'],
'xchan_pubkey' => $arr['public_key'],
+ 'xchan_epubkey' => $arr['xchan_epubkey'] ?? '',
'xchan_photo_mimetype' => $arr['photo']['type'],
'xchan_photo_l' => $arr['photo']['url'],
'xchan_addr' => escape_tags($arr['primary_location']['address']),
@@ -1139,7 +1143,6 @@ class Libzot {
if ($env['encoding'] === 'activitystreams') {
$AS = new ActivityStreams($data);
-
if (!$AS->is_valid()) {
logger('Activity rejected: ' . print_r($data, true));
return;
@@ -1154,7 +1157,6 @@ class Libzot {
else {
$item = [];
}
-
logger($AS->debug(), LOGGER_DATA);
}
@@ -1201,7 +1203,6 @@ class Libzot {
// @fixme;
$deliveries = self::public_recips($env, $AS);
-
}
$deliveries = array_unique($deliveries);
@@ -1222,10 +1223,6 @@ class Libzot {
$author_url = $AS->actor['id'];
- if ($AS->type === 'Announce') {
- $author_url = Activity::get_attributed_to_actor_url($AS);
- }
-
$r = Activity::get_actor_hublocs($author_url);
if (!$r) {
@@ -1286,7 +1283,7 @@ class Libzot {
}
}
- if (isset($AS->meta['hubloc']) && $AS->meta['hubloc']) {
+ if (!empty($AS->meta['hubloc']) || $AS->sigok) {
$item['item_verified'] = true;
}
@@ -1304,6 +1301,8 @@ class Libzot {
$relay = (($env['type'] === 'response') ? true : false);
$result = self::process_delivery($env['sender'], $AS, $item, $deliveries, $relay, false, $message_request);
+
+ Activity::init_background_fetch($env['sender']);
}
elseif ($env['type'] === 'sync') {
// $item = get_channelsync_elements($data);
@@ -1324,6 +1323,7 @@ class Libzot {
if ($result) {
$return = array_merge($return, $result);
}
+
return $return;
}
@@ -1366,11 +1366,13 @@ class Libzot {
static function find_parent_owner_hashes($env, $act) {
$r = [];
- $thread_parent = self::find_parent($env, $act);
- if ($thread_parent) {
- $uids = q("SELECT uid FROM item WHERE thr_parent = '%s' OR parent_mid = '%s'",
- dbesc($thread_parent),
- dbesc($thread_parent)
+ $parent = self::find_parent($env, $act);
+
+ if ($parent) {
+ $uids = q("SELECT uid FROM item WHERE thr_parent = '%s' OR parent_mid = '%s' OR mid = '%s'",
+ dbesc($parent),
+ dbesc($parent),
+ dbesc($parent)
);
if ($uids) {
@@ -1530,7 +1532,7 @@ class Libzot {
$local_public = $public;
$item_result = null;
- $DR = new DReport(z_root(), $sender, $d, $arr['mid']);
+ $DR = new DReport(z_root(), $sender, $d, $arr['mid'], $arr['uuid']);
$channel = channelx_by_hash($d);
@@ -1581,6 +1583,39 @@ class Libzot {
continue;
}
+ $arr['item_wall'] = 0;
+
+ // This is our own post, possibly coming from a channel clone
+ if ($arr['owner_xchan'] === $d) {
+ $arr['item_wall'] = 1;
+ }
+
+ if (isset($arr['item_deleted']) && $arr['item_deleted']) {
+
+ // remove_community_tag is a no-op if this isn't a community tag activity
+ // self::remove_community_tag($sender, $arr, $channel['channel_id']);
+
+ // set these just in case we need to store a fresh copy of the deleted post.
+ // This could happen if the delete got here before the original post did.
+
+ $arr['aid'] = $channel['channel_account_id'];
+ $arr['uid'] = $channel['channel_id'];
+
+ $item_id = self::delete_imported_item($sender, $act, $arr, $channel['channel_id'], $relay);
+ $DR->update(($item_id) ? 'deleted' : 'delete_failed');
+ $result[] = $DR->get();
+
+ if ($relay && $item_id) {
+ logger('process_delivery: invoking relay');
+ Master::Summon(['Notifier', 'relay', intval($item_id), 'delete']);
+ $DR->update('relayed');
+ $result[] = $DR->get();
+ }
+
+ continue;
+ }
+
+
// allow public postings to the sys channel regardless of permissions, but not
// for comments travelling upstream. Wait and catch them on the way down.
// They may have been blocked by the owner.
@@ -1607,116 +1642,23 @@ class Libzot {
}
$tag_delivery = tgroup_check($channel['channel_id'], $arr);
- $perm = 'send_stream';
- if (($arr['mid'] !== $arr['parent_mid']) && ($relay))
- $perm = 'post_comments';
-
- // This is our own post, possibly coming from a channel clone
- if ($arr['owner_xchan'] == $d) {
- $arr['item_wall'] = 1;
- }
- else {
- $arr['item_wall'] = 0;
- }
-
- $friendofriend = false;
-
- if ((!$tag_delivery) && (!$local_public)) {
- $allowed = (perm_is_allowed($channel['channel_id'], $sender, $perm));
-
- $permit_mentions = intval(PConfig::Get($channel['channel_id'], 'system', 'permit_all_mentions') && i_am_mentioned($channel, $arr));
-
- if (!$allowed) {
- if ($perm === 'post_comments') {
- $parent = q("select * from item where mid = '%s' and uid = %d limit 1",
- dbesc($arr['parent_mid']),
- intval($channel['channel_id'])
- );
- if ($parent) {
- $allowed = can_comment_on_post($sender, $parent[0]);
- if (!$allowed && $permit_mentions) {
- $allowed = true;
- }
-
- if (!$allowed) {
- if (PConfig::Get($channel['channel_id'], 'system', 'moderate_unsolicited_comments') && $arr['obj_type'] !== 'Answer') {
- $arr['item_blocked'] = ITEM_MODERATED;
- $allowed = true;
- }
- }
- }
-
- } elseif ($permit_mentions) {
- $allowed = true;
- }
- }
-
- if ($request) {
- // Conversation fetches (e.g. $request == true) take place for
- // a) new comments on expired posts
- // b) hyperdrive (friend-of-friend) conversations
- // c) Repeats of posts by others
-
-
- // over-ride normal connection permissions for hyperdrive (friend-of-friend) conversations
- // (if hyperdrive is enabled) and repeated posts by a friend.
- // If $allowed is already true, this is probably the conversation of a direct friend or a
- // conversation fetch for a new comment on an expired post
- // Comments of all these activities are allowed and will only be rejected (later) if the parent
- // doesn't exist.
-
- if ($perm === 'send_stream') {
- if ($force || get_pconfig($channel['channel_id'], 'system', 'hyperdrive', false)) {
- $allowed = true;
- }
- }
- else {
- $allowed = true;
- }
-
- $friendofriend = true;
- }
-
- if (intval($arr['item_private']) === 2) {
- if (!perm_is_allowed($channel['channel_id'], $sender, 'post_mail')) {
- $allowed = false;
- }
- }
-
- if (!$allowed) {
- logger("permission denied for delivery to channel {$channel['channel_id']} {$channel['channel_address']}");
- $DR->update('permission denied');
- $result[] = $DR->get();
- continue;
- }
- }
-
- // logger('item: ' . print_r($arr,true), LOGGER_DATA);
+ $perm = 'send_stream';
if ($arr['mid'] !== $arr['parent_mid']) {
- logger('checking source: "' . $arr['mid'] . '" != "' . $arr['parent_mid'] . '"');
-
- // check source route.
- // We are only going to accept comments from this sender if the comment has the same route as the top-level-post,
- // this is so that permissions mismatches between senders apply to the entire conversation
- // As a side effect we will also do a preliminary check that we have the top-level-post, otherwise
- // processing it is pointless.
+ if ($relay)
+ $perm = 'post_comments';
- $r = q("select route, id, parent_mid, mid, owner_xchan, item_private, obj_type from item where mid = '%s' and uid = %d limit 1",
+ $parent = q("select * from item where mid = '%s' and uid = %d limit 1",
dbesc($arr['parent_mid']),
intval($channel['channel_id'])
);
- if (!$r) {
+ if (!$parent) {
$DR->update('comment parent not found');
$result[] = $DR->get();
- if ($relay || $request || $local_public) {
- continue;
- }
-
// We don't seem to have a copy of this conversation or at least the parent
// - so request a copy of the entire conversation to date.
// Don't do this if it's a relay post as we're the ones who are supposed to
@@ -1728,24 +1670,40 @@ class Libzot {
// the top level post is unlikely to be imported and
// this is just an exercise in futility.
- if (perm_is_allowed($channel['channel_id'], $sender, 'send_stream')) {
- Master::Summon(['Zotconvo', $channel['channel_id'], $arr['parent_mid']]);
+ if ($relay || $request || (!$local_public && !perm_is_allowed($channel['channel_id'], $sender, 'send_stream'))) {
+ continue;
+ }
+
+ if ($arr['verb'] === 'Announce') {
+ App::$cache['as_fetch_objects'][$arr['mid']]['channels'][] = $channel['channel_id'];
+ App::$cache['as_fetch_objects'][$arr['mid']]['force'] = true;
+ }
+ else {
+ App::$cache['zot_fetch_objects'][$arr['mid']]['channels'][] = $channel['channel_id'];
+ App::$cache['zot_fetch_objects'][$arr['mid']]['force'] = false;
}
continue;
}
- if ($r[0]['obj_type'] === 'Question') {
+ logger('checking source: "' . $arr['mid'] . '" != "' . $arr['parent_mid'] . '"');
+
+ // check source route.
+ // We are only going to accept comments from this sender if the comment has the same route as the top-level-post,
+ // this is so that permissions mismatches between senders apply to the entire conversation
+ // As a side effect we will also do a preliminary check that we have the top-level-post, otherwise
+ // processing it is pointless.
+
+ if ($parent[0]['obj_type'] === 'Question') {
// route checking doesn't work correctly here because we've changed the privacy
- $r[0]['route'] = EMPTY_STR;
+ $parent[0]['route'] = EMPTY_STR;
// If this is a poll response, convert the obj_type to our (internal-only) "Answer" type
- if ($arr['obj_type'] === ACTIVITY_OBJ_COMMENT && $arr['title'] && (!$arr['body'])) {
+ if (in_array($arr['obj_type'], ['Note', ACTIVITY_OBJ_COMMENT]) && $arr['title'] && (!$arr['body'])) {
$arr['obj_type'] = 'Answer';
}
}
-
- if ($relay || $friendofriend || (intval($r[0]['item_private']) === 0 && intval($arr['item_private']) === 0)) {
+ if ($relay || (intval($parent[0]['item_private']) === 0 && intval($arr['item_private']) === 0)) {
// reset the route in case it travelled a great distance upstream
// use our parent's route so when we go back downstream we'll match
// with whatever route our parent has.
@@ -1753,8 +1711,8 @@ class Libzot {
// but we are now getting comments via listener delivery
// and if there is no privacy on this or the parent, we don't care about the route,
// so just set the owner and route accordingly.
- $arr['route'] = $r[0]['route'];
- $arr['owner_xchan'] = $r[0]['owner_xchan'];
+ $arr['route'] = $parent[0]['route'];
+ $arr['owner_xchan'] = $parent[0]['owner_xchan'];
}
else {
@@ -1764,7 +1722,7 @@ class Libzot {
// only compare the last hop since it could have arrived at the last location any number of ways.
// Always accept empty routes and firehose items (route contains 'undefined') .
- $existing_route = explode(',', $r[0]['route']);
+ $existing_route = explode(',', $parent[0]['route']);
$routes = count($existing_route);
if ($routes) {
$last_hop = array_pop($existing_route);
@@ -1781,8 +1739,8 @@ class Libzot {
$current_route = ((isset($arr['route']) && $arr['route']) ? $arr['route'] . ',' : '') . $sender;
if ($last_hop && $last_hop != $sender) {
- logger('comment route mismatch: parent route = ' . $r[0]['route'] . ' expected = ' . $current_route, LOGGER_DEBUG);
- logger('comment route mismatch: parent msg = ' . $r[0]['id'], LOGGER_DEBUG);
+ logger('comment route mismatch: parent route = ' . $parent[0]['route'] . ' expected = ' . $current_route, LOGGER_DEBUG);
+ logger('comment route mismatch: parent msg = ' . $parent[0]['id'], LOGGER_DEBUG);
$DR->update('comment route mismatch');
$result[] = $DR->get();
continue;
@@ -1795,42 +1753,80 @@ class Libzot {
}
}
- // This is used to fetch allow/deny rules if either the sender
- // or owner is a connection. post_is_importable() evaluates all of them
- $abook = q("select * from abook where abook_channel = %d and ( abook_xchan = '%s' OR abook_xchan = '%s' )",
- intval($channel['channel_id']),
- dbesc($arr['owner_xchan']),
- dbesc($arr['author_xchan'])
- );
+ if (!$tag_delivery && !$local_public) {
+ $allowed = perm_is_allowed($channel['channel_id'], $sender, $perm);
- if (isset($arr['item_deleted']) && $arr['item_deleted']) {
+ $permit_mentions = intval(PConfig::Get($channel['channel_id'], 'system', 'permit_all_mentions') && i_am_mentioned($channel, $arr));
- // remove_community_tag is a no-op if this isn't a community tag activity
- self::remove_community_tag($sender, $arr, $channel['channel_id']);
+ if (!$allowed) {
+ if ($parent && $perm === 'send_stream') {
+ // if we own the parent we will accept its comments
+ $allowed = true;
+ }
- // set these just in case we need to store a fresh copy of the deleted post.
- // This could happen if the delete got here before the original post did.
+ elseif ($parent && $perm === 'post_comments') {
+ $allowed = can_comment_on_post($sender, $parent[0]);
- $arr['aid'] = $channel['channel_account_id'];
- $arr['uid'] = $channel['channel_id'];
+ if (!$allowed && $permit_mentions) {
+ $allowed = true;
+ }
- $item_id = self::delete_imported_item($sender, $act, $arr, $channel['channel_id'], $relay);
- $DR->update(($item_id) ? 'deleted' : 'delete_failed');
- $result[] = $DR->get();
+ if (!$allowed) {
+ if (PConfig::Get($channel['channel_id'], 'system', 'moderate_unsolicited_comments') && $arr['obj_type'] !== 'Answer') {
+ $arr['item_blocked'] = ITEM_MODERATED;
+ $allowed = true;
+ }
+ }
- if ($relay && $item_id) {
- logger('process_delivery: invoking relay');
- Master::Summon(['Notifier', 'relay', intval($item_id)]);
- $DR->update('relayed');
- $result[] = $DR->get();
+ }
+ elseif ($permit_mentions) {
+ $allowed = true;
+ }
}
- continue;
+ if ($request) {
+ // Conversation fetches (e.g. $request == true) take place for
+ // a) new comments on expired posts
+ // b) manual import of posts via search (in this case force will be true)
+ // c) import of conversations from friends of friends (they can currently arriuve from streams if a channel is configured to do so)
+
+ // Comments of all these activities are allowed and will only be rejected (later) if the parent
+ // doesn't exist.
+
+ if ($perm === 'send_stream') {
+ if ($force) {
+ $allowed = true;
+ }
+ }
+ else {
+ $allowed = true;
+ }
+ }
+
+ if (intval($arr['item_private']) === 2) {
+ if (!perm_is_allowed($channel['channel_id'], $sender, 'post_mail')) {
+ $allowed = false;
+ }
+ }
+
+ if (!$allowed) {
+ logger("permission denied for delivery to channel {$channel['channel_id']} {$channel['channel_address']}");
+ $DR->update('permission denied');
+ $result[] = $DR->get();
+ continue;
+ }
}
+ // This is used to fetch allow/deny rules if either the sender
+ // or owner is a connection. post_is_importable() evaluates all of them
+ $abook = q("select * from abook where abook_channel = %d and ( abook_xchan = '%s' OR abook_xchan = '%s' )",
+ intval($channel['channel_id']),
+ dbesc($arr['owner_xchan']),
+ dbesc($arr['author_xchan'])
+ );
+
// reactions such as like and dislike could have an mid with /activity/ in it.
// Check for both forms in order to prevent duplicates.
-
$r = q("select * from item where mid in ('%s','%s') and uid = %d limit 1",
dbesc($arr['mid']),
dbesc(str_replace(z_root() . '/activity/', z_root() . '/item/', $arr['mid'])),
@@ -1838,14 +1834,6 @@ class Libzot {
);
if ($r) {
- // We already have this post.
- // Dismiss its announce
- if ($act->type === 'Announce') {
- $DR->update('update ignored');
- $result[] = $DR->get();
- continue;
- }
-
$item_id = $r[0]['id'];
if (intval($r[0]['item_deleted'])) {
@@ -1902,12 +1890,12 @@ class Libzot {
$maxlen = get_max_import_size();
- if ($maxlen && mb_strlen($arr['body']) > $maxlen) {
+ if ($maxlen && isset($arr['body']) && mb_strlen($arr['body']) > $maxlen) {
$arr['body'] = mb_substr($arr['body'], 0, $maxlen, 'UTF-8');
logger('message length exceeds max_import_size: truncated');
}
- if ($maxlen && mb_strlen($arr['summary']) > $maxlen) {
+ if ($maxlen && isset($arr['summary']) && mb_strlen($arr['summary']) > $maxlen) {
$arr['summary'] = mb_substr($arr['summary'], 0, $maxlen, 'UTF-8');
logger('message summary length exceeds max_import_size: truncated');
}
@@ -1915,7 +1903,6 @@ class Libzot {
if (post_is_importable($arr['uid'], $arr, $abook)) {
$item_result = item_store($arr);
if ($item_result['success']) {
-
$item_id = $item_result['item_id'];
if ($item_source && in_array($item_result['item']['obj_type'], ['Event', ACTIVITY_OBJ_EVENT])) {
@@ -1957,6 +1944,7 @@ class Libzot {
// preserve conversations with which you are involved from expiration
$stored = ((isset($item_result['item'])) ? $item_result['item'] : false);
+
if ((is_array($stored)) && ($stored['id'] != $stored['parent'])
&& ($stored['author_xchan'] === $channel['channel_hash'])) {
retain_item($stored['item']['parent']);
@@ -2005,11 +1993,14 @@ class Libzot {
$ret = [];
-
$signer = q("select hubloc_hash, hubloc_url from hubloc where hubloc_id_url = '%s' and hubloc_network = 'zot6' order by hubloc_id desc limit 1",
dbesc($a['signature']['signer'])
);
+ $signer_hash = $signer[0]['hubloc_hash'] ?? $a['signature']['signer'];
+ $conv_owner = $signer_hash;
+
+ $i = 0;
foreach ($items as $activity) {
@@ -2023,14 +2014,14 @@ class Libzot {
}
if (!$AS->is_valid()) {
- logger('FOF Activity rejected: ' . print_r($activity, true));
+ logger('Fetched activity rejected: ' . print_r($activity, true));
continue;
}
// logger($AS->debug());
if(empty($AS->actor['id'])) {
- logger('No actor id: ' . print_r($AS, true));
+ logger('Fetched activity no actor id: ' . print_r($AS, true));
continue;
}
@@ -2043,7 +2034,7 @@ class Libzot {
$r = self::zot_record_preferred($r);
}
if (!$r) {
- logger('FOF Activity: no actor');
+ logger('Fetched activity: no actor');
continue;
}
}
@@ -2058,7 +2049,7 @@ class Libzot {
$ro = self::zot_record_preferred($ro);
}
if (!$ro) {
- logger('FOF Activity: no obj actor');
+ logger('Fetched activity: no obj actor');
continue;
}
}
@@ -2073,14 +2064,18 @@ class Libzot {
$arr['author_xchan'] = $r['hubloc_hash'];
- if ($signer) {
- $arr['owner_xchan'] = $signer[0]['hubloc_hash'];
- }
- else {
- $arr['owner_xchan'] = $a['signature']['signer'];
+ if ($i === 0) {
+ // Set the author of the toplevel post as conv_owner
+ $conv_owner = $r['hubloc_hash'];
}
- if (isset($AS->meta['hubloc']) || $arr['author_xchan'] === $arr['owner_xchan']) {
+ $arr['owner_xchan'] = $conv_owner;
+ $arr['source_xchan'] = $signer_hash;
+
+ // WARNING: the presence of both source_xchan and non-zero item_uplink here will cause a delivery loop
+ $arr['item_uplink'] = 0;
+
+ if (!empty($AS->meta['hubloc']) || $arr['author_xchan'] === $arr['owner_xchan'] || $AS->sigok) {
$arr['item_verified'] = true;
}
@@ -2092,13 +2087,15 @@ class Libzot {
}
}
- logger('FOF Activity received: ' . print_r($arr, true), LOGGER_DATA, LOG_DEBUG);
- logger('FOF Activity recipient: ' . $channel['channel_hash'], LOGGER_DATA, LOG_DEBUG);
+ logger('Fetched activity received: ' . print_r($arr, true), LOGGER_DATA, LOG_DEBUG);
+ logger('Fetched activity recipient: ' . $channel['channel_hash'], LOGGER_DATA, LOG_DEBUG);
$result = self::process_delivery($arr['owner_xchan'], $AS, $arr, [$channel['channel_hash']], false, false, true, $force);
if ($result) {
$ret = array_merge($ret, $result);
}
+
+ $i++;
}
return $ret;
@@ -2320,12 +2317,20 @@ class Libzot {
// this information from the metadata should have no other discernible impact.
if (($stored['id'] != $stored['parent']) && intval($stored['item_origin'])) {
- q("update item set item_origin = 0 where id = %d and uid = %d",
- intval($stored['id']),
- intval($stored['uid'])
+ q("update item set item_origin = 0 where id = %d",
+ intval($stored['id'])
);
}
- }
+ } else {
+ if ($stored['id'] !== $stored['parent']) {
+ q(
+ "update item set commented = '%s', changed = '%s' where id = %d",
+ dbesc(datetime_convert()),
+ dbesc(datetime_convert()),
+ intval($stored['parent'])
+ );
+ }
+ }
// Use phased deletion to set the deleted flag, call both tag_deliver and the notifier to notify downstream channels
@@ -2814,6 +2819,7 @@ class Libzot {
];
$ret['public_key'] = $e['channel_pubkey'];
+ $ret['ed25519_key'] = $e['xchan_epubkey'];
$ret['signing_algorithm'] = 'rsa-sha256';
$ret['username'] = $e['channel_address'];
$ret['name'] = $e['channel_name'];
@@ -2979,7 +2985,7 @@ class Libzot {
$ret['site']['admin'] = get_config('system', 'admin_email');
$visible_plugins = [];
- if (is_array(\App::$plugins) && count(\App::$plugins)) {
+ if (is_array(App::$plugins) && count(App::$plugins)) {
$r = q("select * from addon where hidden = 0");
if ($r)
foreach ($r as $rr)
diff --git a/Zotlabs/Lib/Multibase.php b/Zotlabs/Lib/Multibase.php
new file mode 100644
index 000000000..099723630
--- /dev/null
+++ b/Zotlabs/Lib/Multibase.php
@@ -0,0 +1,34 @@
+<?php
+namespace Zotlabs\Lib;
+
+use StephenHill\Base58;
+
+class Multibase {
+
+ protected $key = null;
+
+ public function __construct() {
+ return $this;
+ }
+
+ public function publicKey($key) {
+ $base58 = new Base58();
+ $raw = hex2bin('ed01') . sodium_base642bin($key, SODIUM_BASE64_VARIANT_ORIGINAL_NO_PADDING);
+ return 'z' . $base58->encode($raw);
+ }
+
+ public function secretKey($key) {
+ $base58 = new Base58();
+ $raw = hex2bin('8026') . sodium_base642bin($key, SODIUM_BASE64_VARIANT_ORIGINAL_NO_PADDING);
+ return 'z' . $base58->encode($raw);
+ }
+
+ public function decode($key, $binary = false) {
+ $base58 = new Base58();
+ $key = substr($key,1);
+ $raw = $base58->decode($key);
+ $binaryKey = substr($raw, 2);
+ return $binary ? $binaryKey : sodium_bin2base64($binaryKey, SODIUM_BASE64_VARIANT_ORIGINAL_NO_PADDING);
+ }
+
+}
diff --git a/Zotlabs/Lib/Share.php b/Zotlabs/Lib/Share.php
index 81f378d0d..8abbfda80 100644
--- a/Zotlabs/Lib/Share.php
+++ b/Zotlabs/Lib/Share.php
@@ -112,7 +112,7 @@ class Share {
if(! $this->item)
return $bb;
- $is_photo = (($this->item['obj_type'] === ACTIVITY_OBJ_PHOTO) ? true : false);
+ $is_photo = ((in_array($this->item['obj_type'], ['Image', ACTIVITY_OBJ_PHOTO])) ? true : false);
if($is_photo) {
$object = json_decode($this->item['obj'],true);
$photo_bb = $object['body'];
diff --git a/Zotlabs/Lib/Text.php b/Zotlabs/Lib/Text.php
new file mode 100644
index 000000000..f593f9dd6
--- /dev/null
+++ b/Zotlabs/Lib/Text.php
@@ -0,0 +1,24 @@
+<?php
+
+namespace Zotlabs\Lib;
+
+class Text {
+
+ /**
+ * use this on "body" or "content" input where angle chars shouldn't be removed,
+ * and allow them to be safely displayed.
+ *
+ * @param string $string
+ *
+ * @return string
+ */
+
+ public static function escape_tags(string $string): string {
+ if (!$string) {
+ return EMPTY_STR;
+ }
+
+ return htmlspecialchars($string, ENT_COMPAT, 'UTF-8', false);
+ }
+
+}
diff --git a/Zotlabs/Lib/ThreadItem.php b/Zotlabs/Lib/ThreadItem.php
index 037ddb19e..8f364e945 100644
--- a/Zotlabs/Lib/ThreadItem.php
+++ b/Zotlabs/Lib/ThreadItem.php
@@ -19,7 +19,7 @@ class ThreadItem {
private $comment_box_template = 'comment_item.tpl';
private $commentable = false;
// list of supported reaction emojis - a site can over-ride this via config system.reactions
- private $reactions = ['1f60a','1f44f','1f37e','1f48b','1f61e','2665','1f606','1f62e','1f634','1f61c','1f607','1f608'];
+ private $reactions = ['slightly_smiling_face','clapping_hands','bottle_with_popping_cork','kiss_mark','disappointed_face','red_heart','grinning_face','astonished_face','sleeping_face','winking_face_with_tongue','smiling_face_with_halo','smiling_face_with_horns'];
private $toplevel = false;
private $children = array();
private $parent = null;
@@ -34,6 +34,7 @@ class ThreadItem {
private $channel = null;
private $display_mode = 'normal';
private $reload = '';
+ private $mid_uuid_map = [];
public function __construct($data) {
@@ -46,6 +47,7 @@ class ThreadItem {
// Prepare the children
if(isset($data['children'])) {
+
foreach($data['children'] as $item) {
/*
@@ -56,7 +58,6 @@ class ThreadItem {
continue;
}
-
$child = new ThreadItem($item);
$this->add_child($child);
}
@@ -65,6 +66,8 @@ class ThreadItem {
unset($this->data['children']);
}
+
+
// allow a site to configure the order and content of the reaction emoji list
if($this->toplevel) {
$x = get_config('system','reactions');
@@ -82,7 +85,7 @@ class ThreadItem {
* _ false on failure
*/
- public function get_template_data($conv_responses, $thread_level=1, $conv_flags = []) {
+ public function get_template_data($conv_responses, $mid_uuid_map, $thread_level=1, $conv_flags = []) {
$result = [];
$item = $this->get_data();
@@ -121,12 +124,14 @@ class ThreadItem {
$locktype = 0;
}
- $shareable = ((($conv->get_profile_owner() == local_channel() && local_channel()) && ($item['item_private'] != 1)) ? true : false);
+ $shareable = ((($conv->get_profile_owner() == local_channel() && local_channel()) && (intval($item['item_private']) === 0)) ? true : false);
// allow an exemption for sharing stuff from your private feeds
if($item['author']['xchan_network'] === 'rss')
$shareable = true;
+ $repeatable = ((($conv->get_profile_owner() == local_channel() && local_channel()) && (intval($item['item_private']) === 0) && (in_array($item['author']['xchan_network'], ['zot6', 'activitypub']))) ? true : false);
+
// @fixme
// Have recently added code to properly handle polls in group reshares by redirecting all of the poll responses to the group.
// Sharing a poll using a regular embedded share is harder because the poll will need to fork. This is due to comment permissions.
@@ -194,9 +199,14 @@ class ThreadItem {
$attend = null;
// process action responses - e.g. like/dislike/attend/agree/whatever
- $response_verbs = array('like');
- if(feature_enabled($conv->get_profile_owner(),'dislike'))
+ $response_verbs[] = 'like';
+
+ if(feature_enabled($conv->get_profile_owner(),'dislike')) {
$response_verbs[] = 'dislike';
+ }
+
+ $response_verbs[] = 'announce';
+
if(in_array($item['obj_type'], ['Event', ACTIVITY_OBJ_EVENT])) {
$response_verbs[] = 'attendyes';
$response_verbs[] = 'attendno';
@@ -222,6 +232,8 @@ class ThreadItem {
$my_responses[$v] = ((isset($conv_responses[$v][$item['mid'] . '-m'])) ? 1 : 0);
}
+/*
+
$like_count = ((x($conv_responses['like'],$item['mid'])) ? $conv_responses['like'][$item['mid']] : '');
$like_list = ((x($conv_responses['like'],$item['mid'])) ? $conv_responses['like'][$item['mid'] . '-l'] : '');
if (($like_list) && (count($like_list) > MAX_LIKERS)) {
@@ -232,6 +244,16 @@ class ThreadItem {
}
$like_button_label = tt('Like','Likes',$like_count,'noun');
+ $repeat_count = ((x($conv_responses['announce'],$item['mid'])) ? $conv_responses['announce'][$item['mid']] : '');
+ $repeat_list = ((x($conv_responses['announce'],$item['mid'])) ? $conv_responses['announce'][$item['mid'] . '-l'] : '');
+ if (($repeat_list) && (count($repeat_list) > MAX_LIKERS)) {
+ $repeat_list_part = array_slice($repeat_list, 0, MAX_LIKERS);
+ array_push($repeat_list_part, '<a class="dropdown-item" href="#" data-toggle="modal" data-target="#repeatModal-' . $this->get_id() . '"><b>' . t('View all') . '</b></a>');
+ } else {
+ $repeat_list_part = '';
+ }
+ $repeat_button_label = tt('Repeat','Repeats',$repeat_count,'noun');
+
$showdislike = '';
if (feature_enabled($conv->get_profile_owner(),'dislike')) {
$dislike_count = ((x($conv_responses['dislike'],$item['mid'])) ? $conv_responses['dislike'][$item['mid']] : '');
@@ -248,6 +270,7 @@ class ThreadItem {
}
$showlike = ((x($conv_responses['like'],$item['mid'])) ? format_like($conv_responses['like'][$item['mid']],$conv_responses['like'][$item['mid'] . '-l'],'like',$item['mid']) : '');
+*/
/*
* We should avoid doing this all the time, but it depends on the conversation mode
@@ -315,13 +338,11 @@ class ThreadItem {
$share = [];
$embed = [];
if ($shareable) {
- // This actually turns out not to be possible in some protocol stacks without opening up hundreds of new issues.
- // Will allow it only for uri resolvable sources.
- if(strpos($item['mid'],'http') === 0) {
- //Not yet ready for primetime
- //$share = array( t('Repeat This'), t('repeat'));
- }
- $embed = [t('Share This'), t('share')];
+ $embed = [t('Share'), t('share')];
+ }
+
+ if ($repeatable) {
+ $share = [t('Repeat'), t('repeat')];
}
$dreport = '';
@@ -333,7 +354,7 @@ class ThreadItem {
$dreport_link = '';
if((intval($item['item_type']) == ITEM_TYPE_POST) && (! get_config('system','disable_dreport')) && strcmp(datetime_convert('UTC','UTC',$item['created']),datetime_convert('UTC','UTC',"now - $keep_reports days")) > 0) {
$dreport = t('Delivery Report');
- $dreport_link = gen_link_id($item['mid']);
+ $dreport_link = '?mid=' . $item['mid'];
}
$is_new = false;
@@ -352,8 +373,8 @@ class ThreadItem {
if($conv->get_mode() === 'channel')
$viewthread = z_root() . '/channel/' . $owner_address . '?f=&mid=' . urlencode(gen_link_id($item['mid']));
- $comment_count_txt = sprintf(tt('%d Comment', '%d Comments', $total_children), $total_children);
- $list_unseen_txt = (($unseen_comments) ? sprintf(t('%d unseen'), $unseen_comments) : '');
+ $comment_count_txt = ['label' => sprintf(tt('%d comment', '%d comments', $total_children), $total_children), 'count' => $total_children];
+ $list_unseen_txt = $unseen_comments ? ['label' => sprintf(t('%d unseen'), $unseen_comments), 'count' => $unseen_comments] : [];
$children = $this->get_children();
@@ -363,8 +384,8 @@ class ThreadItem {
call_hooks('dropdown_extras',$dropdown_extras_arr);
$dropdown_extras = $dropdown_extras_arr['dropdown_extras'];
- $midb64 = gen_link_id($item['mid']);
- $mids = [ $midb64 ];
+ $midb64 = $item['uuid'];
+ $mids = [ $item['uuid'] ];
$response_mids = [];
foreach($response_verbs as $v) {
if(isset($conv_responses[$v]['mids'][$item['mid']])) {
@@ -480,35 +501,44 @@ class ThreadItem {
'comment_count' => $total_children,
'comment_count_txt' => $comment_count_txt,
'list_unseen_txt' => $list_unseen_txt,
- 'markseen' => t('Mark all seen'),
+ 'markseen' => t('Mark all comments seen'),
'responses' => $responses,
'my_responses' => $my_responses,
+ /*
'like_count' => $like_count,
'like_list' => $like_list,
'like_list_part' => $like_list_part,
'like_button_label' => $like_button_label,
'like_modal_title' => t('Likes','noun'),
+
+ 'repeat_count' => $repeat_count,
+ 'repeat_list' => $repeat_list,
+ 'repeat_list_part' => $repeat_list_part,
+ 'repeat_button_label' => $repeat_button_label,
+ 'repeat_modal_title' => t('Repeats','noun'),
+
+
'dislike_modal_title' => t('Dislikes','noun'),
'dislike_count' => ((feature_enabled($conv->get_profile_owner(),'dislike')) ? $dislike_count : ''),
'dislike_list' => ((feature_enabled($conv->get_profile_owner(),'dislike')) ? $dislike_list : ''),
'dislike_list_part' => ((feature_enabled($conv->get_profile_owner(),'dislike')) ? $dislike_list_part : ''),
'dislike_button_label' => ((feature_enabled($conv->get_profile_owner(),'dislike')) ? $dislike_button_label : ''),
+*/
'modal_dismiss' => t('Close'),
- 'showlike' => $showlike,
- 'showdislike' => $showdislike,
+ // 'showlike' => $showlike,
+ // 'showdislike' => $showdislike,
'comment' => ($item['item_delayed'] ? '' : $this->get_comment_box()),
'previewing' => ($conv->is_preview() ? true : false ),
'preview_lbl' => t('This is an unsaved preview'),
'wait' => t('Please wait'),
'thread_level' => $thread_level,
'settings' => $settings,
- 'thr_parent' => (($item['parent_mid'] != $item['thr_parent']) ? gen_link_id($item['thr_parent']) : ''),
+ 'thr_parent_uuid' => (($item['parent_mid'] != $item['thr_parent']) ? $mid_uuid_map[$item['thr_parent']] : ''),
'contact_id' => (($contact) ? $contact['abook_id'] : ''),
'moderate' => ($item['item_blocked'] == ITEM_MODERATED),
'moderate_approve' => t('Approve'),
'moderate_delete' => t('Delete'),
- 'rtl' => in_array($item['lang'], rtl_languages())
-
+ 'rtl' => in_array($item['lang'], rtl_languages()),
);
$arr = array('item' => $item, 'output' => $tmp_item);
@@ -531,12 +561,12 @@ class ThreadItem {
if(($this->get_display_mode() === 'normal') && ($nb_children > 0)) {
foreach($children as $child) {
- $result['children'][] = $child->get_template_data($conv_responses, $thread_level + 1,$conv_flags);
+ $result['children'][] = $child->get_template_data($conv_responses, $mid_uuid_map, $thread_level + 1,$conv_flags);
}
// Collapse
if(($nb_children > $visible_comments) || ($thread_level > 1)) {
$result['children'][0]['comment_firstcollapsed'] = true;
- $result['children'][0]['num_comments'] = $comment_count_txt;
+ $result['children'][0]['num_comments'] = $comment_count_txt['label'];
$result['children'][0]['hide_text'] = sprintf( t('%s show all'), '<i class="fa fa-chevron-down"></i>');
if($thread_level > 1) {
$result['children'][$nb_children - 1]['comment_lastcollapsed'] = true;
@@ -613,7 +643,7 @@ class ThreadItem {
* Only add what will be displayed
*/
- if(activity_match($item->get_data_value('verb'),ACTIVITY_LIKE) || activity_match($item->get_data_value('verb'),ACTIVITY_DISLIKE)) {
+ if(activity_match($item->get_data_value('verb'), ['Like', 'Dislike', ACTIVITY_LIKE, ACTIVITY_DISLIKE])) {
return false;
}
@@ -878,6 +908,12 @@ class ThreadItem {
$this->owner_name = $this->data['owner']['xchan_name'];
$this->wall_to_wall = true;
}
+ elseif($this->is_toplevel() && $this->get_data_value('verb') === 'Announce' && isset($this->data['source'])) {
+ $this->owner_url = chanlink_hash($this->data['source']['xchan_hash']);
+ $this->owner_photo = $this->data['source']['xchan_photo_s'];
+ $this->owner_name = $this->data['source']['xchan_name'];
+ $this->wall_to_wall = true;
+ }
}
private function is_wall_to_wall() {
diff --git a/Zotlabs/Lib/ThreadStream.php b/Zotlabs/Lib/ThreadStream.php
index 2ad24a690..fb3b6dd9b 100644
--- a/Zotlabs/Lib/ThreadStream.php
+++ b/Zotlabs/Lib/ThreadStream.php
@@ -171,7 +171,7 @@ class ThreadStream {
*/
- if(($item->get_data_value('id') != $item->get_data_value('parent')) && (activity_match($item->get_data_value('verb'),ACTIVITY_LIKE) || activity_match($item->get_data_value('verb'),ACTIVITY_DISLIKE))) {
+ if($item->get_data_value('id') != $item->get_data_value('parent') && activity_match($item->get_data_value('verb'), ['Like', 'Dislike', ACTIVITY_LIKE, ACTIVITY_DISLIKE])) {
return false;
}
@@ -211,7 +211,7 @@ class ThreadStream {
* _ The data requested on success
* _ false on failure
*/
- public function get_template_data($conv_responses) {
+ public function get_template_data($conv_responses, $mid_uuid_map) {
$result = array();
foreach($this->threads as $item) {
@@ -220,7 +220,7 @@ class ThreadStream {
$item_data = $this->prepared_item;
}
else {
- $item_data = $item->get_template_data($conv_responses);
+ $item_data = $item->get_template_data($conv_responses, $mid_uuid_map);
}
if(!$item_data) {
logger('Failed to get item template data ('. $item->get_id() .').', LOGGER_DEBUG, LOG_ERR);
diff --git a/Zotlabs/Module/Activity.php b/Zotlabs/Module/Activity.php
index 4ddfe602d..133312e28 100644
--- a/Zotlabs/Module/Activity.php
+++ b/Zotlabs/Module/Activity.php
@@ -7,7 +7,6 @@ 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;
@@ -26,7 +25,7 @@ class Activity extends Controller {
$portable_id = EMPTY_STR;
- $item_normal_extra = sprintf(" and not verb in ('%s', '%s') ",
+ $item_normal_extra = sprintf(" and not verb in ('Follow', 'Ignore', '%s', '%s') ",
dbesc(ACTIVITY_FOLLOW),
dbesc(ACTIVITY_UNFOLLOW)
);
@@ -155,22 +154,7 @@ class Activity extends Controller {
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();
+ as_return_and_die($i, $chan);
}
@@ -202,7 +186,7 @@ class Activity extends Controller {
}
}
- $item_normal_extra = sprintf(" and not verb in ('%s', '%s') ",
+ $item_normal_extra = sprintf(" and not verb in ('Follow', 'Ignore', '%s', '%s') ",
dbesc(ACTIVITY_FOLLOW),
dbesc(ACTIVITY_UNFOLLOW)
);
@@ -260,25 +244,7 @@ class Activity extends Controller {
$channel = channelx_by_n($items[0]['uid']);
- $x = array_merge( ['@context' => [
- ACTIVITYSTREAMS_JSONLD_REV,
- 'https://w3id.org/security/v1',
- z_root() . ZOT_APSCHEMA_REV
- ]], ZlibActivity::encode_activity($items[0],true));
-
- $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();
-
+ as_return_and_die(ZlibActivity::encode_activity($items[0]), $channel);
}
goaway(z_root() . '/item/' . argv(1));
diff --git a/Zotlabs/Module/Admin/Site.php b/Zotlabs/Module/Admin/Site.php
index b24821b28..dae5e7c77 100644
--- a/Zotlabs/Module/Admin/Site.php
+++ b/Zotlabs/Module/Admin/Site.php
@@ -68,7 +68,7 @@ class Site {
$login_on_homepage = ((x($_POST,'login_on_homepage')) ? True : False);
$enable_context_help = ((x($_POST,'enable_context_help')) ? True : False);
$no_community_page = !((x($_POST,'no_community_page')) ? True : False);
- $default_expire_days = ((array_key_exists('default_expire_days',$_POST)) ? intval($_POST['default_expire_days']) : 0);
+ $default_expire_days = ((array_key_exists('default_expire_days',$_POST)) ? intval($_POST['default_expire_days']) : 30);
$active_expire_days = ((array_key_exists('active_expire_days',$_POST)) ? intval($_POST['active_expire_days']) : 7);
$reply_address = ((array_key_exists('reply_address',$_POST) && trim($_POST['reply_address'])) ? trim($_POST['reply_address']) : 'noreply@' . \App::get_hostname());
@@ -227,20 +227,6 @@ class Site {
*/
function get() {
- /* Installed langs */
- $lang_choices = array();
- $langs = glob('view/*/hstrings.php');
-
- if(is_array($langs) && count($langs)) {
- if(! in_array('view/en/hstrings.php',$langs))
- $langs[] = 'view/en/';
- asort($langs);
- foreach($langs as $l) {
- $t = explode("/",$l);
- $lang_choices[$t[1]] = $t[1];
- }
- }
-
/* Installed themes */
$theme_choices_mobile["---"] = t("Default");
$theme_choices = array();
@@ -425,7 +411,6 @@ class Site {
'$banner' => array('banner', t("Banner/Logo"), $banner, t('Unfiltered HTML/CSS/JS is allowed')),
'$admininfo' => array('admininfo', t("Administrator Information"), $admininfo, t("Contact information for site administrators. Displayed on siteinfo page. BBCode can be used here")),
'$siteinfo' => array('siteinfo', t('Site Information'), get_config('system','siteinfo'), t("Publicly visible description of this site. Displayed on siteinfo page. BBCode can be used here")),
- //'$language' => array('language', t("System language"), get_config('system','language'), "", $lang_choices),
'$theme' => array('theme', t("System theme"), get_config('system','theme'), t("Default system theme - may be over-ridden by user profiles - <a href='#' id='cnftheme'>change theme settings</a>"), $theme_choices),
// '$theme_mobile' => array('theme_mobile', t("Mobile system theme"), get_config('system','mobile_theme'), t("Theme for mobile devices"), $theme_choices_mobile),
// '$site_channel' => array('site_channel', t("Channel to use for this website's static pages"), get_config('system','site_channel'), t("Site Channel")),
@@ -536,7 +521,7 @@ class Site {
'$poll_interval' => array('poll_interval', t("Poll interval"), (x(get_config('system','poll_interval'))?get_config('system','poll_interval'):2), t("Delay background polling processes by this many seconds to reduce system load. If 0, use delivery interval.")),
'$imagick_path' => array('imagick_path', t("Path to ImageMagick convert program"), get_config('system','imagick_convert_path'), t("If set, use this program to generate photo thumbnails for huge images ( > 4000 pixels in either dimension), otherwise memory exhaustion may occur. Example: /usr/bin/convert")),
'$maxloadavg' => array('maxloadavg', t("Maximum Load Average"), ((intval(get_config('system','maxloadavg')) > 0)?get_config('system','maxloadavg'):50), t("Maximum system load before delivery and poll processes are deferred - default 50.")),
- '$default_expire_days' => array('default_expire_days', t('Expiration period in days for imported (grid/network) content'), intval(get_config('system','default_expire_days')), t('0 for no expiration of imported content')),
+ '$default_expire_days' => array('default_expire_days', t('Expiration period in days for imported (grid/network) content'), intval(get_config('system','default_expire_days', 30)), t('0 for no expiration of imported content')),
'$active_expire_days' => array('active_expire_days', t('Do not expire any posts which have comments less than this many days ago'), intval(get_config('system','active_expire_days',7)), ''),
'$sellpage' => array('site_sellpage', t('Public servers: Optional landing (marketing) webpage for new registrants'), get_config('system','sellpage',''), sprintf( t('Create this page first. Default is %s/register'),z_root())),
'$first_page' => array('first_page', t('Page to display after creating a new channel'), get_config('system','workflow_channel_next','profiles'), t('Default: profiles')),
diff --git a/Zotlabs/Module/Api.php b/Zotlabs/Module/Api.php
index aa0fca54d..5b835ac51 100644
--- a/Zotlabs/Module/Api.php
+++ b/Zotlabs/Module/Api.php
@@ -24,42 +24,42 @@ class Api extends \Zotlabs\Web\Controller {
notice( t('Permission denied.') . EOL);
return;
}
-
+
}
-
+
function get() {
if(\App::$cmd === 'api/oauth/authorize'){
-
- /*
+
+ /*
* api/oauth/authorize interact with the user. return a standard page
*/
-
+
\App::$page['template'] = 'minimal';
-
+
// get consumer/client from request token
try {
$request = \OAuth1Request::from_request();
}
catch(\Exception $e) {
logger('OAuth exception: ' . print_r($e,true));
- // echo "<pre>"; var_dump($e);
+ // echo "<pre>"; var_dump($e);
killme();
}
-
-
+
+
if(x($_POST,'oauth_yes')){
-
+
$app = $this->oauth_get_client($request);
- if (is_null($app))
+ if (is_null($app))
return "Invalid request. Unknown token.";
$consumer = new \OAuth1Consumer($app['client_id'], $app['pw'], $app['redirect_uri']);
-
+
$verifier = md5($app['secret'] . local_channel());
set_config('oauth', $verifier, local_channel());
-
-
+
+
if($consumer->callback_url != null) {
$params = $request->get_parameters();
$glue = '?';
@@ -68,28 +68,28 @@ class Api extends \Zotlabs\Web\Controller {
goaway($consumer->callback_url . $glue . "oauth_token=" . \OAuth1Util::urlencode_rfc3986($params['oauth_token']) . "&oauth_verifier=" . \OAuth1Util::urlencode_rfc3986($verifier));
killme();
}
-
+
$tpl = get_markup_template("oauth_authorize_done.tpl");
$o = replace_macros($tpl, array(
'$title' => t('Authorize application connection'),
'$info' => t('Return to your app and insert this Security Code:'),
'$code' => $verifier,
));
-
+
return $o;
}
-
-
+
+
if(! local_channel()) {
//TODO: we need login form to redirect to this page
notice( t('Please login to continue.') . EOL );
return login(false,'api-login',$request->get_parameters());
}
-
+
$app = $this->oauth_get_client($request);
if (is_null($app))
return "Invalid request. Unknown token.";
-
+
$tpl = get_markup_template('oauth_authorize.tpl');
$o = replace_macros($tpl, array(
'$title' => t('Authorize application connection'),
@@ -98,12 +98,12 @@ class Api extends \Zotlabs\Web\Controller {
'$yes' => t('Yes'),
'$no' => t('No'),
));
-
+
//echo "<pre>"; var_dump($app); killme();
-
+
return $o;
}
-
+
echo api_call();
killme();
}
@@ -112,8 +112,8 @@ class Api extends \Zotlabs\Web\Controller {
$params = $request->get_parameters();
$token = $params['oauth_token'];
-
- $r = q("SELECT clients.* FROM clients, tokens WHERE clients.client_id = tokens.client_id
+
+ $r = q("SELECT clients.* FROM clients, tokens WHERE clients.client_id = tokens.client_id
AND tokens.id = '%s' AND tokens.auth_scope = 'request' ",
dbesc($token)
);
@@ -121,7 +121,7 @@ class Api extends \Zotlabs\Web\Controller {
return $r[0];
return null;
-
+
}
-
+
}
diff --git a/Zotlabs/Module/Apschema.php b/Zotlabs/Module/Apschema.php
index e8d45c522..2ec11086a 100644
--- a/Zotlabs/Module/Apschema.php
+++ b/Zotlabs/Module/Apschema.php
@@ -2,68 +2,13 @@
namespace Zotlabs\Module;
+use Zotlabs\Web\Controller;
+use Zotlabs\Lib\Activity;
-class Apschema extends \Zotlabs\Web\Controller {
-
+class Apschema extends Controller {
function init() {
-
- $base = z_root();
-
- $arr = [
- '@context' => [
- 'zot' => z_root() . '/apschema#',
- 'id' => '@id',
- 'type' => '@type',
- 'commentPolicy' => 'zot:commentPolicy',
- 'meData' => 'zot:meData',
- 'meDataType' => 'zot:meDataType',
- 'meEncoding' => 'zot:meEncoding',
- 'meAlgorithm' => 'zot:meAlgorithm',
- 'meCreator' => 'zot:meCreator',
- 'meSignatureValue' => 'zot:meSignatureValue',
- 'locationAddress' => 'zot:locationAddress',
- 'locationPrimary' => 'zot:locationPrimary',
- 'locationDeleted' => 'zot:locationDeleted',
- 'nomadicLocation' => 'zot:nomadicLocation',
- 'nomadicHubs' => 'zot:nomadicHubs',
- 'emojiReaction' => 'zot:emojiReaction',
- 'expires' => 'zot:expires',
- 'directMessage' => 'zot:directMessage',
- 'schema' => 'http://schema.org#',
- 'PropertyValue' => 'schema:PropertyValue',
- 'value' => 'schema:value',
-
- 'manuallyApprovesFollowers' => 'as:manuallyApprovesFollowers',
-
-
- 'magicEnv' => [
- '@id' => 'zot:magicEnv',
- '@type' => '@id'
- ],
-
- 'nomadicLocations' => [
- '@id' => 'zot:nomadicLocations',
- '@type' => '@id'
- ],
-
- 'ostatus' => 'http://ostatus.org#',
- 'conversation' => 'ostatus:conversation',
-
- 'diaspora' => 'https://diasporafoundation.org/ns/',
- 'guid' => 'diaspora:guid',
-
- 'Hashtag' => 'as:Hashtag'
-
- ]
- ];
-
header('Content-Type: application/ld+json');
- echo json_encode($arr,JSON_UNESCAPED_SLASHES);
+ echo json_encode(Activity::ap_context(), JSON_UNESCAPED_SLASHES);
killme();
-
}
-
-
-
-
}
diff --git a/Zotlabs/Module/Channel.php b/Zotlabs/Module/Channel.php
index e8c3316e9..4e6811a10 100644
--- a/Zotlabs/Module/Channel.php
+++ b/Zotlabs/Module/Channel.php
@@ -101,16 +101,23 @@ class Channel extends Controller {
App::$meta->set('robots', 'noindex, noarchive');
}
+ $identifier = 'uuid';
+ $mid = $_REQUEST['mid'] ?? '';
+
+ if (str_starts_with($mid, 'b64.')) {
+ $mid = unpack_link_id($mid);
+ $identifier = 'mid';
+ }
+
+ if ($mid === false) {
+ http_status_exit(404, 'Not found');
+ }
+
if (ActivityStreams::is_as_request($channel)) {
// Somebody may attempt an ActivityStreams fetch on one of our message permalinks
// Make it do the right thing.
- $mid = ((x($_REQUEST, 'mid')) ? unpack_link_id($_REQUEST['mid']) : '');
- if ($mid === false) {
- http_status_exit(404, 'Not found');
- }
-
if ($mid) {
$obj = null;
if (strpos($mid, z_root() . '/item/') === 0) {
@@ -127,6 +134,7 @@ class Channel extends Controller {
$obj->init();
}
}
+
as_return_and_die(Activity::encode_person($channel, true), $channel);
}
@@ -155,15 +163,9 @@ class Channel extends Controller {
profile_load($which, $profile);
// Add Opengraph markup
- $mid = ((x($_REQUEST, 'mid')) ? unpack_link_id($_REQUEST['mid']) : '');
-
- if ($mid === false) {
- notice(t('Malformed message id.') . EOL);
- return;
- }
if ($mid) {
- $r = q("SELECT * FROM item WHERE mid = '%s' AND uid = %d AND item_private = 0 LIMIT 1",
+ $r = q("SELECT * FROM item WHERE $identifier = '%s' AND uid = %d AND item_private = 0 LIMIT 1",
dbesc($mid),
intval($channel['channel_id'])
);
@@ -178,7 +180,16 @@ class Channel extends Controller {
$category = $datequery = $datequery2 = '';
- $mid = ((x($_REQUEST, 'mid')) ? unpack_link_id($_REQUEST['mid']) : '');
+ $mid = $_REQUEST['mid'] ?? '';
+ $identifier = 'uuid';
+ $encoded_mid = null;
+
+ if (str_starts_with($mid, 'b64.')) {
+ $encoded_mid = $mid;
+ $mid = unpack_link_id($mid);
+ $identifier = 'mid';
+ }
+
if ($mid === false) {
notice(t('Malformed message id.') . EOL);
return;
@@ -322,7 +333,7 @@ class Channel extends Controller {
if (($update) && (!$load)) {
if ($mid) {
- $r = q("SELECT parent AS item_id from item where mid = '%s' and uid = %d $item_normal_update
+ $r = q("SELECT parent AS item_id, uuid from item where $identifier = '%s' and uid = %d $item_normal_update
AND item_wall = 1 $simple_update $sql_extra limit 1",
dbesc($mid),
intval(App::$profile['profile_uid'])
@@ -370,7 +381,7 @@ class Channel extends Controller {
if ($noscript_content || $load) {
if ($mid) {
- $r = q("SELECT parent AS item_id from item where mid = '%s' and uid = %d $item_normal
+ $r = q("SELECT parent AS item_id, uuid from item where $identifier = '%s' and uid = %d $item_normal
AND item_wall = 1 $sql_extra limit 1",
dbesc($mid),
intval(App::$profile['profile_uid'])
@@ -396,7 +407,6 @@ class Channel extends Controller {
}
}
if ($r) {
-
$parents_str = ids_to_querystr($r, 'item_id');
$r = q("SELECT item.*, item.id AS item_id
@@ -427,12 +437,8 @@ class Channel extends Controller {
$mode = (($search) ? 'search' : 'channel');
- if ((!$update) && (!$load)) {
-
-
- //if we got a decoded hash we must encode it again before handing to javascript
- $mid = gen_link_id($mid);
+ if ((!$update) && (!$load)) {
// This is ugly, but we can't pass the profile_uid through the session to the ajax updater,
// because browser prefetching might change it on us. We have to deliver it with the page.
@@ -470,7 +476,7 @@ class Channel extends Controller {
'$file' => '',
'$cats' => (($category) ? urlencode($category) : ''),
'$tags' => (($hashtags) ? urlencode($hashtags) : ''),
- '$mid' => (($mid) ? urlencode($mid) : ''),
+ '$mid' => $encoded_mid ?? $mid,
'$verb' => '',
'$net' => '',
'$dend' => $datequery,
diff --git a/Zotlabs/Module/Cloud.php b/Zotlabs/Module/Cloud.php
index 05109247a..527b06b3a 100644
--- a/Zotlabs/Module/Cloud.php
+++ b/Zotlabs/Module/Cloud.php
@@ -54,9 +54,9 @@ class Cloud extends Controller {
if (local_channel()) {
$channel = \App::get_channel();
$auth->setCurrentUser($channel['channel_address']);
+ $auth->channel_account_id = $channel['channel_account_id'];
$auth->channel_id = $channel['channel_id'];
$auth->channel_hash = $channel['channel_hash'];
- $auth->channel_account_id = $channel['channel_account_id'];
if($channel['channel_timezone'])
$auth->setTimezone($channel['channel_timezone']);
}
diff --git a/Zotlabs/Module/Contactedit.php b/Zotlabs/Module/Contactedit.php
index 58c3380a1..3527e9380 100644
--- a/Zotlabs/Module/Contactedit.php
+++ b/Zotlabs/Module/Contactedit.php
@@ -177,22 +177,8 @@ class Contactedit extends Controller {
intval($channel['channel_id'])
);
if (($pr) && (!intval($contact['abook_hidden'])) && (intval(get_pconfig($channel['channel_id'], 'system', 'post_newfriend')))) {
- $xarr = [];
-
- $xarr['item_wall'] = 1;
- $xarr['item_origin'] = 1;
- $xarr['item_thread_top'] = 1;
- $xarr['owner_xchan'] = $xarr['author_xchan'] = $channel['channel_hash'];
- $xarr['allow_cid'] = $channel['channel_allow_cid'];
- $xarr['allow_gid'] = $channel['channel_allow_gid'];
- $xarr['deny_cid'] = $channel['channel_deny_cid'];
- $xarr['deny_gid'] = $channel['channel_deny_gid'];
- $xarr['item_private'] = (($xarr['allow_cid'] || $xarr['allow_gid'] || $xarr['deny_cid'] || $xarr['deny_gid']) ? 1 : 0);
-
$xarr['body'] = '[zrl=' . $channel['xchan_url'] . ']' . $channel['xchan_name'] . '[/zrl]' . ' ' . t('is now connected to') . ' ' . '[zrl=' . $contact['xchan_url'] . ']' . $contact['xchan_name'] . '[/zrl]';
-
- $xarr['body'] .= "\n\n\n" . '[zrl=' . $contact['xchan_url'] . '][zmg=80x80]' . $contact['xchan_photo_m'] . '[/zmg][/zrl]';
-
+ $xarr['body'] .= "\n\n\n" . '[zrl=' . $contact['xchan_url'] . '][zmg=' . $contact['xchan_photo_m'] . ']' . $contact['xchan_name'] . '[/zmg][/zrl]';
post_activity_item($xarr);
}
@@ -494,28 +480,32 @@ class Contactedit extends Controller {
'message' => ''
];
- if ($cmd === 'resetphoto') {
- q("update xchan set xchan_photo_date = '2001-01-01 00:00:00' where xchan_hash = '%s'",
+ if ($cmd === 'refresh') {
+ q("update xchan set xchan_photo_date = '0001-01-01 00:00:00', xchan_name_date = '0001-01-01 00:00:00' where xchan_hash = '%s'",
dbesc($contact['xchan_hash'])
);
- $cmd = 'refresh';
- }
- if ($cmd === 'refresh') {
if ($contact['xchan_network'] === 'zot6') {
if (Libzot::refresh($contact, App::get_channel())) {
$ret['success'] = true;
$ret['message'] = t('Refresh succeeded');
}
else {
- $ret['message'] = t('Refresh failed - channel is currently unavailable');
+ $ret['message'] = t('Refresh failed');
}
}
else {
// if you are on a different network we'll force a refresh of the connection basic info
- Master::Summon(['Notifier', 'permission_update', $contact['abook_id']]);
- $ret['success'] = true;
- $ret['message'] = t('Refresh succeeded');
+ $hookinfo = [
+ 'contact' => $contact,
+ 'success' => false,
+ 'message' => ''
+ ];
+
+ call_hooks('actor_refetch', $hookinfo);
+
+ $ret['success'] = $hookinfo['success'];
+ $ret['message'] = $hookinfo['message'];
}
return $ret;
@@ -625,16 +615,10 @@ class Contactedit extends Controller {
return [
'refresh' => [
- 'label' => t('Refresh Permissions'),
- 'title' => t('Fetch updated permissions'),
+ 'label' => t('Refresh'),
+ 'title' => t('Refetch contact info'),
],
- 'rephoto' => [
- 'label' => t('Refresh Photo'),
- 'title' => t('Fetch updated photo'),
- ],
-
-
'block' => [
'label' => (intval($contact['abook_blocked']) ? t('Unblock') : t('Block')),
'sel' => (intval($contact['abook_blocked']) ? 'active' : ''),
diff --git a/Zotlabs/Module/Conversation.php b/Zotlabs/Module/Conversation.php
index 86ce66caa..aa8349f55 100644
--- a/Zotlabs/Module/Conversation.php
+++ b/Zotlabs/Module/Conversation.php
@@ -25,7 +25,7 @@ class Conversation extends Controller {
$portable_id = EMPTY_STR;
- $item_normal_extra = sprintf(" and not verb in ('%s', '%s') ",
+ $item_normal_extra = sprintf(" and not verb in ('Follow', 'Ignore', '%s', '%s') ",
dbesc(ACTIVITY_FOLLOW),
dbesc(ACTIVITY_UNFOLLOW)
);
diff --git a/Zotlabs/Module/Cover_photo.php b/Zotlabs/Module/Cover_photo.php
index 1ecbfce3e..1c1240e29 100644
--- a/Zotlabs/Module/Cover_photo.php
+++ b/Zotlabs/Module/Cover_photo.php
@@ -93,8 +93,6 @@ class Cover_photo extends \Zotlabs\Web\Controller {
$image_id = substr($image_id,0,-2);
}
-
-
$srcX = intval($_POST['xstart']);
$srcY = intval($_POST['ystart']);
$srcW = intval($_POST['xfinal']) - $srcX;
@@ -228,7 +226,7 @@ class Cover_photo extends \Zotlabs\Web\Controller {
return;
}
- $this->send_cover_photo_activity($channel,$base_image,$profile);
+ profile_activity([t('Cover Photo')], $base_image['resource_id']);
$sync = attach_export_data($channel,$base_image['resource_id']);
if($sync)
@@ -245,13 +243,12 @@ class Cover_photo extends \Zotlabs\Web\Controller {
}
-
$hash = photo_new_resource();
$smallest = 0;
require_once('include/attach.php');
- $res = attach_store(\App::get_channel(), get_observer_hash(), '', array('album' => t('Cover Photos'), 'hash' => $hash, 'nosync' => true));
+ $res = attach_store(\App::get_channel(), get_observer_hash(), '', ['album' => t('Cover Photos'), 'hash' => $hash, 'nosync' => true, 'source' => 'photos']);
logger('attach_store: ' . print_r($res,true));
@@ -287,45 +284,6 @@ class Cover_photo extends \Zotlabs\Web\Controller {
}
- function send_cover_photo_activity($channel,$photo,$profile) {
-
- $arr = array();
- $arr['item_thread_top'] = 1;
- $arr['item_origin'] = 1;
- $arr['item_wall'] = 1;
-
- if($profile && stripos($profile['gender'],t('female')) !== false)
- $t = t('%1$s updated her %2$s');
- elseif($profile && stripos($profile['gender'],t('male')) !== false)
- $t = t('%1$s updated his %2$s');
- else
- $t = t('%1$s updated their %2$s');
-
- $ptext = '[zrl=' . z_root() . '/photos/' . $channel['channel_address'] . '/image/' . $photo['resource_id'] . ']' . t('cover photo') . '[/zrl]';
-
- $ltext = '[zrl=' . z_root() . '/profile/' . $channel['channel_address'] . ']' . '[zmg]' . z_root() . '/photo/' . $photo['resource_id'] . '-8[/zmg][/zrl]';
-
- $arr['body'] = sprintf($t,$channel['channel_name'],$ptext) . "\n\n" . $ltext;
-
- $acl = new \Zotlabs\Access\AccessList($channel);
- $x = $acl->get();
- $arr['allow_cid'] = $x['allow_cid'];
-
- $arr['allow_gid'] = $x['allow_gid'];
- $arr['deny_cid'] = $x['deny_cid'];
- $arr['deny_gid'] = $x['deny_gid'];
-
- $arr['uid'] = $channel['channel_id'];
- $arr['aid'] = $channel['channel_account_id'];
-
- $arr['owner_xchan'] = $channel['channel_hash'];
- $arr['author_xchan'] = $channel['channel_hash'];
-
- post_activity_item($arr);
-
-
- }
-
/**
* @brief Generate content of profile-photo view
@@ -334,7 +292,6 @@ class Cover_photo extends \Zotlabs\Web\Controller {
*
*/
-
function get() {
if(! local_channel()) {
diff --git a/Zotlabs/Module/Display.php b/Zotlabs/Module/Display.php
index 9e46d7620..f08aedc95 100644
--- a/Zotlabs/Module/Display.php
+++ b/Zotlabs/Module/Display.php
@@ -38,7 +38,14 @@ class Display extends Controller {
$item_hash = $_REQUEST['mid'];
}
- $item_hash = unpack_link_id($item_hash);
+ $identifier = 'uuid';
+ $encoded_item_hash = null;
+
+ if (str_starts_with($item_hash, 'b64.')) {
+ $encoded_item_hash = $item_hash;
+ $item_hash = unpack_link_id($item_hash);
+ $identifier = 'mid';
+ }
if ($item_hash === false) {
App::$error = 400;
@@ -104,7 +111,7 @@ class Display extends Controller {
$target_item = null;
- $r = q("select id, uid, mid, parent, parent_mid, thr_parent, verb, item_type, item_deleted, author_xchan, item_blocked from item where mid = '%s' limit 1",
+ $r = q("select id, uid, mid, parent, parent_mid, thr_parent, verb, item_type, item_deleted, author_xchan, item_blocked from item where $identifier = '%s' limit 1",
dbesc($item_hash)
);
@@ -152,18 +159,11 @@ class Display extends Controller {
call_hooks('item_custom_display', $target_item);
$simple_update = '';
- if($update && $_SESSION['loadtime'])
+ if($update && isset($_SESSION['loadtime']))
$simple_update = " AND (( item_unseen = 1 AND item.changed > '" . datetime_convert('UTC','UTC',$_SESSION['loadtime']) . "' ) OR item.changed > '" . datetime_convert('UTC','UTC',$_SESSION['loadtime']) . "' ) ";
if((! $update) && (! $load)) {
- // if the target item is not a post (eg a like) we want to address its thread parent
-
- //$mid = ((($target_item['verb'] == ACTIVITY_LIKE) || ($target_item['verb'] == ACTIVITY_DISLIKE)) ? $target_item['thr_parent'] : $target_item['mid']);
-
- // if we got a decoded hash we must encode it again before handing to javascript
- $mid = gen_link_id($target_item['mid']);
-
$o .= '<div id="live-display"></div>' . "\r\n";
$o .= "<script> var profile_uid = " . ((intval(local_channel())) ? local_channel() : (-1))
. "; var netargs = '?f='; var profile_page = " . App::$pager['page'] . "; </script>\r\n";
@@ -196,7 +196,7 @@ class Display extends Controller {
'$dbegin' => '',
'$verb' => '',
'$net' => '',
- '$mid' => (($mid) ? urlencode($mid) : '')
+ '$mid' => $encoded_item_hash ?? $item_hash
));
head_add_link([
diff --git a/Zotlabs/Module/Dreport.php b/Zotlabs/Module/Dreport.php
index f5ad80eef..5db607545 100644
--- a/Zotlabs/Module/Dreport.php
+++ b/Zotlabs/Module/Dreport.php
@@ -13,11 +13,10 @@ class Dreport extends \Zotlabs\Web\Controller {
$table = 'item';
$channel = \App::get_channel();
- $mid = ((argc() > 1) ? unpack_link_id(argv(1)) : '');
+ $mid = $_REQUEST['mid'] ?? '';
- if($mid === 'push') {
+ if(argv(1) === 'push') {
$table = 'push';
- $mid = ((argc() > 2) ? unpack_link_id(argv(2)) : '');
if($mid) {
$i = q("select id from item where mid = '%s' and uid = %d and ( author_xchan = '%s' or ( owner_xchan = '%s' and item_wall = 1 )) ",
@@ -31,7 +30,7 @@ class Dreport extends \Zotlabs\Web\Controller {
}
}
sleep(3);
- goaway(z_root() . '/dreport/' . gen_link_id($mid));
+ goaway(z_root() . '/dreport?mid=' . $mid);
}
if(! $mid) {
@@ -114,7 +113,7 @@ class Dreport extends \Zotlabs\Web\Controller {
}
}
- usort($r,'self::dreport_gravity_sort');
+ usort($r, [self::class, 'dreport_gravity_sort']);
$entries = array();
foreach($r as $rr) {
diff --git a/Zotlabs/Module/Emoji.php b/Zotlabs/Module/Emoji.php
new file mode 100644
index 000000000..a1459d179
--- /dev/null
+++ b/Zotlabs/Module/Emoji.php
@@ -0,0 +1,57 @@
+<?php
+namespace Zotlabs\Module;
+
+use Zotlabs\Web\Controller;
+use Zotlabs\Daemon\Master;
+use Zotlabs\Lib\ActivityStreams;
+use App;
+
+
+class Emoji extends Controller {
+
+ function init() {
+
+ $shortname = argv(1);
+
+ if (!$shortname) {
+ killme();
+ }
+
+ $emojis = get_emojis();
+
+ if (!isset($emojis[$shortname])) {
+ killme();
+ }
+
+ $emoji = $emojis[$shortname];
+
+ if (!file_exists($emoji['filepath'])) {
+ killme();
+ }
+
+ $image = getimagesize($emoji['filepath']);
+
+ if(ActivityStreams::is_as_request()) {
+ $last_modified = date(ATOM_TIME, filemtime($emoji['filepath']));
+
+ $obj = [
+ 'id' => z_root() . '/emoji/' . $shortname,
+ 'type' => 'Emoji',
+ 'name' => $emoji['shortname'],
+ 'updated' => $last_modified,
+ 'icon' => [
+ 'type' => 'Image',
+ 'mediaType' => $image['mime'],
+ 'url' => z_root() . '/' . $emoji['filepath']
+ ]
+ ];
+
+ as_return_and_die($obj);
+ }
+
+ header('Content-Type: ' . $image['mime']);
+ echo file_get_contents($emoji['filepath']);
+ killme();
+ }
+
+}
diff --git a/Zotlabs/Module/Event.php b/Zotlabs/Module/Event.php
index 22a1341cc..767a8f494 100644
--- a/Zotlabs/Module/Event.php
+++ b/Zotlabs/Module/Event.php
@@ -4,7 +4,6 @@ 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 {
@@ -17,7 +16,7 @@ class Event extends Controller {
if(! $item_id)
return;
- $item_normal = " and item.item_hidden = 0 and item.item_type = 0 and item.item_unpublished = 0
+ $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);
@@ -49,28 +48,9 @@ class Event extends Controller {
$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();
-
+ as_return_and_die($obj, $channel);
}
}
-} \ No newline at end of file
+}
diff --git a/Zotlabs/Module/Follow.php b/Zotlabs/Module/Follow.php
index f8bfc11f3..55ff507c8 100644
--- a/Zotlabs/Module/Follow.php
+++ b/Zotlabs/Module/Follow.php
@@ -7,7 +7,6 @@ use Zotlabs\Lib\Libsync;
use Zotlabs\Lib\ActivityStreams;
use Zotlabs\Lib\Activity;
use Zotlabs\Web\HTTPSig;
-use Zotlabs\Lib\LDSignatures;
use Zotlabs\Lib\Connect;
use Zotlabs\Daemon\Master;
@@ -39,30 +38,14 @@ class Follow extends Controller {
http_status_exit(404, 'Not found');
}
- $x = array_merge(['@context' => [
- ACTIVITYSTREAMS_JSONLD_REV,
- 'https://w3id.org/security/v1',
- z_root() . ZOT_APSCHEMA_REV
- ]],
- [
+ $obj = [
'id' => z_root() . '/follow/' . $r[0]['abook_id'],
'type' => 'Follow',
'actor' => $actor,
'object' => $r[0]['xchan_url']
- ]);
-
- $headers = [];
- $headers['Content-Type'] = 'application/ld+json; profile="https://www.w3.org/ns/activitystreams"' ;
- $x['signature'] = LDSignatures::sign($x,$chan);
- $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,$chan['channel_prvkey'],channel_url($chan));
- HTTPSig::set_headers($h);
- echo $ret;
- killme();
+ ];
+ as_return_and_die($obj, $chan);
}
if (! local_channel()) {
diff --git a/Zotlabs/Module/Hq.php b/Zotlabs/Module/Hq.php
index 5c3ae9273..ae6a016fc 100644
--- a/Zotlabs/Module/Hq.php
+++ b/Zotlabs/Module/Hq.php
@@ -30,16 +30,20 @@ class Hq extends \Zotlabs\Web\Controller {
$item_hash = '';
if(argc() > 1 && argv(1) !== 'load') {
- $item_hash = unpack_link_id(argv(1));
+ $item_hash = argv(1);
}
if(isset($_REQUEST['mid'])) {
- $item_hash = unpack_link_id($_REQUEST['mid']);
+ $item_hash = $_REQUEST['mid'];
}
- if($item_hash === false) {
- notice(t('Malformed message id.') . EOL);
- return;
+ $identifier = 'uuid';
+ $encoded_item_hash = null;
+
+ if (str_starts_with($item_hash, 'b64.')) {
+ $encoded_item_hash = $item_hash;
+ $item_hash = unpack_link_id($item_hash);
+ $identifier = 'mid';
}
$item_normal = item_normal();
@@ -54,7 +58,7 @@ class Hq extends \Zotlabs\Web\Controller {
// select the target item with a bias to our own item
$sql_order = ((local_channel() > $sys['channel_id']) ? 'DESC' : 'ASC');
- $r = q("select id, uid, mid, parent_mid, thr_parent, verb, item_type, item_deleted, item_blocked from item where uid in (%d, %d) and mid = '%s' order by uid $sql_order limit 2",
+ $r = q("select id, uid, mid, parent_mid, thr_parent, verb, item_type, item_deleted, item_blocked from item where uid in (%d, %d) and $identifier = '%s' order by uid $sql_order limit 2",
intval(local_channel()),
intval($sys['channel_id']),
dbesc($item_hash)
@@ -118,7 +122,7 @@ class Hq extends \Zotlabs\Web\Controller {
//$mid = ((($target_item['verb'] == ACTIVITY_LIKE) || ($target_item['verb'] == ACTIVITY_DISLIKE)) ? $target_item['thr_parent'] : $target_item['mid']);
// if we got a decoded hash we must encode it again before handing to javascript
- $mid = gen_link_id($target_item['mid']);
+ // $mid = gen_link_id($target_item['mid']);
}
else {
$mid = '';
@@ -156,7 +160,7 @@ class Hq extends \Zotlabs\Web\Controller {
'$dbegin' => '',
'$verb' => '',
'$net' => '',
- '$mid' => (($mid) ? urlencode($mid) : '')
+ '$mid' => $encoded_item_hash ?? $item_hash
]);
}
diff --git a/Zotlabs/Module/Item.php b/Zotlabs/Module/Item.php
index 25ccb0cbf..b158ed4e0 100644
--- a/Zotlabs/Module/Item.php
+++ b/Zotlabs/Module/Item.php
@@ -52,7 +52,7 @@ class Item extends Controller {
$portable_id = EMPTY_STR;
- $item_normal_extra = sprintf(" and not verb in ('%s', '%s') ",
+ $item_normal_extra = sprintf(" and not verb in ('Follow', 'Ignore', '%s', '%s') ",
dbesc(ACTIVITY_FOLLOW),
dbesc(ACTIVITY_UNFOLLOW)
);
@@ -168,7 +168,7 @@ class Item extends Controller {
$portable_id = EMPTY_STR;
- $item_normal_extra = sprintf(" and not verb in ('%s', '%s') ",
+ $item_normal_extra = sprintf(" and not verb in ('Follow', 'Ignore', '%s', '%s') ",
dbesc(ACTIVITY_FOLLOW),
dbesc(ACTIVITY_UNFOLLOW)
);
@@ -275,7 +275,7 @@ class Item extends Controller {
if (argc() > 1 && argv(1) !== 'drop') {
- $x = q("select uid, item_wall, llink, mid from item where mid = '%s' or mid = '%s' or uuid = '%s'",
+ $x = q("select uid, item_wall, llink, mid, uuid from item where mid = '%s' or mid = '%s' or uuid = '%s'",
dbesc(z_root() . '/item/' . argv(1)),
dbesc(z_root() . '/activity/' . argv(1)),
dbesc(argv(1))
@@ -285,7 +285,7 @@ class Item extends Controller {
if (intval($xv['item_wall'])) {
$c = channelx_by_n($xv['uid']);
if ($c) {
- goaway(z_root() . '/channel/' . $c['channel_address'] . '?mid=' . gen_link_id($xv['mid']));
+ goaway(z_root() . '/channel/' . $c['channel_address'] . '?mid=' . $xv['uuid']);
}
}
}
@@ -298,7 +298,6 @@ class Item extends Controller {
function post() {
-
// This will change. Figure out who the observer is and whether or not
// they have permission to post here. Else ignore the post.
@@ -405,7 +404,7 @@ class Item extends Controller {
$pagetitle = ((x($_REQUEST, 'pagetitle')) ? escape_tags($_REQUEST['pagetitle']) : '');
$layout_mid = ((x($_REQUEST, 'layout_mid')) ? escape_tags($_REQUEST['layout_mid']) : '');
$plink = ((x($_REQUEST, 'permalink')) ? escape_tags($_REQUEST['permalink']) : '');
- $obj_type = ((x($_REQUEST, 'obj_type')) ? escape_tags($_REQUEST['obj_type']) : ACTIVITY_OBJ_NOTE);
+ $obj_type = ((x($_REQUEST, 'obj_type')) ? escape_tags($_REQUEST['obj_type']) : 'Article');
// allow API to bulk load a bunch of imported items with sending out a bunch of posts.
$nopush = ((x($_REQUEST, 'nopush')) ? intval($_REQUEST['nopush']) : 0);
@@ -444,9 +443,6 @@ class Item extends Controller {
if (!x($_REQUEST, 'type'))
$_REQUEST['type'] = 'net-comment';
- if ($obj_type == ACTIVITY_OBJ_NOTE)
- $obj_type = ACTIVITY_OBJ_COMMENT;
-
if ($parent) {
$r = q("SELECT * FROM item WHERE id = %d LIMIT 1",
intval($parent)
@@ -679,7 +675,7 @@ class Item extends Controller {
$verb = $orig_post['verb'];
$app = $orig_post['app'];
$title = escape_tags(trim($_REQUEST['title']));
- $summary = trim($_REQUEST['summary']);
+ $summary = escape_tags(trim($_REQUEST['summary']));
$body = trim($_REQUEST['body']);
$item_flags = $orig_post['item_flags'];
$item_origin = $orig_post['item_origin'];
@@ -740,7 +736,7 @@ class Item extends Controller {
$coord = ((isset($_REQUEST['coord'])) ? notags(trim($_REQUEST['coord'])) : '');
$verb = ((isset($_REQUEST['verb'])) ? notags(trim($_REQUEST['verb'])) : '');
$title = ((isset($_REQUEST['title'])) ? escape_tags(trim($_REQUEST['title'])) : '');
- $summary = ((isset($_REQUEST['summary'])) ? trim($_REQUEST['summary']) : '');
+ $summary = ((isset($_REQUEST['summary'])) ? escape_tags(trim($_REQUEST['summary'])) : '');
$body = ((isset($_REQUEST['body'])) ? trim($_REQUEST['body']) : '');
$body .= ((isset($_REQUEST['attachment'])) ? trim($_REQUEST['attachment']) : '');
$postopts = '';
@@ -793,7 +789,6 @@ class Item extends Controller {
&& ($channel['channel_pageflags'] & PAGE_ALLOWCODE)) ? true : false);
if ($preview) {
- $summary = z_input_filter($summary, $mimetype, $execflag);
$body = z_input_filter($body, $mimetype, $execflag);
}
@@ -943,6 +938,30 @@ class Item extends Controller {
}
}
+ if (preg_match_all('/(\:(\w|\+|\-)+\:)(?=|[\!\.\?]|$)/', $body, $match)) {
+ // emoji shortcodes
+ $emojis = get_emojis();
+ foreach ($match[0] as $mtch) {
+ $shortname = trim($mtch, ':');
+
+ if (!isset($emojis[$shortname])) {
+ continue;
+ }
+
+ $emoji = $emojis[$shortname];
+
+ $post_tags[] = [
+ 'uid' => $profile_uid,
+ 'ttype' => TERM_EMOJI,
+ 'otype' => TERM_OBJ_POST,
+ 'term' => trim($mtch),
+ 'url' => z_root() . '/emoji/' . $shortname,
+ 'imgurl' => z_root() . '/' . $emoji['filepath']
+ ];
+ }
+ }
+
+
// BBCODE end alert
}
@@ -963,6 +982,10 @@ class Item extends Controller {
}
}
+
+
+
+
if ($orig_post) {
// preserve original tags
$t = q("select * from term where oid = %d and otype = %d and uid = %d and ttype in ( %d, %d, %d )",
@@ -1009,7 +1032,7 @@ class Item extends Controller {
if (!strlen($verb))
- $verb = ACTIVITY_POST;
+ $verb = 'Create';
$notify_type = (($parent) ? 'comment-new' : 'wall-new');
@@ -1220,18 +1243,6 @@ class Item extends Controller {
$this->add_listeners($datarray);
}
- // We only need edit activities for other federated protocols
- // which do not support edits natively. While this does federate
- // edits, it presents a number of issues locally - such as #757 and #758.
- // The SQL check for an edit activity would not perform that well so to fix these issues
- // requires an additional item flag (perhaps 'item_edit_activity') that we can add to the
- // query for searches and notifications.
-
- // For now we'll just forget about trying to make edits work on network protocols that
- // don't support them.
-
- // item_create_edit_activity($x);
-
if (!$parent) {
$r = q("select * from item where id = %d",
intval($post_id)
@@ -1285,8 +1296,8 @@ class Item extends Controller {
'from_xchan' => $datarray['author_xchan'],
'to_xchan' => $datarray['owner_xchan'],
'item' => $datarray,
- 'link' => z_root() . '/display/' . gen_link_id($datarray['mid']),
- 'verb' => ACTIVITY_POST,
+ 'link' => z_root() . '/display/' . $datarray['uuid'],
+ 'verb' => 'Create',
'otype' => 'item',
'parent' => $parent,
'parent_mid' => $parent_item['mid']
@@ -1303,8 +1314,8 @@ class Item extends Controller {
'from_xchan' => $datarray['author_xchan'],
'to_xchan' => $datarray['owner_xchan'],
'item' => $datarray,
- 'link' => z_root() . '/display/' . gen_link_id($datarray['mid']),
- 'verb' => ACTIVITY_POST,
+ 'link' => z_root() . '/display/' . $datarray['uuid'],
+ 'verb' => 'Create',
'otype' => 'item'
]);
}
@@ -1349,7 +1360,7 @@ class Item extends Controller {
}
$datarray['id'] = $post_id;
- $datarray['llink'] = z_root() . '/display/' . gen_link_id($datarray['mid']);
+ $datarray['llink'] = z_root() . '/display/' . $datarray['uuid'];
call_hooks('post_local_end', $datarray);
@@ -1373,7 +1384,7 @@ class Item extends Controller {
if ($return_path) {
if ($return_path === 'hq') {
- goaway(z_root() . '/hq/' . gen_link_id($datarray['mid']));
+ goaway(z_root() . '/hq/' . $datarray['uuid']);
}
goaway(z_root() . "/" . $return_path);
diff --git a/Zotlabs/Module/Like.php b/Zotlabs/Module/Like.php
index 54daf6471..4460900a8 100644
--- a/Zotlabs/Module/Like.php
+++ b/Zotlabs/Module/Like.php
@@ -19,14 +19,12 @@ class Like extends Controller {
private function reaction_to_activity($reaction) {
$acts = [
- 'like' => ACTIVITY_LIKE,
- 'dislike' => ACTIVITY_DISLIKE,
- 'agree' => ACTIVITY_AGREE,
- 'disagree' => ACTIVITY_DISAGREE,
- 'abstain' => ACTIVITY_ABSTAIN,
- 'attendyes' => ACTIVITY_ATTEND,
- 'attendno' => ACTIVITY_ATTENDNO,
- 'attendmaybe' => ACTIVITY_ATTENDMAYBE
+ 'like' => 'Like',
+ 'dislike' => 'Dislike',
+ 'announce' => ACTIVITY_SHARE,
+ 'attendyes' => 'Accept',
+ 'attendno' => 'Reject',
+ 'attendmaybe' => 'TentativeAccept'
];
// unlike (etc.) reactions are an undo of positive reactions, rather than a negative action.
@@ -71,11 +69,12 @@ class Like extends Controller {
$activities = q("SELECT item.*, item.id AS item_id FROM item
WHERE uid = %d $item_normal
AND thr_parent = '%s'
- AND verb IN ('%s', '%s', '%s', '%s', '%s')",
+ AND verb IN ('%s', '%s', '%s', '%s', '%s', '%s', 'Accept', 'Reject', 'TentativeAccept')",
intval($arr['item']['uid']),
dbesc($arr['item']['mid']),
- dbesc(ACTIVITY_LIKE),
- dbesc(ACTIVITY_DISLIKE),
+ dbesc('Like'),
+ dbesc('Dislike'),
+ dbesc(ACTIVITY_SHARE),
dbesc(ACTIVITY_ATTEND),
dbesc(ACTIVITY_ATTENDNO),
dbesc(ACTIVITY_ATTENDMAYBE)
@@ -133,7 +132,7 @@ class Like extends Controller {
}
$is_rsvp = false;
- if (in_array($activity, [ACTIVITY_ATTEND, ACTIVITY_ATTENDNO, ACTIVITY_ATTENDMAYBE])) {
+ if (in_array($activity, ['Accept', 'Reject', 'TentativeAccept', ACTIVITY_ATTEND, ACTIVITY_ATTENDNO, ACTIVITY_ATTENDMAYBE])) {
$is_rsvp = true;
}
@@ -182,7 +181,7 @@ class Like extends Controller {
}
}
$post_type = t('channel');
- $obj_type = ACTIVITY_OBJ_PROFILE;
+ $obj_type = 'Profile';
$profile = $r[0];
}
@@ -211,8 +210,8 @@ class Like extends Controller {
$public = false;
$post_type = t('thing');
- $obj_type = ACTIVITY_OBJ_PROFILE;
- $tgttype = ACTIVITY_OBJ_THING;
+ $obj_type = 'Profile';
+ $tgttype = 'Page';
$links = array();
$links[] = array('rel' => 'alternate', 'type' => 'text/html',
@@ -220,12 +219,7 @@ class Like extends Controller {
if ($r[0]['imgurl'])
$links[] = array('rel' => 'photo', 'href' => $r[0]['obj_imgurl']);
- $target = json_encode(array(
- 'type' => $tgttype,
- 'title' => $r[0]['obj_term'],
- 'id' => z_root() . '/thing/' . $r[0]['obj_obj'],
- 'link' => $links
- ));
+ $target = Activity::fetch_thing(['id' => $r[0]['obj_obj']]);
$plink = '[zrl=' . z_root() . '/thing/' . $r[0]['obj_obj'] . ']' . $r[0]['obj_term'] . '[/zrl]';
@@ -323,6 +317,8 @@ class Like extends Controller {
// parent, copy that as well.
if ($r) {
+ $obj_type = $r[0]['obj_type'];
+
if ($r[0]['uid'] === $sys_channel['channel_id'] && local_channel()) {
$r = [copy_of_pubitem(App::get_channel(), $r[0]['mid'])];
}
@@ -370,15 +366,11 @@ class Like extends Controller {
$multi_undo = false;
- // event participation and consensus items are essentially radio toggles. If you make a subsequent choice,
+ // event participation items are essentially radio toggles. If you make a subsequent choice,
// we need to eradicate your first choice.
- if ($activity === ACTIVITY_ATTEND || $activity === ACTIVITY_ATTENDNO || $activity === ACTIVITY_ATTENDMAYBE) {
- $verbs = " '" . dbesc(ACTIVITY_ATTEND) . "','" . dbesc(ACTIVITY_ATTENDNO) . "','" . dbesc(ACTIVITY_ATTENDMAYBE) . "' ";
- $multi_undo = 1;
- }
- if ($activity === ACTIVITY_AGREE || $activity === ACTIVITY_DISAGREE || $activity === ACTIVITY_ABSTAIN) {
- $verbs = " '" . dbesc(ACTIVITY_AGREE) . "','" . dbesc(ACTIVITY_DISAGREE) . "','" . dbesc(ACTIVITY_ABSTAIN) . "' ";
+ if (in_array($activity, ['Accept', 'Reject', 'TentativeAccept', ACTIVITY_ATTEND, ACTIVITY_ATTENDNO, ACTIVITY_ATTENDMAYBE])) {
+ $verbs = "'Accept','Reject','TentativeAccept','" . dbesc(ACTIVITY_ATTEND) . "','" . dbesc(ACTIVITY_ATTENDNO) . "','" . dbesc(ACTIVITY_ATTENDMAYBE) . "' ";
$multi_undo = true;
}
@@ -437,7 +429,7 @@ class Like extends Controller {
}
}
- $uuid = item_message_id();
+ $uuid = new_uuid();
$arr = array();
@@ -450,14 +442,20 @@ class Like extends Controller {
$arr['item_wall'] = 1;
}
else {
- $post_type = (($item['resource_type'] === 'photo') ? t('photo') : t('status'));
- if (in_array($item['obj_type'], ['Event', ACTIVITY_OBJ_EVENT]))
- $post_type = t('event');
-
- $obj_type = (($item['resource_type'] === 'photo') ? ACTIVITY_OBJ_PHOTO : ACTIVITY_OBJ_NOTE);
-
- if ($obj_type === ACTIVITY_OBJ_NOTE && (!intval($item['item_thread_top'])))
- $obj_type = ACTIVITY_OBJ_COMMENT;
+ switch ($item['object_type']) {
+ case 'Image':
+ $post_type = t('image');
+ break;
+ case 'Invite':
+ $post_type = t('event');
+ break;
+ case 'Profile':
+ $post_type = t('profile');
+ break;
+ default:
+ $post_type = t('status');
+ break;
+ }
$object = json_encode(Activity::fetch_item(['id' => $item['mid']]));
@@ -485,12 +483,6 @@ class Like extends Controller {
$bodyverb = t('%1$s likes %2$s\'s %3$s');
if ($verb === 'dislike')
$bodyverb = t('%1$s doesn\'t like %2$s\'s %3$s');
- if ($verb === 'agree')
- $bodyverb = t('%1$s agrees with %2$s\'s %3$s');
- if ($verb === 'disagree')
- $bodyverb = t('%1$s doesn\'t agree with %2$s\'s %3$s');
- if ($verb === 'abstain')
- $bodyverb = t('%1$s abstains from a decision on %2$s\'s %3$s');
if ($verb === 'attendyes')
$bodyverb = t('%1$s is attending %2$s\'s %3$s');
if ($verb === 'attendno')
@@ -511,7 +503,7 @@ class Like extends Controller {
$arr['thr_parent'] = $item['mid'];
$ulink = '[zrl=' . $item_author['xchan_url'] . '][bdi]' . $item_author['xchan_name'] . '[/bdi][/zrl]';
$alink = '[zrl=' . $observer['xchan_url'] . '][bdi]' . $observer['xchan_name'] . '[/bdi][/zrl]';
- $plink = '[zrl=' . z_root() . '/display/' . gen_link_id($item['mid']) . ']' . $post_type . '[/zrl]';
+ $plink = '[zrl=' . z_root() . '/display/' . $item['uuid'] . ']' . $post_type . '[/zrl]';
$allow_cid = $item['allow_cid'];
$allow_gid = $item['allow_gid'];
$deny_cid = $item['deny_cid'];
@@ -532,7 +524,7 @@ class Like extends Controller {
if ($obj_type === 'thing' && $r[0]['imgurl']) {
$arr['body'] .= "\n\n[zmg=80x80]" . $r[0]['imgurl'] . '[/zmg]';
}
- if ($obj_type === 'profile') {
+ if ($obj_type === 'Profile') {
if ($public) {
$arr['body'] .= "\n\n" . '[embed]' . z_root() . '/profile/' . $ch[0]['channel_address'] . '[/embed]';
}
@@ -586,6 +578,7 @@ class Like extends Controller {
Libsync::build_sync_packet($profile_uid, ['item' => [encode_item($sync_item[0], true)]]);
}
+
if ($extended_like) {
$r = q("insert into likes (channel_id,liker,likee,iid,i_mid,verb,target_type,target_id,target) values (%d,'%s','%s',%d,'%s','%s','%s','%s','%s')",
intval($ch[0]['channel_id']),
diff --git a/Zotlabs/Module/Linkinfo.php b/Zotlabs/Module/Linkinfo.php
index 038c739d5..081966dba 100644
--- a/Zotlabs/Module/Linkinfo.php
+++ b/Zotlabs/Module/Linkinfo.php
@@ -291,11 +291,15 @@ class Linkinfo extends \Zotlabs\Web\Controller {
// Check codepage in HTTP headers or HTML if not exist
$cp = (preg_match('/Content-Type: text\/html; charset=(.+)\r\n/i', $header, $o) ? $o[1] : '');
- if(empty($cp))
- $cp = (preg_match('/meta.+content=["\']text\/html; charset=([^"\']+)/i', $body, $o) ? $o[1] : 'AUTO');
+ if(empty($cp)) {
+ $cp = (preg_match('/meta.+content=["\']text\/html; charset=([^"\']+)/i', $body, $o) ? $o[1] : 'AUTO');
+ }
+
+ $body = mb_convert_encoding($body, 'UTF-8', $cp);
- $body = mb_convert_encoding($body, 'UTF-8', $cp);
- $body = mb_convert_encoding($body, 'HTML-ENTITIES', "UTF-8");
+ // Handling HTML entities via mbstring is deprecated
+ //$body = mb_convert_encoding($body, 'HTML-ENTITIES', "UTF-8");
+ $body = mb_encode_numericentity($body, [0x80, 0x10FFFF, 0, ~0], 'UTF-8');
$doc = new \DOMDocument();
@$doc->loadHTML($body);
diff --git a/Zotlabs/Module/Mood.php b/Zotlabs/Module/Mood.php
deleted file mode 100644
index edd3f0e1a..000000000
--- a/Zotlabs/Module/Mood.php
+++ /dev/null
@@ -1,163 +0,0 @@
-<?php
-namespace Zotlabs\Module;
-
-use App;
-use Zotlabs\Lib\Apps;
-use Zotlabs\Web\Controller;
-
-require_once('include/security.php');
-require_once('include/bbcode.php');
-require_once('include/items.php');
-
-
-
-class Mood extends Controller {
-
- function init() {
-
- if(! local_channel())
- return;
-
- if(! Apps::system_app_installed(local_channel(), 'Mood')) {
- return;
- }
-
- $uid = local_channel();
- $channel = App::get_channel();
- $verb = ((isset($_GET['verb'])) ? notags(trim($_GET['verb'])) : '');
-
- if(! $verb)
- return;
-
- $verbs = get_mood_verbs();
-
- if(! array_key_exists($verb,$verbs))
- return;
-
- $activity = ACTIVITY_MOOD . '#' . urlencode($verb);
-
- $parent = ((x($_GET,'parent')) ? intval($_GET['parent']) : 0);
-
-
- logger('mood: verb ' . $verb, LOGGER_DEBUG);
-
-
- if($parent) {
- $r = q("select mid, owner_xchan, private, allow_cid, allow_gid, deny_cid, deny_gid
- from item where id = %d and parent = %d and uid = %d limit 1",
- intval($parent),
- intval($parent),
- intval($uid)
- );
- if(count($r)) {
- $parent_mid = $r[0]['mid'];
- $private = $r[0]['item_private'];
- $allow_cid = $r[0]['allow_cid'];
- $allow_gid = $r[0]['allow_gid'];
- $deny_cid = $r[0]['deny_cid'];
- $deny_gid = $r[0]['deny_gid'];
- }
- }
- else {
-
- $private = 0;
-
- $allow_cid = $channel['channel_allow_cid'];
- $allow_gid = $channel['channel_allow_gid'];
- $deny_cid = $channel['channel_deny_cid'];
- $deny_gid = $channel['channel_deny_gid'];
- }
-
- $poster = App::get_observer();
-
- $uuid = item_message_id();
- $mid = z_root() . '/item/' . $uuid;
-
- $action = sprintf( t('%1$s is %2$s','mood'), '[zrl=' . $poster['xchan_url'] . ']' . $poster['xchan_name'] . '[/zrl]' , $verbs[$verb]);
-
- $arr = array();
-
- $arr['aid'] = get_account_id();
- $arr['uid'] = $uid;
- $arr['uuid'] = $uuid;
- $arr['mid'] = $mid;
- $arr['parent_mid'] = (($parent_mid) ? $parent_mid : $mid);
- $arr['author_xchan'] = $poster['xchan_hash'];
- $arr['owner_xchan'] = (($parent_mid) ? $r[0]['owner_xchan'] : $poster['xchan_hash']);
- $arr['title'] = '';
- $arr['allow_cid'] = $allow_cid;
- $arr['allow_gid'] = $allow_gid;
- $arr['deny_cid'] = $deny_cid;
- $arr['deny_gid'] = $deny_gid;
- $arr['item_private'] = $private;
- $arr['verb'] = $activity;
- $arr['body'] = $action;
- $arr['item_origin'] = 1;
- $arr['item_wall'] = 1;
- $arr['item_unseen'] = 1;
- if(! $parent_mid)
- $item['item_thread_top'] = 1;
-
- if ((! $arr['plink']) && intval($arr['item_thread_top'])) {
- $arr['plink'] = z_root() . '/channel/' . $channel['channel_address'] . '/?f=&mid=' . urlencode($arr['mid']);
- }
-
-
- $post = item_store($arr);
- $item_id = $post['item_id'];
-
- if($item_id) {
- \Zotlabs\Daemon\Master::Summon(array('Notifier','activity', $item_id));
- }
-
- call_hooks('post_local_end', $arr);
-
- if($_SESSION['return_url'])
- goaway(z_root() . '/' . $_SESSION['return_url']);
-
- return;
- }
-
-
-
- function get() {
-
- if(! local_channel()) {
- notice( t('Permission denied.') . EOL);
- return;
- }
-
- if(! Apps::system_app_installed(local_channel(), 'Mood')) {
- //Do not display any associated widgets at this point
- App::$pdl = '';
- $papp = Apps::get_papp('Mood');
- return Apps::app_render($papp, 'module');
- }
-
- nav_set_selected('Mood');
-
- $parent = ((x($_GET,'parent')) ? intval($_GET['parent']) : '0');
-
- $verbs = get_mood_verbs();
-
- $shortlist = array();
- foreach($verbs as $k => $v)
- if($v !== 'NOTRANSLATION')
- $shortlist[] = array($k,$v);
-
-
- $tpl = get_markup_template('mood_content.tpl');
-
- $o = replace_macros($tpl,array(
- '$title' => t('Mood'),
- '$desc' => t('Set your current mood and tell your friends'),
- '$verbs' => $shortlist,
- '$parent' => $parent,
- '$submit' => t('Submit'),
- ));
-
- return $o;
-
- }
-
-}
diff --git a/Zotlabs/Module/Network.php b/Zotlabs/Module/Network.php
index 4f8e2f4e4..3ea813547 100644
--- a/Zotlabs/Module/Network.php
+++ b/Zotlabs/Module/Network.php
@@ -275,7 +275,7 @@ class Network extends \Zotlabs\Web\Controller {
$vnotify = get_pconfig(local_channel(), 'system', 'vnotify');
if(! ($vnotify & VNOTIFY_LIKE))
- $likes_sql = " AND verb NOT IN ('" . dbesc(ACTIVITY_LIKE) . "', '" . dbesc(ACTIVITY_DISLIKE) . "') ";
+ $likes_sql = " AND verb NOT IN ('Like', 'Dislike', '" . dbesc(ACTIVITY_LIKE) . "', '" . dbesc(ACTIVITY_DISLIKE) . "') ";
// This is for nouveau view public forum cid queries (if a forum notification is clicked)
//$p = q("SELECT oid AS parent FROM term WHERE uid = %d AND ttype = %d AND term = '%s'",
diff --git a/Zotlabs/Module/Owa.php b/Zotlabs/Module/Owa.php
index e41435ecd..85467d4f4 100644
--- a/Zotlabs/Module/Owa.php
+++ b/Zotlabs/Module/Owa.php
@@ -63,10 +63,6 @@ class Owa extends Controller {
if ($r) {
foreach ($r as $hubloc) {
- // fix friendica accept header for nginx
- if (str_starts_with($keyId, 'acct:') && $_SERVER['HTTP_ACCEPT'] === 'application/x-zot+json')
- $_SERVER['HTTP_ACCEPT'] = 'application/x-dfrn+json, application/x-zot+json';
-
$verified = HTTPSig::verify(file_get_contents('php://input'), $hubloc['xchan_pubkey']);
if ($verified && $verified['header_signed'] && $verified['header_valid'] && ($verified['content_valid'] || (! $verified['content_signed']))) {
logger('OWA header: ' . print_r($verified,true),LOGGER_DATA);
diff --git a/Zotlabs/Module/Photos.php b/Zotlabs/Module/Photos.php
index 6c73c411e..870a2cb79 100644
--- a/Zotlabs/Module/Photos.php
+++ b/Zotlabs/Module/Photos.php
@@ -1108,7 +1108,6 @@ class Photos extends \Zotlabs\Web\Controller {
$conv_responses = array(
'like' => array('title' => t('Likes','title')),'dislike' => array('title' => t('Dislikes','title')),
- 'agree' => array('title' => t('Agree','title')),'disagree' => array('title' => t('Disagree','title')), 'abstain' => array('title' => t('Abstain','title')),
'attendyes' => array('title' => t('Attending','title')), 'attendno' => array('title' => t('Not attending','title')), 'attendmaybe' => array('title' => t('Might attend','title'))
);
@@ -1152,8 +1151,9 @@ class Photos extends \Zotlabs\Web\Controller {
$template = $tpl;
$sparkle = '';
- if(((activity_match($item['verb'],ACTIVITY_LIKE)) || (activity_match($item['verb'],ACTIVITY_DISLIKE))) && ($item['id'] != $item['parent']))
+ if(activity_match($item['verb'], ['Like', 'Dislike', ACTIVITY_LIKE, ACTIVITY_DISLIKE]) && $item['id'] != $item['parent']) {
continue;
+ }
$redirect_url = z_root() . '/redir/' . $item['cid'] ;
diff --git a/Zotlabs/Module/Pin.php b/Zotlabs/Module/Pin.php
index f82327ce6..de3c75622 100644
--- a/Zotlabs/Module/Pin.php
+++ b/Zotlabs/Module/Pin.php
@@ -37,7 +37,7 @@ class Pin extends \Zotlabs\Web\Controller {
http_status_exit(404, 'Not found');
}
- $midb64 = gen_link_id($r[0]['mid']);
+ $midb64 = $r[0]['uuid'];
$pinned = (in_array($midb64, get_pconfig($r[0]['uid'], 'pinned', $r[0]['item_type'], [])) ? true : false);
switch(argv(1)) {
diff --git a/Zotlabs/Module/Poke.php b/Zotlabs/Module/Poke.php
deleted file mode 100644
index 30585bf3d..000000000
--- a/Zotlabs/Module/Poke.php
+++ /dev/null
@@ -1,207 +0,0 @@
-<?php
-namespace Zotlabs\Module; /** @file */
-
-use App;
-use Zotlabs\Lib\Apps;
-use Zotlabs\Lib\Activity;
-use Zotlabs\Web\Controller;
-
-/**
- *
- * Poke, prod, finger, or otherwise do unspeakable things to somebody - who must be a connection in your address book
- * This function can be invoked with the required arguments (verb and cid and private and possibly parent) silently via ajax or
- * other web request. You must be logged in and connected to a channel.
- * If the required arguments aren't present, we'll display a simple form to choose a recipient and a verb.
- * parent is a special argument which let's you attach this activity as a comment to an existing conversation, which
- * may have started with somebody else poking (etc.) somebody, but this isn't necessary. This can be used in the adult
- * plugin version to have entire conversations where Alice poked Bob, Bob fingered Alice, Alice hugged Bob, etc.
- *
- * private creates a private conversation with the recipient. Otherwise your channel's default post privacy is used.
- *
- */
-
-require_once('include/items.php');
-
-
-class Poke extends Controller {
-
- function init() {
-
- if(! local_channel())
- return;
-
- if(! Apps::system_app_installed(local_channel(), 'Poke')) {
- return;
- }
-
- $uid = local_channel();
- $channel = App::get_channel();
-
- $verb = ((isset($_GET['verb'])) ? notags(trim($_GET['verb'])) : '');
-
- if(! $verb)
- return;
-
- $verbs = get_poke_verbs();
-
- if(! array_key_exists($verb,$verbs))
- return;
-
- $activity = ACTIVITY_POKE . '#' . urlencode($verbs[$verb][0]);
-
- $contact_id = intval($_REQUEST['cid']);
-
- $xchan = trim($_REQUEST['xchan']);
-
- if(! ($contact_id || $xchan))
- return;
-
- $parent = ((x($_REQUEST,'parent')) ? intval($_REQUEST['parent']) : 0);
-
- logger('poke: verb ' . $verb . ' contact ' . $contact_id, LOGGER_DEBUG);
-
-
- if($contact_id) {
- $r = q("SELECT * FROM abook left join xchan on xchan_hash = abook_xchan where abook_id = %d and abook_channel = %d LIMIT 1",
- intval($contact_id),
- intval($uid)
- );
- }
- if($xchan) {
- $r = q("SELECT * FROM xchan where xchan_hash like ( '%s' ) LIMIT 1",
- dbesc($xchan . '%')
- );
- }
-
- if(! $r) {
- logger('poke: no target.');
- return;
- }
-
- $target = $r[0];
- $parent_item = null;
-
- if($parent) {
- $r = q("select mid, item_private, owner_xchan, allow_cid, allow_gid, deny_cid, deny_gid
- from item where id = %d and parent = %d and uid = %d limit 1",
- intval($parent),
- intval($parent),
- intval($uid)
- );
- if($r) {
- $parent_item = $r[0];
- $parent_mid = $r[0]['mid'];
- $item_private = $r[0]['item_private'];
- $allow_cid = $r[0]['allow_cid'];
- $allow_gid = $r[0]['allow_gid'];
- $deny_cid = $r[0]['deny_cid'];
- $deny_gid = $r[0]['deny_gid'];
- }
- }
- elseif($contact_id) {
-
- $item_private = ((x($_GET,'private')) ? intval($_GET['private']) : 0);
-
- $allow_cid = (($item_private) ? '<' . $target['abook_xchan']. '>' : $channel['channel_allow_cid']);
- $allow_gid = (($item_private) ? '' : $channel['channel_allow_gid']);
- $deny_cid = (($item_private) ? '' : $channel['channel_deny_cid']);
- $deny_gid = (($item_private) ? '' : $channel['channel_deny_gid']);
- }
-
- $arr['item_wall'] = 1;
- $arr['owner_xchan'] = (($parent_item) ? $parent_item['owner_xchan'] : $channel['channel_hash']);
- $arr['parent_mid'] = (($parent_mid) ? $parent_mid : '');
- $arr['title'] = '';
- $arr['allow_cid'] = $allow_cid;
- $arr['allow_gid'] = $allow_gid;
- $arr['deny_cid'] = $deny_cid;
- $arr['deny_gid'] = $deny_gid;
- $arr['verb'] = $activity;
- $arr['item_private'] = $item_private;
- $arr['obj_type'] = ACTIVITY_OBJ_NOTE;
- $arr['body'] = '[zrl=' . $channel['xchan_url'] . ']' . $channel['xchan_name'] . '[/zrl]' . ' ' . t($verbs[$verb][0]) . ' ' . '[zrl=' . $target['xchan_url'] . ']' . $target['xchan_name'] . '[/zrl]';
- $arr['item_origin'] = 1;
- $arr['item_unseen'] = 1;
- if(! $parent_item)
- $arr['item_thread_top'] = 1;
-
- $arr['obj'] = Activity::encode_item($arr);
-
-
- post_activity_item($arr);
-
- return;
- }
-
-
-
- function get() {
-
- if(! local_channel()) {
- notice( t('Permission denied.') . EOL);
- return;
- }
-
- if(! Apps::system_app_installed(local_channel(), 'Poke')) {
- //Do not display any associated widgets at this point
- App::$pdl = '';
- $papp = Apps::get_papp('Poke');
- return Apps::app_render($papp, 'module');
- }
-
- nav_set_selected('Poke');
-
- $name = '';
- $id = '';
-
- if(isset($_REQUEST['c']) && intval($_REQUEST['c'])) {
- $r = q("select abook_id, xchan_name from abook left join xchan on abook_xchan = xchan_hash
- where abook_id = %d and abook_channel = %d limit 1",
- intval($_REQUEST['c']),
- intval(local_channel())
- );
- if($r) {
- $name = $r[0]['xchan_name'];
- $id = $r[0]['abook_id'];
- }
- }
-
- $parent = ((x($_REQUEST,'parent')) ? intval($_REQUEST['parent']) : '0');
-
- $verbs = get_poke_verbs();
-
- $shortlist = array();
- foreach($verbs as $k => $v)
- if($v[1] !== 'NOTRANSLATION')
- $shortlist[] = array($k,$v[1]);
-
-
- $poke_basic = get_config('system','poke_basic');
- if($poke_basic) {
- $title = t('Poke');
- $desc = t('Poke somebody');
- }
- else {
- $title = t('Poke');
- $desc = t('Poke or ping somebody');
- }
-
- $o = replace_macros(get_markup_template('poke_content.tpl'),array(
- '$title' => $title,
- '$poke_basic' => $poke_basic,
- '$desc' => $desc,
- '$clabel' => t('Recipient'),
- '$choice' => t('Choose action'),
- '$verbs' => $shortlist,
- '$parent' => $parent,
- '$prv_desc' => t('Make this post private'),
- '$private' => array('private', t('Make this post private'), false, ''),
- '$submit' => t('Submit'),
- '$name' => $name,
- '$id' => $id
- ));
-
- return $o;
-
- }
-}
diff --git a/Zotlabs/Module/Profile_photo.php b/Zotlabs/Module/Profile_photo.php
index d7e2bbce1..dc47d213b 100644
--- a/Zotlabs/Module/Profile_photo.php
+++ b/Zotlabs/Module/Profile_photo.php
@@ -223,7 +223,7 @@ class Profile_photo extends Controller {
intval(local_channel())
);
- send_profile_photo_activity($channel, $base_image, $profile);
+ profile_activity([t('Profile Photo')], $base_image['resource_id']);
}
else {
q("update profile set photo = '%s', thumb = '%s' where id = %d and uid = %d",
@@ -269,7 +269,6 @@ class Profile_photo extends Controller {
// Update directory in background
Master::Summon(['Directory', $channel['channel_id']]);
-
}
else
notice(t('Unable to process image') . EOL);
diff --git a/Zotlabs/Module/Profiles.php b/Zotlabs/Module/Profiles.php
index ce496252b..15252d6e6 100644
--- a/Zotlabs/Module/Profiles.php
+++ b/Zotlabs/Module/Profiles.php
@@ -3,10 +3,6 @@ namespace Zotlabs\Module;
use Zotlabs\Lib\Libsync;
-require_once('include/channel.php');
-require_once('include/selectors.php');
-
-
class Profiles extends \Zotlabs\Web\Controller {
function init() {
@@ -492,7 +488,7 @@ class Profiles extends \Zotlabs\Web\Controller {
$publish = ((x($_POST, 'profile_in_directory') && (intval($_POST['profile_in_directory']) == 1)) ? 1 : 0);
- profile_activity($changes,$value);
+ profile_activity($changes, $value);
}
diff --git a/Zotlabs/Module/Pubstream.php b/Zotlabs/Module/Pubstream.php
index 08de168cb..c40751fdc 100644
--- a/Zotlabs/Module/Pubstream.php
+++ b/Zotlabs/Module/Pubstream.php
@@ -40,7 +40,15 @@ class Pubstream extends \Zotlabs\Web\Controller {
$site_firehose = ((intval(get_config('system','site_firehose',0))) ? true : false);
- $mid = ((isset($_REQUEST['mid'])) ? unpack_link_id($_REQUEST['mid']) : '');
+ $mid = $_REQUEST['mid'] ?? '';
+ $identifier = 'uuid';
+ $encoded_mid = null;
+
+ if (str_starts_with($mid, 'b64.')) {
+ $encoded_mid = $mid;
+ $mid = unpack_link_id($mid);
+ $identifier = 'mid';
+ }
if ($mid === false) {
notice(t('Malformed message id.') . EOL);
@@ -108,9 +116,6 @@ class Pubstream extends \Zotlabs\Web\Controller {
. "; var profile_page = " . \App::$pager['page']
. "; divmore_height = " . intval($maxheight) . "; </script>\r\n";
- //if we got a decoded hash we must encode it again before handing to javascript
- $mid = gen_link_id($mid);
-
\App::$page['htmlhead'] .= replace_macros(get_markup_template("build_query.tpl"),array(
'$baseurl' => z_root(),
'$pgtype' => 'pubstream',
@@ -136,7 +141,7 @@ class Pubstream extends \Zotlabs\Web\Controller {
'$cats' => '',
'$tags' => (($hashtags) ? urlencode($hashtags) : ''),
'$dend' => '',
- '$mid' => (($mid) ? urlencode($mid) : ''),
+ '$mid' => $encoded_mid ?? $mid,
'$verb' => '',
'$net' => (($net) ? urlencode($net) : ''),
'$dbegin' => ''
@@ -198,7 +203,7 @@ class Pubstream extends \Zotlabs\Web\Controller {
$r = q("SELECT parent AS item_id FROM item
left join abook on item.author_xchan = abook.abook_xchan
$net_query
- WHERE item.mid = '%s' and item.item_private = 0
+ WHERE item.$identifier = '%s' and item.item_private = 0
$uids $site_firehose_sql
$item_normal
and (abook.abook_blocked = 0 or abook.abook_flags is null)
@@ -225,7 +230,7 @@ class Pubstream extends \Zotlabs\Web\Controller {
$r = q("SELECT parent AS item_id FROM item
left join abook on item.author_xchan = abook.abook_xchan
$net_query
- WHERE item.mid = '%s' and item.item_private = 0
+ WHERE item.$identifier = '%s' and item.item_private = 0
$uids $site_firehose_sql $item_normal_update $simple_update
and (abook.abook_blocked = 0 or abook.abook_flags is null)
$sql_extra $net_query2",
diff --git a/Zotlabs/Module/React.php b/Zotlabs/Module/React.php
index f80b04a3f..e04b9b257 100644
--- a/Zotlabs/Module/React.php
+++ b/Zotlabs/Module/React.php
@@ -2,82 +2,96 @@
namespace Zotlabs\Module;
+use App;
+use Zotlabs\Web\Controller;
+use Zotlabs\Lib\Activity;
+use Zotlabs\Daemon\Master;
-class React extends \Zotlabs\Web\Controller {
+class React extends Controller {
function get() {
- if(! local_channel())
+ if (!local_channel()) {
return;
+ }
$sys = get_sys_channel();
- $channel = \App::get_channel();
+ $channel = App::get_channel();
$postid = $_REQUEST['postid'];
- if(! $postid)
+ if (!$postid) {
return;
+ }
- $emoji = $_REQUEST['emoji'];
-
-
- if($_REQUEST['emoji']) {
-
- $i = q("select * from item where id = %d and uid = %d",
- intval($postid),
- intval(local_channel())
- );
-
- if(! $i) {
- $i = q("select * from item where id = %d and uid = %d",
- intval($postid),
- intval($sys['channel_id'])
- );
-
- if($i) {
- $i = [ copy_of_pubitem($channel, $i[0]['mid']) ];
- $postid = (($i) ? $i[0]['id'] : 0);
- }
- }
+ $shortname = $_REQUEST['emoji'];
- if(! $i) {
- return;
- }
+ $emojis = get_emojis();
- $uuid = item_message_id();
-
- $n = array();
- $n['aid'] = $channel['channel_account_id'];
- $n['uid'] = $channel['channel_id'];
- $n['item_origin'] = true;
- $n['item_type'] = $i[0]['item_type'];
- $n['parent'] = $postid;
- $n['parent_mid'] = $i[0]['mid'];
- $n['uuid'] = $uuid;
- $n['mid'] = z_root() . '/item/' . $uuid;
- $n['verb'] = ACTIVITY_REACT . '#' . $emoji;
- $n['body'] = "\n\n[zmg=32x32]" . z_root() . '/images/emoji/' . $emoji . '.png[/zmg]' . "\n\n";
- $n['author_xchan'] = $channel['channel_hash'];
+ if (!isset($emojis[$shortname])) {
+ return;
+ }
- $n['tgt_type'] = 'Image';
- $n['target'] = [
- 'type' => 'Image',
- 'name' => $emoji,
- 'url' => z_root() . '/images/emoji/' . $emoji . '.png'
- ];
+ $emoji = $emojis[$shortname];
+ if (!$emoji) {
+ return;
+ }
- $x = item_store($n);
+ $i = q("select * from item where id = %d and uid = %d",
+ intval($postid),
+ intval(local_channel())
+ );
- retain_item($postid);
+ if (!$i) {
+ $i = q("select * from item where id = %d and uid = %d",
+ intval($postid),
+ intval($sys['channel_id'])
+ );
- if($x['success']) {
- $nid = $x['item_id'];
- \Zotlabs\Daemon\Master::Summon(array('Notifier','like',$nid));
+ if ($i) {
+ $i = [ copy_of_pubitem($channel, $i[0]['mid']) ];
+ $postid = (($i) ? $i[0]['id'] : 0);
}
+ }
+ if (!$i) {
+ return;
}
+ $uuid = item_message_id();
+
+ $n['aid'] = $channel['channel_account_id'];
+ $n['uid'] = $channel['channel_id'];
+ $n['item_origin'] = true;
+ $n['item_type'] = $i[0]['item_type'];
+ $n['parent'] = $postid;
+ $n['parent_mid'] = $i[0]['mid'];
+ $n['uuid'] = $uuid;
+ $n['mid'] = z_root() . '/item/' . $uuid;
+ $n['verb'] = 'Create';
+ $n['body'] = $emoji['shortname']; //'[img class="emoji single-emoji"]' . z_root() . '/' . $emoji['filepath'] . '[/img]';
+ $n['author_xchan'] = $channel['channel_hash'];
+ // $n['obj'] = Activity::fetch_item(['id' => $i[0]['mid']]);
+ // $n['obj_type'] = ((array_path_exists('obj/type', $n)) ? $n['obj']['type'] : EMPTY_STR);
+
+ $n['term'][] = [
+ 'uid' => $channel['channel_id'],
+ 'ttype' => TERM_EMOJI,
+ 'otype' => TERM_OBJ_POST,
+ 'term' => $emoji['shortname'],
+ 'url' => z_root() . '/emoji/' . $shortname,
+ 'imgurl' => z_root() . '/' . $emoji['filepath']
+ ];
+
+ $x = item_store($n);
+
+ retain_item($postid);
+
+ if ($x['success']) {
+ $nid = $x['item_id'];
+ Master::Summon(['Notifier', 'like', $nid]);
+ }
}
-} \ No newline at end of file
+}
diff --git a/Zotlabs/Module/Search.php b/Zotlabs/Module/Search.php
index 6b1060570..9511c0389 100644
--- a/Zotlabs/Module/Search.php
+++ b/Zotlabs/Module/Search.php
@@ -73,15 +73,15 @@ class Search extends Controller {
$f = Libzot::fetch_conversation(App::get_channel(), punify($url), true);
if ($f) {
- $mid = $f[0]['message_id'];
+ $uuid = $f[0]['message_uuid'];
foreach ($f as $m) {
- if (str_starts_with($url, $m['message_id'])) {
- $mid = $m['message_id'];
+ if ($url === $m['message_id']) {
+ $uuid = $m['message_uuid'];
break;
}
}
- goaway(z_root() . '/hq/' . gen_link_id($mid));
+ goaway(z_root() . '/hq/' . $uuid);
}
else {
// try other fetch providers (e.g. diaspora, pubcrawl)
diff --git a/Zotlabs/Module/Setup.php b/Zotlabs/Module/Setup.php
index 3a188d9ce..83faf85dc 100644
--- a/Zotlabs/Module/Setup.php
+++ b/Zotlabs/Module/Setup.php
@@ -491,6 +491,19 @@ class Setup extends \Zotlabs\Web\Controller {
}
$this->check_add($checks, t('Generate encryption keys'), $res, true, $help);
+
+ $res = function_exists('sodium_crypto_sign_keypair');
+ if (!$res) {
+ $help = t('Error: the sodium encryption library is not installed.') . EOL;
+ }
+ $this->check_add($checks, t('Generate ed25519 encryption keys'), $res, true, $help);
+
+ $res1 = extension_loaded('bcmath');
+ $res2 = extension_loaded('gmp');
+ if (! ($res1 || $res2)) {
+ $help = t('Error: one of "bcmath" or "gmp" (bigmath library) extensions are required.') . EOL;
+ }
+ $this->check_add($checks, t('Bigmath library (either bcmath or gmp)'), $res1||$res2, $help);
}
/**
@@ -514,6 +527,7 @@ class Setup extends \Zotlabs\Web\Controller {
$this->check_add($ck_funcs, t('mb_string PHP module'), true, true);
$this->check_add($ck_funcs, t('xml PHP module'), true, true);
$this->check_add($ck_funcs, t('zip PHP module'), true, true);
+ $this->check_add($ck_funcs, t('intl PHP module'), true, true);
if(function_exists('apache_get_modules')){
if(! in_array('mod_rewrite', apache_get_modules())) {
@@ -570,6 +584,10 @@ class Setup extends \Zotlabs\Web\Controller {
$ck_funcs[6]['status'] = false;
$ck_funcs[6]['help'] = t('Error: zip PHP module required but not installed.');
}
+ if(! extension_loaded('intl')) {
+ $ck_funcs[6]['status'] = false;
+ $ck_funcs[6]['help'] = t('Error: intl PHP module required but not installed.');
+ }
$checks = array_merge($checks, $ck_funcs);
}
diff --git a/Zotlabs/Module/Share.php b/Zotlabs/Module/Share.php
index 716f7229b..ea9313fa8 100644
--- a/Zotlabs/Module/Share.php
+++ b/Zotlabs/Module/Share.php
@@ -65,9 +65,7 @@ class Share extends \Zotlabs\Web\Controller {
$item = $r[0];
- $owner_uid = $r[0]['uid'];
- $owner_aid = $r[0]['aid'];
-
+/*
$can_comment = false;
if((array_key_exists('owner',$item)) && intval($item['owner']['abook_self']))
$can_comment = perm_is_allowed($item['uid'],$observer['xchan_hash'],'post_comments');
@@ -78,7 +76,7 @@ class Share extends \Zotlabs\Web\Controller {
notice( t('Permission denied') . EOL);
killme();
}
-
+*/
$r = q("select * from xchan where xchan_hash = '%s' limit 1",
dbesc($item['owner_xchan'])
);
@@ -96,25 +94,39 @@ class Share extends \Zotlabs\Web\Controller {
else
killme();
-
- $arr['aid'] = $owner_aid;
- $arr['uid'] = $owner_uid;
+ $arr['aid'] = $item['aid'];
+ $arr['uid'] = $item['uid'];
$arr['item_origin'] = 1;
$arr['item_wall'] = $item['item_wall'];
+ $arr['item_private'] = $item['item_private'];
$arr['uuid'] = item_message_id();
$arr['mid'] = z_root() . '/activity/' . $arr['uuid'];
- $arr['parent_mid'] = $item['mid'];
+ $arr['parent_mid'] = $item['parent_mid'];
+ $arr['thr_parent'] = $item['mid'];
+
+ $created = datetime_convert();
+
+ $arr['created'] = $created;
+ $arr['edited'] = $created;
+ $arr['commented'] = $created;
+ $arr['received'] = $created;
+ $arr['changed'] = $created;
+ $arr['item_type'] = ITEM_TYPE_POST;
$mention = '@[zrl=' . $item['author']['xchan_url'] . ']' . $item['author']['xchan_name'] . '[/zrl]';
$arr['body'] = sprintf( t('&#x1f501; Repeated %1$s\'s %2$s'), $mention, Activity::activity_obj_mapper($item['obj_type']));
$arr['author_xchan'] = $channel['channel_hash'];
- $arr['owner_xchan'] = $item['author_xchan'];
- $arr['obj'] = Activity::encode_item($item);
+ $arr['owner_xchan'] = $item['author_xchan'];
+ $arr['source_xchan'] = '';
+
+ $arr['obj'] = $item['obj'];
$arr['obj_type'] = $item['obj_type'];
$arr['verb'] = ACTIVITY_SHARE;
+ call_hooks('post_local', $arr);
+
$post = item_store($arr);
$post_id = $post['item_id'];
@@ -123,7 +135,7 @@ class Share extends \Zotlabs\Web\Controller {
call_hooks('post_local_end', $arr);
- info( t('Post repeated') . EOL);
+ // info( t('Post repeated') . EOL);
$r = q("select * from item where id = %d",
intval($post_id)
diff --git a/Zotlabs/Module/Sharedwithme.php b/Zotlabs/Module/Sharedwithme.php
index c294079d4..6489959f0 100644
--- a/Zotlabs/Module/Sharedwithme.php
+++ b/Zotlabs/Module/Sharedwithme.php
@@ -40,7 +40,7 @@ class Sharedwithme extends Controller {
//drop all files - localuser
if((argc() > 1) && (argv(1) === 'dropall')) {
- $r = q("SELECT id FROM item WHERE verb = '%s' AND obj_type IN ('Document', 'Video', 'Audio', 'Image') AND uid = %d AND owner_xchan != '%s' $item_normal",
+ $r = q("SELECT id FROM item WHERE (verb = 'Create' OR verb = '%s') AND obj_type IN ('Document', 'Video', 'Audio', 'Image') AND uid = %d AND owner_xchan != '%s' $item_normal",
dbesc(ACTIVITY_POST),
intval(local_channel()),
dbesc($channel['channel_hash'])
@@ -56,7 +56,7 @@ class Sharedwithme extends Controller {
}
//list files
- $r = q("SELECT id, uid, obj, item_unseen FROM item WHERE verb = '%s' AND obj_type IN ('Document', 'Video', 'Audio', 'Image') AND uid = %d AND owner_xchan != '%s' $item_normal",
+ $r = q("SELECT id, uid, obj, item_unseen FROM item WHERE (verb = 'Create' OR verb = '%s') AND obj_type IN ('Document', 'Video', 'Audio', 'Image') AND uid = %d AND owner_xchan != '%s' $item_normal",
dbesc(ACTIVITY_POST),
intval(local_channel()),
dbesc($channel['channel_hash'])
diff --git a/Zotlabs/Module/Smilies.php b/Zotlabs/Module/Smilies.php
index efac07f84..7dde8c834 100644
--- a/Zotlabs/Module/Smilies.php
+++ b/Zotlabs/Module/Smilies.php
@@ -4,18 +4,8 @@ namespace Zotlabs\Module;
class Smilies extends \Zotlabs\Web\Controller {
- function get() {
- if (\App::$argv[1]==="json"){
- $tmp = list_smilies();
- $results = array();
- for($i = 0; $i < count($tmp['texts']); $i++) {
- $results[] = array('text' => $tmp['texts'][$i], 'icon' => $tmp['icons'][$i]);
- }
- json_return_and_die($results);
- }
- else {
- return smilies('',true);
- }
+ function init() {
+ json_return_and_die(get_emojis());
}
-
+
}
diff --git a/Zotlabs/Module/Sse_bs.php b/Zotlabs/Module/Sse_bs.php
index e4cb1c822..9fb295c4b 100644
--- a/Zotlabs/Module/Sse_bs.php
+++ b/Zotlabs/Module/Sse_bs.php
@@ -120,13 +120,17 @@ class Sse_bs extends Controller {
$mids = [];
$str = '';
+ $slice = 0;
- $mids_all_json = Cache::get('sse_mids_all_' . session_id());
+ $mids_all = isset($_SESSION['sse_mids_all']) ? unserialise($_SESSION['sse_mids_all']) : [];
- if (!$mids_all_json)
- $mids_all_json = '[]';
+ if (count($mids_all) > 3000) {
+ $slice = count($mids_all) - 3000;
+ }
- $mids_all = json_decode($mids_all_json, true);
+ if ($slice) {
+ $mids_all = array_slice($mids_all, $slice);
+ }
foreach($arr as $a) {
$mid_str = '\'' . dbesc(unpack_link_id($a)) . '\'';
@@ -137,7 +141,7 @@ class Sse_bs extends Controller {
}
}
- Cache::set('sse_mids_all_' . session_id(), json_encode($mids_all));
+ $_SESSION['sse_mids_all'] = serialise($mids_all);
if(! self::$uid) {
return;
@@ -149,7 +153,7 @@ class Sse_bs extends Controller {
call_hooks('update_unseen',$x);
if($x['update'] === 'unset' || intval($x['update'])) {
- q("UPDATE item SET item_unseen = 0 WHERE uid = %d AND mid in (%s) AND item_unseen = 1",
+ q("UPDATE item SET item_unseen = 0 WHERE uid = %d AND uuid in (%s) AND item_unseen = 1",
intval(self::$uid),
$str // this is dbesc() in the above foreach loop
);
@@ -177,10 +181,10 @@ class Sse_bs extends Controller {
$sql_extra = '';
if (!(self::$vnotify & VNOTIFY_LIKE)) {
- $sql_extra = " AND verb NOT IN ('" . dbesc(ACTIVITY_LIKE) . "', '" . dbesc(ACTIVITY_DISLIKE) . "') ";
+ $sql_extra = " AND verb NOT IN ('Like', 'Dislike', '" . dbesc(ACTIVITY_LIKE) . "', '" . dbesc(ACTIVITY_DISLIKE) . "') ";
}
elseif (!feature_enabled(self::$uid, 'dislike')) {
- $sql_extra = " AND verb NOT IN ('" . dbesc(ACTIVITY_DISLIKE) . "') ";
+ $sql_extra = " AND verb NOT IN ('Dislike', '" . dbesc(ACTIVITY_DISLIKE) . "') ";
}
$sql_extra2 = '';
@@ -189,8 +193,8 @@ class Sse_bs extends Controller {
$item_normal = item_normal();
- // Filter FEP-5624 approvals for comments and internal follow activities
- $item_normal .= " AND verb NOT IN ('Add', 'Remove', '" . dbesc(ACTIVITY_TAG) . "', '" . dbesc(ACTIVITY_ATTEND) . "', 'Accept', '" . dbesc(ACTIVITY_ATTENDNO) . "', 'Reject', '" . dbesc(ACTIVITY_FOLLOW) . "') ";
+ // Filter internal follow activities and strerams add/remove activities
+ $item_normal .= " AND verb NOT IN ('Add', 'Remove', 'Follow', 'Ignore', '" . dbesc(ACTIVITY_FOLLOW) . "') ";
if ($notifications) {
$items = q("SELECT * FROM item
@@ -260,10 +264,10 @@ class Sse_bs extends Controller {
$sql_extra = '';
if (!(self::$vnotify & VNOTIFY_LIKE)) {
- $sql_extra = " AND verb NOT IN ('" . dbesc(ACTIVITY_LIKE) . "', '" . dbesc(ACTIVITY_DISLIKE) . "') ";
+ $sql_extra = " AND verb NOT IN ('Like', 'Dislike', '" . dbesc(ACTIVITY_LIKE) . "', '" . dbesc(ACTIVITY_DISLIKE) . "') ";
}
elseif (!feature_enabled(self::$uid, 'dislike')) {
- $sql_extra = " AND verb NOT IN ('" . dbesc(ACTIVITY_DISLIKE) . "') ";
+ $sql_extra = " AND verb NOT IN ('Dislike', '" . dbesc(ACTIVITY_DISLIKE) . "') ";
}
$sql_extra2 = '';
@@ -272,8 +276,8 @@ class Sse_bs extends Controller {
$item_normal = item_normal();
- // Filter FEP-5624 approvals for comments and internal follow activities
- $item_normal .= " AND verb NOT IN ('Add', 'Remove', '" . dbesc(ACTIVITY_TAG) . "', '" . dbesc(ACTIVITY_ATTEND) . "', 'Accept', '" . dbesc(ACTIVITY_ATTENDNO) . "', 'Reject', '" . dbesc(ACTIVITY_FOLLOW) . "') ";
+ // Filter internal follow activities and strerams add/remove activities
+ $item_normal .= " AND verb NOT IN ('Add', 'Remove', 'Follow', 'Ignore', '" . dbesc(ACTIVITY_FOLLOW) . "') ";
if ($notifications) {
$items = q("SELECT * FROM item
@@ -342,10 +346,10 @@ class Sse_bs extends Controller {
$sql_extra = '';
if (!(self::$vnotify & VNOTIFY_LIKE)) {
- $sql_extra = " AND verb NOT IN ('" . dbesc(ACTIVITY_LIKE) . "', '" . dbesc(ACTIVITY_DISLIKE) . "') ";
+ $sql_extra = " AND verb NOT IN ('Like', 'Dislike', '" . dbesc(ACTIVITY_LIKE) . "', '" . dbesc(ACTIVITY_DISLIKE) . "') ";
}
elseif (!feature_enabled(self::$uid, 'dislike')) {
- $sql_extra = " AND verb NOT IN ('" . dbesc(ACTIVITY_DISLIKE) . "') ";
+ $sql_extra = " AND verb NOT IN ('Dislike', '" . dbesc(ACTIVITY_DISLIKE) . "') ";
}
$sql_extra2 = '';
@@ -355,8 +359,8 @@ class Sse_bs extends Controller {
$item_normal = item_normal();
- // Filter FEP-5624 approvals for comments and internal follow activities
- $item_normal .= " AND verb NOT IN ('Add', 'Remove', '" . dbesc(ACTIVITY_TAG) . "', '" . dbesc(ACTIVITY_ATTEND) . "', 'Accept', '" . dbesc(ACTIVITY_ATTENDNO) . "', 'Reject', '" . dbesc(ACTIVITY_FOLLOW) . "') ";
+ // Filter internal follow activities and strerams add/remove activities
+ $item_normal .= " AND verb NOT IN ('Add', 'Remove', 'Follow', 'Ignore', '" . dbesc(ACTIVITY_FOLLOW) . "') ";
if ($notifications) {
$items = q("SELECT * FROM item
@@ -437,10 +441,10 @@ class Sse_bs extends Controller {
$sys = get_sys_channel();
$sql_extra = '';
if (!(self::$vnotify & VNOTIFY_LIKE)) {
- $sql_extra = " AND verb NOT IN ('" . dbesc(ACTIVITY_LIKE) . "', '" . dbesc(ACTIVITY_DISLIKE) . "') ";
+ $sql_extra = " AND verb NOT IN ('Like', 'Dislike', '" . dbesc(ACTIVITY_LIKE) . "', '" . dbesc(ACTIVITY_DISLIKE) . "') ";
}
elseif (!feature_enabled(self::$uid, 'dislike')) {
- $sql_extra = " AND verb NOT IN ('" . dbesc(ACTIVITY_DISLIKE) . "') ";
+ $sql_extra = " AND verb NOT IN ('Dislike', '" . dbesc(ACTIVITY_DISLIKE) . "') ";
}
$sql_extra2 = '';
@@ -448,10 +452,9 @@ class Sse_bs extends Controller {
$sql_extra2 = " AND CASE WHEN verb = '" . ACTIVITY_SHARE . "' THEN owner_xchan ELSE author_xchan END IN (" . self::$xchans . ") ";
$sql_extra3 = '';
- $sse_mids_all_json = Cache::get('sse_mids_all_' . session_id());
- if ($sse_mids_all_json) {
- $sse_mids_all = json_decode($sse_mids_all_json, true);
- $sql_extra3 = " AND mid NOT IN (" . protect_sprintf(implode(',', $sse_mids_all)) . ") ";
+ $sse_mids_all = unserialise($_SESSION['sse_mids_all']) ?? [];
+ if ($sse_mids_all) {
+ $sql_extra3 = " AND uuid NOT IN (" . protect_sprintf(implode(',', $sse_mids_all)) . ") ";
}
$uids = " AND uid IN ( " . $sys['channel_id'] . " ) ";
@@ -463,8 +466,8 @@ class Sse_bs extends Controller {
$item_normal = item_normal();
- // Filter FEP-5624 approvals for comments and internal follow activities
- $item_normal .= " AND verb NOT IN ('Add', 'Remove', '" . dbesc(ACTIVITY_TAG) . "', '" . dbesc(ACTIVITY_ATTEND) . "', 'Accept', '" . dbesc(ACTIVITY_ATTENDNO) . "', 'Reject', '" . dbesc(ACTIVITY_FOLLOW) . "') ";
+ // Filter internal follow activities and strerams add/remove activities
+ $item_normal .= " AND verb NOT IN ('Add', 'Remove', 'Follow', 'Ignore', '" . dbesc(ACTIVITY_FOLLOW) . "') ";
if ($notifications) {
$items = q("SELECT * FROM item
@@ -589,7 +592,7 @@ class Sse_bs extends Controller {
$sql_extra = '';
if(! (self::$vnotify & VNOTIFY_LIKE))
- $sql_extra = " AND verb NOT IN ('" . dbesc(ACTIVITY_LIKE) . "', '" . dbesc(ACTIVITY_DISLIKE) . "') ";
+ $sql_extra = " AND verb NOT IN ('Like', 'Dislike', '" . dbesc(ACTIVITY_LIKE) . "', '" . dbesc(ACTIVITY_DISLIKE) . "') ";
$fcount = count($forums);
$i = 0;
@@ -659,11 +662,11 @@ class Sse_bs extends Controller {
$item_normal = item_normal();
- // Filter FEP-5624 approvals for comments and internal follow activities
- $item_normal .= " AND verb NOT IN ('Add', 'Remove', '" . dbesc(ACTIVITY_TAG) . "', '" . dbesc(ACTIVITY_ATTEND) . "', 'Accept', '" . dbesc(ACTIVITY_ATTENDNO) . "', 'Reject', '" . dbesc(ACTIVITY_FOLLOW) . "') ";
+ // Filter internal follow activities and strerams add/remove activities
+ $item_normal .= " AND verb NOT IN ('Add', 'Remove', 'Follow', 'Ignore', '" . dbesc(ACTIVITY_FOLLOW) . "') ";
$r = q("SELECT * FROM item
- WHERE verb = '%s'
+ WHERE (verb = 'Create' OR verb = '%s')
AND obj_type IN ('Document', 'Video', 'Audio', 'Image')
AND uid = %d
AND author_xchan != '%s'
diff --git a/Zotlabs/Module/Subthread.php b/Zotlabs/Module/Subthread.php
index a796d85cb..b927ee480 100644
--- a/Zotlabs/Module/Subthread.php
+++ b/Zotlabs/Module/Subthread.php
@@ -24,9 +24,9 @@ class Subthread extends \Zotlabs\Web\Controller {
$item_id = ((argc() > 2) ? notags(trim(argv(2))) : 0);
if(argv(1) === 'sub')
- $activity = ACTIVITY_FOLLOW;
+ $activity = 'Follow';
elseif(argv(1) === 'unsub')
- $activity = ACTIVITY_UNFOLLOW;
+ $activity = 'Ignore';
$i = q("select * from item where id = %d and uid = %d",
@@ -106,16 +106,13 @@ class Subthread extends \Zotlabs\Web\Controller {
else
killme();
-
-
-
$uuid = item_message_id();
$mid = z_root() . '/item/' . $uuid;
$post_type = (($item['resource_type'] === 'photo') ? t('photo') : t('status'));
$links = array(array('rel' => 'alternate','type' => 'text/html', 'href' => $item['plink']));
- $objtype = (($item['resource_type'] === 'photo') ? ACTIVITY_OBJ_PHOTO : ACTIVITY_OBJ_NOTE );
+ $objtype = (($item['resource_type'] === 'photo') ? 'Image' : 'Note');
$body = $item['body'];
@@ -124,9 +121,9 @@ class Subthread extends \Zotlabs\Web\Controller {
if(! intval($item['item_thread_top']))
$post_type = 'comment';
- if($activity === ACTIVITY_FOLLOW)
+ if($activity === 'Follow')
$bodyverb = t('%1$s is following %2$s\'s %3$s');
- if($activity === ACTIVITY_UNFOLLOW)
+ if($activity === 'Ignore')
$bodyverb = t('%1$s stopped following %2$s\'s %3$s');
$arr = array();
@@ -149,7 +146,7 @@ class Subthread extends \Zotlabs\Web\Controller {
$ulink = '[zrl=' . $item_author['xchan_url'] . ']' . $item_author['xchan_name'] . '[/zrl]';
$alink = '[zrl=' . $observer['xchan_url'] . ']' . $observer['xchan_name'] . '[/zrl]';
- $plink = '[zrl=' . z_root() . '/display/' . gen_link_id($item['mid']) . ']' . $post_type . '[/zrl]';
+ $plink = '[zrl=' . z_root() . '/display/' . $item['uuid'] . ']' . $post_type . '[/zrl]';
$arr['body'] = sprintf( $bodyverb, $alink, $ulink, $plink );
diff --git a/Zotlabs/Module/Tagger.php b/Zotlabs/Module/Tagger.php
index 4aaae5885..b6067be5e 100644
--- a/Zotlabs/Module/Tagger.php
+++ b/Zotlabs/Module/Tagger.php
@@ -67,15 +67,15 @@ class Tagger extends \Zotlabs\Web\Controller {
switch($item['resource_type']) {
case 'photo':
- $targettype = ACTIVITY_OBJ_PHOTO;
+ $targettype = 'Image';
$post_type = t('photo');
break;
case 'event':
- $targettype = ACTIVITY_OBJ_EVENT;
+ $targettype = 'Event';
$post_type = t('event');
break;
default:
- $targettype = ACTIVITY_OBJ_NOTE;
+ $targettype = 'Note';
$post_type = t('post');
if($item['mid'] != $item['parent_mid'])
$post_type = t('comment');
@@ -86,7 +86,7 @@ class Tagger extends \Zotlabs\Web\Controller {
$clean_term = trim($term,'"\' ');
$links = array(array('rel' => 'alternate','type' => 'text/html',
- 'href' => z_root() . '/display/' . gen_link_id($item['mid'])));
+ 'href' => z_root() . '/display/' . $item['uuid']));
$target = json_encode(array(
'type' => $targettype,
diff --git a/Zotlabs/Module/Thing.php b/Zotlabs/Module/Thing.php
index b065b0022..2038db8c0 100644
--- a/Zotlabs/Module/Thing.php
+++ b/Zotlabs/Module/Thing.php
@@ -5,27 +5,50 @@
namespace Zotlabs\Module;
+use App;
use Zotlabs\Lib\Libsync;
-
-require_once('include/items.php');
-require_once('include/security.php');
-require_once('include/selectors.php');
-require_once('include/acl_selectors.php');
+use Zotlabs\Lib\Activity;
+use Zotlabs\Lib\ActivityStreams;
+use Zotlabs\Lib\Libzot;
class Thing extends \Zotlabs\Web\Controller {
function init() {
+ if (argv(1) && ActivityStreams::is_as_request()) {
+ $r = q("select obj_channel from obj where obj_type = %d and obj_obj = '%s' limit 1",
+ intval(TERM_OBJ_THING),
+ dbesc(argv(1))
+ );
- if(! local_channel())
- return;
+ if (!$r) {
+ http_status_exit(404, 'Not found');
+ }
- $channel = \App::get_channel();
+ $sql_extra = permissions_sql($r[0]['obj_channel']);
+
+ $r = q("select * from obj where obj_type = %d and obj_obj = '%s' $sql_extra limit 1",
+ intval(TERM_OBJ_THING),
+ dbesc(argv(1))
+ );
+
+ if ($r) {
+ $channel = channelx_by_n($r[0]['obj_channel']);
+ as_return_and_die(Activity::fetch_thing(['id' => $r[0]['obj_obj']]), $channel);
+ }
+
+ http_status_exit(404, 'Not found');
- if($_SERVER['REQUEST_METHOD'] === 'GET' && argc() < 2) {
- profile_load($channel['channel_address']);
}
+ }
+
+
+ function post() {
+
+ if(! local_channel())
+ return;
+ $channel = \App::get_channel();
$term_hash = (($_REQUEST['term_hash']) ? $_REQUEST['term_hash'] : '');
@@ -36,7 +59,7 @@ class Thing extends \Zotlabs\Web\Controller {
$url = $_REQUEST['url'];
$photo = $_REQUEST['img'];
- $hash = random_string();
+ $hash = new_uuid();
$verbs = obj_verbs();
@@ -77,13 +100,7 @@ class Thing extends \Zotlabs\Web\Controller {
return;
$acl = new \Zotlabs\Access\AccessList($channel);
-
- if(array_key_exists('contact_allow',$_REQUEST)
- || array_key_exists('group_allow',$_REQUEST)
- || array_key_exists('contact_deny',$_REQUEST)
- || array_key_exists('group_deny',$_REQUEST)) {
- $acl->set_from_array($_REQUEST);
- }
+ $acl->set_from_array($_REQUEST);
$x = $acl->get();
@@ -181,28 +198,20 @@ class Thing extends \Zotlabs\Web\Controller {
intval(local_channel()),
dbesc($hash)
);
+
if($r) {
Libsync::build_sync_packet(0, array('obj' => $r));
}
if($activity) {
- $arr = array();
- $links = array(array('rel' => 'alternate','type' => 'text/html', 'href' => $url));
- if($local_photo)
- $links[] = array('rel' => 'photo', 'type' => $local_photo_type, 'href' => $local_photo);
- $objtype = ACTIVITY_OBJ_THING;
-
- $obj = json_encode(array(
- 'type' => $objtype,
- 'id' => $url,
- 'link' => $links,
- 'title' => $name,
- 'content' => $name
- ));
+ $obj = Activity::fetch_thing(['id' => $r[0]['obj_obj']]);
$bodyverb = str_replace('OBJ: ', '',t('OBJ: %1$s %2$s %3$s'));
+ $arr['uuid'] = $r[0]['obj_obj'];
+ $arr['mid'] = z_root() . '/thing/' . $arr['uuid'];
+
$arr['owner_xchan'] = $channel['channel_hash'];
$arr['author_xchan'] = $channel['channel_hash'];
@@ -213,29 +222,38 @@ class Thing extends \Zotlabs\Web\Controller {
$ulink = '[zrl=' . $channel['xchan_url'] . ']' . $channel['channel_name'] . '[/zrl]';
$plink = '[zrl=' . $url . ']' . $name . '[/zrl]';
- $arr['body'] = sprintf( $bodyverb, $ulink, $translated_verb, $plink );
+ $arr['title'] = $channel['channel_name'] . ' ' . $translated_verb . ' ' . $name;
+ $arr['body'] = $url;
if($local_photo)
- $arr['body'] .= "\n\n[zmg]" . $local_photo . "[/zmg]";
+ $arr['body'] = '[zrl=' . $url . '][zmg=' . $local_photo . ']' . $name . '[/zmg][/zrl]';
- $arr['verb'] = $verb;
- $arr['obj_type'] = $objtype;
+ $arr['verb'] = 'Create';
+ $arr['obj_type'] = 'Page';
$arr['obj'] = $obj;
- if(! $profile['is_default']) {
+ $arr['allow_cid'] = $x['allow_cid'];
+ $arr['allow_gid'] = $x['allow_gid'];
+ $arr['deny_cid'] = $x['deny_cid'];
+ $arr['deny_gid'] = $x['deny_gid'];
+
+ if (!$profile['is_default']) {
$arr['item_private'] = true;
- $str = '';
+
$r = q("select abook_xchan from abook where abook_channel = %d and abook_profile = '%s'",
intval(local_channel()),
dbesc($profile_guid)
);
+
if($r) {
$arr['allow_cid'] = '';
- foreach($r as $rr)
+ foreach($r as $rr) {
$arr['allow_cid'] .= '<' . $rr['abook_xchan'] . '>';
+ }
}
- else
+ else {
$arr['allow_cid'] = '<' . get_observer_hash() . '>';
+ }
}
$ret = post_activity_item($arr);
@@ -254,21 +272,30 @@ class Thing extends \Zotlabs\Web\Controller {
intval(TERM_OBJ_THING),
dbesc(argv(1))
);
- if($r)
- $sql_extra = permissions_sql($r[0]['obj_channel']);
+
+ if (!$r) {
+ notice( t('item not found.') . EOL);
+ return;
+ }
+
+ $sql_extra = permissions_sql($r[0]['obj_channel']);
$r = q("select * from obj where obj_type = %d and obj_obj = '%s' $sql_extra limit 1",
intval(TERM_OBJ_THING),
dbesc(argv(1))
);
- if($r) {
+ if ($r) {
+ $channel = channelx_by_n($r[0]['obj_channel']);
+ profile_load($channel['channel_address']);
+
return replace_macros(get_markup_template('show_thing.tpl'), array(
- '$header' => t('Show Thing'),
+ '$header' => $channel['xchan_name'] . ' ' . $r[0]['obj_verb'] . ' ' . $r[0]['obj_term'],
'$edit' => t('Edit'),
'$delete' => t('Delete'),
'$canedit' => ((local_channel() && local_channel() == $r[0]['obj_channel']) ? true : false),
- '$thing' => $r[0] ));
+ '$thing' => $r[0]
+ ));
}
else {
notice( t('item not found.') . EOL);
@@ -283,6 +310,8 @@ class Thing extends \Zotlabs\Web\Controller {
return;
}
+ profile_load($channel['channel_address']);
+
$acl = new \Zotlabs\Access\AccessList($channel);
$channel_acl = $acl->get();
@@ -319,7 +348,7 @@ class Thing extends \Zotlabs\Web\Controller {
'$img_lbl' => t('URL for photo of thing (optional)'),
'$imgurl' => $r[0]['obj_imgurl'],
'$permissions' => t('Permissions'),
- '$aclselect' => populate_acl($channel_acl,false),
+ '$aclselect' => populate_acl($channel_acl, false, \Zotlabs\Lib\PermissionDescription::fromGlobalPermission('view_pages')),
'$allow_cid' => acl2json($channel_acl['allow_cid']),
'$allow_gid' => acl2json($channel_acl['allow_gid']),
'$deny_cid' => acl2json($channel_acl['deny_cid']),
@@ -344,7 +373,6 @@ class Thing extends \Zotlabs\Web\Controller {
return '';
}
-
delete_thing_photo($r[0]['obj_imgurl'],get_observer_hash());
$x = q("delete from obj where obj_obj = '%s' and obj_type = %d and obj_channel = %d",
@@ -372,7 +400,7 @@ class Thing extends \Zotlabs\Web\Controller {
'$url_lbl' => t('URL of thing (optional)'),
'$img_lbl' => t('URL for photo of thing (optional)'),
'$permissions' => t('Permissions'),
- '$aclselect' => populate_acl($channel_acl,false),
+ '$aclselect' => populate_acl($channel_acl, false, \Zotlabs\Lib\PermissionDescription::fromGlobalPermission('view_pages')),
'$allow_cid' => acl2json($channel_acl['allow_cid']),
'$allow_gid' => acl2json($channel_acl['allow_gid']),
'$deny_cid' => acl2json($channel_acl['deny_cid']),
diff --git a/Zotlabs/Module/Vote.php b/Zotlabs/Module/Vote.php
index 870fd760c..06c47f91f 100644
--- a/Zotlabs/Module/Vote.php
+++ b/Zotlabs/Module/Vote.php
@@ -98,7 +98,7 @@ class Vote extends Controller {
// now reset the placeholders
- $item['verb'] = ACTIVITY_POST;
+ $item['verb'] = 'Create';
$item['obj_type'] = 'Answer';
unset($item['author']);
diff --git a/Zotlabs/Render/Theme.php b/Zotlabs/Render/Theme.php
index 2e6ca0c32..543bf7a3f 100644
--- a/Zotlabs/Render/Theme.php
+++ b/Zotlabs/Render/Theme.php
@@ -3,7 +3,7 @@
namespace Zotlabs\Render;
use App;
-
+use Zotlabs\Lib\PConfig;
class Theme {
@@ -26,8 +26,8 @@ class Theme {
*/
static public function current(){
- self::$system_theme = ((isset(\App::$config['system']['theme']))
- ? \App::$config['system']['theme'] : '');
+ self::$system_theme = ((isset(App::$config['system']['theme']))
+ ? App::$config['system']['theme'] : '');
self::$session_theme = ((isset($_SESSION) && x($_SESSION, 'theme'))
? $_SESSION['theme'] : self::$system_theme);
@@ -35,7 +35,7 @@ class Theme {
// Find the theme that belongs to the channel whose stuff we are looking at
- if(\App::$profile_uid) {
+ if(App::$profile_uid) {
$r = q("select channel_theme from channel where channel_id = %d limit 1",
intval(\App::$profile_uid)
);
@@ -46,8 +46,9 @@ class Theme {
// Themes from Comanche layouts over-ride the channel theme
- if(array_key_exists('theme', \App::$layout) && \App::$layout['theme'])
- $page_theme = \App::$layout['theme'];
+ if(array_key_exists('theme', \App::$layout) && \App::$layout['theme']) {
+ $page_theme = App::$layout['theme'];
+ }
$chosen_theme = self::$session_theme;
@@ -96,14 +97,11 @@ class Theme {
*
* Provide a sane default if nothing is chosen or the specified theme does not exist.
*
- * @param bool $installing (optional) default false, if true return the name of the first base theme
- *
* @return string
*/
- static public function url($installing = false) {
+ static public function url() {
- if($installing)
- return self::$base_themes[0];
+ $uid = App::$profile_uid ?: local_channel();
$theme = self::current();
@@ -111,16 +109,23 @@ class Theme {
$s = ((count($theme) > 1) ? $theme[1] : '');
$opts = '';
- $opts = ((\App::$profile_uid) ? '?f=&puid=' . \App::$profile_uid : '');
+ $opts = (($uid) ? '?puid=' . $uid : '');
- $schema_str = ((x(\App::$layout,'schema')) ? '&schema=' . App::$layout['schema'] : '');
+ $schema_str = ((x(App::$layout,'schema')) ? '&schema=' . App::$layout['schema'] : '');
if(($s) && (! $schema_str))
$schema_str = '&schema=' . $s;
$opts .= $schema_str;
+ if ($uid) {
+ $timestamp = PConfig::Get($uid, 'system', 'style_update', false);
+ if ($timestamp) {
+ $opts .= '&updt=' . $timestamp;
+ }
+ }
+
if(file_exists('view/theme/' . $t . '/php/style.php'))
- return('/view/theme/' . $t . '/php/style.pcss' . $opts);
+ return('/view/theme/' . $t . '/php/style.css' . $opts);
return('/view/theme/' . $t . '/css/style.css');
}
diff --git a/Zotlabs/Storage/BasicAuth.php b/Zotlabs/Storage/BasicAuth.php
index d23f3d848..3fd1bcc1f 100644
--- a/Zotlabs/Storage/BasicAuth.php
+++ b/Zotlabs/Storage/BasicAuth.php
@@ -34,6 +34,14 @@ class BasicAuth extends DAV\Auth\Backend\AbstractBasic {
* @var int $channel_id
*/
public $channel_id = 0;
+
+ /**
+ * @brief channel_account_id of the current channel of the logged-in account.
+ *
+ * @var int $channel_account_id
+ */
+ public $channel_account_id = 0;
+
/**
* @brief channel_hash of the current channel of the logged-in account.
*
@@ -127,6 +135,7 @@ class BasicAuth extends DAV\Auth\Backend\AbstractBasic {
*/
protected function setAuthenticated($channel) {
$this->channel_name = $channel['channel_address'];
+ $this->channel_account_id = $channel['channel_account_id'];
$this->channel_id = $channel['channel_id'];
$this->channel_hash = $this->observer = $channel['channel_hash'];
@@ -139,8 +148,8 @@ class BasicAuth extends DAV\Auth\Backend\AbstractBasic {
}
}
- $_SESSION['uid'] = $channel['channel_id'];
- $_SESSION['account_id'] = $channel['channel_account_id'];
+ $_SESSION['uid'] = $this->channel_id;
+ $_SESSION['account_id'] = $this->channel_account_id;
$_SESSION['authenticated'] = true;
return true;
}
diff --git a/Zotlabs/Storage/Directory.php b/Zotlabs/Storage/Directory.php
index 683887b31..333251f69 100644
--- a/Zotlabs/Storage/Directory.php
+++ b/Zotlabs/Storage/Directory.php
@@ -173,7 +173,7 @@ class Directory extends DAV\Node implements DAV\ICollection, DAV\IQuota, DAV\IMo
intval($this->auth->owner_id)
);
- $x = attach_syspaths($this->auth->owner_id,$this->folder_hash);
+ $x = attach_syspaths($this->auth->owner_id, $this->folder_hash);
$y = q("update attach set display_path = '%s' where hash = '%s' and uid = %d",
dbesc($x['path']),
@@ -181,6 +181,20 @@ class Directory extends DAV\Node implements DAV\ICollection, DAV\IQuota, DAV\IMo
intval($this->auth->owner_id)
);
+ $z = q("select hash from attach where folder = '%s' and uid = %d",
+ dbesc($this->folder_hash),
+ intval($this->auth->owner_id)
+ );
+
+ if($z) {
+ foreach ($z as $zz) {
+ $rs = attach_move($this->auth->owner_id, $zz['hash'], $this->folder_hash, '', false);
+ if(!$rs['success']) {
+ break;
+ }
+ }
+ }
+
$ch = channelx_by_n($this->auth->owner_id);
if ($ch) {
$sync = attach_export_data($ch, $this->folder_hash);
@@ -481,7 +495,7 @@ class Directory extends DAV\Node implements DAV\ICollection, DAV\IQuota, DAV\IMo
}
- public function moveInto($targetName,$sourcePath, DAV\INode $sourceNode) {
+ public function moveInto($targetName, $sourcePath, DAV\INode $sourceNode) {
$channel_id = $this->auth->owner_id;
// Files have $sourceNode->data['hash'] set. For directories rely on $sourceNode->folder_hash.
diff --git a/Zotlabs/Storage/File.php b/Zotlabs/Storage/File.php
index dc60a72ae..ff379e2e9 100644
--- a/Zotlabs/Storage/File.php
+++ b/Zotlabs/Storage/File.php
@@ -38,6 +38,8 @@ class File extends DAV\Node implements DAV\IFile {
*/
private $name;
+ public $os_path;
+ public $folder_hash;
/**
* Sets up the node, expects a full path name.
diff --git a/Zotlabs/Update/_1260.php b/Zotlabs/Update/_1260.php
new file mode 100644
index 000000000..7f91418f6
--- /dev/null
+++ b/Zotlabs/Update/_1260.php
@@ -0,0 +1,58 @@
+<?php
+namespace Zotlabs\Update;
+
+class _1260 {
+ public function run() {
+
+ $has_sodium = function_exists('sodium_crypto_sign_keypair');
+ $has_bcmath = function_exists('bcadd');
+ $has_gmp = function_exists('gmp_add');
+
+ if (!$has_sodium) {
+ return UPDATE_FAILED;
+ }
+
+ if (!($has_gmp || $has_bcmath)) {
+ return UPDATE_FAILED;
+ }
+
+ dbq("START TRANSACTION");
+
+ if(ACTIVE_DBTYPE == DBTYPE_POSTGRES) {
+ $r1 = dbq("ALTER TABLE channel ADD channel_epubkey text NOT NULL DEFAULT ''");
+ $r2 = dbq("ALTER TABLE channel ADD channel_eprvkey text NOT NULL DEFAULT ''");
+ }
+ else {
+ $r1 = dbq("ALTER TABLE channel ADD channel_epubkey text NOT NULL");
+ $r2 = dbq("ALTER TABLE channel ADD channel_eprvkey text NOT NULL");
+ }
+
+ $channels = dbq("select channel_id from channel where true");
+ if ($channels) {
+ foreach ($channels as $channel) {
+ $keys = sodium_crypto_sign_keypair();
+ $pubkey = sodium_bin2base64(sodium_crypto_sign_publickey($keys), SODIUM_BASE64_VARIANT_ORIGINAL_NO_PADDING);
+ $prvkey = sodium_bin2base64(sodium_crypto_sign_secretkey($keys), SODIUM_BASE64_VARIANT_ORIGINAL_NO_PADDING);
+ q("update channel set channel_epubkey = '%s', channel_eprvkey = '%s' where channel_id = %d",
+ dbesc($pubkey),
+ dbesc($prvkey),
+ intval($channel['channel_id'])
+ );
+ }
+ }
+
+ if ($r1 && $r2) {
+ dbq("COMMIT");
+ return UPDATE_SUCCESS;
+ }
+
+ dbq("ROLLBACK");
+ return UPDATE_FAILED;
+ }
+
+ public function verify() {
+ $columns = db_columns('channel');
+ return in_array('channel_epubkey', $columns) && in_array('channel_eprvkey', $columns);
+ }
+}
+
diff --git a/Zotlabs/Update/_1261.php b/Zotlabs/Update/_1261.php
new file mode 100644
index 000000000..43fd0b098
--- /dev/null
+++ b/Zotlabs/Update/_1261.php
@@ -0,0 +1,58 @@
+<?php
+namespace Zotlabs\Update;
+
+use Zotlabs\Lib\Multibase;
+
+class _1261 {
+ public function run() {
+
+ $has_sodium = function_exists('sodium_crypto_sign_keypair');
+ $has_bcmath = function_exists('bcadd');
+ $has_gmp = function_exists('gmp_add');
+
+ if (!$has_sodium) {
+ return UPDATE_FAILED;
+ }
+
+ if (!($has_gmp || $has_bcmath)) {
+ return UPDATE_FAILED;
+ }
+
+ dbq("START TRANSACTION");
+
+ if(ACTIVE_DBTYPE == DBTYPE_POSTGRES) {
+ $r1 = dbq("ALTER TABLE xchan ADD xchan_epubkey text NOT NULL DEFAULT ''");
+ $r2 = dbq("ALTER TABLE xchan ADD xchan_updated timestamp NOT NULL DEFAULT '0001-01-01 00:00:00'");
+ }
+ else {
+ $r1 = dbq("ALTER TABLE xchan ADD xchan_epubkey text NOT NULL");
+ $r2 = dbq("ALTER TABLE xchan ADD xchan_updated datetime NOT NULL DEFAULT '0001-01-01 00:00:00'");
+ }
+
+ $channels = dbq("select * from channel where true");
+ if ($channels) {
+ foreach ($channels as $channel) {
+ $epubkey = (new Multibase())->publicKey($channel['channel_epubkey']);
+ q("update xchan set xchan_epubkey = '%s' where xchan_url = '%s'",
+ dbesc($epubkey),
+ dbesc(channel_url($channel))
+ );
+ }
+ }
+
+ if ($r1 && $r2) {
+ dbq("COMMIT");
+ return UPDATE_SUCCESS;
+ }
+
+ dbq("ROLLBACK");
+ return UPDATE_FAILED;
+
+ }
+
+ public function verify() {
+ $columns = db_columns('xchan');
+ return in_array('xchan_epubkey', $columns) && in_array('xchan_updated', $columns);
+ }
+}
+
diff --git a/Zotlabs/Update/_1262.php b/Zotlabs/Update/_1262.php
new file mode 100644
index 000000000..f457a0116
--- /dev/null
+++ b/Zotlabs/Update/_1262.php
@@ -0,0 +1,30 @@
+<?php
+
+namespace Zotlabs\Update;
+
+class _1262 {
+
+ function run() {
+
+ dbq("START TRANSACTION");
+
+
+ if(ACTIVE_DBTYPE == DBTYPE_POSTGRES) {
+ $r = true;
+ }
+
+ if(ACTIVE_DBTYPE == DBTYPE_MYSQL) {
+ $r = dbq("ALTER TABLE session MODIFY COLUMN sess_data MEDIUMTEXT NOT NULL");
+ }
+
+ if($r) {
+ dbq("COMMIT");
+ return UPDATE_SUCCESS;
+ }
+
+ q("ROLLBACK");
+ return UPDATE_FAILED;
+
+ }
+
+}
diff --git a/Zotlabs/Update/_1263.php b/Zotlabs/Update/_1263.php
new file mode 100644
index 000000000..bd12e7321
--- /dev/null
+++ b/Zotlabs/Update/_1263.php
@@ -0,0 +1,26 @@
+<?php
+
+namespace Zotlabs\Update;
+
+class _1263 {
+
+ function run() {
+
+ dbq("START TRANSACTION");
+
+ $poke = hash('whirlpool', 'Poke');
+ $mood = hash('whirlpool', 'Mood');
+
+ $r = dbq("DELETE FROM app WHERE (app_id = '$poke' OR app_id = '$mood') AND app_system = 1");
+
+ if($r) {
+ dbq("COMMIT");
+ return UPDATE_SUCCESS;
+ }
+
+ dbq("ROLLBACK");
+ return UPDATE_FAILED;
+
+ }
+
+}
diff --git a/Zotlabs/Web/HTTPSig.php b/Zotlabs/Web/HTTPSig.php
index 36a00528e..793b8cb45 100644
--- a/Zotlabs/Web/HTTPSig.php
+++ b/Zotlabs/Web/HTTPSig.php
@@ -27,9 +27,12 @@ class HTTPSig {
* @param string $alg hash algorithm (one of 'sha256','sha512')
* @return string The generated digest header string for $body
*/
-
static function generate_digest_header($body, $alg = 'sha256') {
+ if ($body === null) {
+ $body = '';
+ }
+
$digest = base64_encode(hash($alg, $body, true));
switch ($alg) {
case 'sha512':
@@ -41,37 +44,42 @@ class HTTPSig {
}
}
- static function find_headers($data, &$body) {
+ public static function find_headers($data, &$body) {
// decide if $data arrived via controller submission or curl
+ // changes $body for the caller
- if (is_array($data) && $data['header']) {
- if (!$data['success'])
+ if (is_array($data) && array_key_exists('header', $data)) {
+ if (!$data['success']) {
+ $body = EMPTY_STR;
return [];
+ }
- $h = new HTTPHeaders($data['header']);
- $headers = $h->fetcharr();
- $body = $data['body'];
- $headers['(request-target)'] = $data['request_target'];
- }
+ if (!$data['header']) {
+ $body = EMPTY_STR;
+ return [];
+ }
- else {
- $headers = [];
+ $h = new HTTPHeaders($data['header']);
+ $headers = $h->fetcharr();
+ $body = $data['body'];
+ $headers['(request-target)'] = $data['request_target'];
+ } else {
+ $headers = [];
$headers['(request-target)'] = strtolower($_SERVER['REQUEST_METHOD']) . ' ' . $_SERVER['REQUEST_URI'];
- $headers['content-type'] = $_SERVER['CONTENT_TYPE'];
- $headers['content-length'] = $_SERVER['CONTENT_LENGTH'];
+ $headers['content-type'] = $_SERVER['CONTENT_TYPE'];
+ $headers['content-length'] = $_SERVER['CONTENT_LENGTH'];
foreach ($_SERVER as $k => $v) {
if (strpos($k, 'HTTP_') === 0) {
- $field = str_replace('_', '-', strtolower(substr($k, 5)));
+ $field = str_replace('_', '-', strtolower(substr($k, 5)));
$headers[$field] = $v;
}
}
}
//logger('SERVER: ' . print_r($_SERVER,true), LOGGER_ALL);
-
- //logger('headers: ' . print_r($headers,true), LOGGER_ALL);
+ //logger('found_headers: ' . print_r($headers,true), LOGGER_ALL);
return $headers;
}
@@ -99,6 +107,10 @@ class HTTPSig {
if (!$headers)
return $result;
+ if (is_array($body)) {
+ btlogger('body is array:' . print_r($body, true));
+ }
+
$sig_block = null;
if (array_key_exists('signature', $headers)) {
@@ -214,8 +226,10 @@ class HTTPSig {
$result['content_signed'] = true;
$digest = explode('=', $headers['digest'], 2);
$digest[0] = strtoupper($digest[0]);
+
if ($digest[0] === 'SHA-256')
$hashalg = 'sha256';
+
if ($digest[0] === 'SHA-512')
$hashalg = 'sha512';
diff --git a/Zotlabs/Widget/Messages.php b/Zotlabs/Widget/Messages.php
index 2a612c2ce..8654d8e8f 100644
--- a/Zotlabs/Widget/Messages.php
+++ b/Zotlabs/Widget/Messages.php
@@ -61,8 +61,8 @@ class Messages {
$channel = App::get_channel();
$item_normal = item_normal();
- $item_normal .= " and item.verb not in ('Add', 'Remove', '" . ACTIVITY_FOLLOW . "', '" . ACTIVITY_TAG . "') ";
-
+ // Filter internal follow activities and strerams add/remove activities
+ $item_normal .= " and item.verb not in ('Add', 'Remove', 'Follow', 'Ignore', '" . ACTIVITY_FOLLOW . "') ";
$item_normal_i = str_replace('item.', 'i.', $item_normal);
$item_normal_c = str_replace('item.', 'c.', $item_normal);
$entries = [];
@@ -76,16 +76,16 @@ class Messages {
$vnotify_sql_i = '';
if (!($vnotify & VNOTIFY_LIKE)) {
- $vnotify_sql_c = " AND c.verb NOT IN ('" . dbesc(ACTIVITY_LIKE) . "', '" . dbesc(ACTIVITY_DISLIKE) . "') ";
- $vnotify_sql_i = " AND i.verb NOT IN ('" . dbesc(ACTIVITY_LIKE) . "', '" . dbesc(ACTIVITY_DISLIKE) . "') ";
+ $vnotify_sql_c = " AND c.verb NOT IN ('Like', 'Dislike', '" . dbesc(ACTIVITY_LIKE) . "', '" . dbesc(ACTIVITY_DISLIKE) . "') ";
+ $vnotify_sql_i = " AND i.verb NOT IN ('Like', 'Dislike', '" . dbesc(ACTIVITY_LIKE) . "', '" . dbesc(ACTIVITY_DISLIKE) . "') ";
}
elseif (!feature_enabled(local_channel(), 'dislike')) {
- $vnotify_sql_c = " AND c.verb NOT IN ('" . dbesc(ACTIVITY_DISLIKE) . "') ";
- $vnotify_sql_i = " AND i.verb NOT IN ('" . dbesc(ACTIVITY_DISLIKE) . "') ";
+ $vnotify_sql_c = " AND c.verb NOT IN ('Dislike', '" . dbesc(ACTIVITY_DISLIKE) . "') ";
+ $vnotify_sql_i = " AND i.verb NOT IN ('Dislike', '" . dbesc(ACTIVITY_DISLIKE) . "') ";
}
if($author) {
- $author_sql = " AND i.owner_xchan = '" . protect_sprintf(dbesc($author)) . "' ";
+ $author_sql = " AND (i.owner_xchan = '" . protect_sprintf(dbesc($author)) . "') ";
}
switch($type) {
@@ -102,11 +102,8 @@ class Messages {
$type_sql = ' AND i.item_private IN (0, 1) ';
}
- // FEP-5624 filter approvals for comments
- $approvals_c = " AND c.verb NOT IN ('" . dbesc(ACTIVITY_ATTEND) . "', 'Accept', '" . dbesc(ACTIVITY_ATTENDNO) . "', 'Reject') ";
-
$items = q("SELECT *,
- (SELECT count(*) FROM item c WHERE c.uid = %d AND c.parent = i.parent AND c.item_unseen = 1 AND c.item_thread_top = 0 $item_normal_c $approvals_c $vnotify_sql_c) AS unseen_count
+ (SELECT count(*) FROM item c WHERE c.uid = %d AND c.parent = i.parent AND c.item_unseen = 1 AND c.item_thread_top = 0 $item_normal_c $vnotify_sql_c) AS unseen_count
FROM item i WHERE i.uid = %d
AND i.created <= '%s'
$type_sql
@@ -148,6 +145,9 @@ class Messages {
if($item['owner_xchan'] !== $item['author_xchan']) {
$info .= t('via') . ' ' . $item['owner']['xchan_name'];
}
+ elseif($item['verb'] === 'Announce' && isset($item['source'])) {
+ $info .= t('via') . ' ' . $item['source']['xchan_name'];
+ }
$summary = $item['title'];
if (!$summary) {
@@ -185,8 +185,9 @@ class Messages {
$entries[$i]['info'] = $info;
$entries[$i]['created'] = datetime_convert('UTC', date_default_timezone_get(), $item['created']);
$entries[$i]['summary'] = $summary;
- $entries[$i]['b64mid'] = gen_link_id($item['mid']);
- $entries[$i]['href'] = z_root() . '/hq/' . gen_link_id($item['mid']);
+ //$entries[$i]['b64mid'] = gen_link_id($item['mid']);
+ $entries[$i]['b64mid'] = $item['uuid'];
+ $entries[$i]['href'] = z_root() . '/hq/' . $item['uuid'];
$entries[$i]['icon'] = $icon;
$entries[$i]['unseen_count'] = (($item['unseen_count']) ? $item['unseen_count'] : (($item['item_unseen']) ? '&#8192;' : ''));
$entries[$i]['unseen_class'] = (($item['item_unseen']) ? 'primary' : 'secondary');
@@ -290,8 +291,8 @@ class Messages {
$entries[$i]['info'] = '';
$entries[$i]['created'] = datetime_convert('UTC', date_default_timezone_get(), $notice['created']);
$entries[$i]['summary'] = $summary;
- $entries[$i]['b64mid'] = (($notice['ntype'] & NOTIFY_INTRO) ? '' : basename($notice['link']));
- $entries[$i]['href'] = (($notice['ntype'] & NOTIFY_INTRO) ? $notice['link'] : z_root() . '/hq/' . basename($notice['link']));
+ $entries[$i]['b64mid'] = (($notice['ntype'] & NOTIFY_INTRO) ? '' : ((str_contains($notice['hash'], '-')) ? $notice['hash'] : basename($notice['link'])));
+ $entries[$i]['href'] = (($notice['ntype'] & NOTIFY_INTRO) ? $notice['link'] : z_root() . '/hq/' . ((str_contains($notice['hash'], '-')) ? $notice['hash'] : basename($notice['link'])));
$entries[$i]['icon'] = (($notice['ntype'] & NOTIFY_INTRO) ? '<i class="fa fa-user-plus"></i>' : '');
$i++;
diff --git a/Zotlabs/Widget/Pinned.php b/Zotlabs/Widget/Pinned.php
index bd1c0e462..1380c156b 100644
--- a/Zotlabs/Widget/Pinned.php
+++ b/Zotlabs/Widget/Pinned.php
@@ -46,7 +46,7 @@ class Pinned {
foreach($items as $item) {
- $midb64 = gen_link_id($item['mid']);
+ $midb64 = $item['uuid'];
if(isset($observer['xchan_hash']) && in_array($observer['xchan_hash'], get_pconfig($item['uid'], 'pinned_hide', $midb64, [])))
continue;
@@ -77,17 +77,6 @@ class Pinned {
}
}
- $consensus = (intval($item['item_consensus']) ? true : false);
- if($consensus) {
- $conv_responses['agree'] = [ 'title' => t('Agree','title') ];
- $conv_responses['disagree'] = [ 'title' => t('Disagree','title') ];
- $conv_responses['abstain'] = [ 'title' => t('Abstain','title') ];
- if($commentable && $observer) {
- $conlabels = [ t('I agree'), t('I disagree'), t('I abstain') ];
- $canvote = true;
- }
- }
-
$this->activity($item, $conv_responses);
$verified = (intval($item['item_verified']) ? t('Message signature validated') : '');
@@ -203,7 +192,7 @@ class Pinned {
if(empty($mids_list))
return [];
- $r = q("SELECT * FROM item WHERE mid IN ( '%s' ) AND uid = %d AND id = parent AND item_private = 0 ORDER BY created DESC",
+ $r = q("SELECT * FROM item WHERE uuid IN ( '%s' ) AND uid = %d AND id = parent AND item_private = 0 ORDER BY created DESC",
dbesc(implode(",", $mids_list)),
intval($this->uid)
);
@@ -225,39 +214,30 @@ class Pinned {
private function activity($item, &$conv_responses) {
foreach(array_keys($conv_responses) as $verb) {
+ $verb_sql = '';
switch($verb) {
case 'like':
- $v = ACTIVITY_LIKE;
+ $verb_sql = " AND verb IN ('Like', '" . ACTIVITY_LIKE . "') ";
break;
case 'dislike':
- $v = ACTIVITY_DISLIKE;
- break;
- case 'agree':
- $v = ACTIVITY_AGREE;
- break;
- case 'disagree':
- $v = ACTIVITY_DISAGREE;
- break;
- case 'abstain':
- $v = ACTIVITY_ABSTAIN;
+ $verb_sql = " AND verb IN ('Dislike', '" . ACTIVITY_DISLIKE . "') ";
break;
case 'attendyes':
- $v = ACTIVITY_ATTEND;
+ $verb_sql = " AND verb IN ('Accept', '" . ACTIVITY_ATTEND . "') ";
break;
case 'attendno':
- $v = ACTIVITY_ATTENDNO;
+ $verb_sql = " AND verb IN ('Reject', '" . ACTIVITY_ATTENDNO . "') ";
break;
case 'attendmaybe':
- $v = ACTIVITY_ATTENDMAYBE;
+ $verb_sql = " AND verb IN ('TentativeAccept', '" . ACTIVITY_ATTENDMAYBE . "') ";
break;
default:
break;
}
- $r = q("SELECT * FROM item WHERE parent = %d AND id <> parent AND verb = '%s' AND item_deleted = 0",
- intval($item['id']),
- dbesc($v)
+ $r = q("SELECT * FROM item WHERE parent = %d AND id <> parent $verb_sql AND item_deleted = 0",
+ intval($item['id'])
);
if(! $r) {
unset($conv_responses[$verb]);