aboutsummaryrefslogtreecommitdiffstats
path: root/Zotlabs/Lib
diff options
context:
space:
mode:
Diffstat (limited to 'Zotlabs/Lib')
-rw-r--r--Zotlabs/Lib/Activity.php127
-rw-r--r--Zotlabs/Lib/ActivityStreams.php2
-rw-r--r--Zotlabs/Lib/Apps.php2
-rw-r--r--Zotlabs/Lib/DReport.php10
-rw-r--r--Zotlabs/Lib/Enotify.php47
-rw-r--r--Zotlabs/Lib/IConfig.php6
-rw-r--r--Zotlabs/Lib/JcsEddsa2022.php25
-rw-r--r--Zotlabs/Lib/JcsEddsa2022SignException.php15
-rw-r--r--Zotlabs/Lib/Libzot.php18
-rw-r--r--Zotlabs/Lib/MessageFilter.php154
-rw-r--r--Zotlabs/Lib/Text.php9
-rw-r--r--Zotlabs/Lib/ThreadItem.php133
-rw-r--r--Zotlabs/Lib/ThreadStream.php9
-rw-r--r--Zotlabs/Lib/Traits/HelpHelperTrait.php2
14 files changed, 324 insertions, 235 deletions
diff --git a/Zotlabs/Lib/Activity.php b/Zotlabs/Lib/Activity.php
index 84b1cb4a2..296129ea2 100644
--- a/Zotlabs/Lib/Activity.php
+++ b/Zotlabs/Lib/Activity.php
@@ -13,6 +13,7 @@ use Zotlabs\Entity\Item;
require_once('include/event.php');
require_once('include/html2plain.php');
require_once('include/items.php');
+require_once('include/markdown.php');
class Activity {
@@ -68,10 +69,10 @@ class Activity {
if ($j) {
xchan_query($j, true);
$items = fetch_post_tags($j);
- }
- if ($items) {
- return self::encode_item(array_shift($items));
+ if ($items) {
+ return self::encode_item(array_shift($items));
+ }
}
return null;
@@ -165,7 +166,7 @@ class Activity {
}
else {
logger('fetch failed: ' . $url);
- logger($x['body']);
+ logger(print_r($x, true), LOGGER_DEBUG);
}
@@ -580,14 +581,12 @@ class Activity {
}
}
- if (intval($i['item_wall'])) {
- $ret['commentPolicy'] = map_scope(PermissionLimits::Get($i['uid'], 'post_comments'));
- }
-
if (intval($i['item_private']) === 2) {
$ret['directMessage'] = true;
}
+ $ret['commentPolicy'] = (($i['item_wall']) ? map_scope(PermissionLimits::Get($i['uid'], 'post_comments')) : '');
+
if (array_key_exists('comments_closed', $i) && $i['comments_closed'] !== EMPTY_STR && $i['comments_closed'] > NULL_DATE) {
if ($ret['commentPolicy']) {
$ret['commentPolicy'] .= ' ';
@@ -853,6 +852,8 @@ class Activity {
$entry['type'] = $att['mediaType'];
} elseif (array_key_exists('type', $att) && $att['type'] === 'Image') {
$entry['type'] = 'image/jpeg';
+ } elseif (array_key_exists('type', $att) && $att['type'] === 'Link') {
+ $entry['type'] = 'text/uri-list';
}
if (array_key_exists('name', $att) && $att['name']) {
$entry['name'] = html2plain(purify_html($att['name']), 256);
@@ -2117,35 +2118,25 @@ class Activity {
$s['owner_xchan'] = $act->actor['id'];
$s['author_xchan'] = $act->actor['id'];
- $content = [];
+ $s['mid'] = self::getMessageID($act);
- if (is_array($act->obj)) {
- $content = self::get_content($act->obj);
+ if (!$s['mid']) {
+ return false;
}
- $s['mid'] = $act->objprop('id');
-
- if (!$s['mid'] && is_string($act->obj)) {
- $s['mid'] = $act->obj;
- }
+ $s['uuid'] = self::getUUID($act);
- // pleroma fetched activities
- if (!$s['mid'] && isset($act->obj['data']['id'])) {
- $s['mid'] = $act->obj['data']['id'];
+ if (!$s['uuid']) {
+ // If we have not found anything useful, create an uuid v5 from the mid
+ $s['uuid'] = uuid_from_url($s['mid']);
}
- if ($act->objprop('type') === 'Profile') {
- $s['mid'] = $act->id;
- }
+ $content = [];
- if (!$s['mid']) {
- return false;
+ if (is_array($act->obj)) {
+ $content = self::get_content($act->obj);
}
- // Friendica sends the diaspora guid in a nonstandard field via AP
- // If no uuid is provided we will create an uuid v5 from the mid
- $s['uuid'] = (($act->objprop('diaspora:guid')) ?: uuid_from_url($s['mid']));
-
$s['parent_mid'] = $act->parent_id;
if (array_key_exists('published', $act->data)) {
@@ -2184,23 +2175,8 @@ class Activity {
$response_activity = true;
- $s['mid'] = $act->id;
- $s['uuid'] = ((!empty($act->data['diaspora:guid'])) ? $act->data['diaspora:guid'] : uuid_from_url($s['mid']));
-
$s['parent_mid'] = $act->objprop('id') ?: $act->obj;
-/*
- if ($act->objprop('inReplyTo')) {
- $s['parent_mid'] = $act->objprop('inReplyTo');
- }
-
- $s['thr_parent'] = $act->objprop('id') ?: $act->obj;
-
- if (empty($s['parent_mid']) || empty($s['thr_parent'])) {
- logger('response activity without parent_mid or thr_parent');
- return;
- }
-*/
// over-ride the object timestamp with the activity
if (isset($act->data['published'])) {
@@ -2300,10 +2276,15 @@ class Activity {
if (!array_key_exists('edited', $s))
$s['edited'] = $s['created'];
- $s['title'] = (($response_activity) ? EMPTY_STR : self::bb_content($content, 'name'));
- $s['summary'] = self::bb_content($content, 'summary');
+ $s['title'] = (($response_activity) ? EMPTY_STR : html2plain($content['name']));
+ $s['summary'] = (($content['summary'] !== $content['content']) ? html2plain($content['summary']) : '');
$s['body'] = ((self::bb_content($content, 'bbcode') && (!$response_activity)) ? self::bb_content($content, 'bbcode') : self::bb_content($content, 'content'));
+ // peertube quirks
+ if ($act->objprop('mediaType') === 'text/markdown') {
+ $s['body'] = markdown_to_bb($act->objprop('content'));
+ }
+
if ($act->objprop('quoteUrl')) {
$quote_bbcode = self::get_quote_bbcode($act->obj['quoteUrl']);
@@ -2435,7 +2416,8 @@ class Activity {
}
}
- $tag = (($poster) ? '[video poster="' . $poster . '"]' : '[video]' );
+ $tag = (($poster) ? '[video poster=\'' . $poster . '\']' : '[video]' );
+
$ptr = null;
if ($act->objprop('url')) {
@@ -2745,7 +2727,7 @@ class Activity {
$relay = $channel['channel_hash'] === $parent[0]['owner_xchan'];
- if (str_contains($parent[0]['tgt_type'], 'Collection') && !$relay && !$isCollectionOperation) {
+ if (str_contains($parent[0]['tgt_type'], 'Collection') && !$relay && !$is_collection_operation) {
logger('not a collection activity');
return;
}
@@ -2977,7 +2959,7 @@ class Activity {
if (intval($parent[0]['item_private']) === 0) {
if (intval($item['item_private'])) {
- $item['item_restrict'] = $item['item_restrict'] | 1;
+ $item['item_restrict'] = ((isset($item['item_restrict'])) ? $item['item_restrict'] | 1 : 1);
$item['allow_cid'] = '<' . $channel['channel_hash'] . '>';
$item['allow_gid'] = $item['deny_cid'] = $item['deny_gid'] = '';
}
@@ -3032,8 +3014,7 @@ class Activity {
}
if ($x['success']) {
-
- if ($relay && $channel['channel_hash'] === $x['item']['owner_xchan'] && $x['item']['verb'] !== 'Add' && !$isCollectionOperation) {
+ if ($relay && $channel['channel_hash'] === $x['item']['owner_xchan'] && $x['item']['verb'] !== 'Add' && !$is_collection_operation) {
$approval = Activity::addToCollection($channel, $act->data, $x['item']['parent_mid'], $x['item'], deliver: false);
}
@@ -3051,13 +3032,8 @@ class Activity {
}
}
- $r = q("select * from item where id = %d limit 1",
- intval($x['item_id'])
- );
+ send_status_notifications($x['item_id'], $x['item']);
- if ($r) {
- send_status_notifications($x['item_id'], $r[0]);
- }
sync_an_item($channel['channel_id'], $x['item_id']);
}
@@ -3374,10 +3350,10 @@ class Activity {
if (array_key_exists('startTime', $act) && strpos($act['startTime'], -1, 1) === 'Z') {
$adjust = true;
$event['adjust'] = 1;
- $event['dtstart'] = datetime_convert('UTC', 'UTC', $event['startTime'] . (($adjust) ? '' : 'Z'));
+ $event['dtstart'] = datetime_convert('UTC', 'UTC', $act['startTime'] . (($adjust) ? '' : 'Z'));
}
if (array_key_exists('endTime', $act)) {
- $event['dtend'] = datetime_convert('UTC', 'UTC', $event['endTime'] . (($adjust) ? '' : 'Z'));
+ $event['dtend'] = datetime_convert('UTC', 'UTC', $act['endTime'] . (($adjust) ? '' : 'Z'));
}
else {
$event['nofinish'] = true;
@@ -3838,4 +3814,39 @@ class Activity {
return $result;
}
+
+ /**
+ * @brief Retrieves message ID from activity object.
+ * @param object $act Activity object
+ * @return string Message ID or empty string if not found
+ */
+ public static function getMessageID($act): string
+ {
+ if (ActivityStreams::is_response_activity($act->type) || $act->objprop('type') === 'Profile') {
+ return $act->id;
+ }
+
+ return $act->objprop('id', null)
+ ?? (is_string($act->obj) ? $act->obj : null)
+ ?? '';
+ }
+
+ /**
+ * @brief Retrieves the UUID from an activity object.
+ * @param object $act Activity object
+ * @return string UUID or empty string if not found
+ */
+ public static function getUUID($act): string
+ {
+ if (ActivityStreams::is_response_activity($act->type)) {
+ return $act->data['uuid']
+ ?? $act->data['diaspora:guid']
+ ?? '';
+ }
+
+ return $act->objprop('uuid', null)
+ ?? $act->objprop('diaspora:guid', null)
+ ?? '';
+ }
+
}
diff --git a/Zotlabs/Lib/ActivityStreams.php b/Zotlabs/Lib/ActivityStreams.php
index 55a1de5dd..f2b9050e3 100644
--- a/Zotlabs/Lib/ActivityStreams.php
+++ b/Zotlabs/Lib/ActivityStreams.php
@@ -529,8 +529,8 @@ class ActivityStreams {
public function checkEddsaSignature() {
$signer = $this->get_property_obj('verificationMethod', $this->sig);
-
$parseUrl = parse_url($signer);
+ $publicKey = null;
if (isset($parseUrl['fragment'])) {
if (str_starts_with($parseUrl['fragment'], 'z6Mk')) {
diff --git a/Zotlabs/Lib/Apps.php b/Zotlabs/Lib/Apps.php
index 0dc405ea9..337344645 100644
--- a/Zotlabs/Lib/Apps.php
+++ b/Zotlabs/Lib/Apps.php
@@ -341,7 +341,7 @@ class Apps {
'Suggest Channels' => t('Suggest Channels'),
'Login' => t('Login'),
'Channel Manager' => t('Channel Manager'),
- 'Network' => t('Stream'),
+ 'Network' => t('Network'),
'Settings' => t('Settings'),
'Files' => t('Files'),
'Webpages' => t('Webpages'),
diff --git a/Zotlabs/Lib/DReport.php b/Zotlabs/Lib/DReport.php
index ac8e0d377..99bb05293 100644
--- a/Zotlabs/Lib/DReport.php
+++ b/Zotlabs/Lib/DReport.php
@@ -35,7 +35,7 @@ class DReport {
}
function addto_update($status) {
- $this->status = $this->status . ' ' . $status;
+ $this->status = $this->status . ', ' . $status;
}
@@ -89,8 +89,14 @@ class DReport {
if(array_key_exists('reject',$dr) && intval($dr['reject']))
return false;
- if(! ($dr['sender']))
+ if (!$dr['sender']) {
return false;
+ }
+
+ // do not store dismissed create activities
+ if ($dr['status'] === 'not a collection activity') {
+ return false;
+ }
// Is the sender one of our channels?
diff --git a/Zotlabs/Lib/Enotify.php b/Zotlabs/Lib/Enotify.php
index 6820091d5..6d5e249ef 100644
--- a/Zotlabs/Lib/Enotify.php
+++ b/Zotlabs/Lib/Enotify.php
@@ -95,8 +95,8 @@ class Enotify {
if (array_key_exists('verb', $params['item'])) {
// localize_item() alters the original item so make a copy first
$i = $params['item'];
- logger('calling localize');
- localize_item($i);
+ // logger('calling localize');
+ // localize_item($i);
$title = $i['title'];
$body = $i['body'];
$private = (($i['item_private']) || intval($i['item_obscured']));
@@ -131,9 +131,9 @@ class Enotify {
logger('notification: mail');
$subject = sprintf( t('[$Projectname:Notify] New direct message received at %s'), $sitename);
- $preamble = sprintf( t('%1$s sent you a new direct message at %2$s'), $sender['xchan_name'], $sitename);
+ $preamble = sprintf( t('%1$s sent you a new private message at %2$s'), $sender['xchan_name'], $sitename);
$epreamble = sprintf( t('%1$s sent you %2$s.'), '[zrl=' . $sender['xchan_url'] . ']' . $sender['xchan_name'] . '[/zrl]', '[zrl=$itemlink]' . t('a direct message') . '[/zrl]');
- $sitelink = t('Please visit %s to view and/or reply to your direct messages.');
+ $sitelink = t('Please visit %s to view and/or reply to your private messages.');
$tsitelink = sprintf( $sitelink, $siteurl . '/hq/' . gen_link_id($params['item']['mid']));
$hsitelink = sprintf( $sitelink, '<a href="' . $siteurl . '/hq/' . gen_link_id($params['item']['mid']) . '">' . $sitename . '</a>');
$itemlink = $siteurl . '/hq/' . gen_link_id($params['item']['mid']);
@@ -146,7 +146,7 @@ class Enotify {
$itemlink = $params['link'];
- $action = (($moderated) ? t('requested to comment on') : t('commented on'));
+ $action = (($moderated) ? t('requested to post in') : t('posted in'));
if(array_key_exists('item',$params)) {
@@ -164,8 +164,8 @@ class Enotify {
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(activity_match($params['verb'], [ACTIVITY_SHARE]))
+ $action = (($moderated) ? t('requested to repeat') : t('repeated'));
}
@@ -271,7 +271,7 @@ class Enotify {
$itemlink = $params['link'];
- if (array_key_exists('item',$params) && (activity_match($params['item']['verb'], ['Like', 'Dislike', ACTIVITY_LIKE, ACTIVITY_DISLIKE]))) {
+ if (array_key_exists('item',$params) && (activity_match($params['item']['verb'], ['Like', 'Dislike', ACTIVITY_LIKE, ACTIVITY_DISLIKE, 'Announce']))) {
if(! $always_show_in_notices || !($vnotify & VNOTIFY_LIKE) || !feature_enabled($recip['channel_id'], 'dislike')) {
logger('notification: not a visible activity. Ignoring.');
pop_lang();
@@ -327,6 +327,9 @@ class Enotify {
if(activity_match($params['item']['verb'], ['Dislike', ACTIVITY_DISLIKE]))
$verb = (($moderated) ? t('requested to dislike') : t('disliked'));
+ if(activity_match($params['item']['verb'], [ACTIVITY_SHARE]))
+ $verb = (($moderated) ? t('requested to repeat') : t('repeated'));
+
// "your post"
if ($parent_item['author']['xchan_hash'] === $recip['channel_hash']) {
$dest_str = sprintf(t('%1$s %2$s [zrl=%3$s]your %4$s[/zrl]'),
@@ -508,9 +511,14 @@ class Enotify {
*/
+ $hash = ((in_array($params['verb'], ['Create', 'Update'])) ? $params['item']['uuid'] : $params['item']['thr_parent_uuid']);
+
+ if (!$hash) {
+ $hash = new_uuid();
+ }
$datarray = [];
- $datarray['hash'] = $params['item']['uuid'] ?? new_uuid();
+ $datarray['hash'] = $hash;
$datarray['sender_hash'] = $sender['xchan_hash'];
$datarray['xname'] = $sender['xchan_name'];
$datarray['url'] = $sender['xchan_url'];
@@ -569,8 +577,9 @@ class Enotify {
dbesc($datarray['otype'])
);
- $r = q("select id from notify where hash = '%s' and ntype = %d and uid = %d limit 1",
+ $r = q("select id from notify where hash = '%s' and link = '%s' and ntype = %d and uid = %d limit 1",
dbesc($datarray['hash']),
+ dbesc($itemlink),
intval($datarray['ntype']),
intval($recip['channel_id'])
);
@@ -848,8 +857,8 @@ class Enotify {
}
else {
$itemem_text = (($item['item_thread_top'])
- ? (($item['obj_type'] === 'Question') ? t('created a new poll') : t('created a new post'))
- : (($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]'))
+ ? (($item['obj_type'] === 'Question') ? t('started a poll') : t('started a conversation'))
+ : (($item['obj_type'] === 'Answer') ? sprintf( t('voted on %s\'s poll'), '[bdi]' . $item['owner']['xchan_name'] . '[/bdi]') : sprintf( t('posted in %s\'s conversation'), '[bdi]' . $item['owner']['xchan_name'] . '[/bdi]'))
);
if(in_array($item['obj_type'], ['Document', 'Video', 'Audio', 'Image'])) {
@@ -861,12 +870,7 @@ class Enotify {
if($item['edited'] > $item['created']) {
$edit = true;
- if($item['item_thread_top']) {
- $itemem_text = sprintf( t('edited a post dated %s'), relative_date($item['created']));
- }
- else {
- $itemem_text = sprintf( t('edited a comment dated %s'), relative_date($item['created']));
- }
+ $itemem_text = sprintf( t('edited a message dated %s'), relative_date($item['created']));
}
@@ -886,7 +890,7 @@ class Enotify {
'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['uuid']) ? $item['uuid'] : ''),
+ 'b64mid' => ((in_array($item['verb'] , ['Like', 'Dislike', 'Announce']) && !empty($item['thr_parent_uuid'])) ? $item['thr_parent_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)),
@@ -906,14 +910,13 @@ class Enotify {
}
static public function format_notify($tt) {
-
$message = trim(strip_tags(bbcode($tt['msg'])));
if(strpos($message, $tt['xname']) === 0)
$message = substr($message, strlen($tt['xname']) + 1);
$x = [
- 'notify_link' => (($tt['ntype'] === NOTIFY_MAIL) ? $tt['link'] : z_root() . '/notify/view/' . $tt['id']),
+ 'notify_link' => (($tt['ntype'] === NOTIFY_INTRO) ? z_root() . '/notify/view/' . $tt['id'] : $tt['link']),
'name' => $tt['xname'],
'url' => $tt['url'],
'photo' => $tt['photo'],
@@ -925,11 +928,9 @@ class Enotify {
];
return $x;
-
}
static public function format_intros($rr) {
-
return [
'notify_link' => z_root() . '/connections#' . $rr['abook_id'],
'name' => $rr['xchan_name'],
diff --git a/Zotlabs/Lib/IConfig.php b/Zotlabs/Lib/IConfig.php
index 74c1107f0..3540c2b24 100644
--- a/Zotlabs/Lib/IConfig.php
+++ b/Zotlabs/Lib/IConfig.php
@@ -13,6 +13,7 @@ class IConfig {
static public function Get(&$item, $family, $key, $default = false) {
$is_item = false;
+ $iid = null;
if(is_array($item)) {
$is_item = true;
@@ -27,12 +28,13 @@ class IConfig {
elseif(intval($item))
$iid = $item;
- if(! $iid)
+ if (!$iid)
return $default;
+
if(is_array($item) && array_key_exists('iconfig',$item) && is_array($item['iconfig'])) {
foreach($item['iconfig'] as $c) {
- if($c['iid'] == $iid && $c['cat'] == $family && $c['k'] == $key)
+ if (isset($c['iid']) && $c['iid'] == $iid && isset($c['cat']) && $c['cat'] == $family && isset($c['k']) && $c['k'] == $key)
return $c['v'];
}
}
diff --git a/Zotlabs/Lib/JcsEddsa2022.php b/Zotlabs/Lib/JcsEddsa2022.php
index 14f16c94b..c56f093af 100644
--- a/Zotlabs/Lib/JcsEddsa2022.php
+++ b/Zotlabs/Lib/JcsEddsa2022.php
@@ -7,11 +7,28 @@ use StephenHill\Base58;
class JcsEddsa2022 {
- public function __construct() {
- return $this;
- }
-
+ /**
+ * Sign arbitrary data with the keys of the provided channel.
+ *
+ * @param $data The data to be signed.
+ * @param array $channel A channel as an array of key/value pairs.
+ *
+ * @return An array with the following fields:
+ * - `type`: The type of signature, always `DataIntegrityProof`.
+ * - `cryptosuite`: The cryptographic algorithm used, always `eddsa-jcs-2022`.
+ * - `created`: The UTC date and timestamp when the signature was created.
+ * - `verificationMethod`: The channel URL and the public key separated by a `#`.
+ * - `proofPurpose`: The purpose of the signature, always `assertionMethod`.
+ * - `proofValue`: The signature itself.
+ *
+ * @throws JcsEddsa2022SignatureException if the channel is missing, or
+ * don't have valid keys.
+ */
public function sign($data, $channel): array {
+ if (!is_array($channel) || !isset($channel['channel_epubkey'], $channel['channel_eprvkey'])) {
+ throw new JcsEddsa2022SignException('Invalid or missing channel provided.');
+ }
+
$base58 = new Base58();
$pubkey = (new Multibase())->publicKey($channel['channel_epubkey']);
$options = [
diff --git a/Zotlabs/Lib/JcsEddsa2022SignException.php b/Zotlabs/Lib/JcsEddsa2022SignException.php
new file mode 100644
index 000000000..81d02d631
--- /dev/null
+++ b/Zotlabs/Lib/JcsEddsa2022SignException.php
@@ -0,0 +1,15 @@
+<?php
+/*
+ * SPDX-FileCopyrightText: 2025 The Hubzilla Community
+ * SPDX-FileContributor: Harald Eilertsen <haraldei@anduin.net>
+ *
+ * SPDX-License-Identifier: MIT
+ */
+
+namespace Zotlabs\Lib;
+
+use Exception;
+
+class JcsEddsa2022SignException extends Exception
+{
+}
diff --git a/Zotlabs/Lib/Libzot.php b/Zotlabs/Lib/Libzot.php
index 60fb5e034..d2d696356 100644
--- a/Zotlabs/Lib/Libzot.php
+++ b/Zotlabs/Lib/Libzot.php
@@ -1169,10 +1169,6 @@ class Libzot {
$raw_activity = $AS->data;
$AS = new ActivityStreams($raw_activity['object'], portable_id: $env['sender']);
-
- // Store the original activity id and type for later usage
- $AS->meta['original_id'] = $original_id;
- $AS->meta['original_type'] = $original_type;
}
if (is_array($AS->obj)) {
@@ -1546,6 +1542,7 @@ class Libzot {
$local_public = $public;
$item_result = null;
+ $parent = null;
$DR = new DReport(z_root(), $sender, $d, $arr['mid'], $arr['uuid']);
@@ -1853,19 +1850,12 @@ class Libzot {
dbesc($arr['author_xchan'])
);
- // If we import an add/remove activity ($is_collection_operation) we strip off the
- // add/remove part and only process the object.
- // When looking up the item to pass it to the notifier for relay, we need to look up
- // the original (stripped off) message id which we stored in $act->meta.
-
- $sql_mid = (($is_collection_operation && $relay && $channel['channel_hash'] === $arr['owner_xchan']) ? $act->meta['original_id'] : $arr['mid']);
-
// 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($sql_mid),
- dbesc(reverse_activity_mid($sql_mid)),
+ dbesc($arr['mid']),
+ dbesc(reverse_activity_mid($arr['mid'])),
intval($channel['channel_id'])
);
@@ -2007,7 +1997,7 @@ class Libzot {
}
$DR->addto_update('relayed');
- $result[] = $DR->get();
+ $result = [$DR->get()];
}
}
diff --git a/Zotlabs/Lib/MessageFilter.php b/Zotlabs/Lib/MessageFilter.php
index e7382c0d5..3f2db88c3 100644
--- a/Zotlabs/Lib/MessageFilter.php
+++ b/Zotlabs/Lib/MessageFilter.php
@@ -8,17 +8,18 @@ class MessageFilter {
public static function evaluate($item, $incl, $excl) {
- $text = prepare_text($item['body'],((isset($item['mimetype'])) ? $item['mimetype'] : 'text/bbcode'));
- $text = html2plain(($item['title']) ? $item['title'] . ' ' . $text : $text);
+ $text = prepare_text($item['body'], ((isset($item['mimetype'])) ? $item['mimetype'] : 'text/bbcode'));
+ $text = html2plain((!empty($item['title'])) ? $item['title'] . ' ' . $text : $text);
$lang = null;
-
if ((strpos($incl, 'lang=') !== false) || (strpos($excl, 'lang=') !== false) || (strpos($incl, 'lang!=') !== false) || (strpos($excl, 'lang!=') !== false)) {
$lang = detect_language($text);
}
$tags = ((isset($item['term']) && is_array($item['term']) && count($item['term'])) ? $item['term'] : false);
+ $until = null;
+
// exclude always has priority
$exclude = (($excl) ? explode("\n", $excl) : null);
@@ -41,7 +42,13 @@ class MessageFilter {
return false;
}
}
- elseif (substr($word, 0, 1) === '#' && $tags) {
+ elseif (str_starts_with($word, 'until=')) {
+ $until = strtotime(trim(substr($word, 6)));
+ if ($until > strtotime($item['created'] . ' UTC')) {
+ return false;
+ }
+ }
+ elseif (substr($word, 0, 1) === '#' && $tags) {
foreach ($tags as $t) {
if ((($t['ttype'] == TERM_HASHTAG) || ($t['ttype'] == TERM_COMMUNITYTAG)) && (($t['term'] === substr($word, 1)) || (substr($word, 1) === '*'))) {
return false;
@@ -89,7 +96,13 @@ class MessageFilter {
return true;
}
}
- elseif (substr($word, 0, 1) === '#' && $tags) {
+ elseif (str_starts_with($word, 'until=')) {
+ $until = strtotime(trim(substr($word, 6)));
+ if ($until > strtotime($item['created'] . ' UTC')) {
+ return true;
+ }
+ }
+ elseif (substr($word, 0, 1) === '#' && $tags) {
foreach ($tags as $t) {
if ((($t['ttype'] == TERM_HASHTAG) || ($t['ttype'] == TERM_COMMUNITYTAG)) && (($t['term'] === substr($word, 1)) || (substr($word, 1) === '*'))) {
return true;
@@ -124,9 +137,7 @@ class MessageFilter {
/**
- * @brief Test for Conditional Execution conditions. Shamelessly ripped off from Code/Render/Comanche
- *
- * This is extensible. The first version of variable testing supports tests of the forms:
+ * Evaluate a conditional expression with support for AND (&&) and OR (||) operators.
*
* - ?foo ~= baz which will check if item.foo contains the string 'baz';
* - ?foo == baz which will check if item.foo is the string 'baz';
@@ -143,103 +154,110 @@ class MessageFilter {
*
* The values 0, '', an empty array, and an unset value will all evaluate to false.
*
- * @param string $s
- * @param array $item
- * @return bool
+ * @param string $s The condition string to evaluate.
+ * @param array $item The associative array providing variable values.
+ * @return bool True if the condition is met, false otherwise.
*/
- public static function test_condition($s,$item) {
+ public static function test_condition($s, $item) {
+ $s = trim($s);
- if (preg_match('/(.*?)\s\~\=\s(.*?)$/', $s, $matches)) {
- $x = ((array_key_exists(trim($matches[1]),$item)) ? $item[trim($matches[1])] : EMPTY_STR);
- if (stripos($x, trim($matches[2])) !== false) {
- return true;
+ // Handle OR (||)
+ // Split on '||' not inside quotes
+ $or_parts = preg_split('/\s*\|\|\s*/', $s);
+ if (count($or_parts) > 1) {
+ foreach ($or_parts as $part) {
+ if (self::test_condition(ltrim($part, '?+'), $item)) {
+ return true;
+ }
}
return false;
}
- if (preg_match('/(.*?)\s\=\=\s(.*?)$/', $s, $matches)) {
- $x = ((array_key_exists(trim($matches[1]),$item)) ? $item[trim($matches[1])] : EMPTY_STR);
- if ($x == trim($matches[2])) {
- return true;
+ // Handle AND (&&)
+ // Split on '&&' not inside quotes
+ $and_parts = preg_split('/\s*\&\&\s*/', $s);
+ if (count($and_parts) > 1) {
+ foreach ($and_parts as $part) {
+ if (!self::test_condition(ltrim($part, '?+'), $item)) {
+ return false;
+ }
}
- return false;
+ return true;
+ }
+
+ // Basic checks
+
+ // Contains substring (case-insensitive)
+ if (preg_match('/(.*?)\s\~\=\s(.*?)$/', $s, $matches)) {
+ $x = ((array_key_exists(trim($matches[1]), $item)) ? $item[trim($matches[1])] : EMPTY_STR);
+ return (stripos($x, trim($matches[2])) !== false);
+ }
+
+ // Equality
+ if (preg_match('/(.*?)\s\=\=\s(.*?)$/', $s, $matches)) {
+ $x = ((array_key_exists(trim($matches[1]), $item)) ? $item[trim($matches[1])] : EMPTY_STR);
+ return ($x == trim($matches[2]));
}
+ // Inequality
if (preg_match('/(.*?)\s\!\=\s(.*?)$/', $s, $matches)) {
- $x = ((array_key_exists(trim($matches[1]),$item)) ? $item[trim($matches[1])] : EMPTY_STR);
- if ($x != trim($matches[2])) {
- return true;
- }
- return false;
+ $x = ((array_key_exists(trim($matches[1]), $item)) ? $item[trim($matches[1])] : EMPTY_STR);
+ return ($x != trim($matches[2]));
}
+ // Greater than or equal
if (preg_match('/(.*?)\s\>\=\s(.*?)$/', $s, $matches)) {
- $x = ((array_key_exists(trim($matches[1]),$item)) ? $item[trim($matches[1])] : EMPTY_STR);
- if ($x >= trim($matches[2])) {
- return true;
- }
- return false;
+ $x = ((array_key_exists(trim($matches[1]), $item)) ? $item[trim($matches[1])] : EMPTY_STR);
+ return ($x >= trim($matches[2]));
}
+ // Less than or equal
if (preg_match('/(.*?)\s\<\=\s(.*?)$/', $s, $matches)) {
- $x = ((array_key_exists(trim($matches[1]),$item)) ? $item[trim($matches[1])] : EMPTY_STR);
- if ($x <= trim($matches[2])) {
- return true;
- }
- return false;
+ $x = ((array_key_exists(trim($matches[1]), $item)) ? $item[trim($matches[1])] : EMPTY_STR);
+ return ($x <= trim($matches[2]));
}
+ // Greater than
if (preg_match('/(.*?)\s\>\s(.*?)$/', $s, $matches)) {
- $x = ((array_key_exists(trim($matches[1]),$item)) ? $item[trim($matches[1])] : EMPTY_STR);
- if ($x > trim($matches[2])) {
- return true;
- }
- return false;
+ $x = ((array_key_exists(trim($matches[1]), $item)) ? $item[trim($matches[1])] : EMPTY_STR);
+ return ($x > trim($matches[2]));
}
- if (preg_match('/(.*?)\s\>\s(.*?)$/', $s, $matches)) {
- $x = ((array_key_exists(trim($matches[1]),$item)) ? $item[trim($matches[1])] : EMPTY_STR);
- if ($x < trim($matches[2])) {
- return true;
- }
- return false;
+ // Less than
+ if (preg_match('/(.*?)\s\<\s(.*?)$/', $s, $matches)) {
+ $x = ((array_key_exists(trim($matches[1]), $item)) ? $item[trim($matches[1])] : EMPTY_STR);
+ return ($x < trim($matches[2]));
}
- if (preg_match('/[\$](.*?)\s\{\}\s(.*?)$/', $s, $matches)) {
- $x = ((array_key_exists(trim($matches[1]),$item)) ? $item[trim($matches[1])] : EMPTY_STR);
- if (is_array($x) && in_array(trim($matches[2]), $x)) {
- return true;
- }
- return false;
+ // Array contains value
+ if (preg_match('/(.*?)\s\{\}\s(.*?)$/', $s, $matches)) {
+ $x = ((array_key_exists(trim($matches[1]), $item)) ? $item[trim($matches[1])] : EMPTY_STR);
+ return (is_array($x) && in_array(trim($matches[2]), $x));
}
+ // Array contains key
if (preg_match('/(.*?)\s\{\*\}\s(.*?)$/', $s, $matches)) {
- $x = ((array_key_exists(trim($matches[1]),$item)) ? $item[trim($matches[1])] : EMPTY_STR);
- if (is_array($x) && array_key_exists(trim($matches[2]), $x)) {
- return true;
- }
- return false;
+ $x = ((array_key_exists(trim($matches[1]), $item)) ? $item[trim($matches[1])] : EMPTY_STR);
+ return (is_array($x) && array_key_exists(trim($matches[2]), $x));
}
// Ordering of this check (for falsiness) with relation to the following one (check for truthiness) is important.
+ // Falsy check
if (preg_match('/\!(.*?)$/', $s, $matches)) {
- $x = ((array_key_exists(trim($matches[1]),$item)) ? $item[trim($matches[1])] : EMPTY_STR);
- if (!$x) {
- return true;
- }
- return false;
+ $x = ((array_key_exists(trim($matches[1]), $item)) ? $item[trim($matches[1])] : EMPTY_STR);
+ return !$x;
}
+ // Truthy check (default)
if (preg_match('/(.*?)$/', $s, $matches)) {
- $x = ((array_key_exists(trim($matches[1]),$item)) ? $item[trim($matches[1])] : EMPTY_STR);
- if ($x) {
- return true;
- }
- return false;
+ $x = ((array_key_exists(trim($matches[1]), $item)) ? $item[trim($matches[1])] : EMPTY_STR);
+ return (bool)$x;
}
+ // If no conditions matched, return false
return false;
}
+
}
diff --git a/Zotlabs/Lib/Text.php b/Zotlabs/Lib/Text.php
index f593f9dd6..4a962670a 100644
--- a/Zotlabs/Lib/Text.php
+++ b/Zotlabs/Lib/Text.php
@@ -21,4 +21,13 @@ class Text {
return htmlspecialchars($string, ENT_COMPAT, 'UTF-8', false);
}
+ public static function rawurlencode_parts(string $string): string {
+ if (!$string) {
+ return EMPTY_STR;
+ }
+
+ return implode('/', array_map('rawurlencode', explode('/', $string)));
+ }
+
+
}
diff --git a/Zotlabs/Lib/ThreadItem.php b/Zotlabs/Lib/ThreadItem.php
index d0fa1e587..46fe6d815 100644
--- a/Zotlabs/Lib/ThreadItem.php
+++ b/Zotlabs/Lib/ThreadItem.php
@@ -4,8 +4,6 @@ namespace Zotlabs\Lib;
use App;
use Zotlabs\Access\AccessList;
-use Zotlabs\Lib\Apps;
-use Zotlabs\Lib\Config;
require_once('include/text.php');
@@ -26,6 +24,7 @@ class ThreadItem {
private $parent = null;
private $conversation = null;
private $redirect_url = null;
+ private $owner_addr = '';
private $owner_url = '';
private $owner_photo = '';
private $owner_name = '';
@@ -35,14 +34,12 @@ class ThreadItem {
private $channel = null;
private $display_mode = 'normal';
private $reload = '';
- private $mid_uuid_map = [];
-
public function __construct($data) {
$this->data = $data;
$this->toplevel = ($this->get_id() == $this->get_data_value('parent'));
- $this->threaded = Config::Get('system','thread_allow');
+ $this->threaded = ((local_channel()) ? PConfig::Get(local_channel(), 'system', 'thread_allow', true) : Config::Get('system', 'thread_allow', true));
// Prepare the children
if(isset($data['children'])) {
@@ -65,8 +62,6 @@ class ThreadItem {
unset($this->data['children']);
}
-
-
// allow a site to configure the order and content of the reaction emoji list
if($this->toplevel) {
$x = Config::Get('system','reactions');
@@ -84,7 +79,7 @@ class ThreadItem {
* _ false on failure
*/
- public function get_template_data($conv_responses, $mid_uuid_map, $thread_level=1, $conv_flags = []) {
+ public function get_template_data($thread_level=1, $conv_flags = []) {
$result = [];
$item = $this->get_data();
@@ -103,6 +98,8 @@ class ThreadItem {
$conv = $this->get_conversation();
$observer = $conv->get_observer();
+ $conv->mid_uuid_map[$item['mid']] = $item['uuid'];
+
$acl = new AccessList([]);
$acl->set($item);
@@ -114,7 +111,7 @@ class ThreadItem {
$locktype = intval($item['item_private']);
if ($locktype === 2) {
- $lock = t('Direct message');
+ $lock = t('Private message');
}
// 0 = limited based on public policy
@@ -209,9 +206,9 @@ class ThreadItem {
}
if (in_array($item['obj_type'], ['Event', ACTIVITY_OBJ_EVENT])) {
- $response_verbs[] = 'attendyes';
- $response_verbs[] = 'attendno';
- $response_verbs[] = 'attendmaybe';
+ $response_verbs[] = 'accept';
+ $response_verbs[] = 'reject';
+ $response_verbs[] = 'tentativeaccept';
if($this->is_commentable() && $observer) {
$isevent = true;
$attend = array( t('I will attend'), t('I will not attend'), t('I might attend'));
@@ -222,17 +219,8 @@ class ThreadItem {
$response_verbs[] = 'answer';
}
- if (!feature_enabled($conv->get_profile_owner(),'dislike')) {
- unset($conv_responses['dislike']);
- }
-
- $responses = get_responses($conv_responses,$response_verbs,$this,$item);
-
- $my_responses = [];
- foreach($response_verbs as $v) {
- $my_responses[$v] = ((isset($conv_responses[$v][$item['mid'] . '-m'])) ? 1 : 0);
- }
-
+ $response_verbs[] = 'comment';
+ $responses = get_responses($response_verbs, $item);
/*
* We should avoid doing this all the time, but it depends on the conversation mode
@@ -242,7 +230,13 @@ class ThreadItem {
$this->check_wall_to_wall();
+ $children = $this->get_children();
+ $children_count = count($children);
+
if($this->is_toplevel()) {
+ $conv->comments_total = $responses['comment']['count'] ?? 0;
+ $conv->comments_loaded = $children_count;
+
if((local_channel() && $conv->get_profile_owner() === local_channel()) || (local_channel() && App::$module === 'pubstream')) {
$star = [
'toggle' => t("Toggle Star Status"),
@@ -254,7 +248,6 @@ class ThreadItem {
$is_comment = true;
}
-
$verified = (intval($item['item_verified']) ? t('Message signature validated') : '');
$forged = ((($item['sig']) && (! intval($item['item_verified']))) ? t('Message signature incorrect') : '');
$unverified = '' ; // (($this->is_wall_to_wall() && (! intval($item['item_verified']))) ? t('Message cannot be verified') : '');
@@ -287,15 +280,11 @@ class ThreadItem {
if((in_array($item['obj_type'], ['Event', ACTIVITY_OBJ_EVENT])) && $conv->get_profile_owner() == local_channel())
$has_event = true;
- $like = [];
- $dislike = [];
$reply_to = [];
$reactions_allowed = false;
if($this->is_commentable() && $observer) {
- $like = array( t("I like this \x28toggle\x29"), t("like"));
- $dislike = array( t("I don't like this \x28toggle\x29"), t("dislike"));
- $reply_to = array( t("Reply to this comment"), t("reply"), t("Reply to"));
+ $reply_to = array( t("Reply to this message"), t("reply"), t("Reply to"));
$reactions_allowed = true;
}
@@ -339,9 +328,8 @@ class ThreadItem {
$viewthread = z_root() . '/channel/' . $owner_address . '?f=&mid=' . urlencode(gen_link_id($item['mid']));
$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();
+ $list_unseen_txt = $unseen_comments ? ['label' => sprintf(t('%d unseen'), $unseen_comments), 'count' => $unseen_comments] : [];
$has_tags = (($body['tags'] || $body['categories'] || $body['mentions'] || $body['attachments'] || $body['folders']) ? true : false);
@@ -351,14 +339,7 @@ class ThreadItem {
$midb64 = $item['uuid'];
$mids = [ $item['uuid'] ];
- $response_mids = [];
- foreach($response_verbs as $v) {
- if(isset($conv_responses[$v]['mids'][$item['mid']])) {
- $response_mids = array_merge($response_mids, $conv_responses[$v]['mids'][$item['mid']]);
- }
- }
- $mids = array_merge($mids, $response_mids);
$json_mids = json_encode($mids);
// Pinned item processing
@@ -372,6 +353,22 @@ class ThreadItem {
$contact = App::$contacts[$item['author_xchan']];
}
+ $blog_mode = $this->get_display_mode() === 'list';
+ $load_more = false;
+ $load_more_title = '';
+ $comments_total_percent = 0;
+ if (($conv->comments_total > $conv->comments_loaded) || ($blog_mode && $conv->comments_total > 3)) {
+ // provide a load more comments button
+ $load_more = true;
+ $load_more_title = sprintf(t('Load the next few of total %d comments'), $conv->comments_total);
+ $comments_total_percent = round(100 * 3 / $conv->comments_total);
+ }
+
+ $expand = '';
+ if ($this->threaded && !empty($item['comment_count'] && !$this->is_toplevel())) {
+ $expand = t('Expand Replies');
+ }
+
$tmp_item = array(
'template' => $this->get_template(),
'mode' => $mode,
@@ -384,9 +381,9 @@ class ThreadItem {
'folders' => $body['folders'],
'text' => strip_tags($body['html']),
'id' => $this->get_id(),
+ 'parent' => $item['parent'],
'mid' => $midb64,
'mids' => $json_mids,
- 'parent' => $item['parent'],
'author_id' => (($item['author']['xchan_addr']) ? $item['author']['xchan_addr'] : $item['author']['xchan_url']),
'author_is_group_actor' => (($item['author']['xchan_pubforum']) ? t('Forum') : ''),
'isevent' => $isevent,
@@ -431,6 +428,7 @@ class ThreadItem {
'vote_title' => t('Voting Options'),
'is_comment' => $is_comment,
'is_new' => $is_new,
+ 'owner_addr' => $this->get_owner_addr(),
'owner_url' => $this->get_owner_url(),
'owner_photo' => $this->get_owner_photo(),
'owner_name' => $this->get_owner_name(),
@@ -440,13 +438,12 @@ class ThreadItem {
'reactions' => $this->reactions,
// Item toolbar buttons
'emojis' => (($this->is_toplevel() && $this->is_commentable() && $observer && feature_enabled($conv->get_profile_owner(),'emojis')) ? '1' : ''),
- 'like' => $like,
- 'dislike' => ((feature_enabled($conv->get_profile_owner(),'dislike')) ? $dislike : ''),
- 'reply_to' => (((! $this->is_toplevel()) && feature_enabled($conv->get_profile_owner(),'reply_to')) ? $reply_to : ''),
+ 'reply_to' => ((feature_enabled($conv->get_profile_owner(),'reply_to')) ? $reply_to : ''),
'top_hint' => t("Go to previous comment"),
'share' => $share,
'embed' => $embed,
'rawmid' => $item['mid'],
+ 'parent_mid' => $item['parent_mid'],
'plink' => get_plink($item),
'edpost' => $edpost,
'star' => ((feature_enabled($conv->get_profile_owner(),'star_posts') && ($item['item_type'] == ITEM_TYPE_POST)) ? $star : ''),
@@ -466,16 +463,17 @@ class ThreadItem {
'list_unseen_txt' => $list_unseen_txt,
'markseen' => t('Mark all comments seen'),
'responses' => $responses,
- 'my_responses' => $my_responses,
+ // 'my_responses' => $my_responses,
'modal_dismiss' => t('Close'),
'comment' => ($item['item_delayed'] ? '' : $this->get_comment_box()),
+ 'comment_hidden' => feature_enabled($conv->get_profile_owner(),'reply_to'),
'no_comment' => (($item['item_thread_top'] && $item['item_nocomment'])? t('Comments disabled') : ''),
'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_uuid' => (($item['parent_mid'] != $item['thr_parent']) ? $mid_uuid_map[$item['thr_parent']] : ''),
+ 'thr_parent_uuid' => (($item['parent_mid'] !== $item['thr_parent'] && isset($conv->mid_uuid_map[$item['thr_parent']])) ? $conv->mid_uuid_map[$item['thr_parent']] : ''),
'contact_id' => (($contact) ? $contact['abook_id'] : ''),
'moderate' => ($item['item_blocked'] == ITEM_MODERATED),
'moderate_approve' => t('Approve'),
@@ -483,7 +481,25 @@ class ThreadItem {
'rtl' => in_array($item['lang'], rtl_languages()),
'reactions_allowed' => $reactions_allowed,
'reaction_str' => [t('Add yours'), t('Remove yours')],
- 'is_contained' => $this->is_toplevel() && str_contains($item['tgt_type'], 'Collection')
+ 'is_contained' => $this->is_toplevel() && str_contains($item['tgt_type'], 'Collection'),
+ 'observer_activity' => [
+ 'like' => intval($item['observer_like_count'] ?? 0),
+ 'dislike' => intval($item['observer_dislike_count'] ?? 0),
+ 'announce' => intval($item['observer_announce_count'] ?? 0),
+ 'comment' => intval($item['observer_comment_count'] ?? 0),
+ 'accept' => intval($item['observer_accept_count'] ?? 0),
+ 'reject' => intval($item['observer_reject_count'] ?? 0),
+ 'tentativeaccept' => intval($item['observer_tentativeaccept_count'] ?? 0)
+ ],
+ 'threaded' => $this->threaded,
+ 'blog_mode' => $blog_mode,
+ 'collapse_comments' => t('show less'),
+ 'expand_comments' => $this->threaded ? t('show more') : t('show all'),
+ 'load_more' => $load_more,
+ 'load_more_title' => $load_more_title,
+ 'comments_total' => $conv->comments_total,
+ 'comments_total_percent' => $comments_total_percent,
+ 'expand' => $expand
);
$arr = array('item' => $item, 'output' => $tmp_item);
@@ -492,25 +508,19 @@ class ThreadItem {
$result = $arr['output'];
$result['children'] = array();
- $nb_children = count($children);
- $visible_comments = Config::Get('system', 'expanded_comments', 3);
+ $visible_comments = 3; // Config::Get('system', 'expanded_comments', 3);
- if(($this->get_display_mode() === 'normal') && ($nb_children > 0)) {
+ if(($this->get_display_mode() === 'normal') && ($children_count > 0)) {
foreach($children as $child) {
- $result['children'][] = $child->get_template_data($conv_responses, $mid_uuid_map, $thread_level + 1,$conv_flags);
+ $result['children'][] = $child->get_template_data($thread_level + 1, $conv_flags);
}
+
// Collapse
- if(($nb_children > $visible_comments) || ($thread_level > 1)) {
+ if($thread_level === 1 && $children_count > $visible_comments) {
$result['children'][0]['comment_firstcollapsed'] = true;
$result['children'][0]['num_comments'] = $comment_count_txt['label'];
- $result['children'][0]['hide_text'] = t('show all');
- if($thread_level > 1) {
- $result['children'][$nb_children - 1]['comment_lastcollapsed'] = true;
- }
- else {
- $result['children'][$nb_children - ($visible_comments + 1)]['comment_lastcollapsed'] = true;
- }
+ $result['children'][$children_count - ($visible_comments + 1)]['comment_lastcollapsed'] = true;
}
}
@@ -763,7 +773,7 @@ class ThreadItem {
*/
private function get_comment_box() {
- if(!$this->is_toplevel() && !Config::Get('system','thread_allow')) {
+ if(!$this->is_toplevel()) {
return '';
}
@@ -834,6 +844,7 @@ class ThreadItem {
$conv = $this->get_conversation();
$this->wall_to_wall = false;
$this->owner_url = '';
+ $this->owner_addr = '';
$this->owner_photo = '';
$this->owner_name = '';
@@ -842,12 +853,14 @@ class ThreadItem {
if($this->is_toplevel() && ($this->get_data_value('author_xchan') != $this->get_data_value('owner_xchan'))) {
$this->owner_url = chanlink_hash($this->data['owner']['xchan_hash']);
+ $this->owner_addr = $this->data['owner']['xchan_addr'];
$this->owner_photo = $this->data['owner']['xchan_photo_s'];
$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_addr = $this->data['source']['xchan_addr'];
$this->owner_photo = $this->data['source']['xchan_photo_s'];
$this->owner_name = $this->data['source']['xchan_name'];
$this->wall_to_wall = true;
@@ -862,6 +875,10 @@ class ThreadItem {
return $this->owner_url;
}
+ private function get_owner_addr() {
+ return $this->owner_addr;
+ }
+
private function get_owner_photo() {
return $this->owner_photo;
}
diff --git a/Zotlabs/Lib/ThreadStream.php b/Zotlabs/Lib/ThreadStream.php
index fb3b6dd9b..1d968fa1a 100644
--- a/Zotlabs/Lib/ThreadStream.php
+++ b/Zotlabs/Lib/ThreadStream.php
@@ -24,6 +24,10 @@ class ThreadStream {
private $prepared_item = '';
public $reload = '';
private $cipher = 'AES-128-CCM';
+ public $mid_uuid_map = [];
+ public $comments_total = 0;
+ public $comments_loaded = 0;
+
// $prepared_item is for use by alternate conversation structures such as photos
// wherein we've already prepared a top level item which doesn't look anything like
@@ -211,16 +215,15 @@ class ThreadStream {
* _ The data requested on success
* _ false on failure
*/
- public function get_template_data($conv_responses, $mid_uuid_map) {
+ public function get_template_data() {
$result = array();
foreach($this->threads as $item) {
-
if(($item->get_data_value('id') == $item->get_data_value('parent')) && $this->prepared_item) {
$item_data = $this->prepared_item;
}
else {
- $item_data = $item->get_template_data($conv_responses, $mid_uuid_map);
+ $item_data = $item->get_template_data();
}
if(!$item_data) {
logger('Failed to get item template data ('. $item->get_id() .').', LOGGER_DEBUG, LOG_ERR);
diff --git a/Zotlabs/Lib/Traits/HelpHelperTrait.php b/Zotlabs/Lib/Traits/HelpHelperTrait.php
index 63b0eb22e..69b3cc21b 100644
--- a/Zotlabs/Lib/Traits/HelpHelperTrait.php
+++ b/Zotlabs/Lib/Traits/HelpHelperTrait.php
@@ -89,7 +89,7 @@ trait HelpHelperTrait {
);
return bbcode(
- t("This page is not yet available in {$prefered_language_name}. See [observer.baseurl]/help/developer/developer_guide#Translations for information about how to help.")
+ t("This page is not yet available in {$prefered_language_name}. See [observer.baseurl]/help/developer/developers_guide#Translations for information about how to help.")
);
}
}