From 2de7034e166d88f8f6dc527c1b0a380a4eb49c86 Mon Sep 17 00:00:00 2001 From: Michael Date: Sat, 15 Mar 2025 05:35:55 +0000 Subject: [PATCH 01/24] Improved support for backfilled posts --- src/Model/Item.php | 4 ++-- src/Model/ItemHelper.php | 20 ++++++++++++++++---- 2 files changed, 18 insertions(+), 6 deletions(-) diff --git a/src/Model/Item.php b/src/Model/Item.php index 31f8a8e22e..247bd06435 100644 --- a/src/Model/Item.php +++ b/src/Model/Item.php @@ -1090,9 +1090,9 @@ class Item } if ($update_commented) { - $fields = ['commented' => DateTimeFormat::utcNow(), 'changed' => DateTimeFormat::utcNow()]; + $fields = ['commented' => $posted_item['received'], 'changed' => $posted_item['received']]; } else { - $fields = ['changed' => DateTimeFormat::utcNow()]; + $fields = ['changed' => $posted_item['received']]; } Post::update($fields, ['uri-id' => $posted_item['parent-uri-id'], 'uid' => $posted_item['uid']]); diff --git a/src/Model/ItemHelper.php b/src/Model/ItemHelper.php index 9b585fd544..102b0fecba 100644 --- a/src/Model/ItemHelper.php +++ b/src/Model/ItemHelper.php @@ -205,13 +205,25 @@ final class ItemHelper $item['file'] = trim($item['file'] ?? ''); // Items cannot be stored before they happen ... - if ($item['created'] > DateTimeFormat::utcNow()) { - $item['created'] = DateTimeFormat::utcNow(); + if ($item['received'] > DateTimeFormat::utcNow()) { + $item['received'] = DateTimeFormat::utcNow(); + } + + if ($item['created'] > $item['received']) { + $item['created'] = $item['received']; } // We haven't invented time travel by now. - if ($item['edited'] > DateTimeFormat::utcNow()) { - $item['edited'] = DateTimeFormat::utcNow(); + if ($item['edited'] > $item['received'] ) { + $item['edited'] = $item['received'] ; + } + + if ($item['changed'] > $item['received'] ) { + $item['changed'] = $item['received'] ; + } + + if ($item['commented'] > $item['received'] ) { + $item['commented'] = $item['received'] ; } $item['plink'] = ($item['plink'] ?? '') ?: $this->baseUrl . '/display/' . urlencode($item['guid']); From fcb8892e2fa4055ed4ed3404881bfac8faa554ef Mon Sep 17 00:00:00 2001 From: Michael Date: Tue, 18 Mar 2025 05:33:01 +0000 Subject: [PATCH 02/24] Add missing public contacts and account-user entries --- src/Worker/Cron.php | 3 +++ src/Worker/FixContacts.php | 51 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 54 insertions(+) create mode 100644 src/Worker/FixContacts.php diff --git a/src/Worker/Cron.php b/src/Worker/Cron.php index b576f09ae7..cf0529c8b4 100644 --- a/src/Worker/Cron.php +++ b/src/Worker/Cron.php @@ -121,6 +121,9 @@ class Cron Worker::add(Worker::PRIORITY_LOW, 'UpdateAllSuggestions'); + // add missing public contacts and account-user entries + Worker::add(Worker::PRIORITY_LOW, 'FixContacts'); + if (DI::config()->get('system', 'optimize_tables')) { Worker::add(Worker::PRIORITY_LOW, 'OptimizeTables'); } diff --git a/src/Worker/FixContacts.php b/src/Worker/FixContacts.php new file mode 100644 index 0000000000..2961962bc8 --- /dev/null +++ b/src/Worker/FixContacts.php @@ -0,0 +1,51 @@ +info('Add missing public contacts'); + $contacts = DBA::p("SELECT `contact`.`id` FROM `contact` LEFT JOIN `contact` AS `pcontact` ON `contact`.`uri-id` = `pcontact`.`uri-id` WHERE `pcontact`.`id` IS NULL"); + while ($contact = DBA::fetch($contacts)) { + Contact::selectAccountUserById($contact['id'], ['id']); + $added++; + } + DBA::close($contacts); + + if ($added == 0) { + DI::logger()->info('No public contacts have been added'); + } else { + DI::logger()->info('Missing public contacts have been added', ['added' => $added]); + } + + $added = 0; + DI::logger()->info('Add missing account-user entries'); + $contacts = DBA::p("SELECT `contact`.`id`, `contact`.`uid`, `contact`.`uri-id`, `contact`.`url` FROM `contact` LEFT JOIN `account-user` ON `contact`.`id` = `account-user`.`id` WHERE `contact`.`id` > ? AND `account-user`.`id` IS NULL", 0); + while ($contact = DBA::fetch($contacts)) { + Contact::setAccountUser($contact['id'], $contact['uid'], $contact['uri-id'], $contact['url']); + $added++; + } + DBA::close($contacts); + + if ($added == 0) { + DI::logger()->info('No account-user entries have been added'); + } else { + DI::logger()->info('Missing account-user entries have been added', ['added' => $added]); + } + } +} From 6f159e69bea42f1c018c21821cebe67520ad2afc Mon Sep 17 00:00:00 2001 From: Hypolite Petovan Date: Wed, 12 Mar 2025 22:46:36 -0400 Subject: [PATCH 03/24] Add support for more datetime formats - Unix Timestamp - RFC 3339 extended + timezone name in square brackets - Address https://github.com/friendica/friendica/issues/14647#issuecomment-2719430131 --- src/Util/DateTimeFormat.php | 6 +- tests/src/Util/DateTimeFormatTest.php | 81 +++++++++++++++++++++++++++ 2 files changed, 85 insertions(+), 2 deletions(-) diff --git a/src/Util/DateTimeFormat.php b/src/Util/DateTimeFormat.php index 42b49cbf7d..830b50a2fe 100644 --- a/src/Util/DateTimeFormat.php +++ b/src/Util/DateTimeFormat.php @@ -117,7 +117,7 @@ class DateTimeFormat $tz_to = 'UTC'; } - if (($s === '') || (!is_string($s))) { + if ($s === '') { $s = 'now'; } @@ -135,7 +135,8 @@ class DateTimeFormat } try { - $d = new DateTime($s, $from_obj); + $d = DateTime::createFromFormat('U', $s, $from_obj) + ?: new DateTime($s, $from_obj); } catch (Exception $e) { try { $d = new DateTime(self::fix($s), $from_obj); @@ -176,6 +177,7 @@ class DateTimeFormat $pregPatterns = [ ['#(\w+), (\d+ \w+ \d+) (\d+:\d+:\d+) (.+)#', '$2 $3 $4'], ['#(\d+:\d+) (\w+), (\w+) (\d+), (\d+)#', '$1 $2 $3 $4 $5'], + ['#\[[^\]]*\]#', ''], // 2025-03-07T08:54:14.341+01:00[Europe/Berlin] ]; foreach ($pregPatterns as $pattern) { diff --git a/tests/src/Util/DateTimeFormatTest.php b/tests/src/Util/DateTimeFormatTest.php index 9137659ba8..186741a67c 100644 --- a/tests/src/Util/DateTimeFormatTest.php +++ b/tests/src/Util/DateTimeFormatTest.php @@ -125,6 +125,10 @@ class DateTimeFormatTest extends MockedTestCase 'expectedDate' => '2023-04-02T17:22:42+05:30', 'dateString' => '2023-04-02\T17:22:42+05:30' ], + '2025-03-07T08:54:14.341+01:00[Europe/Berlin]' => [ + 'expectedDate' => '2025-03-07T08:54:14+01:00', + 'dateString' => '2025-03-07T08:54:14.341+01:00[Europe/Berlin]' + ], ]; } @@ -156,4 +160,81 @@ class DateTimeFormatTest extends MockedTestCase $this->assertEquals(259200, $now - $date); } + + public function dataConvert() { + return [ + 'unix timestamp' => [ + 'expected' => '2025-03-12 16:18:27', + 's' => '1741796307', + ], + 'ATOM' => [ + 'expected' => '2022-06-02 15:58:35', + 's' => '2022-06-02T16:58:35+01:00', + ], + 'COOKIE' => [ + 'expected' => '2022-06-02 14:58:35', + 's' => 'Thursday, 02-Jun-2022 16:58:35 Africa/Cairo', + ], + 'ISO 8601/RFC 3339' => [ + 'expected' => '2022-06-02 13:58:35', + 's' => '2022-06-02T16:58:35+0300', + ], + 'RFC 822/RFC 1036' => [ + 'expected' => '2022-06-02 12:58:35', + 's' => 'Thu, 02 Jun 22 16:58:35 +0400', + ], + 'RFC 850' => [ + 'expected' => '2022-06-02 11:58:35', + 's' => 'Thursday, 02-Jun-22 16:58:35 Indian/Kerguelen', + ], + 'RFC 1123/RFC 2822/RSS' => [ + 'expected' => '2022-06-02 10:58:35', + 's' => 'Thu, 02 Jun 2022 16:58:35 +0600', + ], + 'RFC 3339/W3C' => [ + 'expected' => '2025-03-07 01:54:14', + 's' => '2025-03-07T08:54:14+07:00', + ], + 'RFC 3339 extended' => [ + 'expected' => '2025-03-07 00:54:14', + 's' => '2025-03-07T08:54:14.341+08:00', + ], + 'RFC 7231' => [ + 'expected' => '2022-06-02 07:58:35', + 's' => 'Thu, 02 Jun 2022 16:58:35 Asia/Tokyo', + ], + ]; + } + + /** + * @dataProvider dataConvert + */ + public function testConvert($expected, string $s = 'now', string $tz_to = 'UTC', string $tz_from = 'UTC', string $format = DateTimeFormat::MYSQL) + { + $this->assertSame($expected, DateTimeFormat::convert($s, $tz_to, $tz_from, $format)); + } + + public function dataConvertNow() + { + return [ + 'now missing' => [ + ], + 'now empty' => [ + 's' => '', + ], + 'now now' => [ + 's' => 'now', + ], + ]; + } + + /** + * @dataProvider dataConvertNow + */ + public function testConvertNow(string $s = 'now', string $tz_to = 'UTC', string $tz_from = 'UTC', string $format = DateTimeFormat::MYSQL) + { + $this->assertSame(date(DateTimeFormat::MYSQL), DateTimeFormat::convert($s, $tz_to, $tz_from, $format)); + } + + } From 47e4bad15199f744150365f25db644e4c4795b24 Mon Sep 17 00:00:00 2001 From: Hypolite Petovan Date: Tue, 18 Mar 2025 19:07:25 -0400 Subject: [PATCH 04/24] Fix formatting in DateTimeFormatTest --- tests/src/Util/DateTimeFormatTest.php | 69 ++++++++++++++------------- 1 file changed, 35 insertions(+), 34 deletions(-) diff --git a/tests/src/Util/DateTimeFormatTest.php b/tests/src/Util/DateTimeFormatTest.php index 186741a67c..6cc506807e 100644 --- a/tests/src/Util/DateTimeFormatTest.php +++ b/tests/src/Util/DateTimeFormatTest.php @@ -16,39 +16,39 @@ class DateTimeFormatTest extends MockedTestCase { return [ 'validNormal' => [ - 'input' => '1990-10', + 'input' => '1990-10', 'assert' => true, ], 'validOneCharMonth' => [ - 'input' => '1990-1', + 'input' => '1990-1', 'assert' => true, ], 'validTwoCharMonth' => [ - 'input' => '1990-01', + 'input' => '1990-01', 'assert' => true, ], 'invalidFormat' => [ - 'input' => '199-11', + 'input' => '199-11', 'assert' => false, ], 'invalidFormat2' => [ - 'input' => '1990-15', + 'input' => '1990-15', 'assert' => false, ], 'invalidFormat3' => [ - 'input' => '99-101', + 'input' => '99-101', 'assert' => false, ], 'invalidFormat4' => [ - 'input' => '11-1990', + 'input' => '11-1990', 'assert' => false, ], 'invalidFuture' => [ - 'input' => '3030-12', + 'input' => '3030-12', 'assert' => false, ], 'invalidYear' => [ - 'input' => '-100-10', + 'input' => '-100-10', 'assert' => false, ], ]; @@ -79,55 +79,55 @@ class DateTimeFormatTest extends MockedTestCase return [ 'Mo, 19 Sep 2022 14:51:00 +0200' => [ 'expectedDate' => '2022-09-19T14:51:00+02:00', - 'dateString' => 'Mo, 19 Sep 2022 14:51:00 +0200', + 'dateString' => 'Mo, 19 Sep 2022 14:51:00 +0200', ], '2020-11-21T12:00:13.745339ZZ' => [ 'expectedDate' => '2020-11-21T12:00:13+00:00', - 'dateString' => '2020-11-21T12:00:13.745339ZZ', + 'dateString' => '2020-11-21T12:00:13.745339ZZ', ], '2016-09-09T13:32:00ZZ' => [ 'expectedDate' => '2016-09-09T13:32:00+00:00', - 'dateString' => '2016-09-09T13:32:00ZZ', + 'dateString' => '2016-09-09T13:32:00ZZ', ], 'Sun, 10/03/2021 - 12:41' => [ 'expectedDate' => '2021-10-03T12:41:00+00:00', - 'dateString' => 'Sun, 10/03/2021 - 12:41', + 'dateString' => 'Sun, 10/03/2021 - 12:41', ], '4:30 PM, Sep 13, 2022' => [ 'expectedDate' => '2022-09-13T16:30:00+00:00', - 'dateString' => '4:30 PM, Sep 13, 2022', + 'dateString' => '4:30 PM, Sep 13, 2022', ], 'August 27, 2022 - 21:00' => [ 'expectedDate' => '2022-08-27T21:00:00+00:00', - 'dateString' => 'August 27, 2022 - 21:00', + 'dateString' => 'August 27, 2022 - 21:00', ], '2021-09-19T14:06:03+00:00' => [ 'expectedDate' => '2021-09-19T14:06:03+00:00', - 'dateString' => '2021-09-19T14:06:03+00:00', + 'dateString' => '2021-09-19T14:06:03+00:00', ], 'Eastern Time timezone' => [ 'expectedDate' => '2022-09-30T00:00:00-05:00', - 'dateString' => 'September 30, 2022, 12:00 a.m. ET', + 'dateString' => 'September 30, 2022, 12:00 a.m. ET', ], 'German date time string' => [ 'expectedDate' => '2022-10-05T16:34:00+02:00', - 'dateString' => '05 Okt 2022 16:34:00 +0200', + 'dateString' => '05 Okt 2022 16:34:00 +0200', ], '(Coordinated Universal Time)' => [ 'expectedDate' => '2022-12-30T14:29:10+00:00', - 'dateString' => 'Fri Dec 30 2022 14:29:10 GMT+0000 (Coordinated Universal Time)', + 'dateString' => 'Fri Dec 30 2022 14:29:10 GMT+0000 (Coordinated Universal Time)', ], 'Double HTML encode' => [ 'expectedDate' => '2015-05-22T08:48:00+12:00', - 'dateString' => '2015-05-22T08:48:00+12:00' + 'dateString' => '2015-05-22T08:48:00+12:00' ], '2023-04-02\T17:22:42+05:30' => [ 'expectedDate' => '2023-04-02T17:22:42+05:30', - 'dateString' => '2023-04-02\T17:22:42+05:30' + 'dateString' => '2023-04-02\T17:22:42+05:30' ], '2025-03-07T08:54:14.341+01:00[Europe/Berlin]' => [ 'expectedDate' => '2025-03-07T08:54:14+01:00', - 'dateString' => '2025-03-07T08:54:14.341+01:00[Europe/Berlin]' + 'dateString' => '2025-03-07T08:54:14.341+01:00[Europe/Berlin]' ], ]; } @@ -155,53 +155,54 @@ class DateTimeFormatTest extends MockedTestCase */ public function testConvertRelative() { - $now = DateTimeFormat::utcNow('U'); + $now = DateTimeFormat::utcNow('U'); $date = DateTimeFormat::utc('now - 3 days', 'U'); $this->assertEquals(259200, $now - $date); } - public function dataConvert() { + public function dataConvert() + { return [ 'unix timestamp' => [ 'expected' => '2025-03-12 16:18:27', - 's' => '1741796307', + 's' => '1741796307', ], 'ATOM' => [ 'expected' => '2022-06-02 15:58:35', - 's' => '2022-06-02T16:58:35+01:00', + 's' => '2022-06-02T16:58:35+01:00', ], 'COOKIE' => [ 'expected' => '2022-06-02 14:58:35', - 's' => 'Thursday, 02-Jun-2022 16:58:35 Africa/Cairo', + 's' => 'Thursday, 02-Jun-2022 16:58:35 Africa/Cairo', ], 'ISO 8601/RFC 3339' => [ 'expected' => '2022-06-02 13:58:35', - 's' => '2022-06-02T16:58:35+0300', + 's' => '2022-06-02T16:58:35+0300', ], 'RFC 822/RFC 1036' => [ 'expected' => '2022-06-02 12:58:35', - 's' => 'Thu, 02 Jun 22 16:58:35 +0400', + 's' => 'Thu, 02 Jun 22 16:58:35 +0400', ], 'RFC 850' => [ 'expected' => '2022-06-02 11:58:35', - 's' => 'Thursday, 02-Jun-22 16:58:35 Indian/Kerguelen', + 's' => 'Thursday, 02-Jun-22 16:58:35 Indian/Kerguelen', ], 'RFC 1123/RFC 2822/RSS' => [ 'expected' => '2022-06-02 10:58:35', - 's' => 'Thu, 02 Jun 2022 16:58:35 +0600', + 's' => 'Thu, 02 Jun 2022 16:58:35 +0600', ], 'RFC 3339/W3C' => [ 'expected' => '2025-03-07 01:54:14', - 's' => '2025-03-07T08:54:14+07:00', + 's' => '2025-03-07T08:54:14+07:00', ], 'RFC 3339 extended' => [ 'expected' => '2025-03-07 00:54:14', - 's' => '2025-03-07T08:54:14.341+08:00', + 's' => '2025-03-07T08:54:14.341+08:00', ], 'RFC 7231' => [ 'expected' => '2022-06-02 07:58:35', - 's' => 'Thu, 02 Jun 2022 16:58:35 Asia/Tokyo', + 's' => 'Thu, 02 Jun 2022 16:58:35 Asia/Tokyo', ], ]; } From 5c1b5bae616876c363c44796aaee4004e562b6b5 Mon Sep 17 00:00:00 2001 From: Michael Date: Sat, 29 Mar 2025 09:07:11 +0000 Subject: [PATCH 05/24] Added more logging to track down issue 14800 --- src/Module/Photo.php | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/Module/Photo.php b/src/Module/Photo.php index 8843e82b0a..861e0b936a 100644 --- a/src/Module/Photo.php +++ b/src/Module/Photo.php @@ -130,6 +130,7 @@ class Photo extends BaseApi $photo = MPhoto::getPhoto($photoid, $scale, self::getCurrentUserID()); if ($photo === false) { + $this->logger->notice('Photo was not loaded', ['parameters' => $this->parameters, 'id' => $photoid]); throw new HTTPException\NotFoundException(DI::l10n()->t('The Photo with id %s is not available.', $photoid)); } } @@ -137,6 +138,7 @@ class Photo extends BaseApi $fetch = microtime(true) - $stamp; if ($photo === false) { + $this->logger->notice('Photo was not loaded', ['parameters' => $this->parameters]); throw new HTTPException\NotFoundException(); } @@ -151,6 +153,7 @@ class Photo extends BaseApi $mimetype = $photo['type']; } if (empty($imgdata) && empty($photo['blurhash'])) { + $this->logger->notice('Image data was not loaded', ['parameters' => $this->parameters, 'class' => $photo['backend-class'], 'ref' => $photo['backend-ref']]); throw new HTTPException\NotFoundException(); } @@ -317,7 +320,11 @@ class Photo extends BaseApi $photo = MPhoto::selectFirst([], ['scale' => $scale, 'uid' => $contact['uid'], 'profile' => 1]); if (!empty($photo)) { return $photo; + } else { + $this->logger->notice('Profile photo was not loaded', ['scale' => $scale, 'uid' => $contact['uid']]); } + } else { + $this->logger->notice('Local Contact was not found', ['url' => $contact['nurl']]); } } @@ -333,6 +340,7 @@ class Photo extends BaseApi if (!empty($photo)) { return $photo; } else { + $this->logger->notice('Photo was not loaded', ['resource-id' => $resourceid]); $url = $contact['avatar']; } } else { From 6cab9c010d6d9c860fc11478f7729c87a791fd5d Mon Sep 17 00:00:00 2001 From: Michael Date: Mon, 31 Mar 2025 20:38:37 +0000 Subject: [PATCH 06/24] Fix blurred images --- src/Module/Photo.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Module/Photo.php b/src/Module/Photo.php index 861e0b936a..8faef5aa6d 100644 --- a/src/Module/Photo.php +++ b/src/Module/Photo.php @@ -348,6 +348,8 @@ class Photo extends BaseApi } } elseif (!empty($contact['avatar'])) { $url = $contact['avatar']; + } else { + $url = ''; } // If it is a local link, we save resources by just redirecting to it. @@ -389,8 +391,6 @@ class Photo extends BaseApi } } - $url = ''; - if (empty($mimetext) && !empty($contact['blurhash'])) { $image = new Image('', image_type_to_mime_type(IMAGETYPE_WEBP)); $image->getFromBlurHash($contact['blurhash'], $customsize, $customsize); From 7bfd42ac55515df67e5c1bdcb6827906032ae9bf Mon Sep 17 00:00:00 2001 From: Art4 Date: Thu, 10 Apr 2025 11:18:38 +0000 Subject: [PATCH 07/24] log uncaught exceptions as critical --- src/Core/Logger/Handler/ErrorHandler.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Core/Logger/Handler/ErrorHandler.php b/src/Core/Logger/Handler/ErrorHandler.php index 2d311b5150..6d47674581 100644 --- a/src/Core/Logger/Handler/ErrorHandler.php +++ b/src/Core/Logger/Handler/ErrorHandler.php @@ -208,7 +208,7 @@ class ErrorHandler */ private function handleException(Throwable $e): void { - $level = LogLevel::ERROR; + $level = LogLevel::CRITICAL; foreach ($this->uncaughtExceptionLevelMap as $class => $candidate) { if ($e instanceof $class) { $level = $candidate; From 65624e2c190f9d5a80f3839f1cf131a619f10af7 Mon Sep 17 00:00:00 2001 From: Art4 Date: Mon, 14 Apr 2025 07:14:31 +0000 Subject: [PATCH 08/24] Create FileSystemUtil interface --- src/Core/Logger/Util/FileSystem.php | 2 +- src/Core/Logger/Util/FileSystemUtil.php | 38 +++++++++++++++++++++++++ 2 files changed, 39 insertions(+), 1 deletion(-) create mode 100644 src/Core/Logger/Util/FileSystemUtil.php diff --git a/src/Core/Logger/Util/FileSystem.php b/src/Core/Logger/Util/FileSystem.php index 2d05faf50d..b1c402946a 100644 --- a/src/Core/Logger/Util/FileSystem.php +++ b/src/Core/Logger/Util/FileSystem.php @@ -12,7 +12,7 @@ use Friendica\Core\Logger\Exception\LoggerUnusableException; /** * Util class for filesystem manipulation for Logger classes */ -class FileSystem +class FileSystem implements FileSystemUtil { /** * @var string a error message diff --git a/src/Core/Logger/Util/FileSystemUtil.php b/src/Core/Logger/Util/FileSystemUtil.php new file mode 100644 index 0000000000..f4052b8c59 --- /dev/null +++ b/src/Core/Logger/Util/FileSystemUtil.php @@ -0,0 +1,38 @@ + Date: Mon, 14 Apr 2025 08:00:31 +0000 Subject: [PATCH 09/24] Create StreamLoggerFactory with tests --- .../Logger/Factory/StreamLoggerFactory.php | 76 ++++++++++++++++ .../Factory/StreamLoggerFactoryTest.php | 87 +++++++++++++++++++ 2 files changed, 163 insertions(+) create mode 100644 src/Core/Logger/Factory/StreamLoggerFactory.php create mode 100644 tests/Unit/Core/Logger/Factory/StreamLoggerFactoryTest.php diff --git a/src/Core/Logger/Factory/StreamLoggerFactory.php b/src/Core/Logger/Factory/StreamLoggerFactory.php new file mode 100644 index 0000000000..ef8beec100 --- /dev/null +++ b/src/Core/Logger/Factory/StreamLoggerFactory.php @@ -0,0 +1,76 @@ +config = $config; + $this->introspection = $introspection; + $this->fileSystem = $fileSystem; + } + + /** + * Creates and returns a PSR-3 Logger instance. + * + * Calling this method multiple times with the same parameters SHOULD return the same object. + * + * @param \Psr\Log\LogLevel::* $logLevel The log level + * @param \Friendica\Core\Logger\Capability\LogChannel::* $logChannel The log channel + * + * @throws LoggerArgumentException + * @throws LogLevelException + */ + public function createLogger(string $logLevel, string $logChannel): LoggerInterface + { + $logfile = $this->config->get('system', 'logfile'); + + if (!file_exists($logfile) || !is_writable($logfile)) { + throw new LoggerArgumentException(sprintf('"%s" is not a valid logfile.', $logfile)); + } + + if (! array_key_exists($logLevel, StreamLogger::levelToInt)) { + throw new LogLevelException(sprintf('The log level "%s" is not supported by "%s".', $logLevel, StreamLogger::class)); + } + + return new StreamLogger( + $logChannel, + $this->introspection, + $this->fileSystem->createStream($logfile), + StreamLogger::levelToInt[$logLevel], + getmypid() + ); + } +} diff --git a/tests/Unit/Core/Logger/Factory/StreamLoggerFactoryTest.php b/tests/Unit/Core/Logger/Factory/StreamLoggerFactoryTest.php new file mode 100644 index 0000000000..5f2020324f --- /dev/null +++ b/tests/Unit/Core/Logger/Factory/StreamLoggerFactoryTest.php @@ -0,0 +1,87 @@ +createConfiguredMock( + IManageConfigValues::class, + [ + 'get' => dirname(__DIR__, 4) . '/datasets/log/empty.friendica.log.txt', + ] + ); + + $factory = new StreamLoggerFactory( + $config, + $this->createStub(IHaveCallIntrospections::class), + $this->createStub(FileSystemUtil::class), + ); + + $this->assertInstanceOf( + LoggerInterface::class, + $factory->createLogger(LogLevel::DEBUG, LogChannel::DEFAULT) + ); + } + + public function testCreateLoggerWithInvalidLogfileThrowsException(): void + { + $config = $this->createConfiguredMock( + IManageConfigValues::class, + [ + 'get' => dirname(__DIR__, 1) . '/not-existing-logfile.txt', + ] + ); + + $factory = new StreamLoggerFactory( + $config, + $this->createStub(IHaveCallIntrospections::class), + $this->createStub(FileSystemUtil::class), + ); + + $this->expectException(LoggerArgumentException::class); + $this->expectExceptionMessage('tests/Unit/Core/Logger/not-existing-logfile.txt" is not a valid logfile.'); + + $factory->createLogger(LogLevel::DEBUG, LogChannel::DEFAULT); + } + + public function testCreateLoggerWithInvalidLoglevelThrowsException(): void + { + $config = $this->createConfiguredMock( + IManageConfigValues::class, + [ + 'get' => dirname(__DIR__, 4) . '/datasets/log/empty.friendica.log.txt', + ] + ); + + $factory = new StreamLoggerFactory( + $config, + $this->createStub(IHaveCallIntrospections::class), + $this->createStub(FileSystemUtil::class), + ); + + $this->expectException(LogLevelException::class); + $this->expectExceptionMessage('The log level "unsupported-loglevel" is not supported by "Friendica\Core\Logger\Type\StreamLogger".'); + + $factory->createLogger('unsupported-loglevel', LogChannel::DEFAULT); + } +} From 0b41eb1528b99d92083f8259b93c7f5e92f72a5a Mon Sep 17 00:00:00 2001 From: Art4 Date: Mon, 14 Apr 2025 08:05:43 +0000 Subject: [PATCH 10/24] Deprecate StreamLogger factory in favour of StreamLoggerFactory --- src/Core/Logger/Factory/StreamLogger.php | 2 ++ src/Core/Logger/Factory/StreamLoggerFactory.php | 6 +----- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/src/Core/Logger/Factory/StreamLogger.php b/src/Core/Logger/Factory/StreamLogger.php index e14fe8258f..b4fbe73dcc 100644 --- a/src/Core/Logger/Factory/StreamLogger.php +++ b/src/Core/Logger/Factory/StreamLogger.php @@ -20,6 +20,8 @@ use Psr\Log\NullLogger; /** * The logger factory for the StreamLogger instance * + * @deprecated 2025.02 Use `Friendica\Core\Logger\Factory\StreamLoggerFactory` instead + * @see StreamLoggerFactory * @see StreamLoggerClass */ class StreamLogger extends AbstractLoggerTypeFactory diff --git a/src/Core/Logger/Factory/StreamLoggerFactory.php b/src/Core/Logger/Factory/StreamLoggerFactory.php index ef8beec100..8717d6c695 100644 --- a/src/Core/Logger/Factory/StreamLoggerFactory.php +++ b/src/Core/Logger/Factory/StreamLoggerFactory.php @@ -9,22 +9,18 @@ namespace Friendica\Core\Logger\Factory; use Friendica\Core\Config\Capability\IManageConfigValues; use Friendica\Core\Logger\Capability\IHaveCallIntrospections; -use Friendica\Core\Logger\Capability\LogChannel; use Friendica\Core\Logger\Exception\LoggerArgumentException; -use Friendica\Core\Logger\Exception\LoggerException; use Friendica\Core\Logger\Exception\LogLevelException; use Friendica\Core\Logger\Type\StreamLogger; -use Friendica\Core\Logger\Util\FileSystem; use Friendica\Core\Logger\Util\FileSystemUtil; use Psr\Log\LoggerInterface; -use Psr\Log\NullLogger; /** * The logger factory for the StreamLogger instance * * @see StreamLogger */ -class StreamLoggerFactory implements LoggerFactory +final class StreamLoggerFactory implements LoggerFactory { private IManageConfigValues $config; From 0cec45149718602d19697d8224a5fafaa4fc2ede Mon Sep 17 00:00:00 2001 From: Art4 Date: Mon, 14 Apr 2025 10:36:12 +0000 Subject: [PATCH 11/24] Create SyslogLoggerFactory --- .../Logger/Factory/SyslogLoggerFactory.php | 64 +++++++++++++++++++ .../Factory/StreamLoggerFactoryTest.php | 30 ++++----- .../Factory/SyslogLoggerFactoryTest.php | 61 ++++++++++++++++++ 3 files changed, 137 insertions(+), 18 deletions(-) create mode 100644 src/Core/Logger/Factory/SyslogLoggerFactory.php create mode 100644 tests/Unit/Core/Logger/Factory/SyslogLoggerFactoryTest.php diff --git a/src/Core/Logger/Factory/SyslogLoggerFactory.php b/src/Core/Logger/Factory/SyslogLoggerFactory.php new file mode 100644 index 0000000000..44f9d79c73 --- /dev/null +++ b/src/Core/Logger/Factory/SyslogLoggerFactory.php @@ -0,0 +1,64 @@ +config = $config; + $this->introspection = $introspection; + } + + /** + * Creates and returns a PSR-3 Logger instance. + * + * Calling this method multiple times with the same parameters SHOULD return the same object. + * + * @param \Psr\Log\LogLevel::* $logLevel The log level + * @param \Friendica\Core\Logger\Capability\LogChannel::* $logChannel The log channel + * + * @throws LogLevelException + */ + public function createLogger(string $logLevel, string $logChannel): LoggerInterface + { + $logOpts = (string) $this->config->get('system', 'syslog_flags') ?? SyslogLogger::DEFAULT_FLAGS; + $logFacility = (string) $this->config->get('system', 'syslog_facility') ?? SyslogLogger::DEFAULT_FACILITY; + + if (!array_key_exists($logLevel, SyslogLogger::logLevels)) { + throw new LogLevelException(sprintf('The log level "%s" is not supported by "%s".', $logLevel, SyslogLogger::class)); + } + + return new SyslogLogger( + $logChannel, + $this->introspection, + (string) SyslogLogger::logLevels[$logLevel], + $logOpts, + $logFacility + ); + } +} diff --git a/tests/Unit/Core/Logger/Factory/StreamLoggerFactoryTest.php b/tests/Unit/Core/Logger/Factory/StreamLoggerFactoryTest.php index 5f2020324f..744d597f19 100644 --- a/tests/Unit/Core/Logger/Factory/StreamLoggerFactoryTest.php +++ b/tests/Unit/Core/Logger/Factory/StreamLoggerFactoryTest.php @@ -24,12 +24,10 @@ class StreamLoggerFactoryTest extends TestCase { public function testCreateLoggerReturnsPsrLogger(): void { - $config = $this->createConfiguredMock( - IManageConfigValues::class, - [ - 'get' => dirname(__DIR__, 4) . '/datasets/log/empty.friendica.log.txt', - ] - ); + $config = $this->createStub(IManageConfigValues::class); + $config->method('get')->willReturnMap([ + ['system', 'logfile', null, dirname(__DIR__, 4) . '/datasets/log/empty.friendica.log.txt'], + ]); $factory = new StreamLoggerFactory( $config, @@ -45,12 +43,10 @@ class StreamLoggerFactoryTest extends TestCase public function testCreateLoggerWithInvalidLogfileThrowsException(): void { - $config = $this->createConfiguredMock( - IManageConfigValues::class, - [ - 'get' => dirname(__DIR__, 1) . '/not-existing-logfile.txt', - ] - ); + $config = $this->createStub(IManageConfigValues::class); + $config->method('get')->willReturnMap([ + ['system', 'logfile', null, dirname(__DIR__, 1) . '/not-existing-logfile.txt'], + ]); $factory = new StreamLoggerFactory( $config, @@ -66,12 +62,10 @@ class StreamLoggerFactoryTest extends TestCase public function testCreateLoggerWithInvalidLoglevelThrowsException(): void { - $config = $this->createConfiguredMock( - IManageConfigValues::class, - [ - 'get' => dirname(__DIR__, 4) . '/datasets/log/empty.friendica.log.txt', - ] - ); + $config = $this->createStub(IManageConfigValues::class); + $config->method('get')->willReturnMap([ + ['system', 'logfile', null, dirname(__DIR__, 4) . '/datasets/log/empty.friendica.log.txt'], + ]); $factory = new StreamLoggerFactory( $config, diff --git a/tests/Unit/Core/Logger/Factory/SyslogLoggerFactoryTest.php b/tests/Unit/Core/Logger/Factory/SyslogLoggerFactoryTest.php new file mode 100644 index 0000000000..7f94c66fcd --- /dev/null +++ b/tests/Unit/Core/Logger/Factory/SyslogLoggerFactoryTest.php @@ -0,0 +1,61 @@ +createStub(IManageConfigValues::class); + $config->method('get')->willReturnMap([ + ['system', 'syslog_flags', null, SyslogLogger::DEFAULT_FLAGS], + ['system', 'syslog_facility', null, SyslogLogger::DEFAULT_FACILITY], + ]); + + $factory = new SyslogLoggerFactory( + $config, + $this->createStub(IHaveCallIntrospections::class), + ); + + $this->assertInstanceOf( + LoggerInterface::class, + $factory->createLogger(LogLevel::DEBUG, LogChannel::DEFAULT) + ); + } + + public function testCreateLoggerWithInvalidLoglevelThrowsException(): void + { + $config = $this->createStub(IManageConfigValues::class); + $config->method('get')->willReturnMap([ + ['system', 'syslog_flags', null, SyslogLogger::DEFAULT_FLAGS], + ['system', 'syslog_facility', null, SyslogLogger::DEFAULT_FACILITY], + ]); + + $factory = new SyslogLoggerFactory( + $config, + $this->createStub(IHaveCallIntrospections::class), + ); + + $this->expectException(LogLevelException::class); + $this->expectExceptionMessage('The log level "unsupported-loglevel" is not supported by "Friendica\Core\Logger\Type\SyslogLogger".'); + + $factory->createLogger('unsupported-loglevel', LogChannel::DEFAULT); + } +} From 252b3980d03df924994007062c2eb3ebeccc0d4a Mon Sep 17 00:00:00 2001 From: Art4 Date: Mon, 14 Apr 2025 10:47:11 +0000 Subject: [PATCH 12/24] Deprecate SyslogLogger factory in favour of SyslogLoggerFactory --- src/Core/Logger/Factory/StreamLoggerFactory.php | 2 ++ src/Core/Logger/Factory/SyslogLogger.php | 2 ++ 2 files changed, 4 insertions(+) diff --git a/src/Core/Logger/Factory/StreamLoggerFactory.php b/src/Core/Logger/Factory/StreamLoggerFactory.php index 8717d6c695..5a5b994f40 100644 --- a/src/Core/Logger/Factory/StreamLoggerFactory.php +++ b/src/Core/Logger/Factory/StreamLoggerFactory.php @@ -5,6 +5,8 @@ // // SPDX-License-Identifier: AGPL-3.0-or-later +declare(strict_types=1); + namespace Friendica\Core\Logger\Factory; use Friendica\Core\Config\Capability\IManageConfigValues; diff --git a/src/Core/Logger/Factory/SyslogLogger.php b/src/Core/Logger/Factory/SyslogLogger.php index e9b59f1186..f95dcb42ff 100644 --- a/src/Core/Logger/Factory/SyslogLogger.php +++ b/src/Core/Logger/Factory/SyslogLogger.php @@ -16,6 +16,8 @@ use Psr\Log\LoggerInterface; /** * The logger factory for the SyslogLogger instance * + * @deprecated 2025.02 Use `Friendica\Core\Logger\Factory\SyslogLoggerFactory` instead + * @see SyslogLoggerFactory * @see SyslogLoggerClass */ class SyslogLogger extends AbstractLoggerTypeFactory From 8fb2fae841aa1903fd44abdad0d41358bbbdc4cf Mon Sep 17 00:00:00 2001 From: Art4 Date: Mon, 14 Apr 2025 12:58:15 +0000 Subject: [PATCH 13/24] Create DelegatingLoggerFactory --- .../Factory/DelegatingLoggerFactory.php | 62 +++++++++++++++ .../Factory/DelegatingLoggerFactoryTest.php | 75 +++++++++++++++++++ 2 files changed, 137 insertions(+) create mode 100644 src/Core/Logger/Factory/DelegatingLoggerFactory.php create mode 100644 tests/Unit/Core/Logger/Factory/DelegatingLoggerFactoryTest.php diff --git a/src/Core/Logger/Factory/DelegatingLoggerFactory.php b/src/Core/Logger/Factory/DelegatingLoggerFactory.php new file mode 100644 index 0000000000..924e4e7d1a --- /dev/null +++ b/src/Core/Logger/Factory/DelegatingLoggerFactory.php @@ -0,0 +1,62 @@ + */ + private array $factories = []; + + public function __construct(IManageConfigValues $config) + { + $this->config = $config; + } + + public function registerFactory(string $name, LoggerFactory $factory): void + { + $this->factories[$name] = $factory; + } + + /** + * Creates and returns a PSR-3 Logger instance. + * + * Calling this method multiple times with the same parameters SHOULD return the same object. + * + * @param \Psr\Log\LogLevel::* $logLevel The log level + * @param \Friendica\Core\Logger\Capability\LogChannel::* $logChannel The log channel + */ + public function createLogger(string $logLevel, string $logChannel): LoggerInterface + { + $factoryName = $this->config->get('system', 'logger_config') ?? ''; + + if (!array_key_exists($factoryName, $this->factories)) { + return new NullLogger(); + } + + $factory = $this->factories[$factoryName]; + + try { + $logger = $factory->createLogger($logLevel, $logChannel); + } catch (\Throwable $th) { + return new NullLogger(); + } + + return $logger; + } +} diff --git a/tests/Unit/Core/Logger/Factory/DelegatingLoggerFactoryTest.php b/tests/Unit/Core/Logger/Factory/DelegatingLoggerFactoryTest.php new file mode 100644 index 0000000000..b0fd92cafd --- /dev/null +++ b/tests/Unit/Core/Logger/Factory/DelegatingLoggerFactoryTest.php @@ -0,0 +1,75 @@ +createStub(IManageConfigValues::class); + $config->method('get')->willReturnMap([ + ['system', 'logger_config', null, 'test'], + ]); + + $factory = new DelegatingLoggerFactory($config); + + $factory->registerFactory('test', $this->createStub(LoggerFactory::class)); + + $this->assertInstanceOf( + LoggerInterface::class, + $factory->createLogger(LogLevel::DEBUG, LogChannel::DEFAULT) + ); + } + + public function testCreateLoggerWithoutRegisteredFactoryReturnsNullLogger(): void + { + $config = $this->createStub(IManageConfigValues::class); + $config->method('get')->willReturnMap([ + ['system', 'logger_config', null, 'not-existing-factory'], + ]); + + $factory = new DelegatingLoggerFactory($config); + + $this->assertInstanceOf( + NullLogger::class, + $factory->createLogger(LogLevel::DEBUG, LogChannel::DEFAULT) + ); + } + + public function testCreateLoggerWithExceptionThrowingFactoryReturnsNullLogger(): void + { + $config = $this->createStub(IManageConfigValues::class); + $config->method('get')->willReturnMap([ + ['system', 'logger_config', null, 'test'], + ]); + + $factory = new DelegatingLoggerFactory($config); + + $brokenFactory = $this->createStub(LoggerFactory::class); + $brokenFactory->method('createLogger')->willThrowException(new Exception()); + + $factory->registerFactory('test', $brokenFactory); + + $this->assertInstanceOf( + NullLogger::class, + $factory->createLogger(LogLevel::DEBUG, LogChannel::DEFAULT) + ); + } +} From 6b36d3e87f7f9284cd98ad48b8e25afd4fd1fc4e Mon Sep 17 00:00:00 2001 From: Art4 Date: Mon, 14 Apr 2025 13:54:48 +0000 Subject: [PATCH 14/24] Replace LegacyLoggerFactory with DelegatingLoggerFactory --- static/dependencies.config.php | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/static/dependencies.config.php b/static/dependencies.config.php index 5924ae512e..644cb5f765 100644 --- a/static/dependencies.config.php +++ b/static/dependencies.config.php @@ -171,11 +171,24 @@ return (function(string $basepath, array $getVars, array $serverVars, array $coo ], \Friendica\Core\Logger\LoggerManager::class => [ 'substitutions' => [ - \Friendica\Core\Logger\Factory\LoggerFactory::class => \Friendica\Core\Logger\Factory\LegacyLoggerFactory::class, + \Friendica\Core\Logger\Factory\LoggerFactory::class => \Friendica\Core\Logger\Factory\DelegatingLoggerFactory::class, ], ], \Friendica\Core\Logger\Factory\LoggerFactory::class => [ - 'instanceOf' => \Friendica\Core\Logger\Factory\LegacyLoggerFactory::class, + 'instanceOf' => \Friendica\Core\Logger\Factory\DelegatingLoggerFactory::class, + 'call' => [ + ['registerFactory', ['stream', [Dice::INSTANCE => '$StreamLoggerFactory']]], + ['registerFactory', ['syslog', [Dice::INSTANCE => '$SyslogLoggerFactory']]], + ], + ], + '$StreamLoggerFactory' => [ + 'instanceOf' => \Friendica\Core\Logger\Factory\StreamLoggerFactory::class, + 'substitutions' => [ + \Friendica\Core\Logger\Util\FileSystemUtil::class => \Friendica\Core\Logger\Util\FileSystem::class, + ], + ], + '$SyslogLoggerFactory' => [ + 'instanceOf' => \Friendica\Core\Logger\Factory\SyslogLoggerFactory::class, ], \Friendica\Core\Logger\Type\SyslogLogger::class => [ 'instanceOf' => \Friendica\Core\Logger\Factory\SyslogLogger::class, From af29baf11e4847d3c8898018c90077bc318a7642 Mon Sep 17 00:00:00 2001 From: Art4 Date: Mon, 14 Apr 2025 14:36:46 +0000 Subject: [PATCH 15/24] deprecate value `monolog` for config `system.logger_config` --- src/Core/Logger/Factory/DelegatingLoggerFactory.php | 9 +++++++++ static/defaults.config.php | 3 ++- 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/src/Core/Logger/Factory/DelegatingLoggerFactory.php b/src/Core/Logger/Factory/DelegatingLoggerFactory.php index 924e4e7d1a..2091b51bd9 100644 --- a/src/Core/Logger/Factory/DelegatingLoggerFactory.php +++ b/src/Core/Logger/Factory/DelegatingLoggerFactory.php @@ -45,6 +45,15 @@ final class DelegatingLoggerFactory implements LoggerFactory { $factoryName = $this->config->get('system', 'logger_config') ?? ''; + /** + * @deprecated 2025.02 The value `monolog` for `system.logger_config` inside the `config/local.config.php` file is deprecated, please use `stream` or `syslog` instead. + */ + if ($factoryName === 'monolog') { + @trigger_error('The config `system.logger_config` with value `monolog` is deprecated since 2025.02 and will stop working in 5 months, please change the value to `stream` or `syslog` in the `config/local.config.php` file.', \E_USER_DEPRECATED); + + $factoryName = 'stream'; + } + if (!array_key_exists($factoryName, $this->factories)) { return new NullLogger(); } diff --git a/static/defaults.config.php b/static/defaults.config.php index d821ff1813..df84d66899 100644 --- a/static/defaults.config.php +++ b/static/defaults.config.php @@ -334,7 +334,8 @@ return [ 'lock_driver' => '', // logger_config (String) - // Sets the logging adapter of Friendica globally (monolog, syslog, stream) + // Sets the logging adapter of Friendica globally (syslog, stream) + // @deprecated 2025.02 The value `monolog` is deprecated, please use `stream` or `syslog` instead. 'logger_config' => 'stream', // syslog_flags (Integer) From fc1d8e92f3ef7d4150e6491432c34f812ba71f51 Mon Sep 17 00:00:00 2001 From: Art4 Date: Mon, 14 Apr 2025 14:38:49 +0000 Subject: [PATCH 16/24] Fix code style --- src/Core/Logger/Util/FileSystem.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Core/Logger/Util/FileSystem.php b/src/Core/Logger/Util/FileSystem.php index b1c402946a..97162dacb8 100644 --- a/src/Core/Logger/Util/FileSystem.php +++ b/src/Core/Logger/Util/FileSystem.php @@ -31,7 +31,7 @@ class FileSystem implements FileSystemUtil public function createDir(string $file): string { $dirname = null; - $pos = strpos($file, '://'); + $pos = strpos($file, '://'); if (!$pos) { $dirname = realpath(dirname($file)); From c0065a68b39879d728004ffce1f1e2b47f602b84 Mon Sep 17 00:00:00 2001 From: Art4 Date: Mon, 14 Apr 2025 14:39:39 +0000 Subject: [PATCH 17/24] Set new factories as internal --- src/Core/Logger/Factory/StreamLoggerFactory.php | 2 ++ src/Core/Logger/Factory/SyslogLoggerFactory.php | 2 ++ 2 files changed, 4 insertions(+) diff --git a/src/Core/Logger/Factory/StreamLoggerFactory.php b/src/Core/Logger/Factory/StreamLoggerFactory.php index 5a5b994f40..8119c40430 100644 --- a/src/Core/Logger/Factory/StreamLoggerFactory.php +++ b/src/Core/Logger/Factory/StreamLoggerFactory.php @@ -21,6 +21,8 @@ use Psr\Log\LoggerInterface; * The logger factory for the StreamLogger instance * * @see StreamLogger + * + * @internal */ final class StreamLoggerFactory implements LoggerFactory { diff --git a/src/Core/Logger/Factory/SyslogLoggerFactory.php b/src/Core/Logger/Factory/SyslogLoggerFactory.php index 44f9d79c73..24a884a5f9 100644 --- a/src/Core/Logger/Factory/SyslogLoggerFactory.php +++ b/src/Core/Logger/Factory/SyslogLoggerFactory.php @@ -19,6 +19,8 @@ use Psr\Log\LoggerInterface; * The logger factory for the SyslogLogger instance * * @see SyslogLogger + * + * @internal */ final class SyslogLoggerFactory implements LoggerFactory { From d410758cdde6d8763fc55d75fed40dce2d0da5df Mon Sep 17 00:00:00 2001 From: Art4 Date: Mon, 14 Apr 2025 14:40:44 +0000 Subject: [PATCH 18/24] Remove obsolet LegacyLoggerFactory --- .../Logger/Factory/LegacyLoggerFactory.php | 61 ------------------- .../Factory/LegacyLoggerFactoryTest.php | 36 ----------- 2 files changed, 97 deletions(-) delete mode 100644 src/Core/Logger/Factory/LegacyLoggerFactory.php delete mode 100644 tests/Unit/Core/Logger/Factory/LegacyLoggerFactoryTest.php diff --git a/src/Core/Logger/Factory/LegacyLoggerFactory.php b/src/Core/Logger/Factory/LegacyLoggerFactory.php deleted file mode 100644 index 2c7b6c0237..0000000000 --- a/src/Core/Logger/Factory/LegacyLoggerFactory.php +++ /dev/null @@ -1,61 +0,0 @@ -instanceCreator = $instanceCreator; - $this->config = $config; - $this->profiler = $profiler; - } - - /** - * Creates and returns a PSR-3 Logger instance. - * - * Calling this method multiple times with the same parameters SHOULD return the same object. - * - * @param \Psr\Log\LogLevel::* $logLevel The log level - * @param \Friendica\Core\Logger\Capability\LogChannel::* $logChannel The log channel - */ - public function createLogger(string $logLevel, string $logChannel): LoggerInterface - { - $factory = new Logger($logChannel); - - return $factory->create($this->instanceCreator, $this->config, $this->profiler); - } -} diff --git a/tests/Unit/Core/Logger/Factory/LegacyLoggerFactoryTest.php b/tests/Unit/Core/Logger/Factory/LegacyLoggerFactoryTest.php deleted file mode 100644 index 9ef920c71f..0000000000 --- a/tests/Unit/Core/Logger/Factory/LegacyLoggerFactoryTest.php +++ /dev/null @@ -1,36 +0,0 @@ -createStub(ICanCreateInstances::class), - $this->createStub(IManageConfigValues::class), - $this->createStub(Profiler::class), - ); - - $this->assertInstanceOf( - LoggerInterface::class, - $factory->createLogger(LogLevel::DEBUG, LogChannel::DEFAULT) - ); - } -} From 60ad014a6ed589380f944f7b58f00769f8bd9d48 Mon Sep 17 00:00:00 2001 From: Art4 Date: Mon, 14 Apr 2025 14:51:44 +0000 Subject: [PATCH 19/24] Hard-deprecation for now unused factory classes --- src/Core/Logger/Factory/AbstractLoggerTypeFactory.php | 4 ++++ src/Core/Logger/Factory/DelegatingLoggerFactory.php | 2 ++ src/Core/Logger/Factory/Logger.php | 4 ++++ src/Core/Logger/Factory/StreamLogger.php | 4 +++- src/Core/Logger/Factory/SyslogLogger.php | 4 +++- src/Core/Logger/Util/FileSystemUtil.php | 2 ++ 6 files changed, 18 insertions(+), 2 deletions(-) diff --git a/src/Core/Logger/Factory/AbstractLoggerTypeFactory.php b/src/Core/Logger/Factory/AbstractLoggerTypeFactory.php index 08a9559279..d2d606f65f 100644 --- a/src/Core/Logger/Factory/AbstractLoggerTypeFactory.php +++ b/src/Core/Logger/Factory/AbstractLoggerTypeFactory.php @@ -12,6 +12,8 @@ use Psr\Log\LogLevel; /** * Abstract class for creating logger types, which includes common necessary logic/content + * + * @deprecated 2025.02 Implement `\Friendica\Core\Logger\Factory\LoggerFactory` instead */ abstract class AbstractLoggerTypeFactory { @@ -25,6 +27,8 @@ abstract class AbstractLoggerTypeFactory */ public function __construct(IHaveCallIntrospections $introspection, string $channel) { + @trigger_error('Class `' . __CLASS__ . '` is deprecated since 2025.02 and will be removed after 5 months, implement `\Friendica\Core\Logger\Factory\LoggerFactory` instead.', E_USER_DEPRECATED); + $this->channel = $channel; $this->introspection = $introspection; } diff --git a/src/Core/Logger/Factory/DelegatingLoggerFactory.php b/src/Core/Logger/Factory/DelegatingLoggerFactory.php index 2091b51bd9..f0001132dd 100644 --- a/src/Core/Logger/Factory/DelegatingLoggerFactory.php +++ b/src/Core/Logger/Factory/DelegatingLoggerFactory.php @@ -15,6 +15,8 @@ use Psr\Log\NullLogger; /** * Delegates the creation of a logger based on config to other factories + * + * @internal */ final class DelegatingLoggerFactory implements LoggerFactory { diff --git a/src/Core/Logger/Factory/Logger.php b/src/Core/Logger/Factory/Logger.php index fbee580544..78451e713d 100644 --- a/src/Core/Logger/Factory/Logger.php +++ b/src/Core/Logger/Factory/Logger.php @@ -18,6 +18,8 @@ use Throwable; /** * The logger factory for the core logging instances + * + * @deprecated 2025.02 Implement `\Friendica\Core\Logger\Factory\LoggerFactory` instead */ class Logger { @@ -26,6 +28,8 @@ class Logger public function __construct(string $channel = LogChannel::DEFAULT) { + @trigger_error('Class `' . __CLASS__ . '` is deprecated since 2025.02 and will be removed after 5 months, implement `\Friendica\Core\Logger\Factory\LoggerFactory` instead.', E_USER_DEPRECATED); + $this->channel = $channel; } diff --git a/src/Core/Logger/Factory/StreamLogger.php b/src/Core/Logger/Factory/StreamLogger.php index b4fbe73dcc..b2c6de7f3e 100644 --- a/src/Core/Logger/Factory/StreamLogger.php +++ b/src/Core/Logger/Factory/StreamLogger.php @@ -20,7 +20,7 @@ use Psr\Log\NullLogger; /** * The logger factory for the StreamLogger instance * - * @deprecated 2025.02 Use `Friendica\Core\Logger\Factory\StreamLoggerFactory` instead + * @deprecated 2025.02 Implement `\Friendica\Core\Logger\Factory\LoggerFactory` instead * @see StreamLoggerFactory * @see StreamLoggerClass */ @@ -40,6 +40,8 @@ class StreamLogger extends AbstractLoggerTypeFactory */ public function create(IManageConfigValues $config, string $logfile = null, string $channel = null): LoggerInterface { + @trigger_error('Class `' . __CLASS__ . '` is deprecated since 2025.02 and will be removed after 5 months, implement `\Friendica\Core\Logger\Factory\LoggerFactory` instead.', E_USER_DEPRECATED); + $fileSystem = new FileSystem(); $logfile = $logfile ?? $config->get('system', 'logfile'); diff --git a/src/Core/Logger/Factory/SyslogLogger.php b/src/Core/Logger/Factory/SyslogLogger.php index f95dcb42ff..d9f98f05fd 100644 --- a/src/Core/Logger/Factory/SyslogLogger.php +++ b/src/Core/Logger/Factory/SyslogLogger.php @@ -16,7 +16,7 @@ use Psr\Log\LoggerInterface; /** * The logger factory for the SyslogLogger instance * - * @deprecated 2025.02 Use `Friendica\Core\Logger\Factory\SyslogLoggerFactory` instead + * @deprecated 2025.02 Implement `\Friendica\Core\Logger\Factory\LoggerFactory` instead * @see SyslogLoggerFactory * @see SyslogLoggerClass */ @@ -33,6 +33,8 @@ class SyslogLogger extends AbstractLoggerTypeFactory */ public function create(IManageConfigValues $config): LoggerInterface { + @trigger_error('Class `' . __CLASS__ . '` is deprecated since 2025.02 and will be removed after 5 months, implement `\Friendica\Core\Logger\Factory\LoggerFactory` instead.', E_USER_DEPRECATED); + $logOpts = $config->get('system', 'syslog_flags') ?? SyslogLoggerClass::DEFAULT_FLAGS; $logFacility = $config->get('system', 'syslog_facility') ?? SyslogLoggerClass::DEFAULT_FACILITY; $loglevel = SyslogLogger::mapLegacyConfigDebugLevel($config->get('system', 'loglevel')); diff --git a/src/Core/Logger/Util/FileSystemUtil.php b/src/Core/Logger/Util/FileSystemUtil.php index f4052b8c59..1c1bbc2726 100644 --- a/src/Core/Logger/Util/FileSystemUtil.php +++ b/src/Core/Logger/Util/FileSystemUtil.php @@ -11,6 +11,8 @@ use Friendica\Core\Logger\Exception\LoggerUnusableException; /** * interface for Util class for filesystem manipulation for Logger classes + * + * @internal */ interface FileSystemUtil { From 32b657b793ac6ffb1c8175987b20a55eef5abe25 Mon Sep 17 00:00:00 2001 From: Art4 Date: Mon, 14 Apr 2025 15:06:30 +0000 Subject: [PATCH 20/24] fix code style --- .../Logger/Factory/AbstractLoggerTypeFactory.php | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/Core/Logger/Factory/AbstractLoggerTypeFactory.php b/src/Core/Logger/Factory/AbstractLoggerTypeFactory.php index d2d606f65f..98c05e187c 100644 --- a/src/Core/Logger/Factory/AbstractLoggerTypeFactory.php +++ b/src/Core/Logger/Factory/AbstractLoggerTypeFactory.php @@ -48,21 +48,21 @@ abstract class AbstractLoggerTypeFactory // legacy WARNING case "0": return LogLevel::ERROR; - // legacy INFO + // legacy INFO case "1": return LogLevel::WARNING; - // legacy TRACE + // legacy TRACE case "2": return LogLevel::NOTICE; - // legacy DEBUG + // legacy DEBUG case "3": return LogLevel::INFO; - // legacy DATA + // legacy DATA case "4": - // legacy ALL + // legacy ALL case "5": return LogLevel::DEBUG; - // default if nothing set + // default if nothing set default: return $level; } From e7bd6e7663515a318c153e839d6a01f59113e767 Mon Sep 17 00:00:00 2001 From: Michael Date: Tue, 15 Apr 2025 02:03:35 +0000 Subject: [PATCH 21/24] Issue 14881: Fixed language detection for danish --- src/Core/L10n.php | 29 +++++++++++++++++++++++++++-- 1 file changed, 27 insertions(+), 2 deletions(-) diff --git a/src/Core/L10n.php b/src/Core/L10n.php index 79d83950a9..42949fa1fe 100644 --- a/src/Core/L10n.php +++ b/src/Core/L10n.php @@ -51,6 +51,11 @@ class L10n 'zh-cn' => '简体中文', ]; + CONST LANG_PARENTS = [ + 'en-gb' => 'en', 'da-dk' => 'da', 'fi-fi' => 'fi', + 'nb-no' => 'nb', 'pt-br' => 'pt', 'zh-cn' => 'zh' + ]; + /** @var string Undetermined language */ const UNDETERMINED_LANGUAGE = 'un'; @@ -150,6 +155,11 @@ class L10n $a = new \stdClass(); $a->strings = []; + $child = array_search($lang, $this::LANG_PARENTS); + if ($child) { + $lang = $child; + } + // load enabled addons strings $addons = array_keys($this->config->get('addons') ?? []); foreach ($addons as $addon) { @@ -203,6 +213,8 @@ class L10n // start with quality zero (every guessed language is more acceptable ..) $current_q = 0; + $supported = self::getSupportedLanguages(); + foreach ($acceptedLanguages as $acceptedLanguage) { $res = preg_match( '/^([a-z]{1,8}(?:-[a-z]{1,8})*)(?:;\s*q=(0(?:\.[0-9]{1,3})?|1(?:\.0{1,3})?))?$/i', @@ -230,8 +242,7 @@ class L10n while (count($lang_code)) { // try to mix them so we can get double-code parts too $match_lang = strtolower(join('-', $lang_code)); - if (file_exists(__DIR__ . "/../../view/lang/$match_lang") && - is_dir(__DIR__ . "/../../view/lang/$match_lang")) { + if (in_array($match_lang, $supported)) { if ($lang_quality > $current_q) { $current_lang = $match_lang; $current_q = $lang_quality; @@ -247,6 +258,20 @@ class L10n return $current_lang; } + private static function getSupportedLanguages(): array + { + $languages = []; + foreach (glob('view/lang/*/strings.php') as $language) { + $code = str_replace(['view/lang/', '/strings.php'], [], $language); + if (!empty(self::LANG_PARENTS[$code])) { + $languages[] = self::LANG_PARENTS[$code]; + } + $languages[] = $code; + } + + return $languages; + } + /** * Return the localized version of the provided string with optional string interpolation * From cc4ff9584ff1b6e32ac34ef8989d9b3686b5b799 Mon Sep 17 00:00:00 2001 From: Michael Date: Tue, 15 Apr 2025 02:09:19 +0000 Subject: [PATCH 22/24] Fixed codestyle --- src/Core/L10n.php | 34 ++++++++++++++++++++++------------ 1 file changed, 22 insertions(+), 12 deletions(-) diff --git a/src/Core/L10n.php b/src/Core/L10n.php index 42949fa1fe..d1af1c0e61 100644 --- a/src/Core/L10n.php +++ b/src/Core/L10n.php @@ -51,7 +51,7 @@ class L10n 'zh-cn' => '简体中文', ]; - CONST LANG_PARENTS = [ + const LANG_PARENTS = [ 'en-gb' => 'en', 'da-dk' => 'da', 'fi-fi' => 'fi', 'nb-no' => 'nb', 'pt-br' => 'pt', 'zh-cn' => 'zh' ]; @@ -427,8 +427,10 @@ class L10n ]; if (in_array('cld2', get_loaded_extensions())) { - $additional_langs = array_merge($additional_langs, - ['dv', 'kn', 'lo', 'ml', 'or', 'pa', 'sd', 'si', 'te', 'yi']); + $additional_langs = array_merge( + $additional_langs, + ['dv', 'kn', 'lo', 'ml', 'or', 'pa', 'sd', 'si', 'te', 'yi'] + ); } $langs = array_merge($additional_langs, array_keys($this->getAvailableLanguages())); @@ -444,7 +446,7 @@ class L10n */ public function getLanguageCodes(bool $international = false): array { - $iso639 = new \Matriphe\ISO639\ISO639; + $iso639 = new \Matriphe\ISO639\ISO639(); // In ISO 639-2 undetermined languages have got the code "und". // There is no official code for ISO 639-1, but "un" is not assigned to any language. @@ -502,13 +504,17 @@ class L10n */ public function getDay(string $s): string { - $ret = str_replace(['Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday', 'Sunday'], + $ret = str_replace( + ['Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday', 'Sunday'], [$this->t('Monday'), $this->t('Tuesday'), $this->t('Wednesday'), $this->t('Thursday'), $this->t('Friday'), $this->t('Saturday'), $this->t('Sunday')], - $s); + $s + ); - $ret = str_replace(['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December'], + $ret = str_replace( + ['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December'], [$this->t('January'), $this->t('February'), $this->t('March'), $this->t('April'), $this->t('May'), $this->t('June'), $this->t('July'), $this->t('August'), $this->t('September'), $this->t('October'), $this->t('November'), $this->t('December')], - $ret); + $ret + ); return $ret; } @@ -521,13 +527,17 @@ class L10n */ public function getDayShort(string $s): string { - $ret = str_replace(['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'], + $ret = str_replace( + ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'], [$this->t('Mon'), $this->t('Tue'), $this->t('Wed'), $this->t('Thu'), $this->t('Fri'), $this->t('Sat'), $this->t('Sun')], - $s); + $s + ); - $ret = str_replace(['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'], + $ret = str_replace( + ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'], [$this->t('Jan'), $this->t('Feb'), $this->t('Mar'), $this->t('Apr'), $this->t('May'), $this->t('Jun'), $this->t('Jul'), $this->t('Aug'), $this->t('Sep'), $this->t('Oct'), $this->t('Nov'), $this->t('Dec')], - $ret); + $ret + ); return $ret; } From 37d324e63ba9e38d52fc86ccb32972fbff24bf2a Mon Sep 17 00:00:00 2001 From: Michael Date: Wed, 16 Apr 2025 04:13:09 +0000 Subject: [PATCH 23/24] Bluesky: use did based profile links --- src/Network/Probe.php | 2 +- src/Protocol/ATProtocol/Actor.php | 2 +- src/Protocol/ATProtocol/Processor.php | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Network/Probe.php b/src/Network/Probe.php index a7e7b8275c..471f6e313a 100644 --- a/src/Network/Probe.php +++ b/src/Network/Probe.php @@ -1729,7 +1729,7 @@ class Probe $data = [ 'network' => Protocol::BLUESKY, 'url' => $profile->did, - 'alias' => ATProtocol::WEB . '/profile/' . $nick, + 'alias' => ATProtocol::WEB . '/profile/' . $profile->did, 'name' => $name ?: $nick, 'nick' => $nick, 'addr' => $nick, diff --git a/src/Protocol/ATProtocol/Actor.php b/src/Protocol/ATProtocol/Actor.php index 862d10eeea..db310813b3 100755 --- a/src/Protocol/ATProtocol/Actor.php +++ b/src/Protocol/ATProtocol/Actor.php @@ -117,7 +117,7 @@ class Actor $name = $profile->displayName ?? $nick; $fields = [ - 'alias' => ATProtocol::WEB . '/profile/' . $nick, + 'alias' => ATProtocol::WEB . '/profile/' . $profile->did, 'name' => $name ?: $nick, 'nick' => $nick, 'addr' => $nick, diff --git a/src/Protocol/ATProtocol/Processor.php b/src/Protocol/ATProtocol/Processor.php index 502b31ea4a..d7a26f78cf 100755 --- a/src/Protocol/ATProtocol/Processor.php +++ b/src/Protocol/ATProtocol/Processor.php @@ -72,7 +72,7 @@ class Processor public function processIdentity(stdClass $data) { $fields = [ - 'alias' => ATProtocol::WEB . '/profile/' . $data->identity->handle, + 'alias' => ATProtocol::WEB . '/profile/' . $data->identity->did, 'nick' => $data->identity->handle, 'addr' => $data->identity->handle, 'updated' => DateTimeFormat::utc($data->identity->time, DateTimeFormat::MYSQL), From f840d1f582308d89e49c10d87c4eabb2872e14ca Mon Sep 17 00:00:00 2001 From: Marcus Funch Date: Sat, 26 Apr 2025 00:38:25 +0200 Subject: [PATCH 24/24] Frio: Fix bug making navigation tooltips disappear after scroll --- view/theme/frio/templates/nav.tpl | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/view/theme/frio/templates/nav.tpl b/view/theme/frio/templates/nav.tpl index 6aa876a002..37edb7118a 100644 --- a/view/theme/frio/templates/nav.tpl +++ b/view/theme/frio/templates/nav.tpl @@ -61,7 +61,7 @@ {{if $nav.network}} @@ -70,14 +70,14 @@ {{if $nav.channel}} {{/if}} {{if $nav.home}} {{/if}} {{if $nav.messages}} @@ -112,7 +112,7 @@ {{if $nav.contacts}}