From edc6851f0ee016b3140d81a4dcac2a50240ee959 Mon Sep 17 00:00:00 2001 From: Michael Date: Sat, 3 Dec 2022 07:15:29 +0000 Subject: [PATCH 01/27] Issue 12302: Remote self is working again --- src/Model/Item.php | 83 ++++++++++++++++++++++------------------------ 1 file changed, 39 insertions(+), 44 deletions(-) diff --git a/src/Model/Item.php b/src/Model/Item.php index 89925c532..8026d567c 100644 --- a/src/Model/Item.php +++ b/src/Model/Item.php @@ -2291,24 +2291,24 @@ class Item public static function isRemoteSelf(array $contact, array &$datarray): bool { - if (!$contact['remote_self']) { + if ($contact['remote_self'] != Contact::MIRROR_OWN_POST) { return false; } // Prevent the forwarding of posts that are forwarded - if (!empty($datarray["extid"]) && ($datarray["extid"] == Protocol::DFRN)) { + if (!empty($datarray['extid']) && ($datarray['extid'] == Protocol::DFRN)) { Logger::info('Already forwarded'); return false; } // Prevent to forward already forwarded posts - if ($datarray["app"] == DI::baseUrl()->getHostname()) { + if ($datarray['app'] == DI::baseUrl()->getHostname()) { Logger::info('Already forwarded (second test)'); return false; } // Only forward posts - if ($datarray["verb"] != Activity::POST) { + if ($datarray['verb'] != Activity::POST) { Logger::info('No post'); return false; } @@ -2320,54 +2320,49 @@ class Item $datarray2 = $datarray; Logger::info('remote-self start', ['contact' => $contact['url'], 'remote_self'=> $contact['remote_self'], 'item' => $datarray]); - if ($contact['remote_self'] == Contact::MIRROR_OWN_POST) { - $self = DBA::selectFirst('contact', ['id', 'name', 'url', 'thumb'], + + $self = DBA::selectFirst('contact', ['id', 'name', 'url', 'thumb'], ['uid' => $contact['uid'], 'self' => true]); - if (DBA::isResult($self)) { - $datarray['contact-id'] = $self["id"]; - - $datarray['owner-name'] = $self["name"]; - $datarray['owner-link'] = $self["url"]; - $datarray['owner-avatar'] = $self["thumb"]; - - $datarray['author-name'] = $datarray['owner-name']; - $datarray['author-link'] = $datarray['owner-link']; - $datarray['author-avatar'] = $datarray['owner-avatar']; - - unset($datarray['edited']); - - unset($datarray['network']); - unset($datarray['owner-id']); - unset($datarray['author-id']); - } - - if ($contact['network'] != Protocol::FEED) { - $old_uri_id = $datarray["uri-id"] ?? 0; - $datarray["guid"] = System::createUUID(); - unset($datarray["plink"]); - $datarray["uri"] = self::newURI($datarray["guid"]); - $datarray["uri-id"] = ItemURI::getIdByURI($datarray["uri"]); - $datarray["extid"] = Protocol::DFRN; - $urlpart = parse_url($datarray2['author-link']); - $datarray["app"] = $urlpart["host"]; - if (!empty($old_uri_id)) { - Post\Media::copy($old_uri_id, $datarray["uri-id"]); - } - - unset($datarray["parent-uri"]); - unset($datarray["thr-parent"]); - } else { - $datarray['private'] = self::PUBLIC; - } + if (!DBA::isResult($self)) { + Logger::error('Self contact not found', ['uid' => $contact['uid']]); + return false; } + $datarray['contact-id'] = $self['id']; + + $datarray['author-name'] = $datarray['owner-name'] = $self['name']; + $datarray['author-link'] = $datarray['owner-link'] = $self['url']; + $datarray['author-avatar'] = $datarray['owner-avatar'] = $self['thumb']; + + unset($datarray['edited']); + + unset($datarray['network']); + unset($datarray['owner-id']); + unset($datarray['author-id']); + if ($contact['network'] != Protocol::FEED) { + $old_uri_id = $datarray['uri-id'] ?? 0; + $datarray['guid'] = System::createUUID(); + unset($datarray['plink']); + $datarray['uri'] = self::newURI($datarray['guid']); + $datarray['uri-id'] = ItemURI::getIdByURI($datarray['uri']); + $datarray['extid'] = Protocol::DFRN; + $urlpart = parse_url($datarray2['author-link']); + $datarray['app'] = $urlpart['host']; + if (!empty($old_uri_id)) { + Post\Media::copy($old_uri_id, $datarray['uri-id']); + } + + unset($datarray['parent-uri']); + unset($datarray['thr-parent']); + // Store the original post $result = self::insert($datarray2); Logger::info('remote-self post original item', ['contact' => $contact['url'], 'result'=> $result, 'item' => $datarray2]); } else { - Logger::info('No valid mirroring option', ['uid' => $contact['uid'], 'id' => $contact['id'], 'network' => $contact['network'], 'remote_self' => $contact['remote_self']]); - return false; + $datarray['private'] = self::PUBLIC; + $datarray['app'] = 'Feed'; + $result = true; } return (bool)$result; From e389fe945f7445ba43c3586aded9496788530df0 Mon Sep 17 00:00:00 2001 From: Hypolite Petovan Date: Sat, 3 Dec 2022 04:17:54 -0500 Subject: [PATCH 02/27] Ward against empty template variable in frio:contact/entry.tpl --- view/theme/frio/templates/contact/entry.tpl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/view/theme/frio/templates/contact/entry.tpl b/view/theme/frio/templates/contact/entry.tpl index 17436b570..de6faa220 100644 --- a/view/theme/frio/templates/contact/entry.tpl +++ b/view/theme/frio/templates/contact/entry.tpl @@ -89,7 +89,7 @@ {{* The contact description (e.g. Name, Network, kind of connection and so on *}}
-

{{$contact.name}} +

{{$contact.name}} {{if $contact.account_type}} ({{$contact.account_type}}){{/if}} {{if $contact.account_type == 'Forum'}}{{/if}} {{* @todo this needs some changing in core because $contact.account_type contains a translated string which may notbe the same in every language *}} From 3b3192933d51d7d6242639dcdb021a90a86f5084 Mon Sep 17 00:00:00 2001 From: Hypolite Petovan Date: Fri, 2 Dec 2022 07:48:44 -0500 Subject: [PATCH 03/27] Ensure null values aren't processed in Content\Text\Markdown::toBBCode - Address part of https://github.com/friendica/friendica/issues/12011#issuecomment-1335124938 --- src/Content/Text/Markdown.php | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/src/Content/Text/Markdown.php b/src/Content/Text/Markdown.php index 3575c69a8..53983113b 100644 --- a/src/Content/Text/Markdown.php +++ b/src/Content/Text/Markdown.php @@ -21,6 +21,8 @@ namespace Friendica\Content\Text; +use Friendica\Core\Logger; +use Friendica\Core\System; use Friendica\DI; use Friendica\Model\Contact; @@ -106,8 +108,18 @@ class Markdown * So we'll use that to convert to HTML, then convert the HTML back to bbcode, * and then clean up a few Diaspora specific constructs. */ - public static function toBBCode($s) + public static function toBBCode($s): string { + // @TODO Temporary until we find the source of the null value to finally set the correct type-hint + if (is_null($s)) { + Logger::warning('Received null value', ['callstack' => System::callstack()]); + return ''; + } + + if (!$s) { + return $s; + } + DI::profiler()->startRecording('rendering'); // The parser cannot handle paragraphs correctly From e6f8b8c6e0ab6889881fb208bbff4d47676ce528 Mon Sep 17 00:00:00 2001 From: Hypolite Petovan Date: Fri, 2 Dec 2022 08:10:16 -0500 Subject: [PATCH 04/27] Ward against preg_replace_callback null return value in Strings::performWithEscapedBlocks - Add logging to troubleshoot potential issue with regex - Address part of https://github.com/friendica/friendica/issues/12011#issuecomment-1335124938 --- src/Util/Strings.php | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/Util/Strings.php b/src/Util/Strings.php index 0d7bf6691..379f2a252 100644 --- a/src/Util/Strings.php +++ b/src/Util/Strings.php @@ -23,6 +23,7 @@ namespace Friendica\Util; use Friendica\Content\ContactSelector; use Friendica\Core\Logger; +use Friendica\Core\System; use ParagonIE\ConstantTime\Base64; /** @@ -480,7 +481,7 @@ class Strings $blocks = []; - $text = preg_replace_callback($regex, + $return = preg_replace_callback($regex, function ($matches) use ($executionId, &$blocks) { $return = '«block-' . $executionId . '-' . count($blocks) . '»'; @@ -491,7 +492,11 @@ class Strings $text ); - $text = $callback($text) ?? ''; + if (is_null($return)) { + Logger::warning('Received null value from preg_replace_callback', ['text' => $text, 'regex' => $regex, 'blocks' => $blocks, 'executionId' => $executionId, 'callstack' => System::callstack(10)]); + } + + $text = $callback($return ?? $text) ?? ''; // Restore code blocks $text = preg_replace_callback('/«block-' . $executionId . '-([0-9]+)»/iU', From f66420815793015db113315f28c4ef1931c304bb Mon Sep 17 00:00:00 2001 From: "Zane C. Bowers-Hadley" Date: Sat, 3 Dec 2022 04:24:49 -0600 Subject: [PATCH 05/27] get the load average in a portable manner --- src/Core/System.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Core/System.php b/src/Core/System.php index b2da78f1b..dbf49b38d 100644 --- a/src/Core/System.php +++ b/src/Core/System.php @@ -445,7 +445,7 @@ class System if (@is_readable('/proc/loadavg')) { $content = @file_get_contents('/proc/loadavg'); if (empty($content)) { - $content = shell_exec('cat /proc/loadavg'); + $content = shell_exec('uptime | sed "s/.*e: //" | sed "s/,//"'); } } From edaff99307347991a5b0f198cb3643daf5e6682f Mon Sep 17 00:00:00 2001 From: "Zane C. Bowers-Hadley" Date: Sat, 3 Dec 2022 04:31:04 -0600 Subject: [PATCH 06/27] derp, minor fix --- src/Core/System.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Core/System.php b/src/Core/System.php index dbf49b38d..31f17a905 100644 --- a/src/Core/System.php +++ b/src/Core/System.php @@ -445,7 +445,7 @@ class System if (@is_readable('/proc/loadavg')) { $content = @file_get_contents('/proc/loadavg'); if (empty($content)) { - $content = shell_exec('uptime | sed "s/.*e: //" | sed "s/,//"'); + $content = shell_exec('uptime | sed "s/.*averages*: //" | sed "s/,//"'); } } From 8f82fb9a2203e6c4405c3fa5980cb59c044376e3 Mon Sep 17 00:00:00 2001 From: "Zane C. Bowers-Hadley" Date: Sat, 3 Dec 2022 04:34:17 -0600 Subject: [PATCH 07/27] now with g --- src/Core/System.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Core/System.php b/src/Core/System.php index 31f17a905..e0c23e55b 100644 --- a/src/Core/System.php +++ b/src/Core/System.php @@ -445,7 +445,7 @@ class System if (@is_readable('/proc/loadavg')) { $content = @file_get_contents('/proc/loadavg'); if (empty($content)) { - $content = shell_exec('uptime | sed "s/.*averages*: //" | sed "s/,//"'); + $content = shell_exec('uptime | sed "s/.*averages*: //" | sed "s/,//g"'); } } From e4dda7d2ca53326d83a67a86d0dbb8eb40decff0 Mon Sep 17 00:00:00 2001 From: Michael Date: Sat, 3 Dec 2022 13:49:29 +0000 Subject: [PATCH 08/27] Issue 11553: Reliably return the user's contacts --- src/Model/Contact.php | 16 ++++ src/Model/Contact/Relation.php | 36 ++++++++- .../Api/Mastodon/Accounts/Followers.php | 74 +++++++++++++------ .../Api/Mastodon/Accounts/Following.php | 74 +++++++++++++------ src/Worker/ContactDiscoveryForUser.php | 36 +++++++++ 5 files changed, 189 insertions(+), 47 deletions(-) create mode 100644 src/Worker/ContactDiscoveryForUser.php diff --git a/src/Model/Contact.php b/src/Model/Contact.php index eabe378a4..34ce8e684 100644 --- a/src/Model/Contact.php +++ b/src/Model/Contact.php @@ -136,6 +136,18 @@ class Contact return $contact; } + /** + * @param array $fields Array of selected fields, empty for all + * @param array $condition Array of fields for condition + * @param array $params Array of several parameters + * @return array + * @throws \Exception + */ + public static function selectAccountToArray(array $fields = [], array $condition = [], array $params = []): array + { + return DBA::selectToArray('account-user-view', $fields, $condition, $params); + } + /** * @param array $fields Array of selected fields, empty for all * @param array $condition Array of fields for condition @@ -3154,6 +3166,8 @@ class Contact return; } + Worker::add(Worker::PRIORITY_LOW, 'ContactDiscoveryForUser', $contact['uid']); + self::clearFollowerFollowingEndpointCache($contact['uid']); $cdata = self::getPublicAndUserContactID($contact['id'], $contact['uid']); @@ -3177,6 +3191,8 @@ class Contact } else { self::update(['rel' => self::FOLLOWER], ['id' => $contact['id']]); } + + Worker::add(Worker::PRIORITY_LOW, 'ContactDiscoveryForUser', $contact['uid']); } /** diff --git a/src/Model/Contact/Relation.php b/src/Model/Contact/Relation.php index 5616148fa..3db882bb9 100644 --- a/src/Model/Contact/Relation.php +++ b/src/Model/Contact/Relation.php @@ -68,6 +68,26 @@ class Relation DBA::insert('contact-relation', ['last-interaction' => $interaction_date, 'cid' => $target, 'relation-cid' => $actor], Database::INSERT_UPDATE); } + /** + * Fetch the followers of a given user + * + * @param integer $uid User ID + * @return void + */ + public static function discoverByUser(int $uid) + { + $contact = Contact::selectFirst(['id', 'url'], ['uid' => $uid, 'self' => true]); + if (empty($contact)) { + Logger::warning('Self contact for user not found', ['uid' => $uid]); + return; + } + + $followers = self::getContacts($uid, [Contact::FOLLOWER, Contact::FRIEND]); + $followings = self::getContacts($uid, [Contact::SHARING, Contact::FRIEND]); + + self::updateFollowersFollowings($contact, $followers, $followings); + } + /** * Fetches the followers of a given profile and adds them * @@ -113,13 +133,27 @@ class Relation $followings = []; } + self::updateFollowersFollowings($contact, $followers, $followings); + } + + /** + * Update followers and followings for the given contact + * + * @param array $contact + * @param array $followers + * @param array $followings + * @return void + */ + private static function updateFollowersFollowings(array $contact, array $followers, array $followings) + { if (empty($followers) && empty($followings)) { Contact::update(['last-discovery' => DateTimeFormat::utcNow()], ['id' => $contact['id']]); - Logger::info('The contact does not offer discoverable data', ['id' => $contact['id'], 'url' => $url, 'network' => $contact['network']]); + Logger::info('The contact does not offer discoverable data', ['id' => $contact['id'], 'url' => $contact['url'], 'network' => $contact['network']]); return; } $target = $contact['id']; + $url = $contact['url']; if (!empty($followers)) { // Clear the follower list, since it will be recreated in the next step diff --git a/src/Module/Api/Mastodon/Accounts/Followers.php b/src/Module/Api/Mastodon/Accounts/Followers.php index 58d1f7d83..cd03a0176 100644 --- a/src/Module/Api/Mastodon/Accounts/Followers.php +++ b/src/Module/Api/Mastodon/Accounts/Followers.php @@ -24,6 +24,7 @@ namespace Friendica\Module\Api\Mastodon\Accounts; use Friendica\Core\System; use Friendica\Database\DBA; use Friendica\DI; +use Friendica\Model\Contact; use Friendica\Module\BaseApi; /** @@ -55,33 +56,60 @@ class Followers extends BaseApi 'limit' => 40, // Maximum number of results to return. Defaults to 40. ], $request); - $params = ['order' => ['relation-cid' => true], 'limit' => $request['limit']]; + if (false && $id == Contact::getPublicIdByUserId($uid)) { + $params = ['order' => ['pid' => true], 'limit' => $request['limit']]; - $condition = ['cid' => $id, 'follows' => true]; + $condition = ['uid' => $uid, 'self' => false, 'rel' => [Contact::FOLLOWER, Contact::FRIEND]]; + + if (!empty($request['max_id'])) { + $condition = DBA::mergeConditions($condition, ["`pid` < ?", $request['max_id']]); + } + + if (!empty($request['since_id'])) { + $condition = DBA::mergeConditions($condition, ["`pid` > ?", $request['since_id']]); + } + + if (!empty($request['min_id'])) { + $condition = DBA::mergeConditions($condition, ["`pid` > ?", $request['min_id']]); + + $params['order'] = ['pid']; + } + + $accounts = []; + + foreach (Contact::selectAccountToArray(['pid'], $condition, $params) as $follower) { + self::setBoundaries($follower['pid']); + $accounts[] = DI::mstdnAccount()->createFromContactId($follower['pid'], $uid); + } + } else { + $params = ['order' => ['relation-cid' => true], 'limit' => $request['limit']]; - if (!empty($request['max_id'])) { - $condition = DBA::mergeConditions($condition, ["`relation-cid` < ?", $request['max_id']]); + $condition = ['cid' => $id, 'follows' => true]; + + if (!empty($request['max_id'])) { + $condition = DBA::mergeConditions($condition, ["`relation-cid` < ?", $request['max_id']]); + } + + if (!empty($request['since_id'])) { + $condition = DBA::mergeConditions($condition, ["`relation-cid` > ?", $request['since_id']]); + } + + if (!empty($request['min_id'])) { + $condition = DBA::mergeConditions($condition, ["`relation-cid` > ?", $request['min_id']]); + + $params['order'] = ['relation-cid']; + } + + $accounts = []; + + $followers = DBA::select('contact-relation', ['relation-cid'], $condition, $params); + while ($follower = DBA::fetch($followers)) { + self::setBoundaries($follower['relation-cid']); + $accounts[] = DI::mstdnAccount()->createFromContactId($follower['relation-cid'], $uid); + } + DBA::close($followers); } - if (!empty($request['since_id'])) { - $condition = DBA::mergeConditions($condition, ["`relation-cid` > ?", $request['since_id']]); - } - - if (!empty($request['min_id'])) { - $condition = DBA::mergeConditions($condition, ["`relation-cid` > ?", $request['min_id']]); - - $params['order'] = ['cid']; - } - - $accounts = []; - - $followers = DBA::select('contact-relation', ['relation-cid'], $condition, $params); - while ($follower = DBA::fetch($followers)) { - self::setBoundaries($follower['relation-cid']); - $accounts[] = DI::mstdnAccount()->createFromContactId($follower['relation-cid'], $uid); - } - DBA::close($followers); - if (!empty($request['min_id'])) { $accounts = array_reverse($accounts); } diff --git a/src/Module/Api/Mastodon/Accounts/Following.php b/src/Module/Api/Mastodon/Accounts/Following.php index 8e05a9b71..aca6bd5a8 100644 --- a/src/Module/Api/Mastodon/Accounts/Following.php +++ b/src/Module/Api/Mastodon/Accounts/Following.php @@ -24,6 +24,7 @@ namespace Friendica\Module\Api\Mastodon\Accounts; use Friendica\Core\System; use Friendica\Database\DBA; use Friendica\DI; +use Friendica\Model\Contact; use Friendica\Module\BaseApi; /** @@ -55,33 +56,60 @@ class Following extends BaseApi 'limit' => 40, // Maximum number of results to return. Defaults to 40. ], $request); - $params = ['order' => ['cid' => true], 'limit' => $request['limit']]; + if ($id == Contact::getPublicIdByUserId($uid)) { + $params = ['order' => ['pid' => true], 'limit' => $request['limit']]; - $condition = ['relation-cid' => $id, 'follows' => true]; + $condition = ['uid' => $uid, 'self' => false, 'rel' => [Contact::SHARING, Contact::FRIEND]]; + + if (!empty($request['max_id'])) { + $condition = DBA::mergeConditions($condition, ["`pid` < ?", $request['max_id']]); + } + + if (!empty($request['since_id'])) { + $condition = DBA::mergeConditions($condition, ["`pid` > ?", $request['since_id']]); + } + + if (!empty($request['min_id'])) { + $condition = DBA::mergeConditions($condition, ["`pid` > ?", $request['min_id']]); + + $params['order'] = ['pid']; + } + + $accounts = []; + + foreach (Contact::selectAccountToArray(['pid'], $condition, $params) as $follower) { + self::setBoundaries($follower['pid']); + $accounts[] = DI::mstdnAccount()->createFromContactId($follower['pid'], $uid); + } + } else { + $params = ['order' => ['cid' => true], 'limit' => $request['limit']]; - if (!empty($request['max_id'])) { - $condition = DBA::mergeConditions($condition, ["`cid` < ?", $request['max_id']]); + $condition = ['relation-cid' => $id, 'follows' => true]; + + if (!empty($request['max_id'])) { + $condition = DBA::mergeConditions($condition, ["`cid` < ?", $request['max_id']]); + } + + if (!empty($request['since_id'])) { + $condition = DBA::mergeConditions($condition, ["`cid` > ?", $request['since_id']]); + } + + if (!empty($request['min_id'])) { + $condition = DBA::mergeConditions($condition, ["`cid` > ?", $request['min_id']]); + + $params['order'] = ['cid']; + } + + $accounts = []; + + $followers = DBA::select('contact-relation', ['cid'], $condition, $params); + while ($follower = DBA::fetch($followers)) { + self::setBoundaries($follower['cid']); + $accounts[] = DI::mstdnAccount()->createFromContactId($follower['cid'], $uid); + } + DBA::close($followers); } - if (!empty($request['since_id'])) { - $condition = DBA::mergeConditions($condition, ["`cid` > ?", $request['since_id']]); - } - - if (!empty($request['min_id'])) { - $condition = DBA::mergeConditions($condition, ["`cid` > ?", $request['min_id']]); - - $params['order'] = ['cid']; - } - - $accounts = []; - - $followers = DBA::select('contact-relation', ['cid'], $condition, $params); - while ($follower = DBA::fetch($followers)) { - self::setBoundaries($follower['cid']); - $accounts[] = DI::mstdnAccount()->createFromContactId($follower['cid'], $uid); - } - DBA::close($followers); - if (!empty($request['min_id'])) { $accounts = array_reverse($accounts); } diff --git a/src/Worker/ContactDiscoveryForUser.php b/src/Worker/ContactDiscoveryForUser.php new file mode 100644 index 000000000..d02f430e2 --- /dev/null +++ b/src/Worker/ContactDiscoveryForUser.php @@ -0,0 +1,36 @@ +. + * + */ + +namespace Friendica\Worker; + +use Friendica\Model\Contact; + +class ContactDiscoveryForUser +{ + /** + * Discover contact relations + * @param string $url + */ + public static function execute(int $uid) + { + Contact\Relation::discoverByUser($uid); + } +} From 303d556ab0c6a70b4d56025e226fe70eb713fbd2 Mon Sep 17 00:00:00 2001 From: Hypolite Petovan Date: Sat, 3 Dec 2022 07:27:30 -0500 Subject: [PATCH 09/27] Fix last reference of replaced variable $owner_nick in Protocol\Feed::atom() - Address https://github.com/friendica/friendica/issues/11994#issuecomment-1336146405 --- src/Protocol/Feed.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Protocol/Feed.php b/src/Protocol/Feed.php index 3dac91e84..29b192331 100644 --- a/src/Protocol/Feed.php +++ b/src/Protocol/Feed.php @@ -943,7 +943,7 @@ class Feed if ((time() - strtotime($owner['last-item'])) < 15*60) { $result = DI::cache()->get($cachekey); if (!$nocache && !is_null($result)) { - Logger::info('Cached feed duration', ['seconds' => number_format(microtime(true) - $stamp, 3), 'nick' => $owner_nick, 'filter' => $filter, 'created' => $previous_created]); + Logger::info('Cached feed duration', ['seconds' => number_format(microtime(true) - $stamp, 3), 'nick' => $owner['nickname'], 'filter' => $filter, 'created' => $previous_created]); return $result['feed']; } } @@ -997,7 +997,7 @@ class Feed $msg = ['feed' => $feeddata, 'last_update' => $last_update]; DI::cache()->set($cachekey, $msg, Duration::QUARTER_HOUR); - Logger::info('Feed duration', ['seconds' => number_format(microtime(true) - $stamp, 3), 'nick' => $owner_nick, 'filter' => $filter, 'created' => $previous_created]); + Logger::info('Feed duration', ['seconds' => number_format(microtime(true) - $stamp, 3), 'nick' => $owner['nickname'], 'filter' => $filter, 'created' => $previous_created]); return $feeddata; } From 36c7aea214104b4611bde6048da42b77d9bc5e61 Mon Sep 17 00:00:00 2001 From: Michael Date: Sat, 3 Dec 2022 15:37:40 +0000 Subject: [PATCH 10/27] Debug code removed --- src/Module/Api/Mastodon/Accounts/Followers.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Module/Api/Mastodon/Accounts/Followers.php b/src/Module/Api/Mastodon/Accounts/Followers.php index cd03a0176..949ec45f4 100644 --- a/src/Module/Api/Mastodon/Accounts/Followers.php +++ b/src/Module/Api/Mastodon/Accounts/Followers.php @@ -56,7 +56,7 @@ class Followers extends BaseApi 'limit' => 40, // Maximum number of results to return. Defaults to 40. ], $request); - if (false && $id == Contact::getPublicIdByUserId($uid)) { + if ($id == Contact::getPublicIdByUserId($uid)) { $params = ['order' => ['pid' => true], 'limit' => $request['limit']]; $condition = ['uid' => $uid, 'self' => false, 'rel' => [Contact::FOLLOWER, Contact::FRIEND]]; From fa80c69d9451b80aa71a183bc83e1382192b8263 Mon Sep 17 00:00:00 2001 From: Michael Date: Sat, 3 Dec 2022 19:44:50 +0000 Subject: [PATCH 11/27] Only fetch the processes if needed --- src/Core/System.php | 5 +++-- src/Core/Worker.php | 4 ++-- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/src/Core/System.php b/src/Core/System.php index e0c23e55b..aa2d81d89 100644 --- a/src/Core/System.php +++ b/src/Core/System.php @@ -438,11 +438,12 @@ class System /** * Fetch the load and number of processes * + * @param bool $get_processes * @return array */ - public static function getLoadAvg(): array + public static function getLoadAvg(bool $get_processes = true): array { - if (@is_readable('/proc/loadavg')) { + if ($get_processes && @is_readable('/proc/loadavg')) { $content = @file_get_contents('/proc/loadavg'); if (empty($content)) { $content = shell_exec('uptime | sed "s/.*averages*: //" | sed "s/,//g"'); diff --git a/src/Core/Worker.php b/src/Core/Worker.php index 47da04b35..d6f97eed2 100644 --- a/src/Core/Worker.php +++ b/src/Core/Worker.php @@ -462,7 +462,7 @@ class Worker return false; } - $load = System::getLoadAvg(); + $load = System::getLoadAvg($processes_cooldown != 0); if (empty($load)) { return false; } @@ -508,7 +508,7 @@ class Worker $sleeping = false; - while ($load = System::getLoadAvg()) { + while ($load = System::getLoadAvg($processes_cooldown != 0)) { if (($load_cooldown > 0) && ($load['average1'] > $load_cooldown)) { if (!$sleeping) { Logger::notice('Load induced pre execution cooldown.', ['max' => $load_cooldown, 'load' => $load, 'called-by' => System::callstack(1)]); From b6f7d310367e0332417aa5342bcc1e07f4e1f3e2 Mon Sep 17 00:00:00 2001 From: Michael Date: Sat, 3 Dec 2022 20:18:19 +0000 Subject: [PATCH 12/27] API: Central way to fetch the system rules --- src/Core/System.php | 28 ++++++++++++++++++++++ src/Module/Api/Mastodon/Instance.php | 2 +- src/Module/Api/Mastodon/Instance/Rules.php | 17 +------------ src/Object/Api/Mastodon/Instance.php | 4 +++- 4 files changed, 33 insertions(+), 18 deletions(-) diff --git a/src/Core/System.php b/src/Core/System.php index e0c23e55b..0c08fe168 100644 --- a/src/Core/System.php +++ b/src/Core/System.php @@ -21,6 +21,8 @@ namespace Friendica\Core; +use Friendica\Content\Text\BBCode; +use Friendica\Content\Text\HTML; use Friendica\Core\Config\Capability\IManageConfigValues; use Friendica\DI; use Friendica\Module\Response; @@ -659,4 +661,30 @@ class System // Reaching this point means that the operating system is configured badly. return ""; } + + /** + * Fetch the system rules + * @todo We should have got a better way to store and fetch the rules + * + * @return array + */ + public static function getRules(): array + { + $rules = []; + $id = 0; + + if (DI::config()->get('system', 'tosdisplay')) { + $html = BBCode::convert(DI::config()->get('system', 'tostext'), false, BBCode::EXTERNAL); + + $msg = HTML::toPlaintext($html, 0, true); + foreach (explode("\n", $msg) as $line) { + $line = trim($line); + if ($line) { + $rules[] = ['id' => (string)++$id, 'text' => $line]; + } + } + } + + return $rules; + } } diff --git a/src/Module/Api/Mastodon/Instance.php b/src/Module/Api/Mastodon/Instance.php index e5e0a9579..4747ecf51 100644 --- a/src/Module/Api/Mastodon/Instance.php +++ b/src/Module/Api/Mastodon/Instance.php @@ -59,6 +59,6 @@ class Instance extends BaseApi */ protected function rawContent(array $request = []) { - System::jsonExit(new InstanceEntity($this->config, $this->baseUrl, $this->database)); + System::jsonExit(new InstanceEntity($this->config, $this->baseUrl, $this->database, System::getRules())); } } diff --git a/src/Module/Api/Mastodon/Instance/Rules.php b/src/Module/Api/Mastodon/Instance/Rules.php index 4e25024f8..2b6732f26 100644 --- a/src/Module/Api/Mastodon/Instance/Rules.php +++ b/src/Module/Api/Mastodon/Instance/Rules.php @@ -38,21 +38,6 @@ class Rules extends BaseApi */ protected function rawContent(array $request = []) { - $rules = []; - $id = 0; - - if (DI::config()->get('system', 'tosdisplay')) { - $html = BBCode::convert(DI::config()->get('system', 'tostext'), false, BBCode::EXTERNAL); - - $msg = HTML::toPlaintext($html, 0, true); - foreach (explode("\n", $msg) as $line) { - $line = trim($line); - if ($line) { - $rules[] = ['id' => (string)++$id, 'text' => $line]; - } - } - } - - System::jsonExit($rules); + System::jsonExit(System::getRules()); } } diff --git a/src/Object/Api/Mastodon/Instance.php b/src/Object/Api/Mastodon/Instance.php index b659dc306..13b777db8 100644 --- a/src/Object/Api/Mastodon/Instance.php +++ b/src/Object/Api/Mastodon/Instance.php @@ -75,11 +75,12 @@ class Instance extends BaseDataTransferObject * @param IManageConfigValues $config * @param BaseURL $baseUrl * @param Database $database + * @param array $rules * @throws HTTPException\InternalServerErrorException * @throws HTTPException\NotFoundException * @throws \ImagickException */ - public function __construct(IManageConfigValues $config, BaseURL $baseUrl, Database $database) + public function __construct(IManageConfigValues $config, BaseURL $baseUrl, Database $database, array $rules = []) { $register_policy = intval($config->get('config', 'register_policy')); @@ -97,6 +98,7 @@ class Instance extends BaseDataTransferObject $this->approval_required = ($register_policy == Register::APPROVE); $this->invites_enabled = false; $this->contact_account = []; + $this->rules = $rules; $administrator = User::getFirstAdmin(['nickname']); if ($administrator) { From 16b76919c039e95c7b2e125870ab8f0303e32e8d Mon Sep 17 00:00:00 2001 From: Michael Date: Sat, 3 Dec 2022 21:15:08 +0000 Subject: [PATCH 13/27] Rules added --- src/Core/System.php | 4 +- src/Module/Admin/Tos.php | 5 +- src/Module/Tos.php | 13 +++ view/lang/C/messages.po | 220 ++++++++++++++++++----------------- view/templates/admin/tos.tpl | 1 + view/templates/tos.tpl | 5 + 6 files changed, 141 insertions(+), 107 deletions(-) diff --git a/src/Core/System.php b/src/Core/System.php index 0c08fe168..65f700388 100644 --- a/src/Core/System.php +++ b/src/Core/System.php @@ -664,7 +664,6 @@ class System /** * Fetch the system rules - * @todo We should have got a better way to store and fetch the rules * * @return array */ @@ -674,7 +673,8 @@ class System $id = 0; if (DI::config()->get('system', 'tosdisplay')) { - $html = BBCode::convert(DI::config()->get('system', 'tostext'), false, BBCode::EXTERNAL); + $rulelist = DI::config()->get('system', 'tosrules') ?: DI::config()->get('system', 'tostext'); + $html = BBCode::convert($rulelist, false, BBCode::EXTERNAL); $msg = HTML::toPlaintext($html, 0, true); foreach (explode("\n", $msg) as $line) { diff --git a/src/Module/Admin/Tos.php b/src/Module/Admin/Tos.php index f48f57fbc..888614b67 100644 --- a/src/Module/Admin/Tos.php +++ b/src/Module/Admin/Tos.php @@ -57,11 +57,13 @@ class Tos extends BaseAdmin $displaytos = !empty($_POST['displaytos']); $displayprivstatement = !empty($_POST['displayprivstatement']); - $tostext = (!empty($_POST['tostext']) ? strip_tags(trim($_POST['tostext'])) : ''); + $tostext = (!empty($_POST['tostext']) ? strip_tags(trim($_POST['tostext'])) : ''); + $tosrules = (!empty($_POST['tosrules']) ? strip_tags(trim($_POST['tosrules'])) : ''); $this->config->set('system', 'tosdisplay', $displaytos); $this->config->set('system', 'tosprivstatement', $displayprivstatement); $this->config->set('system', 'tostext', $tostext); + $this->config->set('system', 'tosrules', $tosrules); $this->baseUrl->redirect('admin/tos'); } @@ -79,6 +81,7 @@ class Tos extends BaseAdmin '$preview' => $this->t('Privacy Statement Preview'), '$privtext' => $this->tos->privacy_complete, '$tostext' => ['tostext', $this->t('The Terms of Service'), $this->config->get('system', 'tostext'), $this->t('Enter the Terms of Service for your node here. You can use BBCode. Headers of sections should be [h2] and below.')], + '$tosrules' => ['tosrules', $this->t('The rules'), $this->config->get('system', 'tosrules'), $this->t('Enter your system rules here. Each line represents one rule.')], '$form_security_token' => self::getFormSecurityToken('admin_tos'), '$submit' => $this->t('Save Settings'), ]); diff --git a/src/Module/Tos.php b/src/Module/Tos.php index 31beb6651..822aa8813 100644 --- a/src/Module/Tos.php +++ b/src/Module/Tos.php @@ -83,9 +83,22 @@ class Tos extends BaseModule $tpl = Renderer::getMarkupTemplate('tos.tpl'); if ($this->config->get('system', 'tosdisplay')) { + $lines = $this->config->get('system', 'tosrules'); + if (!empty($lines)) { + $rules = "[list=1]"; + foreach (explode("\n", $lines) as $line) { + $rules .= "\n[*]" . $line; + } + $rules .= "\n[/list]\n"; + } else { + $rules = ''; + } + return Renderer::replaceMacros($tpl, [ '$title' => $this->t('Terms of Service'), '$tostext' => BBCode::convert($this->config->get('system', 'tostext')), + '$rulestitle' => $this->t('Rules'), + '$rules' => BBCode::convert($rules), '$displayprivstatement' => $this->config->get('system', 'tosprivstatement'), '$privstatementtitle' => $this->t('Privacy Statement'), '$privacy_operate' => $this->t('At the time of registration, and for providing communications between the user account and their contacts, the user has to provide a display name (pen name), an username (nickname) and a working email address. The names will be accessible on the profile page of the account by any visitor of the page, even if other profile details are not displayed. The email address will only be used to send the user notifications about interactions, but wont be visibly displayed. The listing of an account in the node\'s user directory or the global user directory is optional and can be controlled in the user settings, it is not necessary for communication.'), diff --git a/view/lang/C/messages.po b/view/lang/C/messages.po index 5728520d5..ffc7680a3 100644 --- a/view/lang/C/messages.po +++ b/view/lang/C/messages.po @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: 2022.12-dev\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2022-12-01 18:01-0500\n" +"POT-Creation-Date: 2022-12-03 21:11+0000\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" @@ -1262,7 +1262,7 @@ msgid "Public post" msgstr "" #: src/Content/Conversation.php:396 src/Content/Widget/VCard.php:113 -#: src/Model/Profile.php:465 src/Module/Admin/Logs/View.php:93 +#: src/Model/Profile.php:469 src/Module/Admin/Logs/View.php:93 #: src/Module/Post/Edit.php:177 msgid "Message" msgstr "" @@ -1516,7 +1516,7 @@ msgstr "" msgid "show more" msgstr "" -#: src/Content/Item.php:294 src/Model/Item.php:2919 +#: src/Content/Item.php:294 src/Model/Item.php:2914 msgid "event" msgstr "" @@ -1525,7 +1525,7 @@ msgstr "" msgid "status" msgstr "" -#: src/Content/Item.php:303 src/Model/Item.php:2921 +#: src/Content/Item.php:303 src/Model/Item.php:2916 #: src/Module/Post/Tag/Add.php:123 msgid "photo" msgstr "" @@ -1539,31 +1539,31 @@ msgstr "" msgid "Follow Thread" msgstr "" -#: src/Content/Item.php:387 src/Model/Contact.php:1197 +#: src/Content/Item.php:387 src/Model/Contact.php:1209 msgid "View Status" msgstr "" -#: src/Content/Item.php:388 src/Content/Item.php:406 src/Model/Contact.php:1135 -#: src/Model/Contact.php:1189 src/Model/Contact.php:1198 +#: src/Content/Item.php:388 src/Content/Item.php:406 src/Model/Contact.php:1147 +#: src/Model/Contact.php:1201 src/Model/Contact.php:1210 #: src/Module/Directory.php:157 src/Module/Settings/Profile/Index.php:234 msgid "View Profile" msgstr "" -#: src/Content/Item.php:389 src/Model/Contact.php:1199 +#: src/Content/Item.php:389 src/Model/Contact.php:1211 msgid "View Photos" msgstr "" -#: src/Content/Item.php:390 src/Model/Contact.php:1190 -#: src/Model/Contact.php:1200 +#: src/Content/Item.php:390 src/Model/Contact.php:1202 +#: src/Model/Contact.php:1212 msgid "Network Posts" msgstr "" -#: src/Content/Item.php:391 src/Model/Contact.php:1191 -#: src/Model/Contact.php:1201 +#: src/Content/Item.php:391 src/Model/Contact.php:1203 +#: src/Model/Contact.php:1213 msgid "View Contact" msgstr "" -#: src/Content/Item.php:392 src/Model/Contact.php:1202 +#: src/Content/Item.php:392 src/Model/Contact.php:1214 msgid "Send PM" msgstr "" @@ -1588,7 +1588,7 @@ msgid "Languages" msgstr "" #: src/Content/Item.php:403 src/Content/Widget.php:80 -#: src/Model/Contact.php:1192 src/Model/Contact.php:1203 +#: src/Model/Contact.php:1204 src/Model/Contact.php:1215 #: src/Module/Contact/Follow.php:167 view/theme/vier/theme.php:196 msgid "Connect/Follow" msgstr "" @@ -1774,9 +1774,9 @@ msgstr "" msgid "Information about this friendica instance" msgstr "" -#: src/Content/Nav.php:265 src/Module/Admin/Tos.php:76 +#: src/Content/Nav.php:265 src/Module/Admin/Tos.php:78 #: src/Module/BaseAdmin.php:95 src/Module/Register.php:176 -#: src/Module/Tos.php:87 +#: src/Module/Tos.php:98 msgid "Terms of Service" msgstr "" @@ -1920,8 +1920,8 @@ msgid "" "%2$s %3$s" msgstr "" -#: src/Content/Text/BBCode.php:1245 src/Model/Item.php:3541 -#: src/Model/Item.php:3547 src/Model/Item.php:3548 +#: src/Content/Text/BBCode.php:1245 src/Model/Item.php:3536 +#: src/Model/Item.php:3542 src/Model/Item.php:3543 msgid "Link to source" msgstr "" @@ -1954,7 +1954,7 @@ msgid "The end" msgstr "" #: src/Content/Text/HTML.php:882 src/Content/Widget/VCard.php:109 -#: src/Model/Profile.php:459 src/Module/Contact/Profile.php:427 +#: src/Model/Profile.php:463 src/Module/Contact/Profile.php:427 msgid "Follow" msgstr "" @@ -2081,7 +2081,7 @@ msgstr "" msgid "Organisations" msgstr "" -#: src/Content/Widget.php:523 src/Model/Contact.php:1629 +#: src/Content/Widget.php:523 src/Model/Contact.php:1641 msgid "News" msgstr "" @@ -2150,7 +2150,7 @@ msgid "Matrix:" msgstr "" #: src/Content/Widget/VCard.php:104 src/Model/Event.php:82 -#: src/Model/Event.php:109 src/Model/Event.php:471 src/Model/Event.php:961 +#: src/Model/Event.php:109 src/Model/Event.php:471 src/Model/Event.php:958 #: src/Model/Profile.php:373 src/Module/Calendar/Event/Form.php:239 #: src/Module/Contact/Profile.php:369 src/Module/Directory.php:147 #: src/Module/Notifications/Introductions.php:187 @@ -2158,13 +2158,13 @@ msgstr "" msgid "Location:" msgstr "" -#: src/Content/Widget/VCard.php:107 src/Model/Profile.php:472 +#: src/Content/Widget/VCard.php:107 src/Model/Profile.php:476 #: src/Module/Notifications/Introductions.php:201 msgid "Network:" msgstr "" -#: src/Content/Widget/VCard.php:111 src/Model/Contact.php:1193 -#: src/Model/Contact.php:1204 src/Model/Profile.php:461 +#: src/Content/Widget/VCard.php:111 src/Model/Contact.php:1205 +#: src/Model/Contact.php:1216 src/Model/Profile.php:465 #: src/Module/Contact/Profile.php:419 msgid "Unfollow" msgstr "" @@ -2861,77 +2861,77 @@ msgstr "" msgid "Legacy module file not found: %s" msgstr "" -#: src/Model/Contact.php:1210 src/Module/Moderation/Users/Pending.php:102 +#: src/Model/Contact.php:1222 src/Module/Moderation/Users/Pending.php:102 #: src/Module/Notifications/Introductions.php:132 #: src/Module/Notifications/Introductions.php:204 msgid "Approve" msgstr "" -#: src/Model/Contact.php:1625 +#: src/Model/Contact.php:1637 msgid "Organisation" msgstr "" -#: src/Model/Contact.php:1633 +#: src/Model/Contact.php:1645 msgid "Forum" msgstr "" -#: src/Model/Contact.php:2819 +#: src/Model/Contact.php:2831 msgid "Disallowed profile URL." msgstr "" -#: src/Model/Contact.php:2824 src/Module/Friendica.php:82 +#: src/Model/Contact.php:2836 src/Module/Friendica.php:82 msgid "Blocked domain" msgstr "" -#: src/Model/Contact.php:2829 +#: src/Model/Contact.php:2841 msgid "Connect URL missing." msgstr "" -#: src/Model/Contact.php:2838 +#: src/Model/Contact.php:2850 msgid "" "The contact could not be added. Please check the relevant network " "credentials in your Settings -> Social Networks page." msgstr "" -#: src/Model/Contact.php:2880 +#: src/Model/Contact.php:2892 msgid "The profile address specified does not provide adequate information." msgstr "" -#: src/Model/Contact.php:2882 +#: src/Model/Contact.php:2894 msgid "No compatible communication protocols or feeds were discovered." msgstr "" -#: src/Model/Contact.php:2885 +#: src/Model/Contact.php:2897 msgid "An author or name was not found." msgstr "" -#: src/Model/Contact.php:2888 +#: src/Model/Contact.php:2900 msgid "No browser URL could be matched to this address." msgstr "" -#: src/Model/Contact.php:2891 +#: src/Model/Contact.php:2903 msgid "" "Unable to match @-style Identity Address with a known protocol or email " "contact." msgstr "" -#: src/Model/Contact.php:2892 +#: src/Model/Contact.php:2904 msgid "Use mailto: in front of address to force email check." msgstr "" -#: src/Model/Contact.php:2898 +#: src/Model/Contact.php:2910 msgid "" "The profile address specified belongs to a network which has been disabled " "on this site." msgstr "" -#: src/Model/Contact.php:2903 +#: src/Model/Contact.php:2915 msgid "" "Limited profile. This person will be unable to receive direct/personal " "notifications from you." msgstr "" -#: src/Model/Contact.php:2962 +#: src/Model/Contact.php:2974 msgid "Unable to retrieve contact information." msgstr "" @@ -2940,12 +2940,12 @@ msgid "l F d, Y \\@ g:i A \\G\\M\\TP (e)" msgstr "" #: src/Model/Event.php:75 src/Model/Event.php:92 src/Model/Event.php:469 -#: src/Model/Event.php:943 +#: src/Model/Event.php:940 msgid "Starts:" msgstr "" #: src/Model/Event.php:78 src/Model/Event.php:98 src/Model/Event.php:470 -#: src/Model/Event.php:947 +#: src/Model/Event.php:944 msgid "Finishes:" msgstr "" @@ -3005,32 +3005,32 @@ msgstr "" msgid "Delete event" msgstr "" -#: src/Model/Event.php:899 src/Module/Debug/Localtime.php:38 +#: src/Model/Event.php:896 src/Module/Debug/Localtime.php:38 msgid "l F d, Y \\@ g:i A" msgstr "" -#: src/Model/Event.php:900 +#: src/Model/Event.php:897 msgid "D g:i A" msgstr "" -#: src/Model/Event.php:901 +#: src/Model/Event.php:898 msgid "g:i A" msgstr "" -#: src/Model/Event.php:962 src/Model/Event.php:964 +#: src/Model/Event.php:959 src/Model/Event.php:961 msgid "Show map" msgstr "" -#: src/Model/Event.php:963 +#: src/Model/Event.php:960 msgid "Hide map" msgstr "" -#: src/Model/Event.php:1056 +#: src/Model/Event.php:1053 #, php-format msgid "%s's birthday" msgstr "" -#: src/Model/Event.php:1057 +#: src/Model/Event.php:1054 #, php-format msgid "Happy Birthday %s" msgstr "" @@ -3084,61 +3084,61 @@ msgstr "" msgid "Detected languages in this post:\\n%s" msgstr "" -#: src/Model/Item.php:2923 +#: src/Model/Item.php:2918 msgid "activity" msgstr "" -#: src/Model/Item.php:2925 +#: src/Model/Item.php:2920 msgid "comment" msgstr "" -#: src/Model/Item.php:2928 +#: src/Model/Item.php:2923 msgid "post" msgstr "" -#: src/Model/Item.php:3069 +#: src/Model/Item.php:3064 #, php-format msgid "Content warning: %s" msgstr "" -#: src/Model/Item.php:3453 +#: src/Model/Item.php:3448 msgid "bytes" msgstr "" -#: src/Model/Item.php:3484 +#: src/Model/Item.php:3479 #, php-format msgid "%2$s (%3$d%%, %1$d vote)" msgid_plural "%2$s (%3$d%%, %1$d votes)" msgstr[0] "" msgstr[1] "" -#: src/Model/Item.php:3486 +#: src/Model/Item.php:3481 #, php-format msgid "%2$s (%1$d vote)" msgid_plural "%2$s (%1$d votes)" msgstr[0] "" msgstr[1] "" -#: src/Model/Item.php:3491 +#: src/Model/Item.php:3486 #, php-format msgid "%d voter. Poll end: %s" msgid_plural "%d voters. Poll end: %s" msgstr[0] "" msgstr[1] "" -#: src/Model/Item.php:3493 +#: src/Model/Item.php:3488 #, php-format msgid "%d voter." msgid_plural "%d voters." msgstr[0] "" msgstr[1] "" -#: src/Model/Item.php:3495 +#: src/Model/Item.php:3490 #, php-format msgid "Poll end: %s" msgstr "" -#: src/Model/Item.php:3529 src/Model/Item.php:3530 +#: src/Model/Item.php:3524 src/Model/Item.php:3525 msgid "View on separate page" msgstr "" @@ -3169,129 +3169,129 @@ msgstr "" msgid "About:" msgstr "" -#: src/Model/Profile.php:463 +#: src/Model/Profile.php:467 msgid "Atom feed" msgstr "" -#: src/Model/Profile.php:470 +#: src/Model/Profile.php:474 msgid "This website has been verified to belong to the same person." msgstr "" -#: src/Model/Profile.php:507 +#: src/Model/Profile.php:511 msgid "F d" msgstr "" -#: src/Model/Profile.php:571 src/Model/Profile.php:660 +#: src/Model/Profile.php:575 src/Model/Profile.php:664 msgid "[today]" msgstr "" -#: src/Model/Profile.php:580 +#: src/Model/Profile.php:584 msgid "Birthday Reminders" msgstr "" -#: src/Model/Profile.php:581 +#: src/Model/Profile.php:585 msgid "Birthdays this week:" msgstr "" -#: src/Model/Profile.php:609 +#: src/Model/Profile.php:613 msgid "g A l F d" msgstr "" -#: src/Model/Profile.php:647 +#: src/Model/Profile.php:651 msgid "[No description]" msgstr "" -#: src/Model/Profile.php:673 +#: src/Model/Profile.php:677 msgid "Event Reminders" msgstr "" -#: src/Model/Profile.php:674 +#: src/Model/Profile.php:678 msgid "Upcoming events the next 7 days:" msgstr "" -#: src/Model/Profile.php:869 +#: src/Model/Profile.php:873 #, php-format msgid "OpenWebAuth: %1$s welcomes %2$s" msgstr "" -#: src/Model/Profile.php:1009 +#: src/Model/Profile.php:1013 msgid "Hometown:" msgstr "" -#: src/Model/Profile.php:1010 +#: src/Model/Profile.php:1014 msgid "Marital Status:" msgstr "" -#: src/Model/Profile.php:1011 +#: src/Model/Profile.php:1015 msgid "With:" msgstr "" -#: src/Model/Profile.php:1012 +#: src/Model/Profile.php:1016 msgid "Since:" msgstr "" -#: src/Model/Profile.php:1013 +#: src/Model/Profile.php:1017 msgid "Sexual Preference:" msgstr "" -#: src/Model/Profile.php:1014 +#: src/Model/Profile.php:1018 msgid "Political Views:" msgstr "" -#: src/Model/Profile.php:1015 +#: src/Model/Profile.php:1019 msgid "Religious Views:" msgstr "" -#: src/Model/Profile.php:1016 +#: src/Model/Profile.php:1020 msgid "Likes:" msgstr "" -#: src/Model/Profile.php:1017 +#: src/Model/Profile.php:1021 msgid "Dislikes:" msgstr "" -#: src/Model/Profile.php:1018 +#: src/Model/Profile.php:1022 msgid "Title/Description:" msgstr "" -#: src/Model/Profile.php:1019 src/Module/Admin/Summary.php:217 +#: src/Model/Profile.php:1023 src/Module/Admin/Summary.php:217 #: src/Module/Moderation/Summary.php:77 msgid "Summary" msgstr "" -#: src/Model/Profile.php:1020 +#: src/Model/Profile.php:1024 msgid "Musical interests" msgstr "" -#: src/Model/Profile.php:1021 +#: src/Model/Profile.php:1025 msgid "Books, literature" msgstr "" -#: src/Model/Profile.php:1022 +#: src/Model/Profile.php:1026 msgid "Television" msgstr "" -#: src/Model/Profile.php:1023 +#: src/Model/Profile.php:1027 msgid "Film/dance/culture/entertainment" msgstr "" -#: src/Model/Profile.php:1024 +#: src/Model/Profile.php:1028 msgid "Hobbies/Interests" msgstr "" -#: src/Model/Profile.php:1025 +#: src/Model/Profile.php:1029 msgid "Love/romance" msgstr "" -#: src/Model/Profile.php:1026 +#: src/Model/Profile.php:1030 msgid "Work/employment" msgstr "" -#: src/Model/Profile.php:1027 +#: src/Model/Profile.php:1031 msgid "School/education" msgstr "" -#: src/Model/Profile.php:1028 +#: src/Model/Profile.php:1032 msgid "Contact information and Social Networks" msgstr "" @@ -3583,7 +3583,7 @@ msgstr "" #: src/Module/Admin/Logs/View.php:84 src/Module/Admin/Queue.php:72 #: src/Module/Admin/Site.php:437 src/Module/Admin/Storage.php:138 #: src/Module/Admin/Summary.php:216 src/Module/Admin/Themes/Details.php:90 -#: src/Module/Admin/Themes/Index.php:111 src/Module/Admin/Tos.php:75 +#: src/Module/Admin/Themes/Index.php:111 src/Module/Admin/Tos.php:77 #: src/Module/Moderation/Users/Create.php:61 #: src/Module/Moderation/Users/Pending.php:96 msgid "Administration" @@ -3620,7 +3620,7 @@ msgstr "" #: src/Module/Admin/Addons/Index.php:69 src/Module/Admin/Features.php:87 #: src/Module/Admin/Logs/Settings.php:81 src/Module/Admin/Site.php:440 -#: src/Module/Admin/Themes/Index.php:113 src/Module/Admin/Tos.php:83 +#: src/Module/Admin/Themes/Index.php:113 src/Module/Admin/Tos.php:86 #: src/Module/Settings/Account.php:560 src/Module/Settings/Addons.php:81 #: src/Module/Settings/Connectors.php:159 #: src/Module/Settings/Connectors.php:244 @@ -5154,21 +5154,21 @@ msgstr "" msgid "[Unsupported]" msgstr "" -#: src/Module/Admin/Tos.php:77 +#: src/Module/Admin/Tos.php:79 msgid "Display Terms of Service" msgstr "" -#: src/Module/Admin/Tos.php:77 +#: src/Module/Admin/Tos.php:79 msgid "" "Enable the Terms of Service page. If this is enabled a link to the terms " "will be added to the registration form and the general information page." msgstr "" -#: src/Module/Admin/Tos.php:78 +#: src/Module/Admin/Tos.php:80 msgid "Display Privacy Statement" msgstr "" -#: src/Module/Admin/Tos.php:78 +#: src/Module/Admin/Tos.php:80 #, php-format msgid "" "Show some informations regarding the needed information to operate the node " @@ -5176,20 +5176,28 @@ msgid "" "\">EU-GDPR." msgstr "" -#: src/Module/Admin/Tos.php:79 +#: src/Module/Admin/Tos.php:81 msgid "Privacy Statement Preview" msgstr "" -#: src/Module/Admin/Tos.php:81 +#: src/Module/Admin/Tos.php:83 msgid "The Terms of Service" msgstr "" -#: src/Module/Admin/Tos.php:81 +#: src/Module/Admin/Tos.php:83 msgid "" "Enter the Terms of Service for your node here. You can use BBCode. Headers " "of sections should be [h2] and below." msgstr "" +#: src/Module/Admin/Tos.php:84 +msgid "The rules" +msgstr "" + +#: src/Module/Admin/Tos.php:84 +msgid "Enter your system rules here. Each line represents one rule." +msgstr "" + #: src/Module/Api/ApiResponse.php:279 #, php-format msgid "API endpoint %s %s is not implemented" @@ -10238,7 +10246,7 @@ msgstr "" msgid "Exception thrown in %s:%d" msgstr "" -#: src/Module/Tos.php:57 src/Module/Tos.php:91 +#: src/Module/Tos.php:57 src/Module/Tos.php:104 msgid "" "At the time of registration, and for providing communications between the " "user account and their contacts, the user has to provide a display name (pen " @@ -10251,14 +10259,14 @@ msgid "" "settings, it is not necessary for communication." msgstr "" -#: src/Module/Tos.php:58 src/Module/Tos.php:92 +#: src/Module/Tos.php:58 src/Module/Tos.php:105 msgid "" "This data is required for communication and is passed on to the nodes of the " "communication partners and is stored there. Users can enter additional " "private data that may be transmitted to the communication partners accounts." msgstr "" -#: src/Module/Tos.php:59 src/Module/Tos.php:93 +#: src/Module/Tos.php:59 src/Module/Tos.php:106 #, php-format msgid "" "At any point in time a logged in user can export their account data from the " @@ -10269,10 +10277,14 @@ msgid "" "communication partners." msgstr "" -#: src/Module/Tos.php:62 src/Module/Tos.php:90 +#: src/Module/Tos.php:62 src/Module/Tos.php:103 msgid "Privacy Statement" msgstr "" +#: src/Module/Tos.php:100 +msgid "Rules" +msgstr "" + #: src/Module/Update/Display.php:45 msgid "Parameter uri_id is missing." msgstr "" diff --git a/view/templates/admin/tos.tpl b/view/templates/admin/tos.tpl index d4e1bcb48..2482802a1 100644 --- a/view/templates/admin/tos.tpl +++ b/view/templates/admin/tos.tpl @@ -6,6 +6,7 @@ {{include file="field_checkbox.tpl" field=$displaytos}} {{include file="field_checkbox.tpl" field=$displayprivstatement}} {{include file="field_textarea.tpl" field=$tostext}} + {{include file="field_textarea.tpl" field=$tosrules}}

{{$preview}}

diff --git a/view/templates/tos.tpl b/view/templates/tos.tpl index b0a48406c..62210b278 100644 --- a/view/templates/tos.tpl +++ b/view/templates/tos.tpl @@ -2,6 +2,11 @@ {{$tostext nofilter}} +{{if $rules}} +

{{$rulestitle}}

+ {{$rules nofilter}} +{{/if}} + {{if $displayprivstatement}}

{{$privstatementtitle nofilter}}

{{$privacy_operate nofilter}}

From 1d8c9276f3aeb3c4cc58c29f3b2defac63223ea9 Mon Sep 17 00:00:00 2001 From: Michael Date: Sun, 4 Dec 2022 07:03:11 +0000 Subject: [PATCH 14/27] Issue 10518: "last-activity" instead of "login_date" --- database.sql | 3 ++- src/Console/User.php | 4 ++-- src/Model/User.php | 12 +++++++----- src/Module/Moderation/BaseUsers.php | 2 +- src/Module/Moderation/Users/Active.php | 2 +- src/Module/Moderation/Users/Blocked.php | 2 +- src/Module/Moderation/Users/Deleted.php | 2 +- src/Module/Moderation/Users/Index.php | 2 +- src/Module/NoScrape.php | 7 +++---- src/Security/Authentication.php | 2 +- src/Worker/PollContacts.php | 2 +- static/dbstructure.config.php | 2 +- static/dbview.config.php | 1 + 13 files changed, 23 insertions(+), 20 deletions(-) diff --git a/database.sql b/database.sql index f41defe42..830eff991 100644 --- a/database.sql +++ b/database.sql @@ -1,6 +1,6 @@ -- ------------------------------------------ -- Friendica 2022.12-dev (Giant Rhubarb) --- DB_UPDATE_VERSION 1496 +-- DB_UPDATE_VERSION 1497 -- ------------------------------------------ @@ -2693,6 +2693,7 @@ CREATE VIEW `owner-view` AS SELECT `user`.`language` AS `language`, `user`.`register_date` AS `register_date`, `user`.`login_date` AS `login_date`, + IF (`user`.`last-activity` IS NULL, DATE(`user`.`login_date`), `user`.`last-activity`) AS `last-activity`, `user`.`default-location` AS `default-location`, `user`.`allow_location` AS `allow_location`, `user`.`theme` AS `theme`, diff --git a/src/Console/User.php b/src/Console/User.php index 25f5ceeb0..0d06f427e 100644 --- a/src/Console/User.php +++ b/src/Console/User.php @@ -370,7 +370,7 @@ HELP; $contact['url'], $contact['email'], Temporal::getRelativeDate($contact['created']), - Temporal::getRelativeDate($contact['login_date']), + Temporal::getRelativeDate($contact['last-activity']), Temporal::getRelativeDate($contact['last-item']), ]); } @@ -396,7 +396,7 @@ HELP; 'nickname', 'email', 'register_date', - 'login_date', + 'last-activity', 'verified', 'blocked', ]; diff --git a/src/Model/User.php b/src/Model/User.php index 40384b619..2b8516c25 100644 --- a/src/Model/User.php +++ b/src/Model/User.php @@ -682,6 +682,8 @@ class User if ($user['last-activity'] != $current_day) { User::update(['last-activity' => $current_day], $uid); + // Set the last actitivy for all identities of the user + DBA::update('user', ['last-activity' => $current_day], ['parent-uid' => $uid, 'account_removed' => false]); } } @@ -1729,8 +1731,8 @@ class User 'active_users_weekly' => 0, ]; - $userStmt = DBA::select('owner-view', ['uid', 'login_date', 'last-item'], - ["`verified` AND `login_date` > ? AND NOT `blocked` + $userStmt = DBA::select('owner-view', ['uid', 'last-activity', 'last-item'], + ["`verified` AND `last-activity` > ? AND NOT `blocked` AND NOT `account_removed` AND NOT `account_expired`", DBA::NULL_DATETIME]); if (!DBA::isResult($userStmt)) { @@ -1744,17 +1746,17 @@ class User while ($user = DBA::fetch($userStmt)) { $statistics['total_users']++; - if ((strtotime($user['login_date']) > $halfyear) || (strtotime($user['last-item']) > $halfyear) + if ((strtotime($user['last-activity']) > $halfyear) || (strtotime($user['last-item']) > $halfyear) ) { $statistics['active_users_halfyear']++; } - if ((strtotime($user['login_date']) > $month) || (strtotime($user['last-item']) > $month) + if ((strtotime($user['last-activity']) > $month) || (strtotime($user['last-item']) > $month) ) { $statistics['active_users_monthly']++; } - if ((strtotime($user['login_date']) > $week) || (strtotime($user['last-item']) > $week) + if ((strtotime($user['last-activity']) > $week) || (strtotime($user['last-item']) > $week) ) { $statistics['active_users_weekly']++; } diff --git a/src/Module/Moderation/BaseUsers.php b/src/Module/Moderation/BaseUsers.php index b78027764..62e41c58c 100644 --- a/src/Module/Moderation/BaseUsers.php +++ b/src/Module/Moderation/BaseUsers.php @@ -137,7 +137,7 @@ abstract class BaseUsers extends BaseModeration $user['account_type'] = ($user['page_flags_raw'] == 0) ? $account_types[$user['account-type']] : ''; $user['register_date'] = Temporal::getRelativeDate($user['register_date']); - $user['login_date'] = Temporal::getRelativeDate($user['login_date']); + $user['login_date'] = Temporal::getRelativeDate($user['last-activity']); $user['lastitem_date'] = Temporal::getRelativeDate($user['last-item']); $user['is_admin'] = in_array($user['email'], $adminlist); $user['is_deletable'] = !$user['account_removed'] && intval($user['uid']) != $this->session->getLocalUserId(); diff --git a/src/Module/Moderation/Users/Active.php b/src/Module/Moderation/Users/Active.php index 922351dc7..8463f4137 100644 --- a/src/Module/Moderation/Users/Active.php +++ b/src/Module/Moderation/Users/Active.php @@ -100,7 +100,7 @@ class Active extends BaseUsers 'name', 'email', 'register_date', - 'login_date', + 'last-activity', 'last-item', 'page-flags', ]; diff --git a/src/Module/Moderation/Users/Blocked.php b/src/Module/Moderation/Users/Blocked.php index 022262907..e6bb59973 100644 --- a/src/Module/Moderation/Users/Blocked.php +++ b/src/Module/Moderation/Users/Blocked.php @@ -100,7 +100,7 @@ class Blocked extends BaseUsers 'name', 'email', 'register_date', - 'login_date', + 'last-activity', 'last-item', 'page-flags', ]; diff --git a/src/Module/Moderation/Users/Deleted.php b/src/Module/Moderation/Users/Deleted.php index 2dd6d6a1b..8778c0a77 100644 --- a/src/Module/Moderation/Users/Deleted.php +++ b/src/Module/Moderation/Users/Deleted.php @@ -49,7 +49,7 @@ class Deleted extends BaseUsers 'name', 'email', 'register_date', - 'login_date', + 'last-activity', 'last-item', 'page-flags', ]; diff --git a/src/Module/Moderation/Users/Index.php b/src/Module/Moderation/Users/Index.php index 06528650c..e5cc1ae89 100644 --- a/src/Module/Moderation/Users/Index.php +++ b/src/Module/Moderation/Users/Index.php @@ -114,7 +114,7 @@ class Index extends BaseUsers 'name', 'email', 'register_date', - 'login_date', + 'last-activity', 'last-item', 'page-flags', ]; diff --git a/src/Module/NoScrape.php b/src/Module/NoScrape.php index 84130bd91..bf57ec776 100644 --- a/src/Module/NoScrape.php +++ b/src/Module/NoScrape.php @@ -101,11 +101,10 @@ class NoScrape extends BaseModule $last_active = strtotime($contact['last-item']); } - $condition = ['uid' => $owner['uid']]; - $user = DBA::selectFirst('user', ['login_date'], $condition); + $user = User::getOwnerDataById($owner['uid']); if (DBA::isResult($user)) { - if ($last_active < strtotime($user['login_date'])) { - $last_active = strtotime($user['login_date']); + if ($last_active < strtotime($user['last-activity'])) { + $last_active = strtotime($user['last-activity']); } } $json_info['last-activity'] = date('o-W', $last_active); diff --git a/src/Security/Authentication.php b/src/Security/Authentication.php index f550501ed..5dcc39940 100644 --- a/src/Security/Authentication.php +++ b/src/Security/Authentication.php @@ -356,7 +356,7 @@ class Authentication $this->dba->update('user', ['last-activity' => DateTimeFormat::utcNow('Y-m-d'), 'login_date' => DateTimeFormat::utcNow()], ['uid' => $user_record['uid']]); // Set the login date for all identities of the user - $this->dba->update('user', ['login_date' => DateTimeFormat::utcNow()], + $this->dba->update('user', ['last-activity' => DateTimeFormat::utcNow('Y-m-d'), 'login_date' => DateTimeFormat::utcNow()], ['parent-uid' => $user_record['uid'], 'account_removed' => false]); // Regularly update suggestions diff --git a/src/Worker/PollContacts.php b/src/Worker/PollContacts.php index 454b5a9ae..9aea20e5d 100644 --- a/src/Worker/PollContacts.php +++ b/src/Worker/PollContacts.php @@ -45,7 +45,7 @@ class PollContacts if (!empty($abandon_days)) { $condition = DBA::mergeConditions($condition, - ["`uid` != ? AND `uid` IN (SELECT `uid` FROM `user` WHERE NOT `account_expired` AND NOT `account_removed` AND `login_date` > ?)", 0, DateTimeFormat::utc('now - ' . $abandon_days . ' days')]); + ["`uid` != ? AND `uid` IN (SELECT `uid` FROM `owner-view` WHERE NOT `account_expired` AND NOT `account_removed` AND `last-activity` > ?)", 0, DateTimeFormat::utc('now - ' . $abandon_days . ' days')]); } else { $condition = DBA::mergeConditions($condition, ["`uid` != ? AND `uid` IN (SELECT `uid` FROM `user` WHERE NOT `account_expired` AND NOT `account_removed`)", 0]); diff --git a/static/dbstructure.config.php b/static/dbstructure.config.php index 2979ca80d..a7bf84a46 100644 --- a/static/dbstructure.config.php +++ b/static/dbstructure.config.php @@ -55,7 +55,7 @@ use Friendica\Database\DBA; if (!defined('DB_UPDATE_VERSION')) { - define('DB_UPDATE_VERSION', 1496); + define('DB_UPDATE_VERSION', 1497); } return [ diff --git a/static/dbview.config.php b/static/dbview.config.php index a2e3e1b45..09566a7fd 100644 --- a/static/dbview.config.php +++ b/static/dbview.config.php @@ -889,6 +889,7 @@ "language" => ["user", "language"], "register_date" => ["user", "register_date"], "login_date" => ["user", "login_date"], + "last-activity" => "IF (`user`.`last-activity` IS NULL, DATE(`user`.`login_date`), `user`.`last-activity`)", "default-location" => ["user", "default-location"], "allow_location" => ["user", "allow_location"], "theme" => ["user", "theme"], From e5a531d3b5e7ca261a9265f7a0ab48deae8ac4fc Mon Sep 17 00:00:00 2001 From: Michael Date: Sun, 4 Dec 2022 07:17:26 +0000 Subject: [PATCH 15/27] Simplify "last-activity" handling --- database.sql | 2 +- src/Module/NoScrape.php | 15 +++------------ src/Worker/PollContacts.php | 2 +- static/dbview.config.php | 2 +- update.php | 6 ++++++ 5 files changed, 12 insertions(+), 15 deletions(-) diff --git a/database.sql b/database.sql index 830eff991..bbe1e6557 100644 --- a/database.sql +++ b/database.sql @@ -2693,7 +2693,7 @@ CREATE VIEW `owner-view` AS SELECT `user`.`language` AS `language`, `user`.`register_date` AS `register_date`, `user`.`login_date` AS `login_date`, - IF (`user`.`last-activity` IS NULL, DATE(`user`.`login_date`), `user`.`last-activity`) AS `last-activity`, + `user`.`last-activity` AS `last-activity`, `user`.`default-location` AS `default-location`, `user`.`allow_location` AS `allow_location`, `user`.`theme` AS `theme`, diff --git a/src/Module/NoScrape.php b/src/Module/NoScrape.php index bf57ec776..718c95c10 100644 --- a/src/Module/NoScrape.php +++ b/src/Module/NoScrape.php @@ -94,18 +94,9 @@ class NoScrape extends BaseModule } // We display the last activity (post or login), reduced to year and week number - $last_active = 0; - $condition = ['uid' => $owner['uid'], 'self' => true]; - $contact = DBA::selectFirst('contact', ['last-item'], $condition); - if (DBA::isResult($contact)) { - $last_active = strtotime($contact['last-item']); - } - - $user = User::getOwnerDataById($owner['uid']); - if (DBA::isResult($user)) { - if ($last_active < strtotime($user['last-activity'])) { - $last_active = strtotime($user['last-activity']); - } + $last_active = strtotime($owner['last-item']); + if ($last_active < strtotime($owner['last-activity'])) { + $last_active = strtotime($owner['last-activity']); } $json_info['last-activity'] = date('o-W', $last_active); diff --git a/src/Worker/PollContacts.php b/src/Worker/PollContacts.php index 9aea20e5d..eb092fe2c 100644 --- a/src/Worker/PollContacts.php +++ b/src/Worker/PollContacts.php @@ -45,7 +45,7 @@ class PollContacts if (!empty($abandon_days)) { $condition = DBA::mergeConditions($condition, - ["`uid` != ? AND `uid` IN (SELECT `uid` FROM `owner-view` WHERE NOT `account_expired` AND NOT `account_removed` AND `last-activity` > ?)", 0, DateTimeFormat::utc('now - ' . $abandon_days . ' days')]); + ["`uid` != ? AND `uid` IN (SELECT `uid` FROM `user` WHERE NOT `account_expired` AND NOT `account_removed` AND `last-activity` > ?)", 0, DateTimeFormat::utc('now - ' . $abandon_days . ' days')]); } else { $condition = DBA::mergeConditions($condition, ["`uid` != ? AND `uid` IN (SELECT `uid` FROM `user` WHERE NOT `account_expired` AND NOT `account_removed`)", 0]); diff --git a/static/dbview.config.php b/static/dbview.config.php index 09566a7fd..66f3de3b6 100644 --- a/static/dbview.config.php +++ b/static/dbview.config.php @@ -889,7 +889,7 @@ "language" => ["user", "language"], "register_date" => ["user", "register_date"], "login_date" => ["user", "login_date"], - "last-activity" => "IF (`user`.`last-activity` IS NULL, DATE(`user`.`login_date`), `user`.`last-activity`)", + "last-activity" => ["user", "last-activity"], "default-location" => ["user", "default-location"], "allow_location" => ["user", "allow_location"], "theme" => ["user", "theme"], diff --git a/update.php b/update.php index 2ee753ead..176747057 100644 --- a/update.php +++ b/update.php @@ -1126,4 +1126,10 @@ function update_1491() { DBA::update('contact', ['remote_self' => Contact::MIRROR_OWN_POST], ['remote_self' => Contact::MIRROR_FORWARDED]); return Update::SUCCESS; +} + +function update_1497() +{ + DBA::e("UPDATE `user` SET `last-activity` = DATE(`login_date`) WHERE `last-activity` IS NULL"); + return Update::SUCCESS; } \ No newline at end of file From 1b71b963d7ba2a5a61c0a4221cbed769f71e837c Mon Sep 17 00:00:00 2001 From: Hypolite Petovan Date: Fri, 2 Dec 2022 19:36:57 -0500 Subject: [PATCH 16/27] Fix description not being populated in event form when there's a validation error --- src/Module/Calendar/Event/API.php | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/Module/Calendar/Event/API.php b/src/Module/Calendar/Event/API.php index 174c0b3af..f8ddaf40b 100644 --- a/src/Module/Calendar/Event/API.php +++ b/src/Module/Calendar/Event/API.php @@ -170,12 +170,12 @@ class API extends BaseModule $type = 'event'; $params = [ - 'summary' => $summary, - 'description' => $desc, - 'location' => $location, - 'start' => $strStartDateTime, - 'finish' => $strFinishDateTime, - 'nofinish' => $noFinish, + 'summary' => $summary, + 'desc' => $desc, + 'location' => $location, + 'start' => $strStartDateTime, + 'finish' => $strFinishDateTime, + 'nofinish' => $noFinish, ]; $action = empty($eventId) ? 'new' : 'edit/' . $eventId; From 349436a77a3ff41b54c1828b90fcfad1a89aaab7 Mon Sep 17 00:00:00 2001 From: Hypolite Petovan Date: Fri, 2 Dec 2022 19:37:39 -0500 Subject: [PATCH 17/27] Fix event start time not being properly converted to UTC - This was triggering unexpected time comparison errors --- src/Module/Calendar/Event/API.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Module/Calendar/Event/API.php b/src/Module/Calendar/Event/API.php index f8ddaf40b..ab9a5b577 100644 --- a/src/Module/Calendar/Event/API.php +++ b/src/Module/Calendar/Event/API.php @@ -152,7 +152,7 @@ class API extends BaseModule $share = intval($request['share'] ?? 0); $isPreview = intval($request['preview'] ?? 0); - $start = DateTimeFormat::convert($strStartDateTime ?? DBA::NULL_DATETIME, $this->timezone); + $start = DateTimeFormat::convert($strStartDateTime ?? DBA::NULL_DATETIME, 'UTC', $this->timezone); if (!$noFinish) { $finish = DateTimeFormat::convert($strFinishDateTime ?? DBA::NULL_DATETIME, 'UTC', $this->timezone); } else { From 2f42606c43fb24a1b61a24df19e713fae7bfa7be Mon Sep 17 00:00:00 2001 From: Hypolite Petovan Date: Fri, 2 Dec 2022 19:40:39 -0500 Subject: [PATCH 18/27] Add information about BBCode availability in event fields --- src/Module/Calendar/Event/Form.php | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/Module/Calendar/Event/Form.php b/src/Module/Calendar/Event/Form.php index 2e8dec7fa..74877f0af 100644 --- a/src/Module/Calendar/Event/Form.php +++ b/src/Module/Calendar/Event/Form.php @@ -234,13 +234,13 @@ class Form extends BaseModule 'start_text' ), - '$d_text' => $this->t('Description:'), - '$d_orig' => $d_orig, - '$l_text' => $this->t('Location:'), - '$l_orig' => $l_orig, - '$t_text' => $this->t('Title:') . ' *', + '$t_text' => $this->t('Title (BBCode not allowed)') . ' *', '$t_orig' => $t_orig, - '$summary' => ['summary', $this->t('Title:'), $t_orig, '', '*'], + '$d_text' => $this->t('Description (BBCode allowed)'), + '$d_orig' => $d_orig, + '$l_text' => $this->t('Location (BBCode not allowed)'), + '$l_orig' => $l_orig, + '$summary' => ['summary', $this->t('Title (BBCode not allowed)'), $t_orig, '', '*'], '$sh_text' => $this->t('Share this event'), '$share' => ['share', $this->t('Share this event'), $share_checked, '', $share_disabled], '$sh_checked' => $share_checked, From a0752b1161ca75b23612a3304caf03c355cfd187 Mon Sep 17 00:00:00 2001 From: Hypolite Petovan Date: Fri, 2 Dec 2022 19:41:46 -0500 Subject: [PATCH 19/27] Escape HTML in event mapping callback - This prevents arbitrary Javascript from being executed from the calendar view --- src/Module/Calendar/Event/Get.php | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/Module/Calendar/Event/Get.php b/src/Module/Calendar/Event/Get.php index 9bb86a723..9ed2045f5 100644 --- a/src/Module/Calendar/Event/Get.php +++ b/src/Module/Calendar/Event/Get.php @@ -34,6 +34,7 @@ use Friendica\Module\Response; use Friendica\Network\HTTPException; use Friendica\Util\DateTimeFormat; use Friendica\Util\Profiler; +use Friendica\Util\Strings; use Psr\Log\LoggerInterface; /** @@ -82,12 +83,12 @@ class Get extends \Friendica\BaseModule return [ 'id' => $event['id'], - 'title' => $event['summary'], + 'title' => Strings::escapeHtml($event['summary']), 'start' => DateTimeFormat::local($event['start']), 'end' => DateTimeFormat::local($event['finish']), 'nofinish' => $event['nofinish'], - 'desc' => $event['desc'], - 'location' => $event['location'], + 'desc' => Strings::escapeHtml($event['desc']), + 'location' => Strings::escapeHtml($event['location']), 'item' => $item, ]; }, $events); From 82c2e686d710a8845f228676c379f786afea8fd0 Mon Sep 17 00:00:00 2001 From: Hypolite Petovan Date: Fri, 2 Dec 2022 19:46:23 -0500 Subject: [PATCH 20/27] Updating main translation file after updating a few strings --- view/lang/C/messages.po | 98 ++++++++++++++++++++++------------------- 1 file changed, 52 insertions(+), 46 deletions(-) diff --git a/view/lang/C/messages.po b/view/lang/C/messages.po index ffc7680a3..294256fd9 100644 --- a/view/lang/C/messages.po +++ b/view/lang/C/messages.po @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: 2022.12-dev\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2022-12-03 21:11+0000\n" +"POT-Creation-Date: 2022-12-04 06:41-0500\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" @@ -2151,9 +2151,8 @@ msgstr "" #: src/Content/Widget/VCard.php:104 src/Model/Event.php:82 #: src/Model/Event.php:109 src/Model/Event.php:471 src/Model/Event.php:958 -#: src/Model/Profile.php:373 src/Module/Calendar/Event/Form.php:239 -#: src/Module/Contact/Profile.php:369 src/Module/Directory.php:147 -#: src/Module/Notifications/Introductions.php:187 +#: src/Model/Profile.php:373 src/Module/Contact/Profile.php:369 +#: src/Module/Directory.php:147 src/Module/Notifications/Introductions.php:187 #: src/Module/Profile/Profile.php:186 msgid "Location:" msgstr "" @@ -3295,7 +3294,7 @@ msgstr "" msgid "Contact information and Social Networks" msgstr "" -#: src/Model/User.php:212 src/Model/User.php:1100 +#: src/Model/User.php:212 src/Model/User.php:1102 msgid "SERIOUS ERROR: Generation of security keys failed." msgstr "" @@ -3307,134 +3306,134 @@ msgstr "" msgid "Not enough information to authenticate" msgstr "" -#: src/Model/User.php:750 +#: src/Model/User.php:752 msgid "Password can't be empty" msgstr "" -#: src/Model/User.php:792 +#: src/Model/User.php:794 msgid "Empty passwords are not allowed." msgstr "" -#: src/Model/User.php:796 +#: src/Model/User.php:798 msgid "" "The new password has been exposed in a public data dump, please choose " "another." msgstr "" -#: src/Model/User.php:800 +#: src/Model/User.php:802 msgid "The password length is limited to 72 characters." msgstr "" -#: src/Model/User.php:804 +#: src/Model/User.php:806 msgid "" "The password can't contain accentuated letters, white spaces or colons (:)" msgstr "" -#: src/Model/User.php:983 +#: src/Model/User.php:985 msgid "Passwords do not match. Password unchanged." msgstr "" -#: src/Model/User.php:990 +#: src/Model/User.php:992 msgid "An invitation is required." msgstr "" -#: src/Model/User.php:994 +#: src/Model/User.php:996 msgid "Invitation could not be verified." msgstr "" -#: src/Model/User.php:1002 +#: src/Model/User.php:1004 msgid "Invalid OpenID url" msgstr "" -#: src/Model/User.php:1015 src/Security/Authentication.php:241 +#: src/Model/User.php:1017 src/Security/Authentication.php:241 msgid "" "We encountered a problem while logging in with the OpenID you provided. " "Please check the correct spelling of the ID." msgstr "" -#: src/Model/User.php:1015 src/Security/Authentication.php:241 +#: src/Model/User.php:1017 src/Security/Authentication.php:241 msgid "The error message was:" msgstr "" -#: src/Model/User.php:1021 +#: src/Model/User.php:1023 msgid "Please enter the required information." msgstr "" -#: src/Model/User.php:1035 +#: src/Model/User.php:1037 #, php-format msgid "" "system.username_min_length (%s) and system.username_max_length (%s) are " "excluding each other, swapping values." msgstr "" -#: src/Model/User.php:1042 +#: src/Model/User.php:1044 #, php-format msgid "Username should be at least %s character." msgid_plural "Username should be at least %s characters." msgstr[0] "" msgstr[1] "" -#: src/Model/User.php:1046 +#: src/Model/User.php:1048 #, php-format msgid "Username should be at most %s character." msgid_plural "Username should be at most %s characters." msgstr[0] "" msgstr[1] "" -#: src/Model/User.php:1054 +#: src/Model/User.php:1056 msgid "That doesn't appear to be your full (First Last) name." msgstr "" -#: src/Model/User.php:1059 +#: src/Model/User.php:1061 msgid "Your email domain is not among those allowed on this site." msgstr "" -#: src/Model/User.php:1063 +#: src/Model/User.php:1065 msgid "Not a valid email address." msgstr "" -#: src/Model/User.php:1066 +#: src/Model/User.php:1068 msgid "The nickname was blocked from registration by the nodes admin." msgstr "" -#: src/Model/User.php:1070 src/Model/User.php:1076 +#: src/Model/User.php:1072 src/Model/User.php:1078 msgid "Cannot use that email." msgstr "" -#: src/Model/User.php:1082 +#: src/Model/User.php:1084 msgid "Your nickname can only contain a-z, 0-9 and _." msgstr "" -#: src/Model/User.php:1090 src/Model/User.php:1147 +#: src/Model/User.php:1092 src/Model/User.php:1149 msgid "Nickname is already registered. Please choose another." msgstr "" -#: src/Model/User.php:1134 src/Model/User.php:1138 +#: src/Model/User.php:1136 src/Model/User.php:1140 msgid "An error occurred during registration. Please try again." msgstr "" -#: src/Model/User.php:1161 +#: src/Model/User.php:1163 msgid "An error occurred creating your default profile. Please try again." msgstr "" -#: src/Model/User.php:1168 +#: src/Model/User.php:1170 msgid "An error occurred creating your self contact. Please try again." msgstr "" -#: src/Model/User.php:1173 +#: src/Model/User.php:1175 msgid "Friends" msgstr "" -#: src/Model/User.php:1177 +#: src/Model/User.php:1179 msgid "" "An error occurred creating your default contact group. Please try again." msgstr "" -#: src/Model/User.php:1216 +#: src/Model/User.php:1218 msgid "Profile Photos" msgstr "" -#: src/Model/User.php:1409 +#: src/Model/User.php:1411 #, php-format msgid "" "\n" @@ -3442,7 +3441,7 @@ msgid "" "\t\t\tthe administrator of %2$s has set up an account for you." msgstr "" -#: src/Model/User.php:1412 +#: src/Model/User.php:1414 #, php-format msgid "" "\n" @@ -3480,12 +3479,12 @@ msgid "" "\t\tThank you and welcome to %4$s." msgstr "" -#: src/Model/User.php:1445 src/Model/User.php:1552 +#: src/Model/User.php:1447 src/Model/User.php:1554 #, php-format msgid "Registration details for %s" msgstr "" -#: src/Model/User.php:1465 +#: src/Model/User.php:1467 #, php-format msgid "" "\n" @@ -3501,12 +3500,12 @@ msgid "" "\t\t" msgstr "" -#: src/Model/User.php:1484 +#: src/Model/User.php:1486 #, php-format msgid "Registration at %s" msgstr "" -#: src/Model/User.php:1508 +#: src/Model/User.php:1510 #, php-format msgid "" "\n" @@ -3515,7 +3514,7 @@ msgid "" "\t\t\t" msgstr "" -#: src/Model/User.php:1516 +#: src/Model/User.php:1518 #, php-format msgid "" "\n" @@ -5492,7 +5491,7 @@ msgid "Event Starts:" msgstr "" #: src/Module/Calendar/Event/Form.php:209 -#: src/Module/Calendar/Event/Form.php:241 src/Module/Debug/Probe.php:59 +#: src/Module/Calendar/Event/Form.php:237 src/Module/Debug/Probe.php:59 #: src/Module/Install.php:207 src/Module/Install.php:240 #: src/Module/Install.php:245 src/Module/Install.php:264 #: src/Module/Install.php:275 src/Module/Install.php:280 @@ -5523,14 +5522,17 @@ msgstr "" msgid "Event Finishes:" msgstr "" -#: src/Module/Calendar/Event/Form.php:237 src/Module/Profile/Profile.php:164 -#: src/Module/Settings/Profile/Index.php:247 -msgid "Description:" +#: src/Module/Calendar/Event/Form.php:237 +#: src/Module/Calendar/Event/Form.php:243 +msgid "Title (BBCode not allowed)" +msgstr "" + +#: src/Module/Calendar/Event/Form.php:239 +msgid "Description (BBCode allowed)" msgstr "" #: src/Module/Calendar/Event/Form.php:241 -#: src/Module/Calendar/Event/Form.php:243 -msgid "Title:" +msgid "Location (BBCode not allowed)" msgstr "" #: src/Module/Calendar/Event/Form.php:244 @@ -8239,6 +8241,10 @@ msgid_plural "%d years old" msgstr[0] "" msgstr[1] "" +#: src/Module/Profile/Profile.php:164 src/Module/Settings/Profile/Index.php:247 +msgid "Description:" +msgstr "" + #: src/Module/Profile/Profile.php:226 msgid "Forums:" msgstr "" From a5be5b27e3193c1134855126eab258c810505d93 Mon Sep 17 00:00:00 2001 From: Michael Date: Sun, 4 Dec 2022 13:29:21 +0000 Subject: [PATCH 21/27] Support Blurhash --- composer.json | 3 +- composer.lock | 46 +++++++++++++- database.sql | 4 +- doc/database/db_photo.md | 1 + doc/database/db_post-media.md | 1 + src/Factory/Api/Mastodon/Attachment.php | 3 +- src/Factory/Api/Mastodon/Card.php | 1 + src/Model/Contact/Relation.php | 2 +- src/Model/Photo.php | 1 + src/Model/Post/Media.php | 7 ++- src/Object/Api/Mastodon/Attachment.php | 3 + src/Object/Api/Mastodon/Card.php | 3 + src/Object/Image.php | 83 ++++++++++++++++++++++--- src/Util/Images.php | 6 +- src/Util/ParseUrl.php | 1 + static/dbstructure.config.php | 4 +- 16 files changed, 152 insertions(+), 17 deletions(-) diff --git a/composer.json b/composer.json index d0fc5619b..0e3e42c72 100644 --- a/composer.json +++ b/composer.json @@ -70,7 +70,8 @@ "npm-asset/moment": "^2.24", "npm-asset/perfect-scrollbar": "0.6.16", "npm-asset/textcomplete": "^0.18.2", - "npm-asset/typeahead.js": "^0.11.1" + "npm-asset/typeahead.js": "^0.11.1", + "kornrunner/blurhash": "^1.2" }, "repositories": [ { diff --git a/composer.lock b/composer.lock index 1805c9d21..9a4854f15 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "2e082bac083ca61cc0c22f7055d690bf", + "content-hash": "f8e7baec685d20e6aee56978c275d64c", "packages": [ { "name": "asika/simple-console", @@ -1116,6 +1116,50 @@ ], "time": "2022-06-20T21:43:03+00:00" }, + { + "name": "kornrunner/blurhash", + "version": "v1.2.2", + "source": { + "type": "git", + "url": "https://github.com/kornrunner/php-blurhash.git", + "reference": "bc8a4596cb0a49874f0158696a382ab3933fefe4" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/kornrunner/php-blurhash/zipball/bc8a4596cb0a49874f0158696a382ab3933fefe4", + "reference": "bc8a4596cb0a49874f0158696a382ab3933fefe4", + "shasum": "" + }, + "require": { + "php": "^7.3|^8.0" + }, + "require-dev": { + "ext-gd": "*", + "ocramius/package-versions": "^1.4|^2.0", + "phpstan/phpstan": "^0.12", + "phpunit/phpunit": "^9", + "vimeo/psalm": "^4.3" + }, + "type": "library", + "autoload": { + "psr-4": { + "kornrunner\\Blurhash\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Boris Momčilović", + "email": "boris.momcilovic@gmail.com" + } + ], + "description": "Pure PHP implementation of Blurhash", + "homepage": "https://github.com/kornrunner/php-blurhash", + "time": "2022-07-13T19:38:39+00:00" + }, { "name": "league/html-to-markdown", "version": "4.10.0", diff --git a/database.sql b/database.sql index f41defe42..290f8bed3 100644 --- a/database.sql +++ b/database.sql @@ -1,6 +1,6 @@ -- ------------------------------------------ -- Friendica 2022.12-dev (Giant Rhubarb) --- DB_UPDATE_VERSION 1496 +-- DB_UPDATE_VERSION 1497 -- ------------------------------------------ @@ -1088,6 +1088,7 @@ CREATE TABLE IF NOT EXISTS `photo` ( `height` smallint unsigned NOT NULL DEFAULT 0 COMMENT '', `width` smallint unsigned NOT NULL DEFAULT 0 COMMENT '', `datasize` int unsigned NOT NULL DEFAULT 0 COMMENT '', + `blurhash` varbinary(255) COMMENT 'BlurHash representation of the photo', `data` mediumblob NOT NULL COMMENT '', `scale` tinyint unsigned NOT NULL DEFAULT 0 COMMENT '', `profile` boolean NOT NULL DEFAULT '0' COMMENT '', @@ -1313,6 +1314,7 @@ CREATE TABLE IF NOT EXISTS `post-media` ( `height` smallint unsigned COMMENT 'Height of the media', `width` smallint unsigned COMMENT 'Width of the media', `size` bigint unsigned COMMENT 'Media size', + `blurhash` varbinary(255) COMMENT 'BlurHash representation of the image', `preview` varbinary(512) COMMENT 'Preview URL', `preview-height` smallint unsigned COMMENT 'Height of the preview picture', `preview-width` smallint unsigned COMMENT 'Width of the preview picture', diff --git a/doc/database/db_photo.md b/doc/database/db_photo.md index a93cf65f9..ed417f614 100644 --- a/doc/database/db_photo.md +++ b/doc/database/db_photo.md @@ -25,6 +25,7 @@ Fields | height | | smallint unsigned | NO | | 0 | | | width | | smallint unsigned | NO | | 0 | | | datasize | | int unsigned | NO | | 0 | | +| blurhash | BlurHash representation of the photo | varbinary(255) | YES | | NULL | | | data | | mediumblob | NO | | NULL | | | scale | | tinyint unsigned | NO | | 0 | | | profile | | boolean | NO | | 0 | | diff --git a/doc/database/db_post-media.md b/doc/database/db_post-media.md index d6e3a703c..2d73a39cf 100644 --- a/doc/database/db_post-media.md +++ b/doc/database/db_post-media.md @@ -17,6 +17,7 @@ Fields | height | Height of the media | smallint unsigned | YES | | NULL | | | width | Width of the media | smallint unsigned | YES | | NULL | | | size | Media size | bigint unsigned | YES | | NULL | | +| blurhash | BlurHash representation of the image | varbinary(255) | YES | | NULL | | | preview | Preview URL | varbinary(512) | YES | | NULL | | | preview-height | Height of the preview picture | smallint unsigned | YES | | NULL | | | preview-width | Width of the preview picture | smallint unsigned | YES | | NULL | | diff --git a/src/Factory/Api/Mastodon/Attachment.php b/src/Factory/Api/Mastodon/Attachment.php index accebe343..197548b8a 100644 --- a/src/Factory/Api/Mastodon/Attachment.php +++ b/src/Factory/Api/Mastodon/Attachment.php @@ -94,7 +94,7 @@ class Attachment extends BaseFactory */ public function createFromPhoto(int $id): array { - $photo = Photo::selectFirst(['resource-id', 'uid', 'id', 'title', 'type', 'width', 'height'], ['id' => $id]); + $photo = Photo::selectFirst(['resource-id', 'uid', 'id', 'title', 'type', 'width', 'height', 'blurhash'], ['id' => $id]); if (empty($photo)) { return []; } @@ -104,6 +104,7 @@ class Attachment extends BaseFactory 'description' => $photo['title'], 'width' => $photo['width'], 'height' => $photo['height'], + 'blurhash' => $photo['blurhash'], ]; $photoTypes = Images::supportedTypes(); diff --git a/src/Factory/Api/Mastodon/Card.php b/src/Factory/Api/Mastodon/Card.php index ac5084184..3efc62517 100644 --- a/src/Factory/Api/Mastodon/Card.php +++ b/src/Factory/Api/Mastodon/Card.php @@ -74,6 +74,7 @@ class Card extends BaseFactory $data['image'] = $attached['preview']; $data['width'] = $attached['preview-width']; $data['height'] = $attached['preview-height']; + $data['blurhash'] = $attached['blurhash']; } } diff --git a/src/Model/Contact/Relation.php b/src/Model/Contact/Relation.php index 3db882bb9..defb984f2 100644 --- a/src/Model/Contact/Relation.php +++ b/src/Model/Contact/Relation.php @@ -76,7 +76,7 @@ class Relation */ public static function discoverByUser(int $uid) { - $contact = Contact::selectFirst(['id', 'url'], ['uid' => $uid, 'self' => true]); + $contact = Contact::selectFirst(['id', 'url', 'network'], ['uid' => $uid, 'self' => true]); if (empty($contact)) { Logger::warning('Self contact for user not found', ['uid' => $uid]); return; diff --git a/src/Model/Photo.php b/src/Model/Photo.php index 126bc152b..213551b11 100644 --- a/src/Model/Photo.php +++ b/src/Model/Photo.php @@ -436,6 +436,7 @@ class Photo 'height' => $image->getHeight(), 'width' => $image->getWidth(), 'datasize' => strlen($image->asString()), + 'blurhash' => $image->getBlurHash(), 'data' => $data, 'scale' => $scale, 'photo-type' => $type, diff --git a/src/Model/Post/Media.php b/src/Model/Post/Media.php index 854e5d8f9..62590594b 100644 --- a/src/Model/Post/Media.php +++ b/src/Model/Post/Media.php @@ -117,7 +117,7 @@ class Media */ private static function unsetEmptyFields(array $media): array { - $fields = ['mimetype', 'height', 'width', 'size', 'preview', 'preview-height', 'preview-width', 'description']; + $fields = ['mimetype', 'height', 'width', 'size', 'preview', 'preview-height', 'preview-width', 'blurhash', 'description']; foreach ($fields as $field) { if (empty($media[$field])) { unset($media[$field]); @@ -203,6 +203,7 @@ class Media $media['size'] = $imagedata['size']; $media['width'] = $imagedata[0]; $media['height'] = $imagedata[1]; + $media['blurhash'] = $imagedata['blurhash'] ?? null; } else { Logger::notice('No image data', ['media' => $media]); } @@ -232,6 +233,7 @@ class Media $media['preview'] = $data['images'][0]['src'] ?? null; $media['preview-height'] = $data['images'][0]['height'] ?? null; $media['preview-width'] = $data['images'][0]['width'] ?? null; + $media['blurhash'] = $data['images'][0]['blurhash'] ?? null; $media['description'] = $data['text'] ?? null; $media['name'] = $data['title'] ?? null; $media['author-url'] = $data['author_url'] ?? null; @@ -287,6 +289,7 @@ class Media $media['preview'] = null; $media['preview-height'] = null; $media['preview-width'] = null; + $media['blurhash'] = null; $media['description'] = $item['body']; $media['name'] = $item['title']; $media['author-url'] = $item['author-link']; @@ -328,6 +331,7 @@ class Media $media['preview'] = null; $media['preview-height'] = null; $media['preview-width'] = null; + $media['blurhash'] = null; $media['description'] = $contact['about']; $media['name'] = $contact['name']; $media['author-url'] = $contact['url']; @@ -357,6 +361,7 @@ class Media $media['size'] = $photo['datasize']; $media['width'] = $photo['width']; $media['height'] = $photo['height']; + $media['blurhash'] = $photo['blurhash']; } if (!preg_match('|.*?/photo/(.*[a-fA-F0-9])\-(.*[0-9])\..*[\w]|', $media['preview'] ?? '', $matches)) { diff --git a/src/Object/Api/Mastodon/Attachment.php b/src/Object/Api/Mastodon/Attachment.php index 3f890bf74..da467fc64 100644 --- a/src/Object/Api/Mastodon/Attachment.php +++ b/src/Object/Api/Mastodon/Attachment.php @@ -46,6 +46,8 @@ class Attachment extends BaseDataTransferObject protected $text_url; /** @var string */ protected $description; + /** @var string */ + protected $blurhash; /** @var array */ protected $meta; @@ -68,6 +70,7 @@ class Attachment extends BaseDataTransferObject $this->remote_url = $remote; $this->text_url = $this->remote_url ?? $this->url; $this->description = $attachment['description']; + $this->blurhash = $attachment['blurhash']; if ($type === 'image') { if ((int) $attachment['width'] > 0 && (int) $attachment['height'] > 0) { $this->meta['original']['width'] = (int) $attachment['width']; diff --git a/src/Object/Api/Mastodon/Card.php b/src/Object/Api/Mastodon/Card.php index bf8761719..aa5d8913a 100644 --- a/src/Object/Api/Mastodon/Card.php +++ b/src/Object/Api/Mastodon/Card.php @@ -52,6 +52,8 @@ class Card extends BaseDataTransferObject protected $height; /** @var string */ protected $image; + /** @var string */ + protected $blurhash; /** * Creates a card record from an attachment array. @@ -72,6 +74,7 @@ class Card extends BaseDataTransferObject $this->width = $attachment['width'] ?? 0; $this->height = $attachment['height'] ?? 0; $this->image = $attachment['image'] ?? ''; + $this->blurhash = $attachment['blurhash'] ?? ''; $this->history = $history; } diff --git a/src/Object/Image.php b/src/Object/Image.php index 866ac268f..c2cfa9e2f 100644 --- a/src/Object/Image.php +++ b/src/Object/Image.php @@ -26,13 +26,15 @@ use Friendica\DI; use Friendica\Util\Images; use Imagick; use ImagickPixel; +use GDImage; +use kornrunner\Blurhash\Blurhash; /** * Class to handle images */ class Image { - /** @var Imagick|resource */ + /** @var GDImage|Imagick|resource */ private $image; /* @@ -695,14 +697,13 @@ class Image try { /* Clean it */ $this->image = $this->image->deconstructImages(); - $string = $this->image->getImagesBlob(); - return $string; + return $this->image->getImagesBlob(); } catch (Exception $e) { return false; } } - ob_start(); + $stream = fopen('php://memory','r+'); // Enable interlacing imageinterlace($this->image, true); @@ -710,18 +711,82 @@ class Image switch ($this->getType()) { case 'image/png': $quality = DI::config()->get('system', 'png_quality'); - imagepng($this->image, null, $quality); + imagepng($this->image, $stream, $quality); break; case 'image/jpeg': case 'image/jpg': $quality = DI::config()->get('system', 'jpeg_quality'); - imagejpeg($this->image, null, $quality); + imagejpeg($this->image, $stream, $quality); break; } - $string = ob_get_contents(); - ob_end_clean(); + rewind($stream); + return stream_get_contents($stream); + } - return $string; + /** + * Create a blurhash out of a given image string + * + * @param string $img_str + * @return string + */ + public function getBlurHash(): string + { + if ($this->isImagick()) { + // Imagick is not supported + return ''; + } + + $width = $this->getWidth(); + $height = $this->getHeight(); + + if (max($width, $height) > 90) { + $this->scaleDown(90); + $width = $this->getWidth(); + $height = $this->getHeight(); + } + + $pixels = []; + for ($y = 0; $y < $height; ++$y) { + $row = []; + for ($x = 0; $x < $width; ++$x) { + $index = imagecolorat($this->image, $x, $y); + $colors = imagecolorsforindex($this->image, $index); + + $row[] = [$colors['red'], $colors['green'], $colors['blue']]; + } + $pixels[] = $row; + } + + // The components define the amount of details (1 to 9). + $components_x = 9; + $components_y = 9; + + return Blurhash::encode($pixels, $components_x, $components_y); + } + + /** + * Create an image out of a blurhash + * + * @param string $blurhash + * @param integer $width + * @param integer $height + * @return void + */ + public function getFromBlurHash(string $blurhash, int $width, int $height) + { + if ($this->isImagick()) { + // Imagick is not supported + return; + } + + $pixels = Blurhash::decode($blurhash, $width, $height); + $this->image = imagecreatetruecolor($width, $height); + for ($y = 0; $y < $height; ++$y) { + for ($x = 0; $x < $width; ++$x) { + [$r, $g, $b] = $pixels[$y][$x]; + imagesetpixel($this->image, $x, $y, imagecolorallocate($this->image, $r, $g, $b)); + } + } } } diff --git a/src/Util/Images.php b/src/Util/Images.php index eb1e8c437..533feec84 100644 --- a/src/Util/Images.php +++ b/src/Util/Images.php @@ -25,6 +25,7 @@ use Friendica\Core\Logger; use Friendica\DI; use Friendica\Model\Photo; use Friendica\Network\HTTPClient\Client\HttpClientAccept; +use Friendica\Object\Image; /** * Image utilities @@ -244,7 +245,10 @@ class Images } if ($data) { - $data['size'] = $filesize; + $image = new Image($img_str); + + $data['blurhash'] = $image->getBlurHash(); + $data['size'] = $filesize; } return is_array($data) ? $data : []; diff --git a/src/Util/ParseUrl.php b/src/Util/ParseUrl.php index f0afa1ec2..5b0bf6a5c 100644 --- a/src/Util/ParseUrl.php +++ b/src/Util/ParseUrl.php @@ -567,6 +567,7 @@ class ParseUrl $image['width'] = $photodata[0]; $image['height'] = $photodata[1]; $image['contenttype'] = $photodata['mime']; + $image['blurhash'] = $photodata['blurhash'] ?? null; unset($image['url']); ksort($image); } else { diff --git a/static/dbstructure.config.php b/static/dbstructure.config.php index 2979ca80d..f8e01e51e 100644 --- a/static/dbstructure.config.php +++ b/static/dbstructure.config.php @@ -55,7 +55,7 @@ use Friendica\Database\DBA; if (!defined('DB_UPDATE_VERSION')) { - define('DB_UPDATE_VERSION', 1496); + define('DB_UPDATE_VERSION', 1497); } return [ @@ -1129,6 +1129,7 @@ return [ "height" => ["type" => "smallint unsigned", "not null" => "1", "default" => "0", "comment" => ""], "width" => ["type" => "smallint unsigned", "not null" => "1", "default" => "0", "comment" => ""], "datasize" => ["type" => "int unsigned", "not null" => "1", "default" => "0", "comment" => ""], + "blurhash" => ["type" => "varbinary(255)", "comment" => "BlurHash representation of the photo"], "data" => ["type" => "mediumblob", "not null" => "1", "comment" => ""], "scale" => ["type" => "tinyint unsigned", "not null" => "1", "default" => "0", "comment" => ""], "profile" => ["type" => "boolean", "not null" => "1", "default" => "0", "comment" => ""], @@ -1340,6 +1341,7 @@ return [ "height" => ["type" => "smallint unsigned", "comment" => "Height of the media"], "width" => ["type" => "smallint unsigned", "comment" => "Width of the media"], "size" => ["type" => "bigint unsigned", "comment" => "Media size"], + "blurhash" => ["type" => "varbinary(255)", "comment" => "BlurHash representation of the image"], "preview" => ["type" => "varbinary(512)", "comment" => "Preview URL"], "preview-height" => ["type" => "smallint unsigned", "comment" => "Height of the preview picture"], "preview-width" => ["type" => "smallint unsigned", "comment" => "Width of the preview picture"], From 1a8722c1f6564a9c828e7d964eb2d87590607a16 Mon Sep 17 00:00:00 2001 From: Philipp Date: Sun, 4 Dec 2022 14:47:31 +0100 Subject: [PATCH 22/27] Add /about route --- static/routes.config.php | 2 ++ 1 file changed, 2 insertions(+) diff --git a/static/routes.config.php b/static/routes.config.php index f33cd9581..e3c87f150 100644 --- a/static/routes.config.php +++ b/static/routes.config.php @@ -318,6 +318,8 @@ return [ '/proofs' => [Module\Api\Mastodon\Proofs::class, [R::GET ]], // Dummy, not supported ], + '/about[/more]' => [Module\Friendica::class, [R::GET]], + '/admin' => [ '[/]' => [Module\Admin\Summary::class, [R::GET]], From f79c81023680e65e838bcaf327cd92da09485bbb Mon Sep 17 00:00:00 2001 From: Philipp Date: Sun, 4 Dec 2022 14:53:14 +0100 Subject: [PATCH 23/27] Try PHP8.1 again --- .woodpecker/.phpunit.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.woodpecker/.phpunit.yml b/.woodpecker/.phpunit.yml index 6ae4669ca..bdfcf8097 100644 --- a/.woodpecker/.phpunit.yml +++ b/.woodpecker/.phpunit.yml @@ -6,8 +6,8 @@ matrix: PHP_VERSION: 7.4.33 - PHP_MAJOR_VERSION: 8.0 PHP_VERSION: 8.0.25 -# - PHP_MAJOR_VERSION: 8.1 -# PHP_VERSION: 8.1.12 + - PHP_MAJOR_VERSION: 8.1 + PHP_VERSION: 8.1.12 pipeline: php-lint: From 10e423237e94a1a10ff6509d054c93acb3b60d01 Mon Sep 17 00:00:00 2001 From: Philipp Date: Sun, 4 Dec 2022 15:06:10 +0100 Subject: [PATCH 24/27] Force phpunit execution at the opensocial node --- .woodpecker/.phpunit.yml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.woodpecker/.phpunit.yml b/.woodpecker/.phpunit.yml index bdfcf8097..47b2f4bda 100644 --- a/.woodpecker/.phpunit.yml +++ b/.woodpecker/.phpunit.yml @@ -9,6 +9,10 @@ matrix: - PHP_MAJOR_VERSION: 8.1 PHP_VERSION: 8.1.12 +# This forces PHP Unit executions at the "opensocial" labeled location (because of much more power...) +labels: + location: opensocial + pipeline: php-lint: image: php:${PHP_MAJOR_VERSION} From cfe5101b9b89ebf3821439069f9f05fc4890dddf Mon Sep 17 00:00:00 2001 From: Michael Date: Sun, 4 Dec 2022 14:58:53 +0000 Subject: [PATCH 25/27] Use the blurhash when the remote picture doesn't load --- src/Model/Photo.php | 8 +++++++- src/Module/Photo.php | 22 +++++++++++++++------- src/Object/Image.php | 16 ++++++++++++---- 3 files changed, 34 insertions(+), 12 deletions(-) diff --git a/src/Model/Photo.php b/src/Model/Photo.php index 213551b11..6dd921283 100644 --- a/src/Model/Photo.php +++ b/src/Model/Photo.php @@ -346,11 +346,14 @@ class Photo * @param string $url Image URL * @param int $uid User ID of the requesting person * @param string $mimetype Image mime type. Is guessed by file name when empty. + * @param string $blurhash The blurhash that will be used to generate a picture when the original picture can't be fetched + * @param int $width Image width + * @param int $height Image height * * @return array * @throws \Exception */ - public static function createPhotoForExternalResource(string $url, int $uid = 0, string $mimetype = ''): array + public static function createPhotoForExternalResource(string $url, int $uid = 0, string $mimetype = '', string $blurhash = '', int $width = 0, int $height = 0): array { if (empty($mimetype)) { $mimetype = Images::guessTypeByExtension($url); @@ -364,6 +367,9 @@ class Photo $photo['backend-ref'] = json_encode(['url' => $url, 'uid' => $uid]); $photo['type'] = $mimetype; $photo['cacheable'] = true; + $photo['blurhash'] = $blurhash; + $photo['width'] = $width; + $photo['height'] = $height; return $photo; } diff --git a/src/Module/Photo.php b/src/Module/Photo.php index 1847b4f70..a4ef6ab41 100644 --- a/src/Module/Photo.php +++ b/src/Module/Photo.php @@ -153,8 +153,12 @@ class Photo extends BaseModule $stamp = microtime(true); $imgdata = MPhoto::getImageDataForPhoto($photo); - if (empty($imgdata)) { + if (empty($imgdata) && empty($photo['blurhash'])) { throw new HTTPException\NotFoundException(); + } elseif (empty($imgdata) && !empty($photo['blurhash'])) { + $image = New Image('', 'image/png'); + $image->getFromBlurHash($photo['blurhash'], $photo['width'], $photo['height']); + $imgdata = $image->asString(); } // The mimetype for an external or system resource can only be known reliably after it had been fetched @@ -240,14 +244,18 @@ class Photo extends BaseModule { switch($type) { case 'preview': - $media = DBA::selectFirst('post-media', ['preview', 'url', 'mimetype', 'type', 'uri-id'], ['id' => $id]); + $media = DBA::selectFirst('post-media', ['preview', 'url', 'preview-height', 'preview-width', 'height', 'width', 'mimetype', 'type', 'uri-id', 'blurhash'], ['id' => $id]); if (empty($media)) { return false; } - $url = $media['preview']; + $url = $media['preview']; + $width = $media['preview-width']; + $height = $media['preview-height']; if (empty($url) && ($media['type'] == Post\Media::IMAGE)) { - $url = $media['url']; + $url = $media['url']; + $width = $media['width']; + $height = $media['height']; } if (empty($url)) { @@ -258,9 +266,9 @@ class Photo extends BaseModule return MPhoto::getPhoto($matches[1], $matches[2]); } - return MPhoto::createPhotoForExternalResource($url, (int)DI::userSession()->getLocalUserId(), $media['mimetype'] ?? ''); + return MPhoto::createPhotoForExternalResource($url, (int)DI::userSession()->getLocalUserId(), $media['mimetype'] ?? '', $media['blurhash'], $width, $height); case 'media': - $media = DBA::selectFirst('post-media', ['url', 'mimetype', 'uri-id'], ['id' => $id, 'type' => Post\Media::IMAGE]); + $media = DBA::selectFirst('post-media', ['url', 'height', 'width', 'mimetype', 'uri-id', 'blurhash'], ['id' => $id, 'type' => Post\Media::IMAGE]); if (empty($media)) { return false; } @@ -269,7 +277,7 @@ class Photo extends BaseModule return MPhoto::getPhoto($matches[1], $matches[2]); } - return MPhoto::createPhotoForExternalResource($media['url'], (int)DI::userSession()->getLocalUserId(), $media['mimetype']); + return MPhoto::createPhotoForExternalResource($media['url'], (int)DI::userSession()->getLocalUserId(), $media['mimetype'], $media['blurhash'], $media['width'], $media['height']); case 'link': $link = DBA::selectFirst('post-link', ['url', 'mimetype'], ['id' => $id]); if (empty($link)) { diff --git a/src/Object/Image.php b/src/Object/Image.php index c2cfa9e2f..87401304d 100644 --- a/src/Object/Image.php +++ b/src/Object/Image.php @@ -780,13 +780,21 @@ class Image return; } - $pixels = Blurhash::decode($blurhash, $width, $height); - $this->image = imagecreatetruecolor($width, $height); - for ($y = 0; $y < $height; ++$y) { - for ($x = 0; $x < $width; ++$x) { + $scaled = Images::getScalingDimensions($width, $height, 90); + $pixels = Blurhash::decode($blurhash, $scaled['width'], $scaled['height']); + + $this->image = imagecreatetruecolor($scaled['width'], $scaled['height']); + for ($y = 0; $y < $scaled['height']; ++$y) { + for ($x = 0; $x < $scaled['width']; ++$x) { [$r, $g, $b] = $pixels[$y][$x]; imagesetpixel($this->image, $x, $y, imagecolorallocate($this->image, $r, $g, $b)); } } + + $this->width = imagesx($this->image); + $this->height = imagesy($this->image); + $this->valid = true; + + $this->scaleUp(min($width, $height)); } } From 1c8690a872729a96f737f6aa7dc5b769c73a85f6 Mon Sep 17 00:00:00 2001 From: Michael Date: Sun, 4 Dec 2022 15:14:43 +0000 Subject: [PATCH 26/27] Fix calls for pictures without blurhash --- src/Model/Photo.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Model/Photo.php b/src/Model/Photo.php index 6dd921283..f9940f51e 100644 --- a/src/Model/Photo.php +++ b/src/Model/Photo.php @@ -353,7 +353,7 @@ class Photo * @return array * @throws \Exception */ - public static function createPhotoForExternalResource(string $url, int $uid = 0, string $mimetype = '', string $blurhash = '', int $width = 0, int $height = 0): array + public static function createPhotoForExternalResource(string $url, int $uid = 0, string $mimetype = '', string $blurhash = null, int $width = null, int $height = null): array { if (empty($mimetype)) { $mimetype = Images::guessTypeByExtension($url); From 5c74113bf66429909757cbb41901c51b6f6b5735 Mon Sep 17 00:00:00 2001 From: Michael Date: Sun, 4 Dec 2022 16:33:29 +0000 Subject: [PATCH 27/27] Issue 12275: Banner upload via avatar does work now --- src/Model/Photo.php | 2 +- src/Module/Api/Mastodon/Accounts/UpdateCredentials.php | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Model/Photo.php b/src/Model/Photo.php index f9940f51e..16acab69a 100644 --- a/src/Model/Photo.php +++ b/src/Model/Photo.php @@ -1325,7 +1325,7 @@ class Photo logger::warning('profile banner upload with scale 3 (960) failed'); } - logger::info('new profile banner upload ended'); + logger::info('new profile banner upload ended', ['uid' => $uid, 'resource_id' => $resource_id, 'filename' => $filename]); $condition = ["`photo-type` = ? AND `resource-id` != ? AND `uid` = ?", self::USER_BANNER, $resource_id, $uid]; self::update(['photo-type' => self::DEFAULT], $condition); diff --git a/src/Module/Api/Mastodon/Accounts/UpdateCredentials.php b/src/Module/Api/Mastodon/Accounts/UpdateCredentials.php index 6427a904a..ac6337ebf 100644 --- a/src/Module/Api/Mastodon/Accounts/UpdateCredentials.php +++ b/src/Module/Api/Mastodon/Accounts/UpdateCredentials.php @@ -89,11 +89,11 @@ class UpdateCredentials extends BaseApi Logger::info('Update profile and user', ['uid' => $uid, 'user' => $user, 'profile' => $profile]); if (!empty($request['avatar'])) { - Photo::uploadAvatar(1, $request['avatar']); + Photo::uploadAvatar($uid, $request['avatar']); } if (!empty($request['header'])) { - Photo::uploadBanner(1, $request['header']); + Photo::uploadBanner($uid, $request['header']); } User::update($user, $uid);