diff options
Diffstat (limited to 'Zotlabs')
62 files changed, 1125 insertions, 918 deletions
diff --git a/Zotlabs/Daemon/Cache_embeds.php b/Zotlabs/Daemon/Cache_embeds.php index d5adfcc59..99b1c7ac0 100644 --- a/Zotlabs/Daemon/Cache_embeds.php +++ b/Zotlabs/Daemon/Cache_embeds.php @@ -6,23 +6,28 @@ class Cache_embeds { static public function run($argc,$argv) { - if(! $argc == 2) + if(!$argc == 2) { return; + } - $c = q("select body from item where id = %d ", - dbesc(intval($argv[1])) + $c = q("select uid, aid, body, item_private from item where uuid = '%s'", + dbesc($argv[1]) ); - if(! $c) + if(!$c) { return; + } $item = $c[0]; // bbcode conversion by default processes embeds that aren't already cached. // Ignore the returned html output. - bbcode($item['body']); + // photocache addon hook to prefetch one copy of public item images for the sys channel + call_hooks('cache_prefetch_hook', $item); + return; } + } diff --git a/Zotlabs/Daemon/Cron.php b/Zotlabs/Daemon/Cron.php index a38809f45..131abe2e1 100644 --- a/Zotlabs/Daemon/Cron.php +++ b/Zotlabs/Daemon/Cron.php @@ -125,14 +125,15 @@ 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(Config::Get('system', 'cache_expire_days', 7) . ' DAY') + db_quoteinterval(Config::Get('system', 'default_expire_days', 30) . ' DAY') ); if ($r) { q("DELETE FROM photo WHERE photo_usage = %d AND expires < %s - INTERVAL %s", intval(PHOTO_CACHE), db_utcnow(), - db_quoteinterval(Config::Get('system', 'cache_expire_days', 7) . ' DAY') + db_quoteinterval(Config::Get('system', 'default_expire_days', 30) . ' DAY') ); + foreach ($r as $rr) { $file = dbunescbin($rr['content']); if (is_file($file)) { diff --git a/Zotlabs/Daemon/Notifier.php b/Zotlabs/Daemon/Notifier.php index b32a047a3..9fdb1defb 100644 --- a/Zotlabs/Daemon/Notifier.php +++ b/Zotlabs/Daemon/Notifier.php @@ -346,6 +346,10 @@ class Notifier { $relay_to_owner = (!$top_level_post && intval($target_item['item_origin']) && comment_local_origin($target_item)); + if (self::$channel['channel_hash'] === $target_item['owner_xchan']) { + $relay_to_owner = false; + } + // $cmd === 'relay' indicates the owner is sending it to the original recipients // don't allow the item in the relay command to relay to owner under any circumstances, it will loop 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.") ); } } diff --git a/Zotlabs/Module/Acl.php b/Zotlabs/Module/Acl.php index 1de7a3d02..cf9278f5e 100644 --- a/Zotlabs/Module/Acl.php +++ b/Zotlabs/Module/Acl.php @@ -29,11 +29,11 @@ class Acl extends \Zotlabs\Web\Controller { // logger('mod_acl: ' . print_r($_GET,true),LOGGER_DATA); - $start = (x($_REQUEST,'start') ? $_REQUEST['start'] : 0); - $count = (x($_REQUEST,'count') ? $_REQUEST['count'] : 500); - $search = (x($_REQUEST,'search') ? $_REQUEST['search'] : ''); - $type = (x($_REQUEST,'type') ? $_REQUEST['type'] : ''); - $noforums = (x($_REQUEST,'n') ? $_REQUEST['n'] : false); + $start = (!empty($_REQUEST['start']) ? $_REQUEST['start'] : 0); + $count = (!empty($_REQUEST['count']) ? $_REQUEST['count'] : 500); + $search = (!empty($_REQUEST['search']) ? $_REQUEST['search'] : ''); + $type = (!empty($_REQUEST['type']) ? $_REQUEST['type'] : ''); + $noforums = (!empty($_REQUEST['n']) ? $_REQUEST['n'] : false); // $type = @@ -53,7 +53,7 @@ class Acl extends \Zotlabs\Web\Controller { // List of channels whose connections to also suggest, // e.g. currently viewed channel or channels mentioned in a post - $extra_channels = (x($_REQUEST,'extra_channels') ? $_REQUEST['extra_channels'] : array()); + $extra_channels = (!empty($_REQUEST['extra_channels']) ? $_REQUEST['extra_channels'] : []); // The different autocomplete libraries use different names for the search text // parameter. Internally we'll use $search to represent the search text no matter @@ -416,7 +416,7 @@ class Acl extends \Zotlabs\Web\Controller { } $dirmode = intval(Config::Get('system','directory_mode')); - $search = ((x($_REQUEST,'search')) ? htmlentities($_REQUEST['search'],ENT_COMPAT,'UTF-8',false) : ''); + $search = ((!empty($_REQUEST['search'])) ? htmlentities($_REQUEST['search'], ENT_COMPAT, 'UTF-8', false) : ''); if(! $search || mb_strlen($search) < 2) return array(); @@ -446,7 +446,7 @@ class Acl extends \Zotlabs\Web\Controller { $token = Config::Get('system','realm_token'); - $count = (x($_REQUEST,'count') ? $_REQUEST['count'] : 100); + $count = (!empty($_REQUEST['count']) ? $_REQUEST['count'] : 100); if($url) { $query = $url . '?f=' . (($token) ? '&t=' . urlencode($token) : ''); $query .= '&name=' . urlencode($search) . "&limit=$count" . (($address) ? '&address=' . urlencode(punify($search)) : ''); diff --git a/Zotlabs/Module/Activity.php b/Zotlabs/Module/Activity.php index 85b9f3e7c..64da2586b 100644 --- a/Zotlabs/Module/Activity.php +++ b/Zotlabs/Module/Activity.php @@ -23,7 +23,7 @@ class Activity extends Controller { if (! $item_id) http_status_exit(404, 'Not found'); - $portable_id = EMPTY_STR; + $portable_id = null; $item_normal_extra = sprintf(" and not verb in ('Follow', 'Ignore', '%s', '%s') ", dbesc(ACTIVITY_FOLLOW), @@ -166,6 +166,7 @@ class Activity extends Controller { return; } + $portable_id = null; $ob_authorize = false; $item_uid = 0; diff --git a/Zotlabs/Module/Attach_edit.php b/Zotlabs/Module/Attach_edit.php index 5880d8f13..4cde1c168 100644 --- a/Zotlabs/Module/Attach_edit.php +++ b/Zotlabs/Module/Attach_edit.php @@ -133,6 +133,11 @@ class Attach_edit extends Controller { } $x = attach_move($channel_id, $resource, $newfolder, (($single) ? $newfilename : '')); + if (!$x['success']) { + notice($x['message'] . EOL); + goaway($return_path); + } + $actions_done .= 'move,'; } diff --git a/Zotlabs/Module/Channel.php b/Zotlabs/Module/Channel.php index f3855b7e8..e35a611d0 100644 --- a/Zotlabs/Module/Channel.php +++ b/Zotlabs/Module/Channel.php @@ -85,7 +85,7 @@ class Channel extends Controller { $headers = [ 'Content-Type' => 'application/x-zot+json', 'Digest' => HTTPSig::generate_digest_header($data), - '(request-target)' => strtolower($_SERVER['REQUEST_METHOD']) . ' ' . $_SERVER['REQUEST_URI'] + 'Date' => datetime_convert('UTC','UTC', 'now', 'D, d M Y H:i:s \\G\\M\\T') ]; $h = HTTPSig::create_sig($headers, $channel['channel_prvkey'], channel_url($channel)); @@ -298,12 +298,15 @@ class Channel extends Controller { $item_normal = item_normal(); $item_normal_update = item_normal_update(); - $sql_extra = item_permissions_sql(App::$profile['profile_uid']); + $sql_extra = ''; + $permission_sql = item_permissions_sql(App::$profile['profile_uid']); - if (feature_enabled(App::$profile['profile_uid'], 'channel_list_mode') && (!$mid)) + $page_mode = 'client'; + + $blog_mode = feature_enabled(App::$profile['profile_uid'], 'channel_list_mode') && !$mid; + if ($blog_mode) { $page_mode = 'list'; - else - $page_mode = 'client'; + } $abook_uids = " and abook.abook_channel = " . intval(App::$profile['profile_uid']) . " "; @@ -334,8 +337,8 @@ class Channel extends Controller { if (($update) && (!$load)) { if ($mid) { - $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", + $r = q("SELECT *, parent AS item_id from item where $identifier = '%s' and uid = %d $item_normal_update + AND item_wall = 1 $simple_update $permission_sql $sql_extra limit 1", dbesc($mid), intval(App::$profile['profile_uid']) ); @@ -346,6 +349,7 @@ class Channel extends Controller { WHERE uid = %d $item_normal_update AND item_wall = 1 $simple_update AND (abook.abook_blocked = 0 or abook.abook_flags is null) + $permission_sql $sql_extra ORDER BY created DESC", intval(App::$profile['profile_uid']) @@ -382,8 +386,8 @@ class Channel extends Controller { if ($noscript_content || $load) { if ($mid) { - $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", + $r = q("SELECT *, parent AS item_id from item where $identifier = '%s' and uid = %d $item_normal + AND item_wall = 1 $permission_sql $sql_extra limit 1", dbesc($mid), intval(App::$profile['profile_uid']) ); @@ -392,13 +396,18 @@ class Channel extends Controller { } } else { - $r = q("SELECT DISTINCT item.parent AS item_id, $ordering FROM item - left join abook on ( item.author_xchan = abook.abook_xchan $abook_uids ) - WHERE true and item.uid = %d $item_normal + $r = q("SELECT parent AS item_id, $ordering FROM item + LEFT JOIN abook ON (item.author_xchan = abook.abook_xchan $abook_uids) + WHERE item.uid = %d + AND item.id = item.parent AND (abook.abook_blocked = 0 or abook.abook_flags is null) - AND item.item_wall = 1 AND item.item_thread_top = 1 - $sql_extra $sql_extra2 - ORDER BY $ordering DESC, item_id $pager_sql ", + AND item.item_wall = 1 + $item_normal + $permission_sql + $sql_extra + $sql_extra2 + ORDER BY $ordering DESC, item_id + $pager_sql", intval(App::$profile['profile_uid']) ); } @@ -408,19 +417,15 @@ class Channel extends Controller { } } if ($r) { - $parents_str = ids_to_querystr($r, 'item_id'); - - $r = q("SELECT item.*, item.id AS item_id - FROM item - WHERE item.uid = %d $item_normal - AND item.parent IN ( %s ) - $sql_extra ", - intval(App::$profile['profile_uid']), - dbesc($parents_str) - ); + $thr_parents = null; + if ($mid) { + $thr_parents = get_recursive_thr_parents($r[0]); + } - xchan_query($r); - $items = fetch_post_tags($r, true); + $items = items_by_parent_ids($r, $thr_parents, $permission_sql, $blog_mode); + + xchan_query($items); + $items = fetch_post_tags($items, true); $items = conv_sort($items, $ordering); if ($load && $mid && (!count($items))) { @@ -434,11 +439,8 @@ class Channel extends Controller { $items = []; } - - $mode = (($search) ? 'search' : 'channel'); - if ((!$update) && (!$load)) { // This is ugly, but we can't pass the profile_uid through the session to the ajax updater, diff --git a/Zotlabs/Module/Cloud.php b/Zotlabs/Module/Cloud.php index 510f91c1e..f9abd767a 100644 --- a/Zotlabs/Module/Cloud.php +++ b/Zotlabs/Module/Cloud.php @@ -7,6 +7,7 @@ namespace Zotlabs\Module; * Module for accessing the DAV storage area. */ +use App; use Sabre\DAV as SDAV; use Zotlabs\Web\Controller; use Zotlabs\Storage\BasicAuth; @@ -32,6 +33,15 @@ class Cloud extends Controller { */ function init() { + // TODO: why is this required? + // if we arrived at this path with any query parameters in the url, build a clean url without + // them and redirect. + + $parsed = parse_url(App::$query_string); + if (!empty($parsed['query'])) { + goaway(z_root() . '/' . $parsed['path']); + } + if (! is_dir('store')) os_mkdir('store', STORAGE_DEFAULT_PERMISSIONS, false); @@ -44,15 +54,13 @@ class Cloud extends Controller { if ($which) profile_load( $which, $profile); - - $auth = new BasicAuth(); $ob_hash = get_observer_hash(); if ($ob_hash) { if (local_channel()) { - $channel = \App::get_channel(); + $channel = App::get_channel(); $auth->setCurrentUser($channel['channel_address']); $auth->channel_account_id = $channel['channel_account_id']; $auth->channel_id = $channel['channel_id']; @@ -63,19 +71,12 @@ class Cloud extends Controller { $auth->observer = $ob_hash; } - // if we arrived at this path with any query parameters in the url, build a clean url without - // them and redirect. - if(! array_key_exists('cloud_sort',$_SESSION)) { $_SESSION['cloud_sort'] = 'name'; } $_SESSION['cloud_sort'] = ((isset($_REQUEST['sort']) && $_REQUEST['sort']) ? trim(notags($_REQUEST['sort'])) : $_SESSION['cloud_sort']); - $x = clean_query_string(); - if($x !== \App::$query_string) - goaway(z_root() . '/' . $x); - $rootDirectory = new Directory('/', [], $auth); // A SabreDAV server-object @@ -116,16 +117,16 @@ class Cloud extends Controller { function DAVException($err) { if($err instanceof \Sabre\DAV\Exception\NotFound) { - \App::$page['content'] = '<h2>404 Not found</h2>'; + App::$page['content'] = '<h2>404 Not found</h2>'; } elseif($err instanceof \Sabre\DAV\Exception\Forbidden) { - \App::$page['content'] = '<h2>403 Forbidden</h2>'; + App::$page['content'] = '<h2>403 Forbidden</h2>'; } elseif($err instanceof \Sabre\DAV\Exception\NotImplemented) { - goaway(z_root() . '/' . \App::$query_string); + goaway(z_root() . '/' . App::$query_string); } else { - \App::$page['content'] = '<h2>Unknown error</h2>'; + App::$page['content'] = '<h2>Unknown error</h2>'; } construct_page(); diff --git a/Zotlabs/Module/Display.php b/Zotlabs/Module/Display.php index 090e0c92e..094466665 100644 --- a/Zotlabs/Module/Display.php +++ b/Zotlabs/Module/Display.php @@ -212,7 +212,7 @@ class Display extends Controller { $observer_hash = get_observer_hash(); $item_normal = item_normal(); $item_normal_update = item_normal_update(); - $sql_extra = ''; + $permission_sql = ''; $r = []; if($noscript_content || $load) { @@ -231,7 +231,7 @@ class Display extends Controller { } if(!$r) { - $sql_extra = item_permissions_sql(0, $observer_hash); + $permission_sql = item_permissions_sql(0, $observer_hash); $r = q("SELECT item.id AS item_id FROM item WHERE ((mid = '%s' @@ -239,7 +239,7 @@ class Display extends Controller { AND item.deny_gid = '' AND item_private = 0 ) AND uid IN ( " . stream_perms_api_uids(($observer_hash) ? (PERMS_NETWORK|PERMS_PUBLIC) : PERMS_PUBLIC) . " )) OR uid = %d ))) OR - (mid = '%s' $sql_extra )) + (mid = '%s' $permission_sql )) $item_normal limit 1", dbesc($target_item['parent_mid']), @@ -269,7 +269,7 @@ class Display extends Controller { } if(!$r) { - $sql_extra = item_permissions_sql(0, $observer_hash); + $permission_sql = item_permissions_sql(0, $observer_hash); $r = q("SELECT item.id as item_id from item WHERE ((parent_mid = '%s' @@ -277,7 +277,7 @@ class Display extends Controller { AND item.deny_gid = '' AND item_private = 0 ) and uid in ( " . stream_perms_api_uids(($observer_hash) ? (PERMS_NETWORK|PERMS_PUBLIC) : PERMS_PUBLIC) . " )) OR uid = %d ))) OR - (parent_mid = '%s' $sql_extra )) + (parent_mid = '%s' $permission_sql )) $item_normal limit 1", dbesc($target_item['parent_mid']), @@ -288,17 +288,12 @@ class Display extends Controller { } if($r) { - $parents_str = ids_to_querystr($r,'item_id'); - if($parents_str) { - $items = q("SELECT item.*, item.id AS item_id - FROM item - WHERE parent in ( %s ) $sql_extra $item_normal ", - dbesc($parents_str) - ); - xchan_query($items); - $items = fetch_post_tags($items,true); - $items = conv_sort($items,'created'); - } + $thr_parents = get_recursive_thr_parents($target_item); + $items = items_by_parent_ids($r, $thr_parents, $permission_sql); + + xchan_query($items); + $items = fetch_post_tags($items,true); + $items = conv_sort($items,'created'); } else { $items = array(); diff --git a/Zotlabs/Module/File_upload.php b/Zotlabs/Module/File_upload.php index 8956ce16f..2586265f8 100644 --- a/Zotlabs/Module/File_upload.php +++ b/Zotlabs/Module/File_upload.php @@ -11,39 +11,42 @@ require_once('include/photos.php'); class File_upload extends \Zotlabs\Web\Controller { function post() { - logger('file upload: ' . print_r($_REQUEST,true)); + logger('file upload: ' . print_r($_POST,true)); logger('file upload: ' . print_r($_FILES,true)); - $channel = (($_REQUEST['channick']) ? channelx_by_nick($_REQUEST['channick']) : null); + $channel = (($_POST['channick']) ? channelx_by_nick($_POST['channick']) : null); - if(! $channel) { + if (!$channel) { logger('channel not found'); - killme(); + is_ajax() ? killme() : goaway(z_root() . '/' . $_POST['return_url']); } - $_REQUEST['source'] = 'file_upload'; + $_POST['source'] = 'file_upload'; if($channel['channel_id'] != local_channel()) { - $_REQUEST['contact_allow'] = expand_acl($channel['channel_allow_cid']); - $_REQUEST['group_allow'] = expand_acl($channel['channel_allow_gid']); - $_REQUEST['contact_deny'] = expand_acl($channel['channel_deny_cid']); - $_REQUEST['group_deny'] = expand_acl($channel['channel_deny_gid']); + $_POST['contact_allow'] = expand_acl($channel['channel_allow_cid']); + $_POST['group_allow'] = expand_acl($channel['channel_allow_gid']); + $_POST['contact_deny'] = expand_acl($channel['channel_deny_cid']); + $_POST['group_deny'] = expand_acl($channel['channel_deny_gid']); } - $_REQUEST['allow_cid'] = ((isset($_REQUEST['contact_allow'])) ? perms2str($_REQUEST['contact_allow']) : ''); - $_REQUEST['allow_gid'] = ((isset($_REQUEST['group_allow'])) ? perms2str($_REQUEST['group_allow']) : ''); - $_REQUEST['deny_cid'] = ((isset($_REQUEST['contact_deny'])) ? perms2str($_REQUEST['contact_deny']) : ''); - $_REQUEST['deny_gid'] = ((isset($_REQUEST['group_deny'])) ? perms2str($_REQUEST['group_deny']) : ''); - - if(isset($_REQUEST['filename']) && strlen($_REQUEST['filename'])) { - $r = attach_mkdir($channel, get_observer_hash(), $_REQUEST); - if($r['success']) { - $hash = $r['data']['hash']; - $sync = attach_export_data($channel,$hash); - if($sync) { - Libsync::build_sync_packet($channel['channel_id'],array('file' => array($sync))); - } - goaway(z_root() . '/' . $_REQUEST['return_url']); + $_POST['allow_cid'] = ((isset($_POST['contact_allow'])) ? perms2str($_POST['contact_allow']) : ''); + $_POST['allow_gid'] = ((isset($_POST['group_allow'])) ? perms2str($_POST['group_allow']) : ''); + $_POST['deny_cid'] = ((isset($_POST['contact_deny'])) ? perms2str($_POST['contact_deny']) : ''); + $_POST['deny_gid'] = ((isset($_POST['group_deny'])) ? perms2str($_POST['group_deny']) : ''); + + if(isset($_POST['filename']) && strlen($_POST['filename'])) { + $r = attach_mkdir($channel, get_observer_hash(), $_POST); + + if (!$r['success']) { + notice($r['message'] . EOL); + is_ajax() ? killme() : goaway(z_root() . '/' . $_POST['return_url']); + } + + $hash = $r['data']['hash']; + $sync = attach_export_data($channel,$hash); + if ($sync) { + Libsync::build_sync_packet($channel['channel_id'], ['file' => [$sync]]); } } else { @@ -90,19 +93,19 @@ class File_upload extends \Zotlabs\Web\Controller { } } - $r = attach_store($channel, get_observer_hash(), '', $_REQUEST); - if($r['success']) { - $sync = attach_export_data($channel,$r['data']['hash']); - if($sync) - Libsync::build_sync_packet($channel['channel_id'],array('file' => array($sync))); + $r = attach_store($channel, get_observer_hash(), '', $_POST); + if (!$r['success']) { + notice($r['message'] . EOL); + is_ajax() ? killme() : goaway(z_root() . '/' . $_POST['return_url']); + } + $sync = attach_export_data($channel,$r['data']['hash']); + if ($sync) { + Libsync::build_sync_packet($channel['channel_id'], ['file' => [$sync]]); } } - if(is_ajax()) - killme(); - - goaway(z_root() . '/' . $_REQUEST['return_url']); + is_ajax() ? killme() : goaway(z_root() . '/' . $_POST['return_url']); } diff --git a/Zotlabs/Module/Help.php b/Zotlabs/Module/Help.php index fc0ef2708..52e23e4e0 100644 --- a/Zotlabs/Module/Help.php +++ b/Zotlabs/Module/Help.php @@ -30,7 +30,7 @@ class Help extends \Zotlabs\Web\Controller { $this->determine_help_language(); if (empty($_REQUEST['search']) && argc() === 1) { - goaway("/help/about/about"); + goaway("/help/about"); killme(); } } @@ -85,7 +85,7 @@ class Help extends \Zotlabs\Web\Controller { } - if(argc() > 2 && argv(argc()-2) === 'assets') { + if(argc() > 2 && argv(argc()-2) === 'pic') { $path = ''; for($x = 1; $x < argc(); $x ++) { if(strlen($path)) diff --git a/Zotlabs/Module/Home.php b/Zotlabs/Module/Home.php index 3ac445f9f..0dec432d0 100644 --- a/Zotlabs/Module/Home.php +++ b/Zotlabs/Module/Home.php @@ -24,9 +24,13 @@ class Home extends Controller { $key = Config::Get('system', 'prvkey'); $ret = json_encode(Libzot::site_info()); - $headers = ['Content-Type' => 'application/x-zot+json', 'Digest' => HTTPSig::generate_digest_header($ret)]; - $headers['(request-target)'] = strtolower($_SERVER['REQUEST_METHOD']) . ' ' . $_SERVER['REQUEST_URI']; - $h = HTTPSig::create_sig($headers, $key, z_root()); + $headers = [ + 'Content-Type' => 'application/x-zot+json', + 'Digest' => HTTPSig::generate_digest_header($ret), + 'Date' => datetime_convert('UTC','UTC', 'now', 'D, d M Y H:i:s \\G\\M\\T') + ]; + + $h = HTTPSig::create_sig($headers, $key, z_root()); HTTPSig::set_headers($h); echo $ret; @@ -68,9 +72,9 @@ class Home extends Controller { $o = ''; - if (x($_SESSION, 'theme')) + if (isset($_SESSION['theme'])) unset($_SESSION['theme']); - if (x($_SESSION, 'mobile_theme')) + if (isset($_SESSION['mobile_theme'])) unset($_SESSION['mobile_theme']); $splash = ((argc() > 1 && argv(1) === 'splash') ? true : false); diff --git a/Zotlabs/Module/Hq.php b/Zotlabs/Module/Hq.php index 51caa179c..241a5101a 100644 --- a/Zotlabs/Module/Hq.php +++ b/Zotlabs/Module/Hq.php @@ -3,6 +3,7 @@ namespace Zotlabs\Module; use App; use Zotlabs\Widget\Messages; +use Zotlabs\Lib\Config; class Hq extends \Zotlabs\Web\Controller { @@ -50,12 +51,13 @@ 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 $identifier = '%s' order by uid $sql_order limit 2", + $r = q("select id, uid, mid, parent, 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) ); + if($r) { $target_item = $r[0]; if (intval($target_item['uid']) === intval($sys['channel_id'])) { @@ -129,7 +131,7 @@ class Hq extends \Zotlabs\Web\Controller { '$nouveau' => '0', '$wall' => '0', '$page' => '1', - '$list' => ((x($_REQUEST,'list')) ? intval($_REQUEST['list']) : 0), + '$list' => ((!empty($_REQUEST['list'])) ? intval($_REQUEST['list']) : 0), '$search' => '', '$xchan' => '', '$order' => '', @@ -145,7 +147,6 @@ class Hq extends \Zotlabs\Web\Controller { } if($load && $target_item) { - if (!$sys_item) { $r = q("SELECT item.id AS item_id FROM item WHERE uid = %d @@ -199,11 +200,8 @@ class Hq extends \Zotlabs\Web\Controller { } if($r) { - $items = q("SELECT item.*, item.id AS item_id - FROM item - WHERE parent = '%s' $item_normal $sql_extra", - dbesc($r[0]['item_id']) - ); + $thr_parents = get_recursive_thr_parents($target_item); + $items = items_by_parent_ids($r, $thr_parents); xchan_query($items,true,(($sys_item) ? local_channel() : 0)); $items = fetch_post_tags($items,true); diff --git a/Zotlabs/Module/Id.php b/Zotlabs/Module/Id.php index e08568d00..004cad6e7 100644 --- a/Zotlabs/Module/Id.php +++ b/Zotlabs/Module/Id.php @@ -6,8 +6,8 @@ namespace Zotlabs\Module; * * Controller for responding to x-zot: protocol requests * x-zot:_jkfRG85nJ-714zn-LW_VbTFW8jSjGAhAydOcJzHxqHkvEHWG2E0RbA_pbch-h4R63RG1YJZifaNzgccoLa3MQ/453c1678-1a79-4af7-ab65-6b012f6cab77 - * - */ + * + */ use Zotlabs\Lib\Activity; use Zotlabs\Lib\ActivityStreams; @@ -104,7 +104,7 @@ class Id extends Controller { $headers['Content-Type'] = 'application/x-zot+json' ; $ret = json_encode($x, JSON_UNESCAPED_SLASHES); $headers['Digest'] = HTTPSig::generate_digest_header($ret); - $headers['(request-target)'] = strtolower($_SERVER['REQUEST_METHOD']) . ' ' . $_SERVER['REQUEST_URI']; + $headers['Date'] = datetime_convert('UTC','UTC', 'now', 'D, d M Y H:i:s \\G\\M\\T'); $h = HTTPSig::create_sig($headers,$chan['channel_prvkey'],channel_url($chan)); HTTPSig::set_headers($h); echo $ret; diff --git a/Zotlabs/Module/Invite.php b/Zotlabs/Module/Invite.php index 3e1e98f89..aff0e9340 100644 --- a/Zotlabs/Module/Invite.php +++ b/Zotlabs/Module/Invite.php @@ -310,9 +310,9 @@ class Invite extends Controller { function get() { - // zai1 + $channel_id = local_channel(); - if(! local_channel()) { + if ($channel_id === false || $channel_id < 1) { notice( 'ZAI0101E,' . t('Permission denied.') . EOL); return; } @@ -330,15 +330,15 @@ class Invite extends Controller { return $o; } - // invitation_by_user may still not configured, the default 'na' will tell this - // if configured, 0 disables invitations by users, other numbers are how many invites a user may propagate - $invuser = Config::Get('system','invitation_by_user', 'na'); + $ihave = $this->count_invites_by_user($channel_id); - // if the mortal user drives the invitation - If (! is_site_admin()) { - - // when not configured, 4 is the default - $invuser = ($invuser === 'na') ? 4 : $invuser; + if (is_site_admin()) { + // Admins have unlimited invites + $invuser = '∞'; + } else { + // invitation_by_user may still not configured, the default 'na' will tell this + // if configured, 0 disables invitations by users, other numbers are how many invites a user may propagate + $invuser = Config::Get('system','invitation_by_user', 4); // a config value 0 disables invitation by users if (!$invuser) { @@ -350,12 +350,6 @@ class Invite extends Controller { notice( 'ZAI0105W,' . t('You have no more invitations available') . EOL); return ''; } - - } else { - // general deity admin invite limit infinite (theoretical) - if ($invuser === 'na') Config::Set('system','invitation_by_user', 4); - // for display only - $invuser = '∞'; } // xchan record of the page observer @@ -394,17 +388,6 @@ class Invite extends Controller { } } - if ($wehave > $invmaxau) { - if (! is_site_admin()) { - $feedbk .= 'ZAI0200E,' . t('All users invitation limit exceeded.') . $eol; - } - } - - // let see how many invites currently used by the user - $r = q("SELECT count(reg_id) AS n FROM register WHERE reg_vital = 1 AND reg_byc = %d", - intval(local_channel())); - $ihave = $r ? $r[0]['n'] : 0; - $tpl = get_markup_template('invite.tpl'); $inv_rabots = array( @@ -420,11 +403,11 @@ class Invite extends Controller { 'field' => array( 'name' => 'expire', 'title' => t('duration up from now'), - 'value' => ($invexpire_n ? $invexpire_n : 2), + 'value' => 2, 'min' => '1', 'max' => '99', 'size' => '2', - 'default' => ($invexpire_u ? $invexpire_u : 'd') + 'default' => 'd', ), 'rabot' => $inv_rabots ) @@ -583,5 +566,18 @@ class Invite extends Controller { } return false; } + + /** + * Find how many invites the given channel is currently using. + * + * @param int $channel_id The id of the channel + * + * @return int Number of invites this channel is currently using. + */ + private function count_invites_by_user(int $channel): int { + $r = q("SELECT count(reg_id) AS n FROM register WHERE reg_vital = 1 AND reg_byc = %d", $channel); + + return $r ? $r[0]['n'] : 0; + } } diff --git a/Zotlabs/Module/Item.php b/Zotlabs/Module/Item.php index ea561ee25..83e8d609e 100644 --- a/Zotlabs/Module/Item.php +++ b/Zotlabs/Module/Item.php @@ -78,7 +78,7 @@ class Item extends Controller { // This will change. Figure out who the observer is and whether or not // they have permission to post here. Else ignore the post. - if ((!local_channel()) && (!remote_channel()) && (!x($_REQUEST, 'anonname'))) + if ((!local_channel()) && (!remote_channel()) && (empty($_POST['anonname']))) return; $uid = local_channel(); @@ -107,12 +107,13 @@ class Item extends Controller { * Is this a reply to something? */ - $parent = ((x($_REQUEST, 'parent')) ? intval($_REQUEST['parent']) : 0); - $parent_mid = ((x($_REQUEST, 'parent_mid')) ? trim($_REQUEST['parent_mid']) : ''); - $mode = ((isset($_REQUEST['conv_mode']) && $_REQUEST['conv_mode'] === 'channel') ? 'channel' : 'network'); + $parent = ((!empty($_POST['parent'])) ? intval($_POST['parent']) : 0); + $thr_parent_id = $parent; + $parent_mid = ((!empty($_POST['parent_mid'])) ? trim($_POST['parent_mid']) : ''); + $mode = ((isset($_POST['conv_mode']) && $_POST['conv_mode'] === 'channel') ? 'channel' : 'network'); - $remote_xchan = ((x($_REQUEST, 'remote_xchan')) ? trim($_REQUEST['remote_xchan']) : false); - $r = q("select * from xchan where xchan_hash = '%s' limit 1", + $remote_xchan = ((!empty($_POST['remote_xchan'])) ? trim($_POST['remote_xchan']) : false); + $r = q("select * from xchan where xchan_hash = '%s' limit 1", dbesc($remote_xchan) ); if ($r) @@ -120,7 +121,7 @@ class Item extends Controller { else $remote_xchan = $remote_observer = false; - $profile_uid = ((x($_REQUEST, 'profile_uid')) ? intval($_REQUEST['profile_uid']) : 0); + $profile_uid = ((!empty($_POST['profile_uid'])) ? intval($_POST['profile_uid']) : 0); require_once('include/channel.php'); $sys = get_sys_channel(); @@ -130,25 +131,25 @@ class Item extends Controller { $observer = $sys; } - if (x($_REQUEST, 'dropitems')) { + if (!empty($_POST['dropitems'])) { require_once('include/items.php'); - $arr_drop = explode(',', $_REQUEST['dropitems']); + $arr_drop = explode(',', $_POST['dropitems']); drop_items($arr_drop); $json = ['success' => 1]; echo json_encode($json); killme(); } - call_hooks('post_local_start', $_REQUEST); + call_hooks('post_local_start', $_POST); - // logger('postvars ' . print_r($_REQUEST,true), LOGGER_DATA); + // logger('postvars ' . print_r($_POST,true), LOGGER_DATA); - $api_source = ((x($_REQUEST, 'api_source') && $_REQUEST['api_source']) ? true : false); + $api_source = ((!empty($_POST['api_source'])) ? true : false); - $consensus = $_REQUEST['consensus'] ?? 0; - $nocomment = $_REQUEST['nocomment'] ?? 0; + $consensus = $_POST['consensus'] ?? 0; + $nocomment = $_POST['nocomment'] ?? 0; - $is_poll = ((isset($_REQUEST['poll_answers'][0]) && $_REQUEST['poll_answers'][0]) && (isset($_REQUEST['poll_answers'][1]) && $_REQUEST['poll_answers'][1])); + $is_poll = ((isset($_POST['poll_answers'][0]) && $_POST['poll_answers'][0]) && (isset($_POST['poll_answers'][1]) && $_POST['poll_answers'][1])); // 'origin' (if non-zero) indicates that this network is where the message originated, // for the purpose of relaying comments to other conversation members. @@ -159,43 +160,43 @@ class Item extends Controller { // If you are unsure, it is prudent (and important) to leave it unset. - $origin = (($api_source && array_key_exists('origin', $_REQUEST)) ? intval($_REQUEST['origin']) : 1); + $origin = (($api_source && array_key_exists('origin', $_POST)) ? intval($_REQU_POSTEST['origin']) : 1); // To represent message-ids on other networks - this will create an iconfig record - $namespace = (($api_source && array_key_exists('namespace', $_REQUEST)) ? strip_tags($_REQUEST['namespace']) : ''); - $remote_id = (($api_source && array_key_exists('remote_id', $_REQUEST)) ? strip_tags($_REQUEST['remote_id']) : ''); + $namespace = (($api_source && array_key_exists('namespace', $_POST)) ? strip_tags($_POST['namespace']) : ''); + $remote_id = (($api_source && array_key_exists('remote_id', $_POST)) ? strip_tags($_POST['remote_id']) : ''); $owner_hash = null; - $message_id = ((x($_REQUEST, 'message_id') && $api_source) ? strip_tags($_REQUEST['message_id']) : null); - $created = ((x($_REQUEST, 'created')) ? datetime_convert(date_default_timezone_get(), 'UTC', $_REQUEST['created']) : datetime_convert()); - $post_id = ((x($_REQUEST, 'post_id')) ? intval($_REQUEST['post_id']) : 0); - $app = ((x($_REQUEST, 'source')) ? strip_tags($_REQUEST['source']) : ''); - $return_path = ((x($_REQUEST, 'return')) ? $_REQUEST['return'] : ''); - $preview = ((x($_REQUEST, 'preview')) ? intval($_REQUEST['preview']) : 0); - $categories = ((x($_REQUEST, 'category')) ? escape_tags($_REQUEST['category']) : ''); - $webpage = ((x($_REQUEST, 'webpage')) ? intval($_REQUEST['webpage']) : 0); - $item_obscured = ((x($_REQUEST, 'obscured')) ? intval($_REQUEST['obscured']) : 0); - $item_delayed = ((x($_REQUEST, 'delayed')) ? intval($_REQUEST['delayed']) : 0); - $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']) : 'Note'); + $message_id = ((!empty($_POST['message_id']) && $api_source) ? strip_tags($_POST['message_id']) : null); + $created = ((!empty($_POST['created'])) ? datetime_convert(date_default_timezone_get(), 'UTC', $_POST['created']) : datetime_convert()); + $post_id = ((!empty($_POST['post_id'])) ? intval($_POST['post_id']) : 0); + $app = ((!empty($_POST['source'])) ? strip_tags($_POST['source']) : ''); + $return_path = ((!empty($_POST['return'])) ? $_POST['return'] : ''); + $preview = ((!empty($_POST['preview'])) ? intval($_POST['preview']) : 0); + $categories = ((!empty($_POST['category'])) ? escape_tags($_POST['category']) : ''); + $item_type = ((!empty($_POST['webpage'])) ? intval($_POST['webpage']) : ITEM_TYPE_POST); + $item_obscured = ((!empty($_POST['obscured'])) ? intval($_POST['obscured']) : 0); + $item_delayed = ((!empty($_POST['delayed'])) ? intval($_POST['delayed']) : 0); + $pagetitle = ((!empty($_POST['pagetitle'])) ? escape_tags($_POST['pagetitle']) : ''); + $layout_mid = ((!empty($_POST['layout_mid'])) ? escape_tags($_POST['layout_mid']) : ''); + $plink = ((!empty($_POST['permalink'])) ? escape_tags($_POST['permalink']) : null); + $obj_type = ((!empty($_POST['obj_type'])) ? escape_tags($_POST['obj_type']) : 'Note'); // 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); + $nopush = ((!empty($_POST['nopush'])) ? intval($_POST['nopush']) : 0); /* * Check service class limits */ - if ($uid && !(x($_REQUEST, 'parent')) && !(x($_REQUEST, 'post_id'))) { - $ret = $this->item_check_service_class($uid, (($_REQUEST['webpage'] == ITEM_TYPE_WEBPAGE) ? true : false)); + if ($uid && empty($_POST['parent']) && empty($_POST['post_id'])) { + $ret = $this->item_check_service_class($uid, (($_POST['webpage'] == ITEM_TYPE_WEBPAGE) ? true : false)); if (!$ret['success']) { notice(t($ret['message']) . EOL); if ($api_source) return (['success' => false, 'message' => 'service class exception']); - if (x($_REQUEST, 'return')) + if (!empty($_POST['return'])) goaway(z_root() . "/" . $return_path); killme(); } @@ -216,8 +217,8 @@ class Item extends Controller { if ($parent || $parent_mid) { - if (!x($_REQUEST, 'type')) - $_REQUEST['type'] = 'net-comment'; + if (empty($_POST['type'])) + $_POST['type'] = 'net-comment'; if ($parent) { $r = q("SELECT * FROM item WHERE id = %d LIMIT 1", @@ -253,7 +254,7 @@ class Item extends Controller { notice(t('Unable to locate original post.') . EOL); if ($api_source) return (['success' => false, 'message' => 'invalid post id']); - if (x($_REQUEST, 'return')) + if (!empty($_POST['return'])) goaway(z_root() . "/" . $return_path); killme(); } @@ -276,7 +277,7 @@ class Item extends Controller { if (!$observer) { $observer = App::get_observer(); if (!$observer) { - $observer = anon_identity_init($_REQUEST); + $observer = anon_identity_init($_POST); if ($observer) { $moderated = true; $remote_xchan = $remote_observer = $observer; @@ -288,7 +289,7 @@ class Item extends Controller { notice(t('Permission denied.') . EOL); if ($api_source) return (['success' => false, 'message' => 'permission denied']); - if (x($_REQUEST, 'return')) + if (!empty($_POST['return'])) goaway(z_root() . "/" . $return_path); killme(); } @@ -307,17 +308,17 @@ class Item extends Controller { notice(t('Permission denied.') . EOL); if ($api_source) return (['success' => false, 'message' => 'permission denied']); - if (x($_REQUEST, 'return')) + if (!empty($_POST['return'])) goaway(z_root() . "/" . $return_path); killme(); } } else { - if (!perm_is_allowed($profile_uid, $observer['xchan_hash'], ($webpage) ? 'write_pages' : 'post_wall')) { + if (!perm_is_allowed($profile_uid, $observer['xchan_hash'], (intval($item_type) === ITEM_TYPE_POST) ? 'post_wall' : 'write_pages')) { notice(t('Permission denied.') . EOL); if ($api_source) return (['success' => false, 'message' => 'permission denied']); - if (x($_REQUEST, 'return')) + if (!empty($_POST['return'])) goaway(z_root() . "/" . $return_path); killme(); } @@ -373,7 +374,7 @@ class Item extends Controller { logger("mod_item: no channel."); if ($api_source) return (['success' => false, 'message' => 'no channel']); - if (x($_REQUEST, 'return')) + if (!empty($_POST['return'])) goaway(z_root() . "/" . $return_path); killme(); } @@ -391,7 +392,7 @@ class Item extends Controller { logger("mod_item: no owner."); if ($api_source) return (['success' => false, 'message' => 'no owner']); - if (x($_REQUEST, 'return')) + if (!empty($_POST['return'])) goaway(z_root() . "/" . $return_path); killme(); } @@ -425,17 +426,21 @@ class Item extends Controller { $view_policy = \Zotlabs\Access\PermissionLimits::Get($channel['channel_id'], 'view_stream'); $comment_policy = \Zotlabs\Access\PermissionLimits::Get($channel['channel_id'], 'post_comments'); - $public_policy = ((x($_REQUEST, 'public_policy')) ? escape_tags($_REQUEST['public_policy']) : map_scope($view_policy, true)); - if ($webpage) - $public_policy = ''; - if ($public_policy) + $public_policy = ''; + + if (intval($item_type) === ITEM_TYPE_POST) { + $public_policy = ((!empty($_POST['public_policy'])) ? escape_tags($_POST['public_policy']) : map_scope($view_policy, true)); + } + + if ($public_policy) { $private = 1; + } if ($orig_post) { $private = 0; - // webpages are allowed to change ACLs after the fact. Normal conversation items aren't. - if ($webpage) { - $acl->set_from_array($_REQUEST); + // Normal conversation items are not allowed to change ACL. + if (intval($item_type) !== ITEM_TYPE_POST) { + $acl->set_from_array($_POST); } else { $acl->set($orig_post); @@ -451,9 +456,9 @@ class Item extends Controller { $coord = $orig_post['coord']; $verb = $orig_post['verb']; $app = $orig_post['app']; - $title = escape_tags(trim($_REQUEST['title'])); - $summary = escape_tags(trim($_REQUEST['summary'])); - $body = trim($_REQUEST['body']); + $title = escape_tags(trim($_POST['title'])); + $summary = escape_tags(trim($_POST['summary'])); + $body = trim($_POST['body']); $item_flags = $orig_post['item_flags']; $item_origin = $orig_post['item_origin']; $item_unseen = $orig_post['item_unseen']; @@ -491,11 +496,11 @@ class Item extends Controller { } else { if (!$walltowall) { - 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); + if ((array_key_exists('contact_allow', $_POST)) + || (array_key_exists('group_allow', $_POST)) + || (array_key_exists('contact_deny', $_POST)) + || (array_key_exists('group_deny', $_POST))) { + $acl->set_from_array($_POST); } elseif (!$api_source) { @@ -510,16 +515,16 @@ class Item extends Controller { } - $location = ((isset($_REQUEST['location'])) ? notags(trim($_REQUEST['location'])) : ''); - $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'])) ? escape_tags(trim($_REQUEST['summary'])) : ''); - $body = ((isset($_REQUEST['body'])) ? trim($_REQUEST['body']) : ''); - $body .= ((isset($_REQUEST['attachment'])) ? trim($_REQUEST['attachment']) : ''); + $location = ((isset($_POST['location'])) ? notags(trim($_POST['location'])) : ''); + $coord = ((isset($_POST['coord'])) ? notags(trim($_POST['coord'])) : ''); + $verb = ((isset($_POST['verb'])) ? notags(trim($_POST['verb'])) : ''); + $title = ((isset($_POST['title'])) ? escape_tags(trim($_POST['title'])) : ''); + $summary = ((isset($_POST['summary'])) ? escape_tags(trim($_POST['summary'])) : ''); + $body = ((isset($_POST['body'])) ? trim($_POST['body']) : ''); + $body .= ((isset($_POST['attachment'])) ? trim($_POST['attachment']) : ''); $postopts = ''; - $allow_empty = ((array_key_exists('allow_empty', $_REQUEST)) ? intval($_REQUEST['allow_empty']) : 0); + $allow_empty = ((array_key_exists('allow_empty', $_POST)) ? intval($_POST['allow_empty']) : 0); $private = ((isset($private) && $private) ? $private : intval($acl->is_private() || ($public_policy))); @@ -530,7 +535,7 @@ class Item extends Controller { $private = intval($parent_item['item_private']); $public_policy = $parent_item['public_policy']; $owner_hash = $parent_item['owner_xchan']; - $webpage = $parent_item['item_type']; + $item_type = $parent_item['item_type']; } @@ -541,7 +546,7 @@ class Item extends Controller { info(t('Empty post discarded.') . EOL); if ($api_source) return (['success' => false, 'message' => 'no content']); - if (x($_REQUEST, 'return')) + if (!empty($_POST['return'])) goaway(z_root() . "/" . $return_path); killme(); } @@ -549,15 +554,15 @@ class Item extends Controller { if (feature_enabled($profile_uid, 'content_expire')) { - if (x($_REQUEST, 'expire')) { - $expires = datetime_convert(date_default_timezone_get(), 'UTC', $_REQUEST['expire']); + if (!empty($_POST['expire'])) { + $expires = datetime_convert(date_default_timezone_get(), 'UTC', $_POST['expire']); if ($expires <= datetime_convert()) $expires = NULL_DATE; } } - $mimetype = ((isset($_REQUEST['mimetype'])) ? notags(trim($_REQUEST['mimetype'])) : ''); + $mimetype = ((isset($_POST['mimetype'])) ? notags(trim($_POST['mimetype'])) : ''); if (!$mimetype) $mimetype = 'text/bbcode'; @@ -591,7 +596,7 @@ class Item extends Controller { $is_group = get_pconfig($profile_uid, 'system', 'group_actor'); - if ($is_group && $walltowall && !$walltowall_comment && !$webpage) { + if ($is_group && $walltowall && !$walltowall_comment && (intval($item_type) === ITEM_TYPE_POST)) { $groupww = true; $str_contact_allow = $owner_xchan['xchan_hash']; $str_group_allow = ''; @@ -790,7 +795,7 @@ class Item extends Controller { } $item_unseen = ((local_channel() != $profile_uid) ? 1 : 0); - $item_wall = ((isset($_REQUEST['type']) && ($_REQUEST['type'] === 'wall' || $_REQUEST['type'] === 'wall-comment')) ? 1 : 0); + $item_wall = ((isset($_POST['type']) && ($_POST['type'] === 'wall' || $_POST['type'] === 'wall-comment')) ? 1 : 0); $item_origin = (($origin) ? 1 : 0); $item_consensus = (($consensus) ? 1 : 0); $item_nocomment = (($nocomment) ? 1 : 0); @@ -798,15 +803,13 @@ class Item extends Controller { // determine if this is a wall post + if (in_array($item_type, [ITEM_TYPE_POST, ITEM_TYPE_CARD, ITEM_TYPE_ARTICLE])) { + $item_wall = 1; + } + if ($parent) { $item_wall = $parent_item['item_wall']; } - else { - if (!$webpage) { - $item_wall = 1; - } - } - if ($moderated) { $item_blocked = ITEM_MODERATED; @@ -847,10 +850,10 @@ class Item extends Controller { if ($is_poll) { $poll = [ 'question' => $body, - 'answers' => $_REQUEST['poll_answers'], - 'multiple_answers' => $_REQUEST['poll_multiple_answers'], - 'expire_value' => $_REQUEST['poll_expire_value'], - 'expire_unit' => $_REQUEST['poll_expire_unit'] + 'answers' => $_POST['poll_answers'], + 'multiple_answers' => $_POST['poll_multiple_answers'], + 'expire_value' => $_POST['poll_expire_value'], + 'expire_unit' => $_POST['poll_expire_unit'] ]; $obj = $this->extract_poll_data($poll, ['item_private' => $private, 'allow_cid' => $str_contact_allow, 'allow_gid' => $str_contact_deny]); } @@ -929,7 +932,7 @@ class Item extends Controller { $datarray['item_unseen'] = intval($item_unseen); $datarray['item_wall'] = intval($item_wall); $datarray['item_origin'] = intval($item_origin); - $datarray['item_type'] = $webpage; + $datarray['item_type'] = $item_type; $datarray['item_private'] = intval($private); $datarray['item_thread_top'] = intval($item_thread_top); $datarray['item_starred'] = intval($item_starred); @@ -1008,14 +1011,14 @@ class Item extends Controller { call_hooks('post_local', $datarray); - if (x($datarray, 'cancel')) { + if (!empty($datarray['cancel'])) { logger('mod_item: post cancelled by plugin or duplicate suppressed.'); if ($return_path) goaway(z_root() . "/" . $return_path); if ($api_source) return (['success' => false, 'message' => 'operation cancelled']); $json = ['cancel' => 1]; - $json['reload'] = z_root() . '/' . $_REQUEST['jsreload']; + $json['reload'] = z_root() . '/' . $_POST['jsreload']; echo json_encode($json); killme(); } @@ -1024,8 +1027,8 @@ class Item extends Controller { if (mb_strlen($datarray['title']) > 191) $datarray['title'] = mb_substr($datarray['title'], 0, 191); - if ($webpage) { - IConfig::Set($datarray, 'system', webpage_to_namespace($webpage), + if (intval($item_type) !== ITEM_TYPE_POST) { + IConfig::Set($datarray, 'system', item_type_to_namespace($item_type), (($pagetitle) ? $pagetitle : basename($datarray['mid'])), true); } elseif ($namespace) { @@ -1065,7 +1068,7 @@ class Item extends Controller { if ($api_source) return ($x); - if ((x($_REQUEST, 'return')) && strlen($return_path)) { + if ((!empty($_POST['return'])) && strlen($return_path)) { logger('return: ' . $return_path); if ($return_path === 'hq') { @@ -1218,11 +1221,12 @@ class Item extends Controller { $json = [ 'success' => 1, 'id' => $post_id, + 'thr_parent_id' => $thr_parent_id, 'html' => conversation($item, $mode, true, 'r_preview'), ]; - if (x($_REQUEST, 'jsreload') && strlen($_REQUEST['jsreload'])) - $json['reload'] = z_root() . '/' . $_REQUEST['jsreload']; + if (!empty($_POST['jsreload'])) + $json['reload'] = z_root() . '/' . $_POST['jsreload']; logger('post_json: ' . print_r($json, true), LOGGER_DEBUG); diff --git a/Zotlabs/Module/Lang.php b/Zotlabs/Module/Lang.php index fe185ebea..1eeb29363 100644 --- a/Zotlabs/Module/Lang.php +++ b/Zotlabs/Module/Lang.php @@ -65,8 +65,22 @@ class Lang extends Controller { } nav_set_selected('Language'); - return lang_selector(); + return $this->lang_selector(); } + private function lang_selector(): string + { + $lang_options = language_list(); + array_unshift($lang_options, t('default')); + + $tpl = get_markup_template('lang_selector.tpl'); + + return replace_macros($tpl, [ + '$title' => t('Select an alternate language'), + '$langs' => array($lang_options, App::$language), + + ]); + } + } diff --git a/Zotlabs/Module/Like.php b/Zotlabs/Module/Like.php index 2fb3fab83..52c559a17 100644 --- a/Zotlabs/Module/Like.php +++ b/Zotlabs/Module/Like.php @@ -22,9 +22,9 @@ class Like extends Controller { 'like' => 'Like', 'dislike' => 'Dislike', 'announce' => ACTIVITY_SHARE, - 'attendyes' => 'Accept', - 'attendno' => 'Reject', - 'attendmaybe' => 'TentativeAccept' + 'accept' => 'Accept', + 'reject' => 'Reject', + 'tentativeaccept' => 'TentativeAccept' ]; // unlike (etc.) reactions are an undo of positive reactions, rather than a negative action. @@ -52,43 +52,31 @@ class Like extends Controller { profile_load($parts[0]); } - $item_normal = item_normal(); - if ($page_mode === 'list') { + $item_normal = item_normal(); + $items = q("SELECT item.*, item.id AS item_id FROM item WHERE uid = %d $item_normal AND parent = %d", intval($arr['item']['uid']), intval($arr['item']['parent']) ); + xchan_query($items, true); $items = fetch_post_tags($items, true); $items = conv_sort($items, 'commented'); } else { - $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', '%s', 'Accept', 'Reject', 'TentativeAccept')", - intval($arr['item']['uid']), - dbesc($arr['item']['mid']), - dbesc('Like'), - dbesc('Dislike'), - dbesc(ACTIVITY_SHARE), - dbesc(ACTIVITY_ATTEND), - dbesc(ACTIVITY_ATTENDNO), - dbesc(ACTIVITY_ATTENDMAYBE) - ); - xchan_query($activities, true); - $items = array_merge([$arr['item']], $activities); - $items = fetch_post_tags($items, true); + $item = item_by_item_id($arr['item']['id'], $arr['item']['parent']); + xchan_query($item, true); + $item = fetch_post_tags($item, true); } $ret = [ 'success' => 1, 'orig_id' => $arr['orig_item_id'], //this is required for pubstream items where $item_id != $item['id'] 'id' => $arr['item']['id'], - 'html' => conversation($items, $conv_mode, true, $page_mode), + 'html' => conversation($item, $conv_mode, true, $page_mode), ]; // mod photos @@ -486,11 +474,11 @@ 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 === 'attendyes') + if ($verb === 'accept') $bodyverb = t('%1$s is attending %2$s\'s %3$s'); - if ($verb === 'attendno') + if ($verb === 'reject') $bodyverb = t('%1$s is not attending %2$s\'s %3$s'); - if ($verb === 'attendmaybe') + if ($verb === 'tentativeaccept') $bodyverb = t('%1$s may attend %2$s\'s %3$s'); if (!isset($bodyverb)) @@ -573,7 +561,7 @@ class Like extends Controller { call_hooks('post_local_end', $arr); - if ($is_rsvp && in_array($verb, ['attendyes', 'attendmaybe'])) { + if ($is_rsvp && in_array($verb, ['accept', 'tentativeaccept'])) { event_addtocal($item_id, local_channel()); } diff --git a/Zotlabs/Module/Login.php b/Zotlabs/Module/Login.php index 269990a54..f5a83a91a 100644 --- a/Zotlabs/Module/Login.php +++ b/Zotlabs/Module/Login.php @@ -5,10 +5,17 @@ namespace Zotlabs\Module; class Login extends \Zotlabs\Web\Controller { function get() { - if(local_channel()) + if (local_channel()) { goaway(z_root()); - if(remote_channel() && $_SESSION['atoken']) + } + + if (remote_channel() && $_SESSION['atoken']) { goaway(z_root()); + } + + if (!empty($_GET['retry'])) { + notice( t('Login failed.') . EOL ); + } $o = '<div class="generic-content-wrapper">'; $o .= '<div class="section-title-wrapper">'; diff --git a/Zotlabs/Module/Moderate.php b/Zotlabs/Module/Moderate.php index 2103684ab..1d8f65348 100644 --- a/Zotlabs/Module/Moderate.php +++ b/Zotlabs/Module/Moderate.php @@ -67,7 +67,7 @@ class Moderate extends \Zotlabs\Web\Controller { $item['item_blocked'] = 0; item_update_parent_commented($item); - notice( t('Item approved') . EOL); + info(t('Item approved') . EOL); } elseif($action === 'drop') { // TODO: not implemented @@ -75,7 +75,7 @@ class Moderate extends \Zotlabs\Web\Controller { // Activity::send_rejection_activity(App::get_channel(), $item['author_xchan'], $item); drop_item($post_id); - notice( t('Item deleted') . EOL); + info(t('Item deleted') . EOL); } // refetch the item after changes have been made diff --git a/Zotlabs/Module/Network.php b/Zotlabs/Module/Network.php index 5573ed469..f95d92fe2 100644 --- a/Zotlabs/Module/Network.php +++ b/Zotlabs/Module/Network.php @@ -70,17 +70,19 @@ class Network extends \Zotlabs\Web\Controller { $dm = ((x($_REQUEST,'dm')) ? $_REQUEST['dm'] : 0); - $order = get_pconfig(local_channel(), 'mod_network', 'order', 0); + $order = get_pconfig(local_channel(), 'mod_network', 'order', 'created'); switch($order) { - case 0: - $order = 'comment'; + case 'commented': + $ordering = 'commented'; break; - case 1: - $order = 'post'; + case 'created': + $ordering = 'created'; break; - case 2: + case 'unthreaded': $nouveau = true; break; + default: + $ordering = 'created'; } $search = $_GET['search'] ?? ''; @@ -92,7 +94,7 @@ class Network extends \Zotlabs\Web\Controller { } if($datequery) - $order = 'post'; + $order = 'created'; // filter by collection (e.g. group) @@ -274,8 +276,10 @@ class Network extends \Zotlabs\Web\Controller { elseif($pf && $unseen && $nouveau) { $vnotify = get_pconfig(local_channel(), 'system', 'vnotify'); - if(! ($vnotify & VNOTIFY_LIKE)) + $likes_sql = ''; + if (!($vnotify & VNOTIFY_LIKE)) { $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) $sql_extra = " AND item.parent IN (SELECT DISTINCT parent FROM item WHERE uid = " . intval(local_channel()) . " AND ( author_xchan = '" . dbesc($cid_r[0]['abook_xchan']) . "' OR owner_xchan = '" . dbesc($cid_r[0]['abook_xchan']) . "' ) $item_normal) AND item_unseen = 1 AND verb != 'Announce' $likes_sql "; @@ -373,10 +377,10 @@ class Network extends \Zotlabs\Web\Controller { } if ($dm) { - $sql_extra .= ' AND item_private = 2 '; + $sql_extra .= ' AND item.item_private = 2 '; } else { - $sql_extra .= ' AND item_private IN (0, 1) '; + $sql_extra .= ' AND item.item_private IN (0, 1) '; } @@ -425,10 +429,12 @@ class Network extends \Zotlabs\Web\Controller { $abook_uids = ' and abook.abook_channel = ' . local_channel() . ' '; $uids = ' and item.uid = ' . local_channel() . ' '; - if(feature_enabled(local_channel(), 'network_list_mode')) + $page_mode = 'client'; + + $blog_mode = feature_enabled(local_channel(), 'network_list_mode'); + if ($blog_mode) { $page_mode = 'list'; - else - $page_mode = 'client'; + } $parents_str = ''; @@ -457,6 +463,7 @@ class Network extends \Zotlabs\Web\Controller { $net_query WHERE true $uids $item_normal and (abook.abook_blocked = 0 or abook.abook_flags is null) + AND item.verb NOT IN ('Add', 'Remove') $sql_extra $sql_options $sql_nets $net_query2 ORDER BY item.created DESC $pager_sql " @@ -472,13 +479,6 @@ class Network extends \Zotlabs\Web\Controller { } elseif($update) { - // Normal conversation view - - if($order === 'post') - $ordering = 'created'; - else - $ordering = 'commented'; - if($load) { // Fetch a page full of parent items for this page $r = dbq("SELECT item.parent AS item_id FROM item @@ -507,12 +507,7 @@ class Network extends \Zotlabs\Web\Controller { // Then fetch all the children of the parents that are on this page if($r) { - $parents_str = ids_to_querystr($r, 'item_id'); - $items = dbq("SELECT item.*, item.id AS item_id FROM item - WHERE true $uids $item_normal - AND item.parent IN ( $parents_str ) - $sql_extra " - ); + $items = items_by_parent_ids($r, blog_mode: $blog_mode); xchan_query($items, true); $items = fetch_post_tags($items, true); diff --git a/Zotlabs/Module/Oep.php b/Zotlabs/Module/Oep.php index 37a46a23e..201e5a06f 100644 --- a/Zotlabs/Module/Oep.php +++ b/Zotlabs/Module/Oep.php @@ -23,6 +23,7 @@ class Oep extends \Zotlabs\Web\Controller { if(! $url) http_status_exit(404, 'Not found'); + $arr = []; $maxwidth = $_REQUEST['maxwidth'] ?? 0; $maxheight = $_REQUEST['maxheight'] ?? 0; $format = $_REQUEST['format'] ?? ''; diff --git a/Zotlabs/Module/Photos.php b/Zotlabs/Module/Photos.php index e31aa9dc1..5f6162ba7 100644 --- a/Zotlabs/Module/Photos.php +++ b/Zotlabs/Module/Photos.php @@ -557,7 +557,9 @@ class Photos extends \Zotlabs\Web\Controller { $can_post = false; $visitor = 0; - + $link_item = null; + $like = null; + $dislike = null; $owner_uid = \App::$data['channel']['channel_id']; $owner_aid = \App::$data['channel']['channel_account_id']; @@ -965,7 +967,6 @@ class Photos extends \Zotlabs\Web\Controller { $map = null; if($linked_items) { - xchan_query($linked_items); $linked_items = fetch_post_tags($linked_items,true); @@ -1103,20 +1104,8 @@ class Photos extends \Zotlabs\Web\Controller { $alike = array(); $dlike = array(); - $like = ''; - $dislike = ''; - - $conv_responses = array( - 'like' => array('title' => t('Likes','title')),'dislike' => array('title' => t('Dislikes','title')), - 'attendyes' => array('title' => t('Attending','title')), 'attendno' => array('title' => t('Not attending','title')), 'attendmaybe' => array('title' => t('Might attend','title')) - ); - if($r) { - foreach($r as $item) { - builtin_activity_puller($item, $conv_responses); - } - $like_count = ((x($alike,$link_item['mid'])) ? $alike[$link_item['mid']] : ''); $like_list = ((x($alike,$link_item['mid'])) ? $alike[$link_item['mid'] . '-l'] : ''); @@ -1217,12 +1206,17 @@ class Photos extends \Zotlabs\Web\Controller { $like_e = $like; $dislike_e = $dislike; $paginate = paginate(); + $responses = []; - $response_verbs = array('like'); - if(feature_enabled($owner_uid,'dislike')) - $response_verbs[] = 'dislike'; + if ($link_item) { + $response_verbs = ['like']; - $responses = get_responses($conv_responses,$response_verbs,'',$link_item); + if(feature_enabled($owner_uid,'dislike')) { + $response_verbs[] = 'dislike'; + } + + $responses = get_responses($response_verbs, $link_item); + } $hookdata = [ 'onclick' => '$.colorbox({href: \'' . $photo['href'] . '\'}); return false;', diff --git a/Zotlabs/Module/Pin.php b/Zotlabs/Module/Pin.php index de3c75622..14a45c10d 100644 --- a/Zotlabs/Module/Pin.php +++ b/Zotlabs/Module/Pin.php @@ -29,8 +29,9 @@ class Pin extends \Zotlabs\Web\Controller { if(! $observer) http_status_exit(403, 'Forbidden'); - $r = q("SELECT * FROM item WHERE id = %d AND id = parent AND item_private = 0 LIMIT 1", - $item_id + $r = q("SELECT * FROM item WHERE id = %d AND uid = %d AND id = parent AND item_private = 0 LIMIT 1", + intval($item_id), + intval(local_channel()) ); if(! $r) { notice(t('Unable to locate original post.')); diff --git a/Zotlabs/Module/Pubstream.php b/Zotlabs/Module/Pubstream.php index 234e73792..99b8ab587 100644 --- a/Zotlabs/Module/Pubstream.php +++ b/Zotlabs/Module/Pubstream.php @@ -183,6 +183,7 @@ class Pubstream extends \Zotlabs\Web\Controller { $sql_extra .= protect_sprintf(term_query('item', $hashtags, TERM_HASHTAG, TERM_COMMUNITYTAG)); $sql_extra_order = " ORDER BY item.created DESC "; $thread_top = ''; + } $net_query2 = (($net) ? " and xchan_network = '" . protect_sprintf(dbesc($net)) . "' " : ''); @@ -196,7 +197,7 @@ class Pubstream extends \Zotlabs\Web\Controller { if($update) { - $ordering = Config::Get('system', 'pubstream_ordering', 'commented'); + $ordering = Config::Get('system', 'pubstream_ordering', 'created'); if($load) { if($mid) { @@ -250,15 +251,7 @@ class Pubstream extends \Zotlabs\Web\Controller { $parents_str = ''; if($r) { - - $parents_str = ids_to_querystr($r,'item_id'); - - $items = dbq("SELECT item.*, item.id AS item_id FROM item - WHERE true $uids $item_normal - AND item.parent IN ( $parents_str ) - $sql_extra $sql_extra_order" - ); - + $items = items_by_parent_ids($r); // use effective_uid param of xchan_query to help sort out comment permission // for sys_channel owned items. diff --git a/Zotlabs/Module/Regate.php b/Zotlabs/Module/Regate.php index c67f45a88..956c5e2ea 100644 --- a/Zotlabs/Module/Regate.php +++ b/Zotlabs/Module/Regate.php @@ -375,7 +375,7 @@ class Regate extends \Zotlabs\Web\Controller { ]); $reonar = json_decode( $r['reg_stuff'], true); - $reonar['deny'] = $now . ',' . $ip . ' ' . $did2 . ' ' . $msg; + $reonar['deny'] = $now . ',' . $ip . ' ' . $did2; $flags = ( $r['reg_flags'] &= ( $r['reg_flags'] ^ ACCOUNT_UNVERIFIED) ) | ( $r['reg_flags'] |= REGISTER_DENIED); $rd = q("UPDATE register SET reg_stuff='%s', reg_vital=0, reg_flags=%d WHERE reg_id = %d ", @@ -456,7 +456,7 @@ class Regate extends \Zotlabs\Web\Controller { // $log = ' from § ' . $ip . ' §' . ' (' . dbesc($did2) . ')'; zar_log($msg); $o = replace_macros(get_markup_template('plain.tpl'), [ - '$title' => $title, + '$title' => $msg, '$now' => $nowfmt, '$infos' => $msg ]); diff --git a/Zotlabs/Module/Request.php b/Zotlabs/Module/Request.php new file mode 100644 index 000000000..439f56282 --- /dev/null +++ b/Zotlabs/Module/Request.php @@ -0,0 +1,89 @@ +<?php +namespace Zotlabs\Module; + +use Zotlabs\Web\Controller; + +class Request extends Controller +{ + + private function mapVerb(string $verb) : string + { + $verbs = [ + 'like' => 'Like', + 'dislike' => 'Dislike', + 'announce' => 'Announce', + 'accept' => 'Accept', + 'reject' => 'Reject', + 'tentativeaccept' => 'TentativeAccept' + ]; + + if (array_key_exists($verb, $verbs)) { + return $verbs[$verb]; + } + + return EMPTY_STR; + } + + + private function processSubthreadRequest() : string + { + $mid = $_GET['mid']; + $parent = intval($_GET['parent']); + + $offset = null; + if ($_GET['verb'] === 'load') { + $offset = intval($_GET['offset']); + } + + $module = strip_tags($_GET['module']); + + $items = items_by_thr_parent($mid, $parent, $offset); + xchan_query($items); + + $items = fetch_post_tags($items,true); + + if ($module === 'channel') { + $parts = explode('@', $items[0]['owner']['xchan_addr']); + profile_load($parts[0]); + } + + $ret['html'] = conversation($items, $module, true, 'r_preview'); + + json_return_and_die($ret); + } + + public function get() : string + { + + if (in_array($_GET['verb'], ['comment', 'load'])) { + return self::processSubthreadRequest(); + } + + $verb = self::mapVerb($_GET['verb']); + + if (!$verb) { + killme(); + } + + $text = get_response_button_text($_GET['verb']); + $mid = strip_tags($_GET['mid']); + $parent = intval($_GET['parent']); + $observer_hash = get_observer_hash(); + + $ret['result'] = item_activity_xchans($mid, $parent, $verb); + + $commentable = $ret['result']['is_commentable']; + unset($ret['result']['is_commentable']); + + if ($commentable) { + $ret['action'] = (($verb === 'Announce') ? 'jotShare' : 'dolike'); + $ret['action_label'] = ((find_xchan_in_array($observer_hash, $ret['result'])) ? (($verb === 'Announce') ? t('+ Repeat again') : t('- Remove yours')) : t('+ Add yours')); + } + + $ret['title'] = $text['label']; + + json_return_and_die($ret); + + } + +} diff --git a/Zotlabs/Module/Settings/Display.php b/Zotlabs/Module/Settings/Display.php index a7fccea47..98c3d7543 100644 --- a/Zotlabs/Module/Settings/Display.php +++ b/Zotlabs/Module/Settings/Display.php @@ -24,7 +24,7 @@ class Display { $theme = 'redbasic'; - $preload_images = ((x($_POST,'preload_images')) ? intval($_POST['preload_images']) : 0); + $thread_allow = ((!empty($_POST['thread_allow'])) ? intval($_POST['thread_allow']) : 0); $user_scalable = ((x($_POST,'user_scalable')) ? intval($_POST['user_scalable']) : 0); $nosmile = ((x($_POST,'nosmile')) ? intval($_POST['nosmile']) : 0); $title_tosource = ((x($_POST,'title_tosource')) ? intval($_POST['title_tosource']) : 0); @@ -40,7 +40,7 @@ class Display { $itemspage = 30; - set_pconfig(local_channel(),'system','preload_images',$preload_images); + set_pconfig(local_channel(), 'system', 'thread_allow', $thread_allow); set_pconfig(local_channel(),'system','user_scalable',$user_scalable); set_pconfig(local_channel(),'system','update_interval', $browser_update); set_pconfig(local_channel(),'system','itemspage', $itemspage); @@ -146,8 +146,7 @@ class Display { $start_menu = get_pconfig(local_channel(), 'system', 'start_menu', 0); } - $preload_images = get_pconfig(local_channel(),'system','preload_images'); - $preload_images = (($preload_images===false)? '0': $preload_images); // default if not set: 0 + $thread_allow = get_pconfig(local_channel(), 'system', 'thread_allow', true); $user_scalable = get_pconfig(local_channel(),'system','user_scalable'); $user_scalable = (($user_scalable===false)? '0': $user_scalable); // default if not set: 0 @@ -192,7 +191,7 @@ class Display { '$theme' => (($themes) ? array('theme', t('Display Theme:'), $theme_selected, '', $themes, 'preview') : false), '$schema' => (($schemas) ? array('schema', t('Select scheme'), $existing_schema, '' , $schemas) : false), - '$preload_images' => array('preload_images', t("Preload images before rendering the page"), $preload_images, t("The subjective page load time will be longer but the page will be ready when displayed"), $yes_no), + '$thread_allow' => ['thread_allow', t('Threaded conversation view'), $thread_allow, t('Display replies below their parent message (default yes)'), $yes_no], '$user_scalable' => array('user_scalable', t("Enable user zoom on mobile devices"), $user_scalable, '', $yes_no), '$ajaxint' => array('browser_update', t("Update browser every xx seconds"), $browser_update, t('Minimum of 10 seconds, no maximum')), '$itemspage' => array('itemspage', t("Maximum number of conversations to load at any time:"), $itemspage, t('Maximum of 30 items')), diff --git a/Zotlabs/Module/Sse_bs.php b/Zotlabs/Module/Sse_bs.php index 8847ff242..c457363c0 100644 --- a/Zotlabs/Module/Sse_bs.php +++ b/Zotlabs/Module/Sse_bs.php @@ -42,7 +42,7 @@ class Sse_bs extends Controller { self::$offset = 0; self::$xchans = ''; - if (isset($_REQUEST['sse_rmids'])) { + if (!empty($_REQUEST['sse_rmids'])) { self::mark_read(explode(',', $_REQUEST['sse_rmids'])); } @@ -118,7 +118,6 @@ class Sse_bs extends Controller { } function mark_read($arr) { - $mids = []; $str = ''; $slice = 0; @@ -142,24 +141,51 @@ class Sse_bs extends Controller { } } - $_SESSION['sse_mids_all'] = serialise($mids_all); + $str = implode(',', $mids); + + $sys = get_sys_channel(); + $sql_order = ((self::$uid > $sys['channel_id']) ? 'DESC' : 'ASC'); + + $r = q("SELECT uid, uuid FROM item + WHERE uid in (%d, %d) + AND verb IN ('Like', 'Dislike', 'Announce', 'Accept', 'Reject', 'TentativeAccept') + AND thr_parent IN ( + SELECT mid FROM item WHERE uid IN (%d, %d) AND uuid IN (%s) ORDER BY uid $sql_order + ) + GROUP BY uid, uuid + ORDER BY uid $sql_order", + intval(self::$uid), + intval($sys['channel_id']), + intval(self::$uid), + intval($sys['channel_id']), + $str // this is dbesc() in the above foreach loop + ); + + if ($r) { + $activities_str = ids_to_querystr($r, 'uuid', true); + $str .= ',' . $activities_str; + $activities_arr = explode(',', $activities_str); + $mids_all = array_merge($mids_all, $activities_arr); + } + + $_SESSION['sse_mids_all'] = serialise(array_unique($mids_all)); if(! self::$uid) { return; } - $str = implode(',', $mids); - $x = [ 'channel_id' => self::$uid, 'update' => 'unset' ]; call_hooks('update_unseen',$x); - if($x['update'] === 'unset' || intval($x['update'])) { - q("UPDATE item SET item_unseen = 0 WHERE uid = %d AND uuid in (%s) AND item_unseen = 1", + if ($x['update'] === 'unset' || intval($x['update'])) { + 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 ); } - } function bs_network($notifications) { @@ -182,32 +208,34 @@ class Sse_bs extends Controller { $sql_extra = ''; if (!(self::$vnotify & VNOTIFY_LIKE)) { - $sql_extra = " AND verb NOT IN ('Like', 'Dislike', '" . dbesc(ACTIVITY_LIKE) . "', '" . dbesc(ACTIVITY_DISLIKE) . "') "; + $sql_extra = " AND item.verb NOT IN ('Like', 'Dislike', '" . dbesc(ACTIVITY_LIKE) . "', '" . dbesc(ACTIVITY_DISLIKE) . "') "; } elseif (!feature_enabled(self::$uid, 'dislike')) { - $sql_extra = " AND verb NOT IN ('Dislike', '" . dbesc(ACTIVITY_DISLIKE) . "') "; + $sql_extra = " AND item.verb NOT IN ('Dislike', '" . dbesc(ACTIVITY_DISLIKE) . "') "; } $sql_extra2 = ''; if(self::$xchans) - $sql_extra2 = " AND CASE WHEN verb = '" . dbesc(ACTIVITY_SHARE) . "' THEN owner_xchan ELSE author_xchan END IN (" . self::$xchans . ") "; + $sql_extra2 = " AND CASE WHEN item.verb = '" . dbesc(ACTIVITY_SHARE) . "' THEN item.owner_xchan ELSE item.author_xchan END IN (" . self::$xchans . ") "; $item_normal = item_normal(); // Filter internal follow activities and strerams add/remove activities - $item_normal .= " AND verb NOT IN ('Add', 'Remove', 'Follow', 'Ignore', '" . dbesc(ACTIVITY_FOLLOW) . "') "; + $item_normal .= " AND item.verb NOT IN ('Add', 'Remove', 'Follow', 'Ignore', '" . dbesc(ACTIVITY_FOLLOW) . "') "; if ($notifications) { - $items = q("SELECT * FROM item - WHERE uid = %d - AND created <= '%s' - AND item_unseen = 1 AND item_wall = 0 AND item_private IN (0, 1) - AND obj_type NOT IN ('Document', 'Video', 'Audio', 'Image') - AND author_xchan != '%s' + $items = q("SELECT item.*, tp.uuid AS thr_parent_uuid FROM item + LEFT JOIN item tp ON item.thr_parent = tp.mid AND item.uid = tp.uid + WHERE item.uid = %d + AND item.created <= '%s' + AND item.item_unseen = 1 AND item.item_wall = 0 AND item.item_private IN (0, 1) + AND item.obj_type NOT IN ('Document', 'Video', 'Audio', 'Image') + AND NOT (item.verb = 'Announce' AND item.item_thread_top = 1) -- only show the announce activity and not the resulting item + AND NOT item.author_xchan = '%s' $item_normal $sql_extra $sql_extra2 - ORDER BY created DESC LIMIT $limit OFFSET $offset", + ORDER BY item.created DESC LIMIT $limit OFFSET $offset", intval(self::$uid), dbescdate($_SESSION['sse_loadtime']), dbesc(self::$ob_hash) @@ -265,28 +293,30 @@ class Sse_bs extends Controller { $sql_extra = ''; if (!(self::$vnotify & VNOTIFY_LIKE)) { - $sql_extra = " AND verb NOT IN ('Like', 'Dislike', '" . dbesc(ACTIVITY_LIKE) . "', '" . dbesc(ACTIVITY_DISLIKE) . "') "; + $sql_extra = " AND item.verb NOT IN ('Like', 'Dislike', '" . dbesc(ACTIVITY_LIKE) . "', '" . dbesc(ACTIVITY_DISLIKE) . "') "; } elseif (!feature_enabled(self::$uid, 'dislike')) { - $sql_extra = " AND verb NOT IN ('Dislike', '" . dbesc(ACTIVITY_DISLIKE) . "') "; + $sql_extra = " AND item.verb NOT IN ('Dislike', '" . dbesc(ACTIVITY_DISLIKE) . "') "; } $sql_extra2 = ''; if(self::$xchans) - $sql_extra2 = " AND CASE WHEN verb = '" . ACTIVITY_SHARE . "' THEN owner_xchan ELSE author_xchan END IN (" . self::$xchans . ") "; + $sql_extra2 = " AND CASE WHEN item.verb = '" . ACTIVITY_SHARE . "' THEN item.owner_xchan ELSE item.author_xchan END IN (" . self::$xchans . ") "; $item_normal = item_normal(); // Filter internal follow activities and strerams add/remove activities - $item_normal .= " AND verb NOT IN ('Add', 'Remove', 'Follow', 'Ignore', '" . dbesc(ACTIVITY_FOLLOW) . "') "; + $item_normal .= " AND item.verb NOT IN ('Add', 'Remove', 'Follow', 'Ignore', '" . dbesc(ACTIVITY_FOLLOW) . "') "; if ($notifications) { - $items = q("SELECT * FROM item - WHERE uid = %d - AND created <= '%s' - AND item_unseen = 1 AND item_private = 2 - AND obj_type NOT IN ('Document', 'Video', 'Audio', 'Image') - AND author_xchan != '%s' + $items = q("SELECT item.*, tp.uuid AS thr_parent_uuid FROM item + LEFT JOIN item tp ON item.thr_parent = tp.mid AND item.uid = tp.uid + WHERE item.uid = %d + AND item.created <= '%s' + AND item.item_unseen = 1 AND item.item_private = 2 + AND item.obj_type NOT IN ('Document', 'Video', 'Audio', 'Image') + AND NOT (item.verb = 'Announce' AND item.item_thread_top = 1) -- only show the announce activity and not the resulting item + AND NOT item.author_xchan = '%s' $item_normal $sql_extra $sql_extra2 @@ -347,33 +377,35 @@ class Sse_bs extends Controller { $sql_extra = ''; if (!(self::$vnotify & VNOTIFY_LIKE)) { - $sql_extra = " AND verb NOT IN ('Like', 'Dislike', '" . dbesc(ACTIVITY_LIKE) . "', '" . dbesc(ACTIVITY_DISLIKE) . "') "; + $sql_extra = " AND item.verb NOT IN ('Like', 'Dislike', '" . dbesc(ACTIVITY_LIKE) . "', '" . dbesc(ACTIVITY_DISLIKE) . "') "; } elseif (!feature_enabled(self::$uid, 'dislike')) { - $sql_extra = " AND verb NOT IN ('Dislike', '" . dbesc(ACTIVITY_DISLIKE) . "') "; + $sql_extra = " AND item.verb NOT IN ('Dislike', '" . dbesc(ACTIVITY_DISLIKE) . "') "; } $sql_extra2 = ''; if(self::$xchans) - $sql_extra2 = " AND CASE WHEN verb = '" . ACTIVITY_SHARE . "' THEN owner_xchan ELSE author_xchan END IN (" . self::$xchans . ") "; + $sql_extra2 = " AND CASE WHEN item.verb = '" . ACTIVITY_SHARE . "' THEN item.owner_xchan ELSE item.author_xchan END IN (" . self::$xchans . ") "; $item_normal = item_normal(); // Filter internal follow activities and strerams add/remove activities - $item_normal .= " AND verb NOT IN ('Add', 'Remove', 'Follow', 'Ignore', '" . dbesc(ACTIVITY_FOLLOW) . "') "; + $item_normal .= " AND item.verb NOT IN ('Add', 'Remove', 'Follow', 'Ignore', '" . dbesc(ACTIVITY_FOLLOW) . "') "; if ($notifications) { - $items = q("SELECT * FROM item - WHERE uid = %d - AND created <= '%s' - AND item_unseen = 1 AND item_wall = 1 AND item_private IN (0, 1) - AND obj_type NOT IN ('Document', 'Video', 'Audio', 'Image') - AND author_xchan != '%s' + $items = q("SELECT item.*, tp.uuid AS thr_parent_uuid FROM item + LEFT JOIN item tp ON item.thr_parent = tp.mid AND item.uid = tp.uid + WHERE item.uid = %d + AND item.created <= '%s' + AND item.item_unseen = 1 AND item.item_wall = 1 AND item.item_private IN (0, 1) + AND item.obj_type NOT IN ('Document', 'Video', 'Audio', 'Image') + AND NOT (item.verb = 'Announce' AND item.item_thread_top = 1) -- only show the announce activity and not the resulting item + AND NOT item.author_xchan = '%s' $item_normal $sql_extra $sql_extra2 - ORDER BY created DESC LIMIT $limit OFFSET $offset", + ORDER BY item.created DESC LIMIT $limit OFFSET $offset", intval(self::$uid), dbescdate($_SESSION['sse_loadtime']), dbesc(self::$ob_hash) @@ -442,49 +474,51 @@ class Sse_bs extends Controller { $sys = get_sys_channel(); $sql_extra = ''; if (!(self::$vnotify & VNOTIFY_LIKE)) { - $sql_extra = " AND verb NOT IN ('Like', 'Dislike', '" . dbesc(ACTIVITY_LIKE) . "', '" . dbesc(ACTIVITY_DISLIKE) . "') "; + $sql_extra = " AND item.verb NOT IN ('Like', 'Dislike', '" . dbesc(ACTIVITY_LIKE) . "', '" . dbesc(ACTIVITY_DISLIKE) . "') "; } elseif (!feature_enabled(self::$uid, 'dislike')) { - $sql_extra = " AND verb NOT IN ('Dislike', '" . dbesc(ACTIVITY_DISLIKE) . "') "; + $sql_extra = " AND item.verb NOT IN ('Dislike', '" . dbesc(ACTIVITY_DISLIKE) . "') "; } $sql_extra2 = ''; if(self::$xchans) - $sql_extra2 = " AND CASE WHEN verb = '" . ACTIVITY_SHARE . "' THEN owner_xchan ELSE author_xchan END IN (" . self::$xchans . ") "; + $sql_extra2 = " AND CASE WHEN item.verb = '" . ACTIVITY_SHARE . "' THEN item.owner_xchan ELSE item.author_xchan END IN (" . self::$xchans . ") "; $sql_extra3 = ''; - $sse_mids_all = unserialise($_SESSION['sse_mids_all']) ?? []; + $sse_mids_all = isset($_SESSION['sse_mids_all']) ? unserialise($_SESSION['sse_mids_all']) : []; if ($sse_mids_all) { - $sql_extra3 = " AND uuid NOT IN (" . protect_sprintf(implode(',', $sse_mids_all)) . ") "; + $sql_extra3 = " AND item.uuid NOT IN (" . protect_sprintf(implode(',', $sse_mids_all)) . ") "; } - $uids = " AND uid IN ( " . $sys['channel_id'] . " ) "; + $uids = " AND item.uid IN ( " . $sys['channel_id'] . " ) "; $site_firehose = Config::Get('system', 'site_firehose', 0); if($site_firehose) { - $uids = " AND uid IN ( " . stream_perms_api_uids(PERMS_PUBLIC) . " ) AND item_private = 0 AND item_wall = 1 "; + $uids = " AND item.uid IN ( " . stream_perms_api_uids(PERMS_PUBLIC) . " ) AND item.item_private = 0 AND item.item_wall = 1 "; } $item_normal = item_normal(); // Filter internal follow activities and strerams add/remove activities - $item_normal .= " AND verb NOT IN ('Add', 'Remove', 'Follow', 'Ignore', '" . dbesc(ACTIVITY_FOLLOW) . "') "; + $item_normal .= " AND item.verb NOT IN ('Add', 'Remove', 'Follow', 'Ignore', '" . dbesc(ACTIVITY_FOLLOW) . "') "; if ($notifications) { - $items = q("SELECT * FROM item + $items = q("SELECT item.*, tp.uuid AS thr_parent_uuid FROM item + LEFT JOIN item tp ON item.thr_parent = tp.mid AND item.uid = tp.uid WHERE true $uids - AND created <= '%s' - AND obj_type NOT IN ('Document', 'Video', 'Audio', 'Image') - AND author_xchan != '%s' - AND created > '%s' + AND item.created <= '%s' + AND item.created > '%s' + AND item.obj_type NOT IN ('Document', 'Video', 'Audio', 'Image') + AND NOT (item.verb = 'Announce' AND item.item_thread_top = 1) -- only show the announce activity not the resulting item + AND NOT item.author_xchan = '%s' $item_normal $sql_extra $sql_extra2 $sql_extra3 - ORDER BY created DESC LIMIT $limit OFFSET $offset", + ORDER BY item.created DESC LIMIT $limit OFFSET $offset", dbescdate($_SESSION['sse_loadtime']), - dbesc(self::$ob_hash), - dbescdate($_SESSION['last_login_date'] ?? $_SESSION['static_loadtime']) + dbescdate($_SESSION['last_login_date'] ?? $_SESSION['static_loadtime']), + dbesc(self::$ob_hash) ); if($items) { diff --git a/Zotlabs/Module/Viewsrc.php b/Zotlabs/Module/Viewsrc.php index 3e49b9db4..cfc184a9d 100644 --- a/Zotlabs/Module/Viewsrc.php +++ b/Zotlabs/Module/Viewsrc.php @@ -6,34 +6,34 @@ namespace Zotlabs\Module; class Viewsrc extends \Zotlabs\Web\Controller { function get() { - + $o = ''; - + $sys = get_sys_channel(); - + $item_id = ((argc() > 1) ? intval(argv(1)) : 0); $json = ((argc() > 2 && argv(2) === 'json') ? true : false); $dload = ((argc() > 2 && argv(2) === 'download') ? true : false); - + if(! local_channel()) { notice( t('Permission denied.') . EOL); } - - + + if(! $item_id) { \App::$error = 404; notice( t('Item not found.') . EOL); } - + $item_normal = item_normal_search(); - + if(local_channel() && $item_id) { - $r = q("select id, mid, item_flags, mimetype, item_obscured, body, llink, plink from item where uid in (%d , %d) and id = %d $item_normal limit 1", + $r = q("select id, mid, uuid, item_flags, mimetype, item_obscured, body, llink, plink from item where uid in (%d , %d) and id = %d $item_normal limit 1", intval(local_channel()), intval($sys['channel_id']), intval($item_id) ); - + if($r) { if(intval($r[0]['item_obscured'])) $dload = true; @@ -50,18 +50,18 @@ class Viewsrc extends \Zotlabs\Web\Controller { $o = (($json) ? json_encode($content) : $content); } } - + if(is_ajax()) { echo '<div class="p-1">'; - echo '<div>id: ' . $r[0]['id'] . ' | <a href="' . $r[0]['plink'] . '" target="_blank">plink</a> | <a href="' . $r[0]['llink'] . '" target="_blank">llink</a><br>mid: ' . $r[0]['mid'] . '</div>'; + echo '<div>id: ' . $r[0]['id'] . ' | <a href="' . $r[0]['plink'] . '" target="_blank">plink</a> | <a href="' . $r[0]['llink'] . '" target="_blank">llink</a><br>mid: ' . $r[0]['mid'] . '<br>uuid: ' . $r[0]['uuid'] . '</div>'; echo '<hr>'; echo '<pre class="p-1">' . $o . '</pre>'; echo '</div>'; killme(); - } - + } + return $o; } - - + + } diff --git a/Zotlabs/Module/Wall_attach.php b/Zotlabs/Module/Wall_attach.php index e354f58f1..84c76f8dd 100644 --- a/Zotlabs/Module/Wall_attach.php +++ b/Zotlabs/Module/Wall_attach.php @@ -10,7 +10,7 @@ class Wall_attach extends \Zotlabs\Web\Controller { function init() { logger('request_method: ' . $_SERVER['REQUEST_METHOD'],LOGGER_DATA,LOG_INFO); - logger('wall_attach: ' . print_r($_REQUEST,true),LOGGER_DEBUG,LOG_INFO); + logger('wall_attach: ' . print_r($_POST,true),LOGGER_DEBUG,LOG_INFO); logger('wall_attach files: ' . print_r($_FILES,true),LOGGER_DEBUG,LOG_INFO); // for testing without actually storing anything // http_status_exit(200,'OK'); @@ -18,12 +18,11 @@ class Wall_attach extends \Zotlabs\Web\Controller { function post() { - $using_api = false; $result = []; - if($_REQUEST['api_source'] && array_key_exists('media',$_FILES)) { + if($_POST['api_source'] && array_key_exists('media',$_FILES)) { $using_api = true; } @@ -98,8 +97,8 @@ class Wall_attach extends \Zotlabs\Web\Controller { $r = attach_store($channel, get_observer_hash(), '', $data); - if(! $r['success']) { - notice( $r['message'] . EOL); + if (!$r['success']) { + notice($r['message'] . EOL); killme(); } diff --git a/Zotlabs/Module/Xref.php b/Zotlabs/Module/Xref.php deleted file mode 100644 index e9d494da4..000000000 --- a/Zotlabs/Module/Xref.php +++ /dev/null @@ -1,26 +0,0 @@ -<?php -namespace Zotlabs\Module; - - -class Xref extends \Zotlabs\Web\Controller { - - function init() { - // Sets a referral URL using an xchan directly - // Link format: example.com/xref/[xchan]/[TargetURL] - // Target URL is optional. - // Cookie lasts 24 hours to survive a browser restart. Contains no personal - // information at all - just somebody else's xchan. - $referrer = argv(1); - $expire=time()+60*60*2; - $path = 'xref'; - setcookie($path, $referrer, $expire, "/"); - $url = ''; - - if (argc() > 2) - $url = argv(2); - - goaway (z_root() . '/' . $url); - - } - -} diff --git a/Zotlabs/Photo/PhotoGd.php b/Zotlabs/Photo/PhotoGd.php index a81d9cae2..8d46ae4d5 100644 --- a/Zotlabs/Photo/PhotoGd.php +++ b/Zotlabs/Photo/PhotoGd.php @@ -25,6 +25,8 @@ class PhotoGd extends PhotoDriver { $t['image/gif'] = 'gif'; if(\imagetypes() & IMG_WEBP) $t['image/webp'] = 'webp'; + if(\imagetypes() & IMG_AVIF) + $t['image/avif'] = 'avif'; return $t; } @@ -184,6 +186,19 @@ class PhotoGd extends PhotoDriver { break; + case 'image/avif': + $quality = Config::Get('system', 'avif_quality'); + + if((! $quality) || ($quality > 100)) { + $quality = AVIF_QUALITY; + } + + if (function_exists('imageavif')) { + \imageavif($this->image, NULL, $quality); + } + + break; + case 'image/jpeg': // gd can lack imagejpeg(), but we verify during installation it is available diff --git a/Zotlabs/Render/SmartyInterface.php b/Zotlabs/Render/SmartyInterface.php index a319a4881..003262939 100644 --- a/Zotlabs/Render/SmartyInterface.php +++ b/Zotlabs/Render/SmartyInterface.php @@ -19,7 +19,7 @@ class SmartyInterface extends Smarty { // The order is thus very important here $template_dirs = array('theme' => "view/theme/$thname/tpl/"); - if ( x(App::$theme_info,"extends") ) { + if (!empty(App::$theme_info['extends'])) { $template_dirs = $template_dirs + array('extends' => "view/theme/" . App::$theme_info["extends"] . "/tpl/"); } $template_dirs = $template_dirs + array('base' => 'view/tpl/'); diff --git a/Zotlabs/Render/Theme.php b/Zotlabs/Render/Theme.php index a42b65e03..2faf3631a 100644 --- a/Zotlabs/Render/Theme.php +++ b/Zotlabs/Render/Theme.php @@ -26,9 +26,9 @@ class Theme { */ static public function current() { - self::$system_theme = ((isset(App::$config['system']['theme'])) + self::$system_theme = ((!empty(App::$config['system']['theme'])) ? App::$config['system']['theme'] : ''); - self::$session_theme = ((isset($_SESSION) && x($_SESSION, 'theme')) + self::$session_theme = ((!empty($_SESSION['theme'])) ? $_SESSION['theme'] : self::$system_theme); $page_theme = null; @@ -87,8 +87,13 @@ class Theme { // Find any theme at all and use it. $fallback = array_merge(glob('view/theme/*/css/style.css'), glob('view/theme/*/php/style.php')); - if(count($fallback)) - return(array(str_replace('view/theme/', '', substr($fallback[0], 0, -14)))); + + if (empty($fallback)) { + logger('Unable to find a theme'); + http_status_exit(500, 'internal server error'); + } + + return(array(str_replace('view/theme/', '', substr($fallback[0], 0, -14)))); } @@ -111,7 +116,7 @@ class Theme { $opts = ''; $opts = (($uid) ? '?puid=' . $uid : ''); - $schema_str = ((x(App::$layout,'schema')) ? '&schema=' . App::$layout['schema'] : ''); + $schema_str = ((!empty(App::$layout['schema'])) ? '&schema=' . App::$layout['schema'] : ''); if(($s) && (! $schema_str)) $schema_str = '&schema=' . $s; diff --git a/Zotlabs/Storage/Browser.php b/Zotlabs/Storage/Browser.php index 6a9cd61ef..6ea6031d3 100644 --- a/Zotlabs/Storage/Browser.php +++ b/Zotlabs/Storage/Browser.php @@ -5,6 +5,7 @@ namespace Zotlabs\Storage; use Sabre\DAV; use App; use Zotlabs\Lib\Config; +use Zotlabs\Lib\Text; /** * @brief Provides a DAV frontend for the webbrowser. @@ -260,13 +261,16 @@ class Browser extends DAV\Browser\Plugin { } } + $display_path_encoded = Text::rawurlencode_parts($data['display_path']); + $href_encoded = Text::rawurlencode_parts($href); + // put the array for this file together $ft['attach_id'] = $id; // $ft['icon'] = $icon; $ft['photo_icon'] = $photo_icon; $ft['is_creator'] = $is_creator; - $ft['rel_path'] = (($data) ? '/cloud/' . $nick .'/' . $data['display_path'] : $href); - $ft['full_path'] = z_root() . (($data) ? '/cloud/' . $nick .'/' . $data['display_path'] : $href); + $ft['rel_path'] = (($data) ? '/cloud/' . $nick .'/' . $display_path_encoded : $href_encoded); + $ft['full_path'] = z_root() . (($data) ? '/cloud/' . $nick .'/' . $display_path_encoded : $href_encoded); $ft['name'] = $name; $ft['type'] = $type; $ft['size'] = $size; diff --git a/Zotlabs/Web/HTTPSig.php b/Zotlabs/Web/HTTPSig.php index 7c289ff5f..ce56ae46b 100644 --- a/Zotlabs/Web/HTTPSig.php +++ b/Zotlabs/Web/HTTPSig.php @@ -2,6 +2,7 @@ namespace Zotlabs\Web; +use App; use DateTime; use DateTimeZone; use Zotlabs\Lib\Activity; @@ -11,6 +12,7 @@ use Zotlabs\Lib\Keyutils; use Zotlabs\Lib\Webfinger; use Zotlabs\Lib\Zotfinger; use Zotlabs\Lib\Libzot; +use HttpSignature\HttpMessageSigner; /** * @brief Implements HTTP Signatures per draft-cavage-http-signatures-10. @@ -88,7 +90,7 @@ class HTTPSig { // See draft-cavage-http-signatures-10 - static function verify($data, $key = '', $keytype = '') { + public static function verify($data, $key = '', $keytype = '') { $body = $data; $headers = null; @@ -102,11 +104,49 @@ class HTTPSig { 'content_valid' => false ]; - $headers = self::find_headers($data, $body); - if (!$headers) + if (!$headers) { return $result; + } + + if (array_key_exists('signature-input', $headers) && array_key_exists('signature', $headers)) { + $found = preg_match('/keyid="(.*?)"/', $headers['signature-input'], $matches); + $keyId = ($found) ? $matches[1] : ''; + + if (!$keyId) { + return $result; + } + + $found = preg_match('/alg="(.*?)"/', $headers['signature-input'], $matches); + $alg = ($found) ? $matches[1] : null; + + $keyInfo = self::get_key($key, $keytype, $keyId); + $publicKey = $keyInfo['public_key']; + + $messageSigner = new HttpMessageSigner(); + + $messageSigner->setPublicKey($publicKey); + $messageSigner->setAlgorithm($alg); + $messageSigner->setKeyId($keyId); + + $messageSigner->setNonce(preg_match('/nonce="(.*?)"/', $headers['signature-input'], $matches) ? $matches[1] : ''); + $messageSigner->setTag(preg_match('/tag="(.*?)"/', $headers['signature-input'], $matches) ? $matches[1] : ''); + $messageSigner->setCreated(preg_match('/created=([0-9]+)/', $headers['signature-input'], $matches) ? $matches[1] : ''); + $messageSigner->setExpires(preg_match('/expires=([0-9]+)/', $headers['signature-input'], $matches) ? $matches[1] : ''); + + $verified = $messageSigner->verifyRequest(App::$request); + logger('verified (RFC9421): ' . (($verified) ? 'true' : 'false'), LOGGER_DEBUG); + + return [ + 'signer' => $keyId, + 'portable_id' => $keyInfo['portable_id'] ?? '', + 'header_signed' => true, + 'header_valid' => $verified, + 'content_signed' => array_key_exists('content-digest', $headers), + 'content_valid' => $verified + ]; + } if (is_array($body)) { btlogger('body is array:' . print_r($body, true)); diff --git a/Zotlabs/Web/Router.php b/Zotlabs/Web/Router.php index 122e7ff73..9f4cb4b4a 100644 --- a/Zotlabs/Web/Router.php +++ b/Zotlabs/Web/Router.php @@ -4,6 +4,7 @@ namespace Zotlabs\Web; use App; use Zotlabs\Extend\Route; +use Zotlabs\Render\Theme; use Zotlabs\Lib\Config; use Exception; @@ -33,6 +34,7 @@ use Exception; * so within the module init and/or post functions and then invoke killme() to terminate * further processing. */ + class Router { private $modname = ''; @@ -49,7 +51,7 @@ class Router { $module = App::$module; $modname = "Zotlabs\\Module\\" . ucfirst($module); - if(strlen($module)) { + if(!empty($module)) { /* * We will always have a module name. @@ -57,11 +59,11 @@ class Router { */ $routes = Route::get(); - if($routes) { + if ($routes) { foreach($routes as $route) { - if(is_array($route) && file_exists($route[0]) && strtolower($route[1]) === $module) { + if (is_array($route) && file_exists($route[0]) && strtolower($route[1]) === $module) { include_once($route[0]); - if(class_exists($modname)) { + if (class_exists($modname)) { $this->controller = new $modname; $this->module_loaded = true; } @@ -71,14 +73,14 @@ class Router { // legacy plugins - this can be removed when they have all been converted - if(! ($this->module_loaded)) { - if(is_array(App::$plugins) && in_array($module, App::$plugins) && file_exists("addon/{$module}/{$module}.php")) { + if (!$this->module_loaded) { + if (is_array(App::$plugins) && in_array($module, App::$plugins) && file_exists("addon/{$module}/{$module}.php")) { include_once("addon/{$module}/{$module}.php"); - if(class_exists($modname)) { + if (class_exists($modname)) { $this->controller = new $modname; $this->module_loaded = true; } - elseif(function_exists($module . '_module')) { + elseif (function_exists($module . '_module')) { $this->module_loaded = true; } } @@ -89,7 +91,7 @@ class Router { * Otherwise, look for the standard program module */ - if(! ($this->module_loaded)) { + if (!$this->module_loaded) { try { $filename = 'Zotlabs/SiteModule/'. ucfirst($module). '.php'; if(file_exists($filename)) { @@ -105,15 +107,16 @@ class Router { $this->module_loaded = true; } } - if(! $this->module_loaded) + if (!$this->module_loaded) { throw new Exception('Module not found'); + } } - catch(Exception $e) { - if(file_exists("mod/site/{$module}.php")) { + catch (Exception $e) { + if (file_exists("mod/site/{$module}.php")) { include_once("mod/site/{$module}.php"); $this->module_loaded = true; } - elseif(file_exists("mod/{$module}.php")) { + elseif (file_exists("mod/{$module}.php")) { include_once("mod/{$module}.php"); $this->module_loaded = true; } @@ -125,6 +128,7 @@ class Router { 'installed' => $this->module_loaded, 'controller' => $this->controller ]; + /** * @hooks module_loaded * Called when a module has been successfully locate to server a URL request. @@ -138,7 +142,8 @@ class Router { * * \e mixed \b controller - The initialized module object */ call_hooks('module_loaded', $x); - if($x['installed']) { + + if ($x['installed']) { $this->module_loaded = true; $this->controller = $x['controller']; } @@ -147,7 +152,7 @@ class Router { * The URL provided does not resolve to a valid module. */ - if(! ($this->module_loaded)) { + if (!$this->module_loaded) { // undo the setting of a letsencrypt acme-challenge rewrite rule // which blocks access to our .well-known routes. @@ -155,8 +160,8 @@ class Router { // for a custom .htaccess in the .well-known directory; but they should // make the file read-only so letsencrypt doesn't modify it - if(strpos($_SERVER['REQUEST_URI'],'/.well-known/') === 0) { - if(file_exists('.well-known/.htaccess') && Config::Get('system','fix_apache_acme',true)) { + if (strpos($_SERVER['REQUEST_URI'],'/.well-known/') === 0) { + if (file_exists('.well-known/.htaccess') && Config::Get('system','fix_apache_acme',true)) { rename('.well-known/.htaccess','.well-known/.htaccess.old'); } } @@ -166,16 +171,17 @@ class Router { 'installed' => $this->module_loaded, 'controller' => $this->controller ]; + call_hooks('page_not_found',$x); // Stupid browser tried to pre-fetch our Javascript img template. // Don't log the event or return anything - just quietly exit. - if((x($_SERVER, 'QUERY_STRING')) && preg_match('/{[0-9]}/', $_SERVER['QUERY_STRING']) !== 0) { + if (!empty($_SERVER['QUERY_STRING']) && preg_match('/{[0-9]}/', $_SERVER['QUERY_STRING']) !== 0) { killme(); } - if(Config::Get('system','log_404',true)) { + if (Config::Get('system','log_404',true)) { logger("Module {$module} not found.", LOGGER_DEBUG, LOG_WARNING); logger('index.php: page not found: ' . $_SERVER['REQUEST_URI'] . ' ADDRESS: ' . $_SERVER['REMOTE_ADDR'] . ' QUERY: ' @@ -206,7 +212,7 @@ class Router { * Call module functions */ - if($this->module_loaded) { + if ($this->module_loaded) { App::$page['page_title'] = App::$module; $placeholder = ''; @@ -218,13 +224,18 @@ class Router { * to over-ride them. */ - $arr = array('init' => true, 'replace' => false); + $arr = [ + 'init' => true, + 'replace' => false + ]; + call_hooks(App::$module . '_mod_init', $arr); - if(! $arr['replace']) { - if($this->controller && method_exists($this->controller,'init')) { + + if (!$arr['replace']) { + if ($this->controller && method_exists($this->controller,'init')) { $this->controller->init(); } - elseif(function_exists(App::$module . '_init')) { + elseif (function_exists(App::$module . '_init')) { $func = App::$module . '_init'; $func(); } @@ -250,52 +261,58 @@ class Router { * load current theme info */ - $current_theme = \Zotlabs\Render\Theme::current(); + $current_theme = Theme::current(); $theme_info_file = 'view/theme/' . $current_theme[0] . '/php/theme.php'; if (file_exists($theme_info_file)){ require_once($theme_info_file); } - if(function_exists(str_replace('-', '_', $current_theme[0]) . '_init')) { + if (function_exists(str_replace('-', '_', $current_theme[0]) . '_init')) { $func = str_replace('-', '_', $current_theme[0]) . '_init'; $func(); } - elseif (x(App::$theme_info, 'extends') && file_exists('view/theme/' . App::$theme_info['extends'] . '/php/theme.php')) { + elseif (!empty(App::$theme_info['extends']) && file_exists('view/theme/' . App::$theme_info['extends'] . '/php/theme.php')) { require_once('view/theme/' . App::$theme_info['extends'] . '/php/theme.php'); - if(function_exists(str_replace('-', '_', App::$theme_info['extends']) . '_init')) { + if (function_exists(str_replace('-', '_', App::$theme_info['extends']) . '_init')) { $func = str_replace('-', '_', App::$theme_info['extends']) . '_init'; $func(); } } - if(($_SERVER['REQUEST_METHOD'] === 'POST') && (! App::$error) && (! x($_POST, 'auth-params'))) { + if ($_SERVER['REQUEST_METHOD'] === 'POST' && !App::$error && empty($_POST['auth-params'])) { call_hooks(App::$module . '_mod_post', $_POST); - if($this->controller && method_exists($this->controller,'post')) { + if ($this->controller && method_exists($this->controller, 'post')) { $this->controller->post(); } - elseif(function_exists(App::$module . '_post')) { + elseif (function_exists(App::$module . '_post')) { $func = App::$module . '_post'; $func(); } } - if(! App::$error) { - $arr = array('content' => App::$page['content'], 'replace' => false); + if (!App::$error) { + $arr = [ + 'content' => App::$page['content'], + 'replace' => false + ]; + call_hooks(App::$module . '_mod_content', $arr); - if(! $arr['replace']) { - if($this->controller && method_exists($this->controller,'get')) { + if (!$arr['replace']) { + if ($this->controller && method_exists($this->controller, 'get')) { $arr = array('content' => $this->controller->get()); } - elseif(function_exists(App::$module . '_content')) { + elseif (function_exists(App::$module . '_content')) { $func = App::$module . '_content'; $arr = array('content' => $func()); } } + call_hooks(App::$module . '_mod_aftercontent', $arr); - App::$page['content'] = ((isset($arr['replace'])) ? $arr['content'] : App::$page['content'] . $arr['content']); + + App::$page['content'] = ((empty($arr['replace'])) ? App::$page['content'] . $arr['content'] : $arr['content']); } } } diff --git a/Zotlabs/Web/Session.php b/Zotlabs/Web/Session.php index ec26e6b0d..1762d3832 100644 --- a/Zotlabs/Web/Session.php +++ b/Zotlabs/Web/Session.php @@ -132,7 +132,7 @@ class Session { else logger('no session handler'); - if (x($_COOKIE, 'jsdisabled')) { + if (!empty($_COOKIE['jsdisabled'])) { setcookie( 'jsdisabled', $_COOKIE['jsdisabled'], diff --git a/Zotlabs/Web/WebServer.php b/Zotlabs/Web/WebServer.php index d59effc88..89ef755d9 100644 --- a/Zotlabs/Web/WebServer.php +++ b/Zotlabs/Web/WebServer.php @@ -2,7 +2,9 @@ namespace Zotlabs\Web; +use App; use Zotlabs\Lib\Text; +use GuzzleHttp\Psr7\ServerRequest; class WebServer { @@ -17,9 +19,10 @@ class WebServer { $installed = sys_boot(); + App::$request = ServerRequest::fromGlobals(); - \App::$language = get_best_language(); - load_translation_table(\App::$language, !$installed); + App::$language = get_best_language(); + load_translation_table(App::$language, !$installed); /** @@ -33,8 +36,8 @@ class WebServer { * */ - if(\App::$session) { - \App::$session->start(); + if(App::$session) { + App::$session->start(); } else { session_start(); @@ -53,13 +56,13 @@ class WebServer { unset($_SESSION['language']); } - if ((x($_SESSION, 'language')) && ($_SESSION['language'] !== \App::$language)) { - \App::$language = $_SESSION['language']; + if ((!empty($_SESSION['language'])) && ($_SESSION['language'] !== App::$language)) { + App::$language = $_SESSION['language']; load_translation_table(\App::$language); } - if (x($_GET,'zid') && $installed) { - \App::$query_string = strip_zids(\App::$query_string); + if (!empty($_GET['zid']) && $installed) { + App::$query_string = strip_zids(App::$query_string); if(! local_channel()) { if (!isset($_SESSION['my_address'])) { $_SESSION['my_address'] = Text::escape_tags($_GET['zid']); @@ -71,26 +74,28 @@ class WebServer { } } - if (x($_GET,'zat') && $installed) { - \App::$query_string = strip_zats(\App::$query_string); + if (!empty($_GET['zat']) && $installed) { + App::$query_string = strip_zats(App::$query_string); if(! local_channel()) { zat_init(); } } - if (x($_REQUEST,'owt') && $installed) { + if (!empty($_REQUEST['owt']) && $installed) { $token = $_REQUEST['owt']; - \App::$query_string = strip_query_param(\App::$query_string,'owt'); + App::$query_string = strip_query_param(App::$query_string,'owt'); owt_init($token); } - if((x($_SESSION, 'authenticated')) || (x($_POST, 'auth-params')) || (\App::$module === 'login')) + if(!empty($_SESSION['authenticated']) || !empty($_POST['auth-params']) || App::$module === 'login') { require('include/auth.php'); + } if (!$installed) { /* Allow an exception for the view module so that pcss will be interpreted during installation */ - if(\App::$module != 'view') - \App::$module = 'setup'; + if(App::$module != 'view') { + App::$module = 'setup'; + } } else { @@ -111,17 +116,7 @@ class WebServer { $Router->Dispatch(); - // TODO: this is not used for anything atm and messes up comanche templates by adding some javascript - //$this->set_homebase(); - - // now that we've been through the module content, see if the page reported - // a permission problem and if so, a 403 response would seem to be in order. - - if(isset($_SESSION['sysmsg']) && is_array($_SESSION['sysmsg']) && stristr(implode("", $_SESSION['sysmsg']), t('Permission denied'))) { - header($_SERVER['SERVER_PROTOCOL'] . ' 403 ' . t('Permission denied.')); - } - - call_hooks('page_end', \App::$page['content']); + call_hooks('page_end', App::$page['content']); construct_page(); @@ -133,10 +128,11 @@ class WebServer { /* initialise content region */ - if(! x(\App::$page, 'content')) - \App::$page['content'] = ''; + if(empty(App::$page['content'])) { + App::$page['content'] = ''; + } - call_hooks('page_content_top', \App::$page['content']); + call_hooks('page_content_top', App::$page['content']); } @@ -148,44 +144,24 @@ class WebServer { * to all protocol drivers; thus doing it here avoids duplication. */ - if (( \App::$module === 'channel' ) && argc() > 1) { - \App::$channel_links = [ + if (App::$module === 'channel' && argc() > 1) { + App::$channel_links = [ [ 'rel' => 'lrdd', 'type' => 'application/xrd+xml', - 'url' => z_root() . '/xrd?f=&uri=acct%3A' . argv(1) . '%40' . \App::get_hostname() + 'url' => z_root() . '/xrd?f=&uri=acct%3A' . argv(1) . '%40' . App::get_hostname() ], [ 'rel' => 'jrd', 'type' => 'application/jrd+json', - 'url' => z_root() . '/.well-known/webfinger?f=&resource=acct%3A' . argv(1) . '%40' . \App::get_hostname() + 'url' => z_root() . '/.well-known/webfinger?f=&resource=acct%3A' . argv(1) . '%40' . App::get_hostname() ], ]; - $x = [ 'channel_address' => argv(1), 'channel_links' => \App::$channel_links ]; + $x = [ 'channel_address' => argv(1), 'channel_links' => App::$channel_links ]; call_hooks('channel_links', $x ); - \App::$channel_links = $x['channel_links']; + App::$channel_links = $x['channel_links']; header('Link: ' . \App::get_channel_links()); } } - - private function set_homebase() { - - // If you're just visiting, let javascript take you home - - if(x($_SESSION, 'visitor_home')) { - $homebase = $_SESSION['visitor_home']; - } - elseif(local_channel()) { - $homebase = z_root() . '/channel/' . \App::$channel['channel_address']; - } - - if(isset($homebase)) { - \App::$page['content'] .= '<script>var homebase = "' . $homebase . '";</script>'; - } - - } - - - } diff --git a/Zotlabs/Widget/Activity_order.php b/Zotlabs/Widget/Activity_order.php index e8ee11508..8d47370db 100644 --- a/Zotlabs/Widget/Activity_order.php +++ b/Zotlabs/Widget/Activity_order.php @@ -16,7 +16,7 @@ class Activity_order { return ''; if(! feature_enabled(local_channel(),'order_tab')) { - set_pconfig(local_channel(), 'mod_network', 'order', 0); + set_pconfig(local_channel(), 'mod_network', 'order', 'created'); return ''; } @@ -26,17 +26,17 @@ class Activity_order { if(x($_GET, 'order')) { switch($_GET['order']){ - case 'post': + case 'created': $postord_active = 'active'; - set_pconfig(local_channel(), 'mod_network', 'order', 1); + set_pconfig(local_channel(), 'mod_network', 'order', 'created'); break; - case 'comment': + case 'commented': $commentord_active = 'active'; - set_pconfig(local_channel(), 'mod_network', 'order', 0); + set_pconfig(local_channel(), 'mod_network', 'order', 'commented'); break; case 'unthreaded': $unthreaded_active = 'active'; - set_pconfig(local_channel(), 'mod_network', 'order', 2); + set_pconfig(local_channel(), 'mod_network', 'order', 'unthreaded'); break; default: $commentord_active = 'active'; @@ -44,19 +44,19 @@ class Activity_order { } } else { - $order = get_pconfig(local_channel(), 'mod_network', 'order', 0); + $order = get_pconfig(local_channel(), 'mod_network', 'order', 'created'); switch($order) { - case 0: + case 'commented': $commentord_active = 'active'; break; - case 1: + case 'created': $postord_active = 'active'; break; - case 2: + case 'unthreaded': $unthreaded_active = 'active'; break; default: - $commentord_active = 'active'; + $postord_active = 'active'; } } @@ -90,26 +90,26 @@ class Activity_order { // tabs - $tabs = []; + $tabs[] = [ + 'label' => t('Posted Date'), + 'icon' => '', + 'url'=>z_root() . '/' . $cmd . '?order=created' . $filter, + 'sel'=> $postord_active, + 'title' => t('Order by last posted date'), + ]; $tabs[] = [ 'label' => t('Commented Date'), 'icon' => '', - 'url'=>z_root() . '/' . $cmd . '?f=&order=comment' . $filter, + 'url'=>z_root() . '/' . $cmd . '?order=commented' . $filter, 'sel'=> $commentord_active, 'title' => t('Order by last commented date'), ]; - $tabs[] = [ - 'label' => t('Posted Date'), - 'icon' => '', - 'url'=>z_root() . '/' . $cmd . '?f=&order=post' . $filter, - 'sel'=> $postord_active, - 'title' => t('Order by last posted date'), - ]; + $tabs[] = array( 'label' => t('Date Unthreaded'), 'icon' => '', - 'url' => z_root() . '/' . $cmd . '?f=&order=unthreaded' . $filter, + 'url' => z_root() . '/' . $cmd . '?order=unthreaded' . $filter, 'sel' => $unthreaded_active, 'title' => t('Order unthreaded by date'), ); diff --git a/Zotlabs/Widget/Album.php b/Zotlabs/Widget/Album.php index f1fa69182..667952360 100644 --- a/Zotlabs/Widget/Album.php +++ b/Zotlabs/Widget/Album.php @@ -59,6 +59,9 @@ class Album { //edit album name $album_edit = null; + $ph = photo_factory(''); + $phototypes = $ph->supportedTypes(); + $photos = array(); if($r) { $twist = 'rotright'; diff --git a/Zotlabs/Widget/Channel_activities.php b/Zotlabs/Widget/Channel_activities.php index debaf20d4..2677f82c3 100644 --- a/Zotlabs/Widget/Channel_activities.php +++ b/Zotlabs/Widget/Channel_activities.php @@ -223,7 +223,7 @@ class Channel_activities { $i[] = [ 'url' => z_root() . '/manage/' . $rr['channel_id'], 'title' => '', - 'summary' => '<div class="text-truncate lh-sm"><img src="' . $rr['xchan_photo_s'] . '" class="menu-img-2">' . '<strong>' . $rr['channel_name'] . '</strong><br><small class="opacity-75">' . $rr['xchan_addr'] . '</small></div>', + 'summary' => '<div class="text-truncate lh-sm"><img src="' . $rr['xchan_photo_s'] . '" class="menu-img-2">' . '<strong>' . $rr['channel_name'] . '</strong><br><small class="text-body-secondary">' . $rr['xchan_addr'] . '</small></div>', 'footer' => $footer ]; diff --git a/Zotlabs/Widget/Messages.php b/Zotlabs/Widget/Messages.php index f90b4f99e..cc1811ffc 100644 --- a/Zotlabs/Widget/Messages.php +++ b/Zotlabs/Widget/Messages.php @@ -28,6 +28,8 @@ class Messages { intval(TERM_FILE) ); + $file_tags = []; + if ($r) { foreach($r as $rr) { $file_tags[] = $rr['term']; @@ -42,14 +44,14 @@ class Messages { '$feature_file' => feature_enabled(local_channel(), 'filing'), '$file_tags' => $file_tags, '$strings' => [ - 'messages_title' => t('Public and restricted messages'), - 'direct_messages_title' => t('Direct messages'), - 'starred_messages_title' => t('Starred messages'), + 'messages_title' => t('Public and restricted conversations'), + 'direct_messages_title' => t('Private conversations'), + 'starred_messages_title' => t('Starred conversations'), 'filed_messages_title' => t('Filed messages'), - 'notice_messages_title' => t('Notices'), + 'notice_messages_title' => t('Notifications'), 'loading' => t('Loading'), - 'empty' => t('No messages'), - 'unseen_count' => t('Unseen'), + 'empty' => t('No conversations'), + 'unseen_count' => t('Unseen reactions'), 'filter' => t('Filter by name or address'), 'file_filter' => t('Filter by file name') ] @@ -84,8 +86,6 @@ class Messages { $entries = []; $limit = 30; $order_sql = 'i.created DESC'; - $dummy_order_sql = ''; - $filter_sql = ''; $loadtime = (($offset) ? $_SESSION['messages_loadtime'] : datetime_convert()); $vnotify = get_pconfig(local_channel(), 'system', 'vnotify', -1); @@ -101,14 +101,18 @@ class Messages { $vnotify_sql_i = " AND i.verb NOT IN ('Dislike', '" . dbesc(ACTIVITY_DISLIKE) . "') "; } + $filter_sql = ''; if($type !== 'filed' && $author) { $filter_sql = " AND (i.owner_xchan = '" . protect_sprintf(dbesc($author)) . "') "; } + $filed_filter_sql = ''; if($type === 'filed' && $file) { $filed_filter_sql = " AND (term.term = '" . protect_sprintf(dbesc($file)) . "') "; } + $dummy_order_sql = ''; + switch($type) { case 'direct': $type_sql = ' AND i.item_private = 2 AND i.item_thread_top = 1 '; diff --git a/Zotlabs/Widget/Notifications.php b/Zotlabs/Widget/Notifications.php index 225403535..f9cee6e71 100644 --- a/Zotlabs/Widget/Notifications.php +++ b/Zotlabs/Widget/Notifications.php @@ -21,16 +21,16 @@ class Notifications { 'icon' => 'grid-3x3', 'severity' => 'secondary', 'label' => t('Network'), - 'title' => t('New network activity notifications'), + 'title' => t('Unseen network activity'), 'viewall' => [ 'url' => 'network', 'label' => t('Network stream') ], 'markall' => [ - 'label' => t('Mark all notifications read') + 'label' => t('Mark all read') ], 'filter' => [ - 'posts_label' => t('Show new posts only'), + 'posts_label' => t('Conversation starters'), 'name_label' => t('Filter by name or address') ] ]; @@ -40,17 +40,17 @@ class Notifications { 'type' => 'home', 'icon' => 'house', 'severity' => 'danger', - 'label' => t('Home'), - 'title' => t('New home activity notifications'), + 'label' => t('Channel'), + 'title' => t('Unseen channel activity'), 'viewall' => [ 'url' => 'channel/' . $channel['channel_address'], - 'label' => t('Home stream') + 'label' => t('Channel stream') ], 'markall' => [ - 'label' => t('Mark all notifications seen') + 'label' => t('Mark all seen') ], 'filter' => [ - 'posts_label' => t('Show new posts only'), + 'posts_label' => t('Conversation starters'), 'name_label' => t('Filter by name or address') ] ]; @@ -59,17 +59,17 @@ class Notifications { 'type' => 'dm', 'icon' => 'envelope', 'severity' => 'danger', - 'label' => t('Direct Messages'), - 'title' => t('New direct messages notifications'), + 'label' => t('Private'), + 'title' => t('Unseen private activity'), 'viewall' => [ 'url' => 'network/?dm=1', - 'label' => t('Direct messages stream') + 'label' => t('Private stream') ], 'markall' => [ - 'label' => t('Mark all notifications read') + 'label' => t('Mark all read') ], 'filter' => [ - 'posts_label' => t('Show new posts only'), + 'posts_label' => t('Conversation starters'), 'name_label' => t('Filter by name or address') ] ]; @@ -79,13 +79,13 @@ class Notifications { 'icon' => 'calendar-date', 'severity' => 'secondary', 'label' => t('Events'), - 'title' => t('New events notifications'), + 'title' => t('Unseen events activity'), 'viewall' => [ 'url' => 'cdav/calendar', 'label' => t('View events') ], 'markall' => [ - 'label' => t('Mark all events seen') + 'label' => t('Mark all seen') ] ]; @@ -94,10 +94,10 @@ class Notifications { 'icon' => 'people', 'severity' => 'danger', 'label' => t('New Connections'), - 'title' => t('New connections notifications'), + 'title' => t('New connections'), 'viewall' => [ 'url' => 'connections', - 'label' => t('View all connections') + 'label' => t('View all') ] ]; @@ -106,21 +106,21 @@ class Notifications { 'icon' => 'folder', 'severity' => 'danger', 'label' => t('Files'), - 'title' => t('New files notifications'), + 'title' => t('Useen files activity'), ]; $notifications[] = [ 'type' => 'notify', 'icon' => 'exclamation-circle', 'severity' => 'danger', - 'label' => t('Notices'), - 'title' => t('Notices'), + 'label' => t('Notifications'), + 'title' => t('Unseen notifications'), 'viewall' => [ 'url' => 'notifications/system', - 'label' => t('View all notices') + 'label' => t('View all') ], 'markall' => [ - 'label' => t('Mark all notices seen') + 'label' => t('Mark all seen') ] ]; @@ -129,7 +129,7 @@ class Notifications { 'icon' => 'chat-quote', 'severity' => 'secondary', 'label' => t('Forums'), - 'title' => t('Forums'), + 'title' => t('Unseen forums activity'), 'filter' => [ 'name_label' => t('Filter by name or address') ] @@ -142,7 +142,7 @@ class Notifications { 'icon' => 'person-exclamation', 'severity' => 'danger', 'label' => t('Registrations'), - 'title' => t('New registrations notifications'), + 'title' => t('Unseen registration activity'), ]; } @@ -152,7 +152,7 @@ class Notifications { 'icon' => 'globe', 'severity' => 'secondary', 'label' => t('Public Stream'), - 'title' => t('New public stream notifications'), + 'title' => t('Unseen public stream activity'), 'viewall' => [ 'url' => 'pubstream', 'label' => t('Public stream') @@ -163,7 +163,7 @@ class Notifications { ], */ 'filter' => [ - 'posts_label' => t('Show new posts only'), + 'posts_label' => t('Conversation starters'), 'name_label' => t('Filter by name or address') ] ]; diff --git a/Zotlabs/Widget/Pinned.php b/Zotlabs/Widget/Pinned.php index be6b98434..7b95d3bc6 100644 --- a/Zotlabs/Widget/Pinned.php +++ b/Zotlabs/Widget/Pinned.php @@ -67,19 +67,19 @@ class Pinned { $attend = null; $canvote = false; - $conv_responses = []; - - if(in_array($item['obj_type'], ['Event', ACTIVITY_OBJ_EVENT])) { - $conv_responses['attendyes'] = [ 'title' => t('Attending','title') ]; - $conv_responses['attendno'] = [ 'title' => t('Not attending','title') ]; - $conv_responses['attendmaybe'] = [ 'title' => t('Might attend','title') ]; - if($commentable && $observer) { - $attend = [ t('I will attend'), t('I will not attend'), t('I might attend') ]; - $isevent = true; - } + $response_verbs[] = 'like'; + + if(feature_enabled(\App::$profile['profile_uid'],'dislike')) { + $response_verbs[] = 'dislike'; + } + + $response_verbs[] = 'announce'; + + if ($item['obj_type'] === 'Question') { + $response_verbs[] = 'answer'; } - $this->activity($item, $conv_responses); + $responses = get_responses($response_verbs, $item); $verified = (intval($item['item_verified']) ? t('Message signature validated') : ''); $forged = ((! intval($item['item_verified']) && $item['sig']) ? t('Message signature incorrect') : ''); @@ -110,6 +110,9 @@ class Pinned { 'text' => strip_tags($body['html']), 'id' => $item['id'], 'mids' => json_encode([ $midb64 ]), + 'mid' => $item['uuid'], + 'rawmid' => $item['mid'], + 'parent' => $item['parent'], 'isevent' => $isevent, 'attend' => $attend, 'conlabels' => [], @@ -151,7 +154,7 @@ class Pinned { 'hide' => (! $is_new && isset($observer['xchan_hash']) && $observer['xchan_hash'] != $owner['xchan_hash'] ? t("Don't show") : ''), // end toolbar buttons 'modal_dismiss' => t('Close'), - 'responses' => $conv_responses, + 'responses' => $responses, 'author_id' => (($author['xchan_addr']) ? $author['xchan_addr'] : $author['xchan_url']) ]; @@ -193,74 +196,14 @@ class Pinned { if(empty($mids_list)) return []; - $r = q("SELECT * FROM item WHERE uuid IN ( '%s' ) AND uid = %d AND id = parent AND item_private = 0 ORDER BY created DESC", + + $r = q("SELECT parent AS item_id FROM item WHERE uuid IN ( '%s' ) AND uid = %d AND id = parent AND item_private = 0", dbesc(implode(",", $mids_list)), intval($this->uid) ); - if($r) - return $r; - - return []; - } + return items_by_parent_ids($r, blog_mode: true); - /* - * @brief List activities on item - * - * @param array $item - * @param array $conv_responses - * @return array - * - */ - private function activity($item, &$conv_responses) { - - foreach(array_keys($conv_responses) as $verb) { - $verb_sql = ''; - - switch($verb) { - case 'like': - $verb_sql = " AND verb IN ('Like', '" . ACTIVITY_LIKE . "') "; - break; - case 'dislike': - $verb_sql = " AND verb IN ('Dislike', '" . ACTIVITY_DISLIKE . "') "; - break; - case 'attendyes': - $verb_sql = " AND verb IN ('Accept', '" . ACTIVITY_ATTEND . "') "; - break; - case 'attendno': - $verb_sql = " AND verb IN ('Reject', '" . ACTIVITY_ATTENDNO . "') "; - break; - case 'attendmaybe': - $verb_sql = " AND verb IN ('TentativeAccept', '" . ACTIVITY_ATTENDMAYBE . "') "; - break; - default: - break; - } - - $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]); - continue; - } - - $conv_responses[$verb]['count'] = count($r); - $conv_responses[$verb]['button'] = get_response_button_text($verb, $conv_responses[$verb]['count']); - - foreach($r as $rr) { - - $author = q("SELECT * FROM xchan WHERE xchan_hash = '%s' LIMIT 1", - dbesc($rr['author_xchan']) - ); - $name = (($author && $author[0]['xchan_name']) ? $author[0]['xchan_name'] : t('Unknown')); - $conv_responses[$verb]['list'][] = (($rr['author_xchan'] && $author && $author[0]['xchan_photo_s']) ? - '<a class="dropdown-item" href="' . chanlink_hash($rr['author_xchan']) . '">' . '<img class="menu-img-1" src="' . zid($author[0]['xchan_photo_s']) . '" alt="' . urlencode($name) . '" /> ' . $name . '</a>' : - '<a class="dropdown-item" href="#" class="disabled">' . $name . '</a>' - ); - } - } - - $conv_responses['count'] = count($conv_responses); } + } diff --git a/Zotlabs/Widget/Portfolio.php b/Zotlabs/Widget/Portfolio.php index bde1c7d6a..1c9dc162a 100644 --- a/Zotlabs/Widget/Portfolio.php +++ b/Zotlabs/Widget/Portfolio.php @@ -66,6 +66,10 @@ class Portfolio { //edit album name $album_edit = null; + + $ph = photo_factory(''); + $phototypes = $ph->supportedTypes(); + $photos = array(); if($r) { $twist = 'rotright'; diff --git a/Zotlabs/Zot6/Zot6Handler.php b/Zotlabs/Zot6/Zot6Handler.php index 2ef9e3b8d..1716ff84b 100644 --- a/Zotlabs/Zot6/Zot6Handler.php +++ b/Zotlabs/Zot6/Zot6Handler.php @@ -12,7 +12,7 @@ class Zot6Handler implements IHandler { } function Rekey($sender, $data, $hub) { - return self::reply_rekey_request($sender, $data, $hub); + return self::rekey_request($sender, $data, $hub); } function Refresh($sender, $recipients, $hub, $force) { |