diff options
Diffstat (limited to 'Zotlabs')
33 files changed, 349 insertions, 667 deletions
diff --git a/Zotlabs/Daemon/Cron.php b/Zotlabs/Daemon/Cron.php index d2c863572..3f5ce28eb 100644 --- a/Zotlabs/Daemon/Cron.php +++ b/Zotlabs/Daemon/Cron.php @@ -178,9 +178,6 @@ class Cron { } } - require_once('include/attach.php'); - attach_upgrade(); - // once daily run birthday_updates and then expire in background // FIXME: add birthday updates, both locally and for xprof for use diff --git a/Zotlabs/Lib/Activity.php b/Zotlabs/Lib/Activity.php index ae43a43b5..90d7af8e8 100644 --- a/Zotlabs/Lib/Activity.php +++ b/Zotlabs/Lib/Activity.php @@ -2116,6 +2116,13 @@ class Activity { $s['expires'] = datetime_convert('UTC', 'UTC', $act->obj['expires']); } + if ($act->objprop('location')) { + $s['location'] = ((isset($act->objprop('location')['name'])) ? html2plain(purify_html($act->objprop('location')['name'])) : ''); + if (isset($act->objprop('location')['latitude'], $act->objprop('location')['longitude'])) { + $s['coord'] = floatval($act->objprop('location')['latitude']) . ' ' . floatval($act->objprop('location')['longitude']); + } + } + if (in_array($act->type, ['Invite', 'Create']) && $act->objprop('type') === 'Event') { $s['mid'] = $s['parent_mid'] = $act->id; } @@ -3047,13 +3054,6 @@ class Activity { } $a = new ActivityStreams($n); - if ($a->type === 'Announce' && is_array($a->obj) - && array_key_exists('object', $a->obj) && array_key_exists('actor', $a->obj)) { - // This is a relayed/forwarded Activity (as opposed to a shared/boosted object) - // Reparse the encapsulated Activity and use that instead - logger('relayed activity', LOGGER_DEBUG); - $a = new ActivityStreams($a->obj); - } logger($a->debug(), LOGGER_DATA); @@ -3062,6 +3062,24 @@ class Activity { break; } + if (in_array($a->type, ['Add', 'Remove']) + && is_array($a->obj) + && array_key_exists('object', $a->obj) + && array_key_exists('actor', $a->obj) + && !empty($a->tgt)) { + + logger('unsupported collection operation', LOGGER_DEBUG); + return; + } + + if ($a->type === 'Announce' && is_array($a->obj) + && array_key_exists('object', $a->obj) && array_key_exists('actor', $a->obj)) { + // This is a relayed/forwarded Activity (as opposed to a shared/boosted object) + // Reparse the encapsulated Activity and use that instead + logger('relayed activity', LOGGER_DEBUG); + $a = new ActivityStreams($a->obj); + } + $item = Activity::decode_note($a); if (!$item) { @@ -3464,7 +3482,7 @@ class Activity { $ret[$collection] = $actor_record[$collection]; } } - if (array_path_exists('endpoints/sharedInbox', $actor_record) && $actor_record['endpoints']['sharedInbox']) { + if (!empty($actor_record['endpoints']['sharedInbox'])) { $ret['sharedInbox'] = $actor_record['endpoints']['sharedInbox']; } diff --git a/Zotlabs/Lib/DB_Upgrade.php b/Zotlabs/Lib/DB_Upgrade.php index 981c354a4..e11c2eb10 100644 --- a/Zotlabs/Lib/DB_Upgrade.php +++ b/Zotlabs/Lib/DB_Upgrade.php @@ -1,18 +1,35 @@ <?php +/** + * A class to handle database schema upgrades. + * + * SPDX-FileCopyrightText: 2024 Hubzilla Community + * SPDX-FileContributor: Harald Eilertsen + * + * SPDX-License-Identifier: MIT + */ namespace Zotlabs\Lib; use Zotlabs\Lib\Config; +/** + * Upgrade the database schema if necessary. + * + * Compares the currently active database schema version with the version + * required for this version of Hubzilla, and performs the upgrade if needed. + * + * If the difference consists of more than one revision of the schema, each of + * the intermediate upgrades are performed in turn. + */ class DB_Upgrade { - public $config_name = ''; - public $func_prefix = ''; - - function __construct($db_revision) { - - $this->config_name = 'db_version'; - $this->func_prefix = '_'; + /** + * Check the installed and required schema versions and perform the upgrade + * if necessary. + * + * @param int $db_version The required DB schema version. + */ + public static function run(int $db_revision): void { $build = Config::Get('system', 'db_version', 0); if(! intval($build)) diff --git a/Zotlabs/Lib/Libzot.php b/Zotlabs/Lib/Libzot.php index bc944c97c..05134f433 100644 --- a/Zotlabs/Lib/Libzot.php +++ b/Zotlabs/Lib/Libzot.php @@ -1148,6 +1148,17 @@ class Libzot { logger('Activity rejected: ' . print_r($data, true)); return; } + + if (in_array($AS->type, ['Add', 'Remove']) + && is_array($AS->obj) + && array_key_exists('object', $AS->obj) + && array_key_exists('actor', $AS->obj) + && !empty($AS->tgt)) { + + logger('unsupported collection operation', LOGGER_DEBUG); + return; + } + if (is_array($AS->obj)) { $item = Activity::decode_note($AS); if (!$item) { @@ -1158,6 +1169,7 @@ class Libzot { else { $item = []; } + logger($AS->debug(), LOGGER_DATA); } @@ -2006,7 +2018,13 @@ class Libzot { foreach ($items as $activity) { $AS = new ActivityStreams($activity); - if ($AS->is_valid() && $AS->type === 'Announce' && is_array($AS->obj) + + if (!$AS->is_valid()) { + logger('Fetched activity rejected: ' . print_r($activity, true)); + continue; + } + + if ($AS->type === 'Announce' && is_array($AS->obj) && array_key_exists('object', $AS->obj) && array_key_exists('actor', $AS->obj)) { // This is a relayed/forwarded Activity (as opposed to a shared/boosted object) // Reparse the encapsulated Activity and use that instead @@ -2014,9 +2032,14 @@ class Libzot { $AS = new ActivityStreams($AS->obj); } - if (!$AS->is_valid()) { - logger('Fetched activity rejected: ' . print_r($activity, true)); - continue; + if (in_array($AS->type, ['Add', 'Remove']) + && is_array($AS->obj) + && array_key_exists('object', $AS->obj) + && array_key_exists('actor', $AS->obj) + && !empty($AS->tgt)) { + + logger('unsupported collection operation', LOGGER_DEBUG); + return; } // logger($AS->debug()); diff --git a/Zotlabs/Lib/PermissionDescription.php b/Zotlabs/Lib/PermissionDescription.php index 51d5f890d..13df60b60 100644 --- a/Zotlabs/Lib/PermissionDescription.php +++ b/Zotlabs/Lib/PermissionDescription.php @@ -117,7 +117,7 @@ class PermissionDescription { } /** - * Returns an icon css class name if an appropriate one is available, e.g. "fa-globe" for Public, + * Returns an icon css class name if an appropriate one is available, e.g. "bi-globe" for Public, * otherwise returns empty string. * * @return string icon css class name (often FontAwesome) @@ -125,12 +125,12 @@ class PermissionDescription { public function get_permission_icon() { switch($this->channel_perm) { - case 0:/* only me */ return 'fa-eye-slash'; - case PERMS_PUBLIC: return 'fa-globe'; - case PERMS_NETWORK: return 'fa-share-alt-square'; // fa-share-alt-square is very similiar to the hubzilla logo, but we should create our own logo class to use - case PERMS_SITE: return 'fa-sitemap'; - case PERMS_CONTACTS: return 'fa-group'; - case PERMS_SPECIFIC: return 'fa-list'; + case 0:/* only me */ return 'bi-eye-slash'; + case PERMS_PUBLIC: return 'bi-globe'; + case PERMS_NETWORK: return 'bi-share'; // bi-share is very similiar to the hubzilla logo, but we should create our own logo class to use + case PERMS_SITE: return 'bi-geo'; + case PERMS_CONTACTS: return 'bi-people'; + case PERMS_SPECIFIC: return 'bi-list-ul'; case PERMS_AUTHED: return ''; case PERMS_PENDING: return ''; default: return ''; diff --git a/Zotlabs/Lib/ThreadItem.php b/Zotlabs/Lib/ThreadItem.php index 1082bf642..d21d85105 100644 --- a/Zotlabs/Lib/ThreadItem.php +++ b/Zotlabs/Lib/ThreadItem.php @@ -93,7 +93,7 @@ class ThreadItem { $buttons = ''; $dropping = false; $star = false; - $isstarred = "unstarred fa-star-o"; + $isstarred = "unstarred bi-star"; $is_comment = false; $is_item = false; $osparkle = ''; @@ -123,13 +123,13 @@ class ThreadItem { $locktype = 0; } - $shareable = ((($conv->get_profile_owner() == local_channel() && local_channel()) && (intval($item['item_private']) === 0)) ? true : false); + $shareable = ((local_channel() && $conv->get_profile_owner() == local_channel()) && (intval($item['item_private']) === 0)); // allow an exemption for sharing stuff from your private feeds if($item['author']['xchan_network'] === 'rss') $shareable = true; - $repeatable = ((($conv->get_profile_owner() == local_channel() && local_channel()) && (intval($item['item_private']) === 0) && (in_array($item['author']['xchan_network'], ['zot6', 'activitypub']))) ? true : false); + $repeatable = ((local_channel() && $conv->get_profile_owner() == local_channel()) && intval($item['item_private']) === 0 && in_array($item['author']['xchan_network'], ['zot6', 'activitypub'])); // @fixme // Have recently added code to properly handle polls in group reshares by redirecting all of the poll responses to the group. @@ -187,7 +187,7 @@ class ThreadItem { $drop = [ 'dropping' => true, 'delete' => t('Admin Delete') ]; } - $filer = ((($conv->get_profile_owner() == local_channel()) && (! array_key_exists('real_uid',$item))) ? t("Save to Folder") : false); + $filer = (((local_channel() && $conv->get_profile_owner() === local_channel()) || (local_channel() && App::$module === 'pubstream')) ? t("Save to Folder") : false); $profile_avatar = $item['author']['xchan_photo_s']; $profile_link = chanlink_hash($item['author_xchan']); @@ -204,9 +204,11 @@ class ThreadItem { $response_verbs[] = 'dislike'; } - $response_verbs[] = 'announce'; + if ($repeatable) { + $response_verbs[] = 'announce'; + } - if(in_array($item['obj_type'], ['Event', ACTIVITY_OBJ_EVENT])) { + if (in_array($item['obj_type'], ['Event', ACTIVITY_OBJ_EVENT])) { $response_verbs[] = 'attendyes'; $response_verbs[] = 'attendno'; $response_verbs[] = 'attendmaybe'; @@ -216,18 +218,18 @@ class ThreadItem { } } - if($item['obj_type'] === 'Question') { + if ($item['obj_type'] === 'Question') { $response_verbs[] = 'answer'; } - if(! feature_enabled($conv->get_profile_owner(),'dislike')) + 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); } @@ -327,11 +329,13 @@ class ThreadItem { $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 on this comment"), t("reply"), t("Reply to")); + $reply_to = array( t("Reply to this comment"), t("reply"), t("Reply to")); + $reactions_allowed = true; } $share = []; @@ -539,6 +543,8 @@ class ThreadItem { 'moderate_approve' => t('Approve'), 'moderate_delete' => t('Delete'), 'rtl' => in_array($item['lang'], rtl_languages()), + 'reactions_allowed' => $reactions_allowed, + 'reaction_str' => [t('Add yours'), t('Remove yours')] ); $arr = array('item' => $item, 'output' => $tmp_item); @@ -567,7 +573,7 @@ class ThreadItem { if(($nb_children > $visible_comments) || ($thread_level > 1)) { $result['children'][0]['comment_firstcollapsed'] = true; $result['children'][0]['num_comments'] = $comment_count_txt['label']; - $result['children'][0]['hide_text'] = sprintf( t('%s show all'), '<i class="fa fa-chevron-down"></i>'); + $result['children'][0]['hide_text'] = t('show all'); if($thread_level > 1) { $result['children'][$nb_children - 1]['comment_lastcollapsed'] = true; } diff --git a/Zotlabs/Lib/Traits/HelpHelperTrait.php b/Zotlabs/Lib/Traits/HelpHelperTrait.php index b7711bbd5..63b0eb22e 100644 --- a/Zotlabs/Lib/Traits/HelpHelperTrait.php +++ b/Zotlabs/Lib/Traits/HelpHelperTrait.php @@ -15,6 +15,15 @@ trait HelpHelperTrait { private string $file_type = ''; /** + * Associative array containing the detected language. + */ + private array $lang = [ + 'language' => 'en', //! Detected language, 2-letter ISO 639-1 code ("en") + 'from_url' => false, //! true if language from URL overrides browser default + 'missing' => false, //! true if topic not found in detected language + ]; + + /** * Determines help language. * * If the language was specified in the URL, override the language preference @@ -28,17 +37,15 @@ trait HelpHelperTrait { $languages = $language_repository->getList(); if(array_key_exists(argv(1), $languages)) { - $lang = argv(1); - $from_url = true; + $this->lang['language'] = argv(1); + $this->lang['from_url'] = true; } else { - $lang = \App::$language; - if(! isset($lang)) - $lang = 'en'; + if(isset(\App::$language)) { + $this->lang['language'] = \App::$language; + } - $from_url = false; + $this->lang['from_url'] = false; } - - $this->lang = array('language' => $lang, 'from_url' => $from_url); } /** @@ -53,10 +60,10 @@ trait HelpHelperTrait { // Use local variable until we can use trait constants. $valid_file_ext = ['md', 'bb', 'html']; - $base_path = "doc/{$lang}/${base_path}"; + $base_path_with_lang = "doc/{$lang}/${base_path}"; foreach ($valid_file_ext as $ext) { - $path = "{$base_path}.{$ext}"; + $path = "{$base_path_with_lang}.{$ext}"; if (file_exists($path)) { $this->file_name = $path; $this->file_type = $ext; @@ -64,5 +71,25 @@ trait HelpHelperTrait { break; } } + + if (empty($this->file_name) && $lang !== 'en') { + $this->lang['missing'] = true; + $this->find_help_file($base_path, 'en'); + } + } + + public function missing_translation(): bool { + return !!$this->lang['missing']; + } + + public function missing_translation_message(): string { + $prefered_language_name = get_language_name( + $this->lang['language'], + $this->lang['language'] + ); + + 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.") + ); } } diff --git a/Zotlabs/Module/Admin/Addons.php b/Zotlabs/Module/Admin/Addons.php index b7cfb651c..681eed750 100644 --- a/Zotlabs/Module/Admin/Addons.php +++ b/Zotlabs/Module/Admin/Addons.php @@ -4,7 +4,6 @@ namespace Zotlabs\Module\Admin; use App; use \Zotlabs\Lib\Config; -use \Zotlabs\Storage\GitRepo; use \Michelf\MarkdownExtra; class Addons { @@ -24,227 +23,6 @@ class Addons { goaway(z_root() . '/admin/addons/' . argv(2) ); } - elseif(argc() > 2) { - switch(argv(2)) { - case 'updaterepo': - if (array_key_exists('repoName', $_REQUEST)) { - $repoName = $_REQUEST['repoName']; - } - else { - json_return_and_die(array('message' => 'No repo name provided.', 'success' => false)); - } - $extendDir = 'store/[data]/git/sys/extend'; - $addonDir = $extendDir . '/addon'; - if (!file_exists($extendDir)) { - if (!mkdir($extendDir, 0770, true)) { - logger('Error creating extend folder: ' . $extendDir); - json_return_and_die(array('message' => 'Error creating extend folder: ' . $extendDir, 'success' => false)); - } - else { - if (!symlink(realpath('extend/addon'), $addonDir)) { - logger('Error creating symlink to addon folder: ' . $addonDir); - json_return_and_die(array('message' => 'Error creating symlink to addon folder: ' . $addonDir, 'success' => false)); - } - } - } - $repoDir = 'store/[data]/git/sys/extend/addon/' . $repoName; - if (!is_dir($repoDir)) { - logger('Repo directory does not exist: ' . $repoDir); - json_return_and_die(array('message' => 'Invalid addon repo.', 'success' => false)); - } - if (!is_writable($repoDir)) { - logger('Repo directory not writable to web server: ' . $repoDir); - json_return_and_die(array('message' => 'Repo directory not writable to web server.', 'success' => false)); - } - $git = new GitRepo('sys', null, false, $repoName, $repoDir); - try { - if ($git->pull()) { - $files = array_diff(scandir($repoDir), array('.', '..')); - foreach ($files as $file) { - if (is_dir($repoDir . '/' . $file) && $file !== '.git') { - $source = '../extend/addon/' . $repoName . '/' . $file; - $target = realpath('addon/') . '/' . $file; - unlink($target); - if (!symlink($source, $target)) { - logger('Error linking addons to /addon'); - json_return_and_die(array('message' => 'Error linking addons to /addon', 'success' => false)); - } - } - } - json_return_and_die(array('message' => 'Repo updated.', 'success' => true)); - } else { - json_return_and_die(array('message' => 'Error updating addon repo.', 'success' => false)); - } - } catch (\PHPGit\Exception\GitException $e) { - json_return_and_die(array('message' => 'Error updating addon repo.', 'success' => false)); - } - break; - case 'removerepo': - if (array_key_exists('repoName', $_REQUEST)) { - $repoName = $_REQUEST['repoName']; - } else { - json_return_and_die(array('message' => 'No repo name provided.', 'success' => false)); - } - $extendDir = 'store/[data]/git/sys/extend'; - $addonDir = $extendDir . '/addon'; - if (!file_exists($extendDir)) { - if (!mkdir($extendDir, 0770, true)) { - logger('Error creating extend folder: ' . $extendDir); - json_return_and_die(array('message' => 'Error creating extend folder: ' . $extendDir, 'success' => false)); - } else { - if (!symlink(realpath('extend/addon'), $addonDir)) { - logger('Error creating symlink to addon folder: ' . $addonDir); - json_return_and_die(array('message' => 'Error creating symlink to addon folder: ' . $addonDir, 'success' => false)); - } - } - } - $repoDir = 'store/[data]/git/sys/extend/addon/' . $repoName; - if (!is_dir($repoDir)) { - logger('Repo directory does not exist: ' . $repoDir); - json_return_and_die(array('message' => 'Invalid addon repo.', 'success' => false)); - } - if (!is_writable($repoDir)) { - logger('Repo directory not writable to web server: ' . $repoDir); - json_return_and_die(array('message' => 'Repo directory not writable to web server.', 'success' => false)); - } - /// @TODO remove directory and unlink /addon/files - if (rrmdir($repoDir)) { - json_return_and_die(array('message' => 'Repo deleted.', 'success' => true)); - } else { - json_return_and_die(array('message' => 'Error deleting addon repo.', 'success' => false)); - } - break; - case 'installrepo': - if (array_key_exists('repoURL', $_REQUEST)) { - require_once('library/PHPGit.autoload.php'); // Load PHPGit dependencies - $repoURL = $_REQUEST['repoURL']; - $extendDir = 'store/[data]/git/sys/extend'; - $addonDir = $extendDir . '/addon'; - if (!file_exists($extendDir)) { - if (!mkdir($extendDir, 0770, true)) { - logger('Error creating extend folder: ' . $extendDir); - json_return_and_die(array('message' => 'Error creating extend folder: ' . $extendDir, 'success' => false)); - } else { - if (!symlink(realpath('extend/addon'), $addonDir)) { - logger('Error creating symlink to addon folder: ' . $addonDir); - json_return_and_die(array('message' => 'Error creating symlink to addon folder: ' . $addonDir, 'success' => false)); - } - } - } - if (!is_writable($extendDir)) { - logger('Directory not writable to web server: ' . $extendDir); - json_return_and_die(array('message' => 'Directory not writable to web server.', 'success' => false)); - } - $repoName = null; - if (array_key_exists('repoName', $_REQUEST) && $_REQUEST['repoName'] !== '') { - $repoName = $_REQUEST['repoName']; - } else { - $repoName = GitRepo::getRepoNameFromURL($repoURL); - } - if (!$repoName) { - logger('Invalid git repo'); - json_return_and_die(array('message' => 'Invalid git repo', 'success' => false)); - } - $repoDir = $addonDir . '/' . $repoName; - $tempRepoBaseDir = 'store/[data]/git/sys/temp/'; - $tempAddonDir = $tempRepoBaseDir . $repoName; - - if (!is_writable($addonDir) || !is_writable($tempAddonDir)) { - logger('Temp repo directory or /extend/addon not writable to web server: ' . $tempAddonDir); - json_return_and_die(array('message' => 'Temp repo directory not writable to web server.', 'success' => false)); - } - rename($tempAddonDir, $repoDir); - - if (!is_writable(realpath('addon/'))) { - logger('/addon directory not writable to web server: ' . $tempAddonDir); - json_return_and_die(array('message' => '/addon directory not writable to web server.', 'success' => false)); - } - $files = array_diff(scandir($repoDir), array('.', '..')); - foreach ($files as $file) { - if (is_dir($repoDir . '/' . $file) && $file !== '.git') { - $source = '../extend/addon/' . $repoName . '/' . $file; - $target = realpath('addon/') . '/' . $file; - unlink($target); - if (!symlink($source, $target)) { - logger('Error linking addons to /addon'); - json_return_and_die(array('message' => 'Error linking addons to /addon', 'success' => false)); - } - } - } - $git = new GitRepo('sys', $repoURL, false, $repoName, $repoDir); - $repo = $git->probeRepo(); - json_return_and_die(array('repo' => $repo, 'message' => '', 'success' => true)); - } - break; - case 'addrepo': - if (array_key_exists('repoURL', $_REQUEST)) { - require_once('library/PHPGit.autoload.php'); // Load PHPGit dependencies - $repoURL = $_REQUEST['repoURL']; - $extendDir = 'store/[data]/git/sys/extend'; - $addonDir = $extendDir . '/addon'; - $tempAddonDir = realpath('store/[data]') . '/git/sys/temp'; - if (!file_exists($extendDir)) { - if (!mkdir($extendDir, 0770, true)) { - logger('Error creating extend folder: ' . $extendDir); - json_return_and_die(array('message' => 'Error creating extend folder: ' . $extendDir, 'success' => false)); - } else { - if (!symlink(realpath('extend/addon'), $addonDir)) { - logger('Error creating symlink to addon folder: ' . $addonDir); - json_return_and_die(array('message' => 'Error creating symlink to addon folder: ' . $addonDir, 'success' => false)); - } - } - } - if (!is_dir($tempAddonDir)) { - if (!mkdir($tempAddonDir, 0770, true)) { - logger('Error creating temp plugin repo folder: ' . $tempAddonDir); - json_return_and_die(array('message' => 'Error creating temp plugin repo folder: ' . $tempAddonDir, 'success' => false)); - } - } - $repoName = null; - if (array_key_exists('repoName', $_REQUEST) && $_REQUEST['repoName'] !== '') { - $repoName = $_REQUEST['repoName']; - } else { - $repoName = GitRepo::getRepoNameFromURL($repoURL); - } - if (!$repoName) { - logger('Invalid git repo'); - json_return_and_die(array('message' => 'Invalid git repo: ' . $repoName, 'success' => false)); - } - $repoDir = $tempAddonDir . '/' . $repoName; - if (!is_writable($tempAddonDir)) { - logger('Temporary directory for new addon repo is not writable to web server: ' . $tempAddonDir); - json_return_and_die(array('message' => 'Temporary directory for new addon repo is not writable to web server.', 'success' => false)); - } - // clone the repo if new automatically - $git = new GitRepo('sys', $repoURL, true, $repoName, $repoDir); - - $remotes = $git->git->remote(); - $fetchURL = $remotes['origin']['fetch']; - if ($fetchURL !== $git->url) { - if (rrmdir($repoDir)) { - $git = new GitRepo('sys', $repoURL, true, $repoName, $repoDir); - } else { - json_return_and_die(array('message' => 'Error deleting existing addon repo.', 'success' => false)); - } - } - $repo = $git->probeRepo(); - $repo['readme'] = $repo['manifest'] = null; - foreach ($git->git->tree('master') as $object) { - if ($object['type'] == 'blob' && (strtolower($object['file']) === 'readme.md' || strtolower($object['file']) === 'readme')) { - $repo['readme'] = MarkdownExtra::defaultTransform($git->git->cat->blob($object['hash'])); - } else if ($object['type'] == 'blob' && strtolower($object['file']) === 'manifest.json') { - $repo['manifest'] = $git->git->cat->blob($object['hash']); - } - } - json_return_and_die(array('repo' => $repo, 'message' => '', 'success' => true)); - } else { - json_return_and_die(array('message' => 'No repo URL provided', 'success' => false)); - } - break; - default: - break; - } - } } /** @@ -408,37 +186,6 @@ class Addons { usort($plugins,'self::plugin_sort'); - $allowManageRepos = false; - if(is_writable('extend/addon') && is_writable('store/[data]')) { - $allowManageRepos = true; - } - - $admin_plugins_add_repo_form= replace_macros( - get_markup_template('admin_plugins_addrepo.tpl'), array( - '$post' => 'admin/addons/addrepo', - '$desc' => t('Enter the public git repository URL of the addon repo.'), - '$repoURL' => array('repoURL', t('Addon repo git URL'), '', ''), - '$repoName' => array('repoName', t('Custom repo name'), '', '', t('(optional)')), - '$submit' => t('Download Addon Repo') - ) - ); - $newRepoModalID = random_string(3); - $newRepoModal = replace_macros( - get_markup_template('generic_modal.tpl'), array( - '$id' => $newRepoModalID, - '$title' => t('Install new repo'), - '$ok' => t('Install'), - '$cancel' => t('Cancel') - ) - ); - - $reponames = $this->listAddonRepos(); - $addonrepos = []; - foreach($reponames as $repo) { - $addonrepos[] = array('name' => $repo, 'description' => ''); - /// @TODO Parse repo info to provide more information about repos - } - $t = get_markup_template('admin_plugins.tpl'); return replace_macros($t, array( '$title' => t('Administration'), @@ -449,37 +196,9 @@ class Addons { '$plugins' => $plugins, '$disabled' => t('Disabled - version incompatibility'), '$form_security_token' => get_form_security_token('admin_addons'), - '$allowManageRepos' => $allowManageRepos, - '$managerepos' => t('Manage Repos'), - '$installedtitle' => t('Installed Addon Repositories'), - '$addnewrepotitle' => t('Install a New Addon Repository'), - '$expandform' => false, - '$form' => $admin_plugins_add_repo_form, - '$newRepoModal' => $newRepoModal, - '$newRepoModalID' => $newRepoModalID, - '$addonrepos' => $addonrepos, - '$repoUpdateButton' => t('Update'), - '$repoBranchButton' => t('Switch branch'), - '$repoRemoveButton' => t('Remove') )); } - function listAddonRepos() { - $addonrepos = []; - $addonDir = 'extend/addon/'; - if(is_dir($addonDir)) { - if ($handle = opendir($addonDir)) { - while (false !== ($entry = readdir($handle))) { - if ($entry != "." && $entry != "..") { - $addonrepos[] = $entry; - } - } - closedir($handle); - } - } - return $addonrepos; - } - static public function plugin_sort($a,$b) { return(strcmp(strtolower($a[2]['name']),strtolower($b[2]['name']))); } diff --git a/Zotlabs/Module/Appman.php b/Zotlabs/Module/Appman.php index 5f72d771b..8a842feda 100644 --- a/Zotlabs/Module/Appman.php +++ b/Zotlabs/Module/Appman.php @@ -16,21 +16,21 @@ class Appman extends \Zotlabs\Web\Controller { if(isset($_POST['url']) && $_POST['url']) { $arr = array( 'uid' => intval($_REQUEST['uid']), - 'url' => escape_tags($_REQUEST['url']), - 'guid' => escape_tags($_REQUEST['guid']), - 'author' => escape_tags($_REQUEST['author']), - 'addr' => escape_tags($_REQUEST['addr']), - 'name' => escape_tags($_REQUEST['name']), - 'desc' => escape_tags($_REQUEST['desc']), - 'photo' => escape_tags($_REQUEST['photo']), - 'version' => escape_tags($_REQUEST['version']), - 'price' => escape_tags($_REQUEST['price']), - 'page' => escape_tags($_REQUEST['page']), - 'requires' => escape_tags($_REQUEST['requires']), + 'url' => escape_tags($_REQUEST['url'] ?? ''), + 'guid' => escape_tags($_REQUEST['guid'] ?? ''), + 'author' => escape_tags($_REQUEST['author'] ?? ''), + 'addr' => escape_tags($_REQUEST['addr'] ?? ''), + 'name' => escape_tags($_REQUEST['name'] ?? ''), + 'desc' => escape_tags($_REQUEST['desc'] ?? ''), + 'photo' => escape_tags($_REQUEST['photo'] ?? ''), + 'version' => escape_tags($_REQUEST['version'] ?? ''), + 'price' => escape_tags($_REQUEST['price'] ?? ''), + 'page' => escape_tags($_REQUEST['page'] ?? ''), + 'requires' => escape_tags($_REQUEST['requires'] ?? ''), 'system' => intval($_REQUEST['system']), - 'plugin' => escape_tags($_REQUEST['plugin']), - 'sig' => escape_tags($_REQUEST['sig']), - 'categories' => escape_tags($_REQUEST['categories']) + 'plugin' => escape_tags($_REQUEST['plugin'] ?? ''), + 'sig' => escape_tags($_REQUEST['sig'] ?? ''), + 'categories' => escape_tags($_REQUEST['categories'] ?? '') ); $_REQUEST['appid'] = Apps::app_install(local_channel(),$arr); diff --git a/Zotlabs/Module/Filer.php b/Zotlabs/Module/Filer.php index c2747e6c2..bf472eb67 100644 --- a/Zotlabs/Module/Filer.php +++ b/Zotlabs/Module/Filer.php @@ -1,43 +1,54 @@ <?php namespace Zotlabs\Module; -require_once('include/security.php'); -require_once('include/bbcode.php'); -require_once('include/items.php'); - - +use App; class Filer extends \Zotlabs\Web\Controller { function get() { - - if(! local_channel()) { + + if(!local_channel()) { killme(); } - - $term = unxmlify(trim($_GET['term'])); - $item_id = ((\App::$argc > 1) ? intval(\App::$argv[1]) : 0); - + + $term = unxmlify(trim($_GET['term'] ?? '')); + $item_id = ((App::$argc > 1) ? intval(App::$argv[1]) : 0); + logger('filer: tag ' . $term . ' item ' . $item_id); - + if($item_id && strlen($term)){ + + $sys = get_sys_channel(); + + $r = q("SELECT * FROM item WHERE (uid = %d OR uid = %d) AND id = %d + and item_type in (0,6,7) and item_deleted = 0 and item_unpublished = 0 + and item_delayed = 0 and item_pending_remove = 0 and item_blocked = 0 LIMIT 1", + intval(local_channel()), + intval($sys['channel_id']), + intval($item_id) + ); + + if ($r && $r[0]['uid'] === $sys['channel_id']) { + $r = [copy_of_pubitem(App::get_channel(), $r[0]['mid'])]; + } + + if(!$r) { + killme(); + } + + $item_id = $r[0]['id']; + // file item store_item_tag(local_channel(),$item_id,TERM_OBJ_POST,TERM_FILE,$term,''); - + // protect the entire conversation from periodic expiration - - $r = q("select parent from item where id = %d and uid = %d limit 1", - intval($item_id), + + q("update item set item_retained = 1, changed = '%s' where id = %d and uid = %d", + dbesc(datetime_convert()), + intval($r[0]['parent']), intval(local_channel()) ); - if($r) { - $x = q("update item set item_retained = 1, changed = '%s' where id = %d and uid = %d", - dbesc(datetime_convert()), - intval($r[0]['parent']), - intval(local_channel()) - ); - } - } + } else { $filetags = array(); $r = q("select distinct(term) from term where uid = %d and ttype = %d order by term asc", @@ -55,10 +66,10 @@ class Filer extends \Zotlabs\Web\Controller { '$title' => t('Save to Folder'), '$cancel' => t('Cancel') )); - + echo $o; } killme(); } - + } diff --git a/Zotlabs/Module/Help.php b/Zotlabs/Module/Help.php index fd5cacee6..fc0ef2708 100644 --- a/Zotlabs/Module/Help.php +++ b/Zotlabs/Module/Help.php @@ -6,6 +6,7 @@ use Michelf\MarkdownExtra; /** * You can create local site resources in doc/Site.md and either link to doc/Home.md for the standard resources * or use our include mechanism to include it on your local page. + * *@code * #include doc/Home.md; *@endcode @@ -19,14 +20,6 @@ class Help extends \Zotlabs\Web\Controller { private string $heading_slug = ''; /** - * Associative array containing the detected language. - */ - public array $lang = [ - 'language' => 'en', //! Detected language, 2-letter ISO 639-1 code ("en") - 'from_url' => false, //! true if language from URL overrides browser default - ]; - - /** * Pre-check before processing request. * * Determine language requested, and ensure that a topic was requested. @@ -36,8 +29,8 @@ class Help extends \Zotlabs\Web\Controller { public function init() { $this->determine_help_language(); - if (argc() === 1) { - goaway("/help/{$this->lang['language']}/about/about"); + if (empty($_REQUEST['search']) && argc() === 1) { + goaway("/help/about/about"); killme(); } } @@ -69,6 +62,7 @@ class Help extends \Zotlabs\Web\Controller { $o .= '</div>'; $o .= '<div class="section-content-wrapper">'; + require_once('include/help.php'); $r = search_doc_files($_REQUEST['search']); if($r) { $o .= '<ul class="help-searchlist">'; @@ -160,6 +154,10 @@ class Help extends \Zotlabs\Web\Controller { array_shift($args); } + if (empty($args)) { + goaway("/help/about/about"); + } + // Keep the first remaining arg as the heading slug $this->heading_slug = $args[0]; diff --git a/Zotlabs/Module/Hq.php b/Zotlabs/Module/Hq.php index c8e6efe38..51caa179c 100644 --- a/Zotlabs/Module/Hq.php +++ b/Zotlabs/Module/Hq.php @@ -230,6 +230,7 @@ class Hq extends \Zotlabs\Web\Controller { $options['offset'] = $_REQUEST['offset'] ?? 0; $options['type'] = $_REQUEST['type'] ?? ''; $options['author'] = ((isset($_REQUEST['author'])) ? urldecode($_REQUEST['author']) : ''); + $options['file'] = ((isset($_REQUEST['file'])) ? urldecode($_REQUEST['file']) : ''); $ret = Messages::get_messages_page($options); diff --git a/Zotlabs/Module/Item.php b/Zotlabs/Module/Item.php index 3d13655d2..d96cfd822 100644 --- a/Zotlabs/Module/Item.php +++ b/Zotlabs/Module/Item.php @@ -837,8 +837,10 @@ class Item extends Controller { if ($results) { - // Set permissions based on tag replacements - set_linkified_perms($results, $str_contact_allow, $str_group_allow, $profile_uid, $private, $parent_item); + // Set permissions based on tag replacements only if not editing an existing post + if (!$orig_post) { + set_linkified_perms($results, $str_contact_allow, $str_group_allow, $profile_uid, $private, $parent_item); + } foreach ($results as $result) { $success = $result['success']; @@ -1058,7 +1060,7 @@ class Item extends Controller { $obj['id'] = $mid; $obj['diaspora:guid'] = $uuid; $obj['attributedTo'] = channel_url($channel); - $obj['published'] = $created; + $obj['published'] = datetime_convert('UTC', 'UTC', $created, ATOM_TIME); $obj['name'] = $title; $datarray['obj'] = $obj; @@ -1429,7 +1431,6 @@ class Item extends Controller { require_once('include/items.php'); - $i = q("select id, uid, item_origin, author_xchan, owner_xchan, source_xchan, item_type from item where id = %d limit 1", intval(argv(2)) ); diff --git a/Zotlabs/Module/Like.php b/Zotlabs/Module/Like.php index 4460900a8..d493742e7 100644 --- a/Zotlabs/Module/Like.php +++ b/Zotlabs/Module/Like.php @@ -569,6 +569,11 @@ class Like extends Controller { call_hooks('post_local_end', $arr); + if ($is_rsvp && in_array($verb, ['attendyes', 'attendmaybe'])) { + event_addtocal($item_id, local_channel()); + } + + $r = q("select * from item where id = %d", intval($post_id) ); diff --git a/Zotlabs/Module/Lockview.php b/Zotlabs/Module/Lockview.php index f3ae07f74..4b708a1aa 100644 --- a/Zotlabs/Module/Lockview.php +++ b/Zotlabs/Module/Lockview.php @@ -227,7 +227,7 @@ class Lockview extends Controller { $allowed_xchans = array_unique($allowed_xchans); foreach ($atokens as $atoken) { if (in_array($atoken['xchan_hash'], $allowed_xchans)) { - $guest_access_list[] = '<div class="dropdown-item d-flex justify-content-between cursor-pointer" title="' . sprintf(t('Click to copy link to this ressource for guest %s to clipboard'), $atoken['xchan_name']) . '" data-token="' . $url . '?zat=' . $atoken['atoken_token'] . '" onclick="navigator.clipboard.writeText(this.dataset.token); toast(\'' . t('Link copied') . '\', \'info\');"><span>' . $atoken['xchan_name'] . '</span><i class="fa fa-copy p-1"></i></div>'; + $guest_access_list[] = '<div class="dropdown-item d-flex justify-content-between cursor-pointer" title="' . sprintf(t('Click to copy link to this ressource for guest %s to clipboard'), $atoken['xchan_name']) . '" data-token="' . $url . '?zat=' . $atoken['atoken_token'] . '" onclick="navigator.clipboard.writeText(this.dataset.token); toast(\'' . t('Link copied') . '\', \'info\');"><span>' . $atoken['xchan_name'] . '</span><i class="bi bi-copy p-1"></i></div>'; } } } diff --git a/Zotlabs/Module/Magic.php b/Zotlabs/Module/Magic.php index 8259f7d39..deda4255d 100644 --- a/Zotlabs/Module/Magic.php +++ b/Zotlabs/Module/Magic.php @@ -40,7 +40,11 @@ class Magic extends Controller { goaway($dest); } - $basepath = $parsed['scheme'] . '://' . $parsed['host'] . (isset($parsed['port']) ? ':' . $parsed['port'] : ''); + $basepath = unparse_url(array_filter( + $parsed, + fn (string $key) => in_array($key, ['scheme', 'host', 'port']), + ARRAY_FILTER_USE_KEY + )); $owapath = SConfig::get($basepath,'system','openwebauth', $basepath . '/owa'); // This is ready-made for a plugin that provides a blacklist or "ask me" before blindly authenticating. @@ -106,7 +110,7 @@ class Magic extends Controller { $headers['Content-Type'] = 'application/x-zot+json' ; $headers['X-Open-Web-Auth'] = random_string(); $headers['Host'] = $parsed['host']; - $headers['(request-target)'] = 'get ' . '/owa'; + $headers['(request-target)'] = 'get /owa'; $headers = HTTPSig::create_sig($headers,$channel['channel_prvkey'], channel_url($channel),true,'sha512'); $redirects = 0; diff --git a/Zotlabs/Module/Notes.php b/Zotlabs/Module/Notes.php index 2fd719f25..ba693e4f2 100644 --- a/Zotlabs/Module/Notes.php +++ b/Zotlabs/Module/Notes.php @@ -13,31 +13,34 @@ class Notes extends Controller { function post() { - if(! local_channel()) - return EMPTY_STR; + if(!local_channel()) { + return; + } - if(! Apps::system_app_installed(local_channel(), 'Notes')) - return EMPTY_STR; + if(!Apps::system_app_installed(local_channel(), 'Notes')) { + return; + } $ret = [ 'success' => false, 'html' => '' ]; - - if(array_key_exists('note_text',$_REQUEST)) { + if (array_key_exists('note_text',$_REQUEST)) { $body = escape_tags($_REQUEST['note_text']); // I've had my notes vanish into thin air twice in four years. // Provide a backup copy if there were contents previously // and there are none being saved now. - if(! $body) { - $old_text = get_pconfig(local_channel(),'notes','text'); - if($old_text) - set_pconfig(local_channel(),'notes','text.bak',$old_text); + if(!$body) { + $old_text = get_pconfig(local_channel(), 'notes', 'text'); + if ($old_text) { + set_pconfig(local_channel(), 'notes', 'text.bak', $old_text); + } } - set_pconfig(local_channel(),'notes','text',$body); + + set_pconfig(local_channel(), 'notes', 'text', $body); $ret['html'] = bbcode($body, ['tryoembed' => false]); $ret['success'] = true; @@ -55,8 +58,9 @@ class Notes extends Controller { } function get() { - if(! local_channel()) + if(!local_channel()) { return EMPTY_STR; + } if(! Apps::system_app_installed(local_channel(), 'Notes')) { //Do not display any associated widgets at this point @@ -65,6 +69,8 @@ class Notes extends Controller { return Apps::app_render($papp, 'module'); } + App::$profile_uid = local_channel(); + $w = new \Zotlabs\Widget\Notes; $arr = ['app' => true]; diff --git a/Zotlabs/Module/Pubsites.php b/Zotlabs/Module/Pubsites.php index cbaa66042..f58b4adff 100644 --- a/Zotlabs/Module/Pubsites.php +++ b/Zotlabs/Module/Pubsites.php @@ -49,18 +49,18 @@ class Pubsites extends \Zotlabs\Web\Controller { } $m = parse_url($jj['url']); $host = strtolower(substr($jj['url'],strpos($jj['url'],'://')+3)); - $rate_links = ((local_channel()) ? '<td><a href="rate?f=&target=' . $host . '" class="btn-btn-default"><i class="fa fa-check-square-o"></i> ' . t('Rate') . '</a></td>' : ''); + $rate_links = ((local_channel()) ? '<td><a href="rate?f=&target=' . $host . '" class="btn-btn-default"><i class="bi bi-check-square"></i> ' . t('Rate') . '</a></td>' : ''); $location = ''; if(!empty($jj['location'])) { - $location = '<p title="' . t('Location') . '" style="margin: 5px 5px 0 0; text-align: right"><i class="fa fa-globe"></i> ' . $jj['location'] . '</p>'; + $location = '<p title="' . t('Location') . '" style="margin: 5px 5px 0 0; text-align: right"><i class="bi bi-globe"></i> ' . $jj['location'] . '</p>'; } else { $location = '<br /> '; } $urltext = str_replace(array('https://'), '', $jj['url']); - $o .= '<tr><td><a href="'. (($jj['sellpage']) ? $jj['sellpage'] : $jj['url'] . '/register' ) . '" ><i class="fa fa-link"></i> ' . $urltext . '</a>' . $location . '</td><td>' . $jj['access'] . '</td><td>' . $jj['register'] . '</td><!--td>' . '<a target="stats" href="https://hubchart-tarine.rhcloud.com/hub.jsp?hubFqdn=' . $m['host'] . '"><i class="fa fa-area-chart"></i></a></td--><td>' . ucwords($jj['project']) . (($jj['version']) ? ' ' . $jj['version'] : '') . '</td>'; + $o .= '<tr><td><a href="'. (($jj['sellpage']) ? $jj['sellpage'] : $jj['url'] . '/register' ) . '" ><i class="bi bi-link-45deg"></i> ' . $urltext . '</a>' . $location . '</td><td>' . $jj['access'] . '</td><td>' . $jj['register'] . '</td><!--td>' . '<a target="stats" href="https://hubchart-tarine.rhcloud.com/hub.jsp?hubFqdn=' . $m['host'] . '"><i class="bi bi-graph-up"></i></a></td--><td>' . ucwords($jj['project']) . (($jj['version']) ? ' ' . $jj['version'] : '') . '</td>'; if($rating_enabled) - $o .= '<td><a href="ratings/' . $host . '" class="btn-btn-default"><i class="fa fa-eye"></i> ' . t('View') . '</a></td>' . $rate_links ; + $o .= '<td><a href="ratings/' . $host . '" class="btn-btn-default"><i class="bi bi-eye"></i> ' . t('View') . '</a></td>' . $rate_links ; $o .= '</tr>'; } } diff --git a/Zotlabs/Module/Settings/Conversation.php b/Zotlabs/Module/Settings/Conversation.php index aa0ff6a7e..5f3d903a8 100644 --- a/Zotlabs/Module/Settings/Conversation.php +++ b/Zotlabs/Module/Settings/Conversation.php @@ -11,24 +11,19 @@ class Conversation { $module = substr(strrchr(strtolower(static::class), '\\'), 1); check_form_security_token_redirectOnErr('/settings/' . $module, 'settings_' . $module); - + $features = get_module_features($module); process_module_features_post(local_channel(), $features, $_POST); - + Libsync::build_sync_packet(); - - if($_POST['aj']) { - if($_POST['auto_update'] == 1) - info(t('Settings saved.') . EOL); - else - info(t('Settings saved. Reload page please.') . EOL); + if($_POST['aj']) { killme(); } - else { - return; - } + + return; + } function get() { @@ -48,7 +43,7 @@ class Conversation { '$features' => process_module_features_get(local_channel(), $features), '$submit' => t('Submit') )); - + if($aj) { echo $o; killme(); diff --git a/Zotlabs/Module/Sse.php b/Zotlabs/Module/Sse.php index daf344f2d..fda2f2be4 100644 --- a/Zotlabs/Module/Sse.php +++ b/Zotlabs/Module/Sse.php @@ -19,13 +19,14 @@ class Sse extends Controller { function init() { + // This is important! + session_write_close(); + ignore_user_abort(true); + if((observer_prohibited(true))) { killme(); } - // this is important! - ignore_user_abort(true); - self::$uid = local_channel(); self::$ob_hash = get_observer_hash(); self::$sse_id = false; @@ -102,19 +103,9 @@ class Sse extends Controller { $lock = XConfig::Get(self::$ob_hash, 'sse', 'lock'); if (!$lock) { - $result_db = XConfig::Get(self::$ob_hash, 'sse', 'notifications', []); - } - - if (!empty($_SESSION['sysmsg'])) { - $result['notice']['notifications'] = $_SESSION['sysmsg']; + $result = XConfig::Get(self::$ob_hash, 'sse', 'notifications', []); } - if (!empty($_SESSION['sysmsg_info'])) { - $result['info']['notifications'] = $_SESSION['sysmsg_info']; - } - - $result = array_merge($result, $result_db); - // We do not have the local_channel in the addon. // Reset pubs here if the app is not installed. if (self::$uid && (!(self::$vnotify & VNOTIFY_PUBS) || !Apps::system_app_installed(self::$uid, 'Public Stream'))) { @@ -137,8 +128,9 @@ class Sse extends Controller { if (connection_status() != CONNECTION_NORMAL || connection_aborted()) { - // IMPORTANT: in case the channel was changed we need to reset the - // session here to it's current stored state. + // In case session_write_close() failed for some reason and + // the channel was changed we might need to reset the + // session to it's current stored state here. // Otherwise the uid might switch back to the previous value // in the background. @@ -147,9 +139,6 @@ class Sse extends Controller { XConfig::Set(self::$ob_hash, 'sse', 'timestamp', NULL_DATE); XConfig::Set(self::$ob_hash, 'sse', 'notifications', []); - $_SESSION['sysmsg'] = []; - $_SESSION['sysmsg_info'] = []; - if (ob_get_length() > 0) { ob_end_flush(); } @@ -168,12 +157,7 @@ class Sse extends Controller { usleep($sleep); if ($result) { - if ($result_db) { - XConfig::Set(self::$ob_hash, 'sse', 'notifications', []); - } - - $_SESSION['sysmsg'] = []; - $_SESSION['sysmsg_info'] = []; + XConfig::Set(self::$ob_hash, 'sse', 'notifications', []); } $i++; @@ -184,7 +168,7 @@ class Sse extends Controller { else { // Fallback to traditional polling - if(! self::$sse_id) { + if(!self::$sse_id) { // Update chat presence indication @@ -193,14 +177,14 @@ class Sse extends Controller { dbesc($_SERVER['REMOTE_ADDR']) ); $basic_presence = false; - if($r) { + if ($r) { $basic_presence = true; q("update chatpresence set cp_last = '%s' where cp_id = %d", dbesc(datetime_convert()), intval($r[0]['cp_id']) ); } - if(! $basic_presence) { + if (!$basic_presence) { q("insert into chatpresence ( cp_xchan, cp_last, cp_status, cp_client) values( '%s', '%s', '%s', '%s' ) ", dbesc(self::$ob_hash), @@ -212,34 +196,17 @@ class Sse extends Controller { } $result = []; - $result_db = []; XConfig::Load(self::$ob_hash); $lock = XConfig::Get(self::$ob_hash, 'sse', 'lock'); if (!$lock) { - $result_db = XConfig::Get(self::$ob_hash, 'sse', 'notifications', []); - } - - if (!empty($_SESSION['sysmsg'])) { - $result['notice']['notifications'] = $_SESSION['sysmsg']; - } - - if (!empty($_SESSION['sysmsg_info'])) { - $result['info']['notifications'] = $_SESSION['sysmsg_info']; + $result = XConfig::Get(self::$ob_hash, 'sse', 'notifications', []); } - $result = array_merge($result, $result_db); - - if($result) { - if ($result_db) { - XConfig::Set(self::$ob_hash, 'sse', 'notifications', []); - } - - $_SESSION['sysmsg'] = []; - $_SESSION['sysmsg_info'] = []; - + if ($result) { + XConfig::Set(self::$ob_hash, 'sse', 'notifications', []); json_return_and_die($result); } diff --git a/Zotlabs/Storage/GitRepo.php b/Zotlabs/Storage/GitRepo.php deleted file mode 100644 index 306abc0ba..000000000 --- a/Zotlabs/Storage/GitRepo.php +++ /dev/null @@ -1,159 +0,0 @@ -<?php - -namespace Zotlabs\Storage; - -use PHPGit\Git as PHPGit; - -require __DIR__ . '/../../library/PHPGit.autoload.php'; // Load PHPGit dependencies - -/** - * Wrapper class for PHPGit class for git repositories managed by Hubzilla - * - * @author Andrew Manning <andrewmanning@grid.reticu.li> - */ -class GitRepo { - - public $url = null; - public $name = null; - private $path = null; - private $channel = null; - public $git = null; - private $repoBasePath = null; - - function __construct($channel = 'sys', $url = null, $clone = false, $name = null, $path = null) { - - if ($channel === 'sys' && !is_site_admin()) { - logger('Only admin can use channel sys'); - return null; - } - - $this->repoBasePath = __DIR__ . '/../../store/git'; - $this->channel = $channel; - $this->git = new PHPGit(); - - // Allow custom path for repo in the case of , for example - if ($path) { - $this->path = $path; - } else { - $this->path = $this->repoBasePath . "/" . $this->channel . "/" . $this->name; - } - - if ($this->isValidGitRepoURL($url)) { - $this->url = $url; - } - - if ($name) { - $this->name = $name; - } else { - $this->name = $this->getRepoNameFromURL($url); - } - if (!$this->name) { - logger('Error creating GitRepo. No repo name found.'); - return null; - } - - if (is_dir($this->path)) { - // ignore the $url input if it exists - // TODO: Check if the path is either empty or is a valid git repo and error if not - $this->git->setRepository($this->path); - // TODO: get repo metadata - return; - } - - if ($this->url) { - // create the folder and clone the repo at url to that folder if $clone is true - if ($clone) { - if (mkdir($this->path, 0770, true)) { - $this->git->setRepository($this->path); - if (!$this->cloneRepo()) { - // TODO: throw error - logger('git clone failed: ' . json_encode($this->git)); - } - } else { - logger('git repo path could not be created: ' . json_encode($this->git)); - } - } - } - } - - public function initRepo() { - if(!$this->path) return false; - try { - return $this->git->init($this->path); - } catch (\PHPGit\Exception\GitException $ex) { - return false; - } - } - - public function pull() { - try { - $success = $this->git->pull(); - } catch (\PHPGit\Exception\GitException $ex) { - return false; - } - return $success; - } - - public function getRepoPath() { - return $this->path; - } - - public function setRepoPath($directory) { - if (is_dir($directory)) { - $this->path->$directory; - $this->git->setRepository($directory); - return true; - } - return false; - } - - public function setIdentity($user_name, $user_email) { - // setup user for commit messages - $this->git->config->set("user.name", $user_name, ['global' => false, 'system' => false]); - $this->git->config->set("user.email", $user_email, ['global' => false, 'system' => false]); - } - - public function cloneRepo() { - if (validate_url($this->url) && $this->isValidGitRepoURL($this->url) && is_dir($this->path)) { - return $this->git->clone($this->url, $this->path); - } - } - - public function probeRepo() { - $git = $this->git; - $repo = array(); - $repo['remote'] = $git->remote(); - $repo['branches'] = $git->branch(['all' => true]); - $repo['logs'] = $git->log(array('limit' => 50)); - return $repo; - } - - // Commit changes to the repo. Default is to stage all changes and commit everything. - public function commit($msg, $options = array()) { - try { - return $this->git->commit($msg, $options); - } catch (\PHPGit\Exception\GitException $ex) { - return false; - } - } - - public static function isValidGitRepoURL($url) { - if (validate_url($url) && strrpos(parse_url($url, PHP_URL_PATH), '.')) { - return true; - } else { - return false; - } - } - - public static function getRepoNameFromURL($url) { - $urlpath = parse_url($url, PHP_URL_PATH); - $lastslash = strrpos($urlpath, '/') + 1; - $gitext = strrpos($urlpath, '.'); - if ($gitext) { - return substr($urlpath, $lastslash, $gitext - $lastslash); - } else { - return null; - } - } - -} diff --git a/Zotlabs/Web/WebServer.php b/Zotlabs/Web/WebServer.php index 6f8a4b956..19f14ee8a 100644 --- a/Zotlabs/Web/WebServer.php +++ b/Zotlabs/Web/WebServer.php @@ -2,6 +2,8 @@ namespace Zotlabs\Web; +use Zotlabs\Lib\Text; + class WebServer { public function run() { @@ -60,7 +62,7 @@ class WebServer { \App::$query_string = strip_zids(\App::$query_string); if(! local_channel()) { if (!isset($_SESSION['my_address']) || $_SESSION['my_address'] != $_GET['zid']) { - $_SESSION['my_address'] = $_GET['zid']; + $_SESSION['my_address'] = Text::escape_tags($_GET['zid']); $_SESSION['authenticated'] = 0; } if(!$_SESSION['authenticated']) { diff --git a/Zotlabs/Widget/Activity_filter.php b/Zotlabs/Widget/Activity_filter.php index a8a00bda6..6884963d4 100644 --- a/Zotlabs/Widget/Activity_filter.php +++ b/Zotlabs/Widget/Activity_filter.php @@ -42,7 +42,7 @@ class Activity_filter { $tabs[] = [ 'label' => t('Direct Messages'), - 'icon' => 'envelope-o', + 'icon' => 'envelope', 'url' => z_root() . '/' . $cmd . '/?f=&dm=1', 'sel' => $dm_active, 'title' => t('Show direct (private) messages') @@ -51,7 +51,7 @@ class Activity_filter { if(feature_enabled(local_channel(),'events_tab')) { $tabs[] = [ 'label' => t('Events'), - 'icon' => 'calendar', + 'icon' => 'calendar-date', 'url' => z_root() . '/' . $cmd . '/?verb=%2EEvent', 'sel' => $events_active, 'title' => t('Show posts that include events') @@ -93,7 +93,7 @@ class Activity_filter { $tabs[] = [ 'id' => 'privacy_groups', 'label' => t('Privacy Groups'), - 'icon' => 'users', + 'icon' => 'person', 'url' => '#', 'sel' => (($filter_active == 'group') ? true : false), 'title' => t('Show my privacy groups'), @@ -128,7 +128,7 @@ class Activity_filter { $tabs[] = [ 'id' => 'forums', 'label' => t('Forums'), - 'icon' => 'comments-o', + 'icon' => 'chat-quote', 'url' => '#', 'sel' => (($filter_active == 'forums') ? true : false), 'title' => t('Show forums'), @@ -161,7 +161,7 @@ class Activity_filter { $tabs[] = [ 'label' => t('Personal Posts'), - 'icon' => 'user-circle', + 'icon' => 'person-circle', 'url' => z_root() . '/' . $cmd . '/?f=&conv=1', 'sel' => $conv_active, 'title' => t('Show posts that mention or involve me') @@ -233,7 +233,7 @@ class Activity_filter { if($filter_active) { $reset = [ 'label' => '', - 'icon' => 'remove', + 'icon' => 'x-lg', 'url'=> z_root() . '/' . $cmd, 'sel'=> '', 'title' => t('Remove active filter') diff --git a/Zotlabs/Widget/Channel_activities.php b/Zotlabs/Widget/Channel_activities.php index a799ea81e..debaf20d4 100644 --- a/Zotlabs/Widget/Channel_activities.php +++ b/Zotlabs/Widget/Channel_activities.php @@ -91,7 +91,7 @@ class Channel_activities { self::$activities['photos'] = [ 'label' => t('Photos'), - 'icon' => 'photo', + 'icon' => 'image', 'url' => z_root() . '/photos/' . self::$channel['channel_address'], 'date' => $r[0]['edited'], 'items' => $i, @@ -123,7 +123,7 @@ class Channel_activities { self::$activities['files'] = [ 'label' => t('Files'), - 'icon' => 'folder-open', + 'icon' => 'folder', 'url' => z_root() . '/cloud/' . self::$channel['channel_address'], 'date' => $r[0]['edited'], 'items' => $i, @@ -166,7 +166,7 @@ class Channel_activities { self::$activities['webpages'] = [ 'label' => t('Webpages'), - 'icon' => 'newspaper-o', + 'icon' => 'layout-text-sidebar', 'url' => z_root() . '/webpages/' . self::$channel['channel_address'], 'date' => $r[0]['edited'], 'items' => $i, @@ -237,7 +237,7 @@ class Channel_activities { self::$activities['channels'] = [ 'label' => t('Channels'), - 'icon' => 'home', + 'icon' => 'house', 'url' => z_root() . '/manage', 'date' => datetime_convert(), 'items' => $i, diff --git a/Zotlabs/Widget/Helpindex.php b/Zotlabs/Widget/Helpindex.php index 5264e1947..fd9204c9e 100644 --- a/Zotlabs/Widget/Helpindex.php +++ b/Zotlabs/Widget/Helpindex.php @@ -22,7 +22,9 @@ class Helpindex { $this->find_help_file('toc', $this->lang['language']); if (! empty($this->file_name)) { - $this->contents = file_get_contents($this->file_name); + $this->contents = translate_projectname( + file_get_contents($this->file_name) + ); } $tpl = get_markup_template('widget.tpl'); diff --git a/Zotlabs/Widget/Hq_controls.php b/Zotlabs/Widget/Hq_controls.php index 51212d145..0db492c37 100644 --- a/Zotlabs/Widget/Hq_controls.php +++ b/Zotlabs/Widget/Hq_controls.php @@ -34,7 +34,7 @@ class Hq_controls { 'href' => '#', 'class' => 'btn notes-toggle', 'type' => 'button', - 'icon' => 'sticky-note-o', + 'icon' => 'sticky', 'extra' => 'data-toggle="button"' ]; } @@ -44,7 +44,7 @@ class Hq_controls { 'href' => '#', 'class' => 'btn channel-activities-toggle d-none', 'type' => 'button', - 'icon' => 'user-circle-o', + 'icon' => 'person-circle', 'extra' => 'data-toggle="button"' ]; diff --git a/Zotlabs/Widget/Messages.php b/Zotlabs/Widget/Messages.php index 8654d8e8f..f90b4f99e 100644 --- a/Zotlabs/Widget/Messages.php +++ b/Zotlabs/Widget/Messages.php @@ -23,20 +23,35 @@ class Messages { $_SESSION['messages_loadtime'] = datetime_convert(); + $r = q("SELECT DISTINCT(term) FROM term WHERE uid = %d AND ttype = %d ORDER BY term", + intval(local_channel()), + intval(TERM_FILE) + ); + + if ($r) { + foreach($r as $rr) { + $file_tags[] = $rr['term']; + } + } + $tpl = get_markup_template('messages_widget.tpl'); $o = replace_macros($tpl, [ '$entries' => $page['entries'] ?? [], '$offset' => $page['offset'] ?? 0, '$feature_star' => feature_enabled(local_channel(), 'star_posts'), + '$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'), + 'filed_messages_title' => t('Filed messages'), 'notice_messages_title' => t('Notices'), 'loading' => t('Loading'), 'empty' => t('No messages'), 'unseen_count' => t('Unseen'), - 'filter' => t('Filter by name or address') + 'filter' => t('Filter by name or address'), + 'file_filter' => t('Filter by file name') ] ]); @@ -50,6 +65,7 @@ class Messages { $offset = $options['offset'] ?? 0; $type = $options['type'] ?? ''; $author = $options['author'] ?? ''; + $file = $options['file'] ?? ''; if ($offset == -1) { return; @@ -67,8 +83,9 @@ class Messages { $item_normal_c = str_replace('item.', 'c.', $item_normal); $entries = []; $limit = 30; + $order_sql = 'i.created DESC'; $dummy_order_sql = ''; - $author_sql = ''; + $filter_sql = ''; $loadtime = (($offset) ? $_SESSION['messages_loadtime'] : datetime_convert()); $vnotify = get_pconfig(local_channel(), 'system', 'vnotify', -1); @@ -84,43 +101,55 @@ class Messages { $vnotify_sql_i = " AND i.verb NOT IN ('Dislike', '" . dbesc(ACTIVITY_DISLIKE) . "') "; } - if($author) { - $author_sql = " AND (i.owner_xchan = '" . protect_sprintf(dbesc($author)) . "') "; + if($type !== 'filed' && $author) { + $filter_sql = " AND (i.owner_xchan = '" . protect_sprintf(dbesc($author)) . "') "; + } + + if($type === 'filed' && $file) { + $filed_filter_sql = " AND (term.term = '" . protect_sprintf(dbesc($file)) . "') "; } switch($type) { case 'direct': - $type_sql = ' AND i.item_private = 2 '; + $type_sql = ' AND i.item_private = 2 AND i.item_thread_top = 1 '; // $dummy_order_sql has no other meaning but to trick // some mysql backends into using the right index. $dummy_order_sql = ', i.received DESC '; break; case 'starred': - $type_sql = ' AND i.item_starred = 1 '; + $type_sql = ' AND i.item_starred = 1 AND i.item_thread_top = 1 '; + break; + case 'filed': + $type_sql = ' AND i.id IN (SELECT term.oid FROM term WHERE term.ttype = ' . TERM_FILE . ' AND term.uid = i.uid ' . $filed_filter_sql . ')'; break; default: - $type_sql = ' AND i.item_private IN (0, 1) '; + $type_sql = ' AND i.item_private IN (0, 1) AND i.item_thread_top = 1 '; } $items = q("SELECT *, (SELECT count(*) FROM item c WHERE c.uid = %d AND c.parent = i.parent AND c.item_unseen = 1 AND c.item_thread_top = 0 $item_normal_c $vnotify_sql_c) AS unseen_count - FROM item i WHERE i.uid = %d + FROM item i + WHERE i.uid = %d AND i.created <= '%s' $type_sql - AND i.item_thread_top = 1 - $author_sql + $filter_sql $item_normal_i - ORDER BY i.created DESC $dummy_order_sql + ORDER BY $order_sql $dummy_order_sql LIMIT $limit OFFSET $offset", intval(local_channel()), intval(local_channel()), dbescdate($loadtime) ); + if ($type === 'filed') { + $items = fetch_post_tags($items); + } + xchan_query($items, false); $i = 0; $entries = []; + $ids = []; foreach($items as $item) { @@ -149,6 +178,16 @@ class Messages { $info .= t('via') . ' ' . $item['source']['xchan_name']; } + if ($type == 'filed') { + $info = ''; + foreach ($item['term'] as $t) { + if ($t['ttype'] !== TERM_FILE) { + continue; + } + $info .= '<span class="badge rounded-pill bg-danger me-1"><i class="bi bi-folder"></i> ' . $t['term'] . '</span>'; + } + } + $summary = $item['title']; if (!$summary) { $summary = $item['summary']; @@ -170,10 +209,10 @@ class Messages { switch(intval($item['item_private'])) { case 1: - $icon = '<i class="fa fa-lock"></i>'; + $icon = '<i class="bi bi-lock"></i>'; break; case 2: - $icon = '<i class="fa fa-envelope-o"></i>'; + $icon = '<i class="bi bi-envelope"></i>'; break; default: $icon = ''; @@ -293,7 +332,7 @@ class Messages { $entries[$i]['summary'] = $summary; $entries[$i]['b64mid'] = (($notice['ntype'] & NOTIFY_INTRO) ? '' : ((str_contains($notice['hash'], '-')) ? $notice['hash'] : basename($notice['link']))); $entries[$i]['href'] = (($notice['ntype'] & NOTIFY_INTRO) ? $notice['link'] : z_root() . '/hq/' . ((str_contains($notice['hash'], '-')) ? $notice['hash'] : basename($notice['link']))); - $entries[$i]['icon'] = (($notice['ntype'] & NOTIFY_INTRO) ? '<i class="fa fa-user-plus"></i>' : ''); + $entries[$i]['icon'] = (($notice['ntype'] & NOTIFY_INTRO) ? '<i class="bi bi-person-plus"></i>' : ''); $i++; } diff --git a/Zotlabs/Widget/Notes.php b/Zotlabs/Widget/Notes.php index 836159edd..be4ec64a6 100644 --- a/Zotlabs/Widget/Notes.php +++ b/Zotlabs/Widget/Notes.php @@ -16,14 +16,17 @@ use Zotlabs\Lib\Apps; class Notes { function widget($arr) { - if(! local_channel()) + if(!local_channel()) { return EMPTY_STR; + } - if(App::$profile_uid !== local_channel()) + if (App::$profile_uid !== local_channel()) { return EMPTY_STR; + } - if(! Apps::system_app_installed(local_channel(), 'Notes')) + if(!Apps::system_app_installed(local_channel(), 'Notes')) { return EMPTY_STR; + } $text = get_pconfig(local_channel(),'notes','text'); diff --git a/Zotlabs/Widget/Notifications.php b/Zotlabs/Widget/Notifications.php index eeef1d7d4..225403535 100644 --- a/Zotlabs/Widget/Notifications.php +++ b/Zotlabs/Widget/Notifications.php @@ -18,7 +18,7 @@ class Notifications { if(local_channel()) { $notifications[] = [ 'type' => 'network', - 'icon' => 'th', + 'icon' => 'grid-3x3', 'severity' => 'secondary', 'label' => t('Network'), 'title' => t('New network activity notifications'), @@ -38,7 +38,7 @@ class Notifications { $notifications[] = [ 'type' => 'home', - 'icon' => 'home', + 'icon' => 'house', 'severity' => 'danger', 'label' => t('Home'), 'title' => t('New home activity notifications'), @@ -76,7 +76,7 @@ class Notifications { $notifications[] = [ 'type' => 'all_events', - 'icon' => 'calendar', + 'icon' => 'calendar-date', 'severity' => 'secondary', 'label' => t('Events'), 'title' => t('New events notifications'), @@ -91,7 +91,7 @@ class Notifications { $notifications[] = [ 'type' => 'intros', - 'icon' => 'users', + 'icon' => 'people', 'severity' => 'danger', 'label' => t('New Connections'), 'title' => t('New connections notifications'), @@ -111,7 +111,7 @@ class Notifications { $notifications[] = [ 'type' => 'notify', - 'icon' => 'exclamation', + 'icon' => 'exclamation-circle', 'severity' => 'danger', 'label' => t('Notices'), 'title' => t('Notices'), @@ -126,7 +126,7 @@ class Notifications { $notifications[] = [ 'type' => 'forums', - 'icon' => 'comments-o', + 'icon' => 'chat-quote', 'severity' => 'secondary', 'label' => t('Forums'), 'title' => t('Forums'), @@ -139,7 +139,7 @@ class Notifications { if(local_channel() && is_site_admin()) { $notifications[] = [ 'type' => 'register', - 'icon' => 'user-o', + 'icon' => 'person-exclamation', 'severity' => 'danger', 'label' => t('Registrations'), 'title' => t('New registrations notifications'), diff --git a/Zotlabs/Widget/Permcats.php b/Zotlabs/Widget/Permcats.php index 9bda5b8f1..f0e43b937 100644 --- a/Zotlabs/Widget/Permcats.php +++ b/Zotlabs/Widget/Permcats.php @@ -40,7 +40,7 @@ class Permcats { if($active_role) { $roles[] = [ - 'name' => '<i class="fa fa-plus"></i> ' . t('Add new role'), + 'name' => '<i class="bi bi-plus-lg"></i> ' . t('Add new role'), 'url' => z_root() . '/permcats', 'active' => '' ]; diff --git a/Zotlabs/Widget/Privacygroups.php b/Zotlabs/Widget/Privacygroups.php index 62f343ea6..129c939fd 100644 --- a/Zotlabs/Widget/Privacygroups.php +++ b/Zotlabs/Widget/Privacygroups.php @@ -42,7 +42,7 @@ class Privacygroups { if ($active) { $menu_items[] = [ 'href' => $z_root . '/group', - 'label' => '<i class="fa fa-plus"></i> ' . t('Add new group'), + 'label' => '<i class="bi bi-plus-lg"></i> ' . t('Add new group'), 'title' => '', 'active' => '', 'count' => '' diff --git a/Zotlabs/Widget/Rating.php b/Zotlabs/Widget/Rating.php index f8986ac93..a90052411 100644 --- a/Zotlabs/Widget/Rating.php +++ b/Zotlabs/Widget/Rating.php @@ -60,12 +60,12 @@ class Rating { if((($remote) || (local_channel())) && (! $self)) { if($remote) - $o .= '<a class="btn btn-block btn-primary btn-sm" href="' . $url . '"><i class="fa fa-pencil"></i> ' . t('Rate Me') . '</a>'; + $o .= '<a class="btn btn-block btn-primary btn-sm" href="' . $url . '"><i class="bi bi-pencil"></i> ' . t('Rate Me') . '</a>'; else - $o .= '<div class="btn btn-block btn-primary btn-sm" onclick="doRatings(\'' . $hash . '\'); return false;"><i class="fa fa-pencil"></i> ' . t('Rate Me') . '</div>'; + $o .= '<div class="btn btn-block btn-primary btn-sm" onclick="doRatings(\'' . $hash . '\'); return false;"><i class="bi bi-pencil"></i> ' . t('Rate Me') . '</div>'; } - $o .= '<a class="btn btn-block btn-default btn-sm" href="ratings/' . $hash . '"><i class="fa fa-eye"></i> ' . t('View Ratings') . '</a>'; + $o .= '<a class="btn btn-block btn-default btn-sm" href="ratings/' . $hash . '"><i class="bi bi-eye"></i> ' . t('View Ratings') . '</a>'; $o .= '</div>'; return $o; diff --git a/Zotlabs/Widget/Tokens.php b/Zotlabs/Widget/Tokens.php index 69452d628..08fe67680 100644 --- a/Zotlabs/Widget/Tokens.php +++ b/Zotlabs/Widget/Tokens.php @@ -38,7 +38,7 @@ class Tokens { if ($active) { $menu_items[] = [ 'href' => $z_root . '/tokens', - 'label' => '<i class="fa fa-plus"></i> ' . t('Add new guest'), + 'label' => '<i class="bi bi-plus-lg"></i> ' . t('Add new guest'), 'title' => '', 'active' => '' ]; |