Compare commits

...

215 Commits

Author SHA1 Message Date
Hypolite Petovan cfff03f6e2 [v2.4.2] Bump version number for release 2024-03-07 14:18:49 -05:00
Tobias Diekershoff 4524ad166b Merge pull request 'Move Visit server link to server name' (#103) from MrPetovan/friendica-directory:enhancement/100-move-visit-server-link into stable
Reviewed-on: #103
2024-03-07 15:48:26 +01:00
Hypolite Petovan 2ffb047736 Move Visit server link to server name 2024-03-07 09:42:45 -05:00
heluecht f4bff37432 Merge pull request 'Update source code link to point to Friendica's Forgejo' (#101) from MrPetovan/friendica-directory:enhancement/97-source-code-link into stable
Reviewed-on: #101
2024-03-07 15:36:49 +01:00
heluecht 62c6c0f1cc Merge pull request 'Change icon for Approval registration policy from dungeon door to stamp' (#102) from MrPetovan/friendica-directory:enhancement/99-unstable-version-icon into stable
Reviewed-on: #102
2024-03-07 15:35:43 +01:00
Hypolite Petovan 5f79768a6b Change icon for unstable version from poop to bug 2024-03-07 09:23:51 -05:00
Hypolite Petovan 08950be588 Change icon for Approval registration policy from dungeon door to stamp 2024-03-07 09:23:36 -05:00
Hypolite Petovan 9d77067e79 Update source code link to point to Friendica's Forgejo 2024-03-07 09:21:03 -05:00
Tobias Diekershoff 13b95e22da Merge pull request '[Composer] Change gofabian/negotiation-middleware dependency requirement to support PHP 8.2' (#96) from MrPetovan/friendica-directory:bug/php-8 into stable
Reviewed-on: #96
2023-08-09 07:01:21 +02:00
Hypolite Petovan 9d296b9f5d [Composer] Change gofabian/negotiation-middleware dependency requirement to support PHP 8
- Add now-explicit laminas/laminas-zendframework-bridge dependency
- Bump PHP version requirement to 7.3.0
- Updating willdurand/negotiation (v2.3.1 => 3.1.0) (PHP 8 compatibility)
- Updating psr/http-message (1.0.1 => 1.1)
- Removing gofabian/negotiation-middleware (v0.1.3)
- Installing gofabian/negotiation-middleware (dev-master 4d3cda5)
- Updating gettext/languages (2.9.0 => 2.10.0)
- Updating gettext/gettext (v4.8.6 => v4.8.8)
- Updating guzzlehttp/psr7 (1.8.5 => 1.9.1)
- Updating guzzlehttp/promises (1.5.1 => 1.5.3)
- Updating symfony/polyfill-php72 (v1.26.0 => v1.27.0)
- Updating symfony/polyfill-intl-normalizer (v1.26.0 => v1.27.0)
- Updating symfony/polyfill-intl-idn (v1.26.0 => v1.27.0)
- Updating guzzlehttp/guzzle (6.5.6 => 6.5.8)
- Updating laminas/laminas-escaper (2.6.1 => 2.9.0)
- Updating laminas/laminas-zendframework-bridge (1.1.1 => 1.4.1)
- Updating masterminds/html5 (2.7.5 => 2.8.0)
- Updating monolog/monolog (1.26.1 => 1.27.1)
- Updating psr/container (1.0.0 => 1.1.1)
- Updating pimple/pimple (v3.2.3 => v3.5.0)
- Updating slim/slim (3.12.3 => 3.12.5)
- Updating pear/pear-core-minimal (v1.10.11 => v1.10.13)
2023-07-31 01:24:50 +02:00
Hypolite Petovan bf85c8f986 Catch rare exception receiving line folding characters in header value of redirected request 2023-07-31 01:17:33 +02:00
Hypolite Petovan 064fd922e7 Add default profile picture path dependency
- Update default profile picture
2023-07-31 01:11:33 +02:00
Hypolite Petovan 95ffadb19f Fix ternary operator ambiguity deprecation in Utils\L10n 2023-07-31 01:10:37 +02:00
Tobias Diekershoff 8bd1cb11ae Merge pull request 'Rename "forum" to "group"' (#94) from MrPetovan/friendica-directory:task/rename-forum-to-group into stable
Reviewed-on: #94
2023-06-05 06:37:27 +02:00
Hypolite Petovan aade2ec534 Rename "forum" to "group"
- This reflects a change in Friendica Core
2023-06-04 00:16:38 -04:00
Hypolite Petovan f854d7d5f7 Fix several PHP messages
- Add logging for exceptions
2023-03-12 17:49:44 -04:00
Tobias Diekershoff 230f17ef36 Merge pull request 'Normalize HTTP client user agent' (#90) from MrPetovan/friendica-directory:task/89-normalize-user-agent into stable
Reviewed-on: #90
2022-06-07 05:40:29 +00:00
Hypolite Petovan 30f6e6f5de [Composer] Remove unused dependency byjg/webrequest 2022-06-06 02:23:57 -04:00
Hypolite Petovan d1a72232b3 Replace ByJG\WebRequest by GuzzleHttp\ClientInterface 2022-06-06 02:22:56 -04:00
Hypolite Petovan 9ff681a460 Fix database error with false bolean value in profile table 2022-06-06 02:22:39 -04:00
Hypolite Petovan 0ee5bf55b1 Replace direct curl uses with Guzzle HTTP client
- Add http dependency with custom User Agent
- Simplify profile poll to remove the second profile call to check availability
- Remove obsolete Network::fetchURL and Network::testURL
2022-06-06 02:22:37 -04:00
Hypolite Petovan 5829108c0e [Composer] Require guzzlehttp/guzzle:^6.5 2022-06-06 01:32:00 -04:00
Hypolite Petovan 91aa66ed77 [v2.4.1] Bump version number for release 2022-05-12 14:18:43 -04:00
Hypolite Petovan 7fdbaa3678 Don't encode the zrl parameter in external links
- Fix a "Forbidden" issue with /remote_follow links
2022-05-12 14:18:19 -04:00
Hypolite Petovan df660b66c9 [v2.4.0] Bump version number for release 2022-05-12 09:15:19 -04:00
heluecht 3f38b3adea Merge pull request 'Remove obsolete profile.dfrn_request field' (#87) from MrPetovan/friendica-directory:task/86-remove-dfrn_request into stable
Reviewed-on: #87
2022-05-11 04:17:41 +00:00
Hypolite Petovan e65bb660ce [Database v0010] Drop unused column profile.dfrn_request 2022-05-07 16:10:43 -04:00
Hypolite Petovan a5700cb2c9 Remove references to dfrn_request 2022-05-07 16:10:43 -04:00
Hypolite Petovan 2627b54349 Replace dfrn_request by subscribe URL when available for the follow link
- Falls back to the `/remote_follow` module available since Friendica version 2020.03
- Falls back to the profile URL
- Remove unused atlas dependency in a couple API controllers
2022-05-07 16:10:42 -04:00
Hypolite Petovan 8988ad9f9d Add subscribe URL retrieval to server poll 2022-05-07 16:09:30 -04:00
Hypolite Petovan 7d5012e72e [Database v0009] Add server.subscribe_url field 2022-05-07 16:09:30 -04:00
Hypolite Petovan b0142f6ab2 Add optional version parameter to console updatedb 2022-05-07 16:09:29 -04:00
Hypolite Petovan cd809f7646 Remove mention of non-existent console parameters in Install and UpdateDb 2022-05-07 16:09:29 -04:00
Hypolite Petovan 2aba91d42c Add HTML link to profile to account name
- Normalize white space in HTML
2022-05-07 16:09:27 -04:00
Hypolite Petovan 2e84f5aa7b Merge pull request 'added DA DK translation THX atjn' (#88) from tobias/friendica-directory:20220501-daDK into stable
Reviewed-on: #88
2022-05-04 21:45:51 +00:00
Tobias Diekershoff 1ebddb042f
PL translation updated THX strebski 2022-05-03 09:17:55 +02:00
Tobias Diekershoff 0344c77336
added DA DK translation THX atjn 2022-05-01 20:17:13 +02:00
Hypolite Petovan 5a16bcb57c Merge pull request 'PL translation updated THX strebski' (#85) from tobias/friendica-directory:20220318-pl into stable
Reviewed-on: #85
2022-04-25 11:27:41 +00:00
Tobias Diekershoff 146d4e6374
PL translation updated THX strebski 2022-03-18 17:41:59 +01:00
Hypolite Petovan ef91f43be0 [v2.3.5] Bump version number for release 2022-01-23 06:13:16 -05:00
Hypolite Petovan a3a5bd8ab4 Merge pull request 'added SV translation THX Kristoffer Grundström' (#84) from tobias/friendica-directory:20220122-sv into stable
Reviewed-on: #84
2022-01-23 01:27:15 +00:00
Tobias Diekershoff c34fcd11f8 added Swedish to the languages array 2022-01-22 19:09:24 +01:00
Tobias Diekershoff 1356001334 added SV translation THX Kristoffer Grundström 2022-01-22 19:08:37 +01:00
Hypolite Petovan b344935edc [v2.3.4] Bump version number for release 2022-01-10 02:24:09 -05:00
Hypolite Petovan 5de396bc67 Add Hungarian to the available languages 2022-01-09 18:16:15 -05:00
Hypolite Petovan fe9b12164d [v2.3.3] Bump version number for release 2021-12-29 18:57:18 +01:00
Hypolite Petovan 6ccbeef59c [Composer] Updated dependencies before release
- Updating atlas/pdo (1.1.2 => 1.2.0)
  - Updating gettext/languages (2.6.0 => 2.9.0)
  - Updating gettext/gettext (v4.8.2 => v4.8.6)
  - Updating masterminds/html5 (2.7.1 => 2.7.5)
  - Updating psr/log (1.1.3 => 1.1.4)
  - Updating monolog/monolog (1.25.4 => 1.26.1)
  - Updating sarahman/simple-filesystem-cache (1.0.1 => 1.0.2)
  - Updating seld/cli-prompt (1.0.3 => 1.0.4)
  - Updating bower-asset/fontawesome (5.13.1 => 5.15.4)
  - Updating laminas/laminas-zendframework-bridge (1.0.4 => 1.1.1)
  - Updating pear/pear_exception (v1.0.1 => v1.0.2)
  - Updating pear/pear-core-minimal (v1.10.10 => v1.10.11)
2021-12-29 17:24:24 +01:00
Hypolite Petovan 4e3dcaf331 Merge pull request 'added HU translation' (#82) from tobias/friendica-directory:20211206-hu into stable
Reviewed-on: #82
2021-12-06 19:41:57 +00:00
Tobias Diekershoff ca605b8f23 added HU translation
fixes #73
2021-12-06 20:29:32 +01:00
Hypolite Petovan 13733e2f6c Merge pull request 'AR translation update THX abidin toumi' (#81) from tobias/friendica-directory:20211030-ar into stable
Reviewed-on: #81
2021-10-30 12:37:07 +00:00
Tobias Diekershoff d22b656a46 AR translation update THX abidin toumi 2021-10-30 08:27:49 +02:00
Tobias Diekershoff b3a8ea85f1 Merge pull request '[Composer] Update mrpetovan/net_ping to version 1.2.1' (#80) from MrPetovan/friendica-directory:task/79-composer-dependencies into stable
Reviewed-on: #80
2021-09-16 05:15:07 +00:00
Hypolite Petovan 2a747cbb06 [Composer] Update mrpetovan/net_ping to version 1.2.1
- Suppresses a couple of Deprecation Notices when running Composer
2021-09-15 21:56:00 -04:00
Hypolite Petovan ffea0fcbdb Add Composer 1.* binary to project
- It is required to keep the fxp/composer-asset-plugin support
2021-09-15 21:16:38 -04:00
Hypolite Petovan 1507bfdcf8
Merge pull request #78 from tobiasd/20210715-gd
add translation for Gaelic, Scottish
2021-07-15 15:40:26 -04:00
Tobias Diekershoff b8332ac785 added Gaelic, Scottish to the language array 2021-07-15 15:10:57 +02:00
Tobias Diekershoff f707df488b added Gaelic, Scottish translation THX GunChleoc 2021-07-15 15:05:44 +02:00
Michael Vogel 750e369f57
Merge pull request #77 from MrPetovan/bug/76-remove-at-sign-fulltext-search
Remove at sign (@) from fulltext query string
2021-06-22 16:05:21 +02:00
Hypolite Petovan 4d7067b381 Remove at sign (@) from fulltext query string
- InnoDB uses it as a special character and it can't be escaped
2021-06-21 11:15:57 -04:00
Hypolite Petovan 7112c65166
Merge pull request #71 from Quix0r/fixes/timeout-class-reference
Fixes:
2020-10-25 22:52:29 -04:00
Roland Häder 193b01cccd
Used \Some\Foo::class instead of '\Some\Foo' which type-safe and easier to find by your editor/IDE
Signed-off-by: Roland Häder <roland@mxchange.org>
2020-10-26 01:19:41 +01:00
Tobias Diekershoff 9b4961e2cc
Merge pull request #68 from MrPetovan/task/3-add-server-language-filter
Add language filter to server list
2020-09-27 21:45:41 +02:00
Hypolite Petovan cb0bfae1ab [Composer] Add expected ext-pdo dependency 2020-09-27 15:39:09 -04:00
Hypolite Petovan 16d1962324 Add language filter to server list 2020-09-27 15:39:09 -04:00
Hypolite Petovan e68b68110b Create new popular server languages widget
- Rename popular profile widget class and template
2020-09-27 15:39:08 -04:00
Tobias Diekershoff a1921c999d
Merge pull request #67 from MrPetovan/task/31-add-limit-to-search
Add page size parameter to search
2020-09-27 20:05:17 +02:00
Hypolite Petovan 959f526cb4 Add page size parameter to search
- Update API call documentation
2020-09-27 13:41:40 -04:00
Tobias Diekershoff 72d5e68c96
Merge pull request #66 from MrPetovan/task/34-no-desc-health-cap
Cap health score if server doesn't have a description
2020-09-27 19:16:49 +02:00
Hypolite Petovan aa83091458 Cap health if server doesn't have a description 2020-09-27 12:51:34 -04:00
Michael Vogel 4205323d31
Merge pull request #65 from MrPetovan/task/36-display-registration-policy
Display registration policy in server panel
2020-09-27 18:03:12 +02:00
Hypolite Petovan 7b906d5e33 Updated base translation file after adding new translation strings 2020-09-27 11:58:07 -04:00
Hypolite Petovan 35194f8110 Display registration policy in server panel
- Add title to health score badge
- Add translation for users badge
2020-09-27 11:58:06 -04:00
Tobias Diekershoff 520ddf5a8c
Merge pull request #63 from MrPetovan/task/54-no-ping-penalty
Factor in the unavailability of ping in the health score
2020-09-27 17:56:49 +02:00
Tobias Diekershoff a24a7d866c
Merge pull request #64 from MrPetovan/task/40-add-account-types
Add additional account types to the profile filter
2020-09-27 17:43:18 +02:00
Hypolite Petovan f1b960c4eb Add additional account types to the profile filter 2020-09-27 11:31:18 -04:00
Hypolite Petovan 0ce9dda5f6 Factor in the unavailability of ping in the health score
- No available ping limits the maximum health score a server can have
2020-09-27 11:08:39 -04:00
Tobias Diekershoff 360c421796
Merge pull request #62 from MrPetovan/task/39-search-match-username
Add missing column in profile fulltext index
2020-09-27 14:46:56 +02:00
Hypolite Petovan b7eab197b5 Update API profile search request to match updated fulltext index definition 2020-09-27 08:39:31 -04:00
Hypolite Petovan 3939800068 [Database v0008b] Add missing column in fulltext index
- Add minimal SQL query to downgrade SQL script to avoid empty query errors
2020-09-27 08:39:18 -04:00
Michael Vogel 96dec7c4df
Merge pull request #60 from MrPetovan/task/39-search-match-username
Profile search now matches username, not full profile URL
2020-09-27 00:47:15 +02:00
Hypolite Petovan d6bf75ae04 Update profile search request to match updated fulltext index definition 2020-09-26 18:32:43 -04:00
Hypolite Petovan 4621ae2001 [Database v0008] Replace profile_url with username in profile full-text index
- Prevents search matches on node domain name
2020-09-26 18:32:21 -04:00
Michael Vogel 7780d3af09
Merge pull request #59 from MrPetovan/bug/38-unique-server-alias
Add unique key on server_alias.alias
2020-09-27 00:28:19 +02:00
Hypolite Petovan e873d62e2c Expand ON DUPLICATE KEY statement to match the new unique key 2020-09-26 18:09:21 -04:00
Hypolite Petovan 8e6aa14704 [Database v0007] Add unique key on server_alias.alias
- Fix inconsistent behaviors with aliases referring to multiple servers
2020-09-26 18:09:08 -04:00
Michael Vogel 751a45fd3c
Merge pull request #58 from MrPetovan/bug/47-drop-server-path-field
[Database v0006] Drop unused server.path field
2020-09-26 23:11:16 +02:00
Michael Vogel b48c436141
Merge pull request #57 from MrPetovan/bug/45-drop-key-validation
Remove validity check on profile key parameter
2020-09-26 23:07:49 +02:00
Hypolite Petovan ca0bb09f81 [Database v0006] Drop unused server.path field
- Fix "Field 'path' doesn't have a default value" errors
2020-09-26 17:06:40 -04:00
Hypolite Petovan c21642d705 Improve logging for invalid parameters during profile polling 2020-09-26 16:33:55 -04:00
Hypolite Petovan c8fa6ef3dc Remove validity check on profile key parameter 2020-09-26 16:33:29 -04:00
Hypolite Petovan ffa16d1935
Merge pull request #55 from realkinetix/server-max-health-fix
Fix $server_max_health value being overwritten while calculating
2020-09-13 22:26:42 -04:00
Adam Clark 69b2356a1b Fix $server_max_health value being overwritten while calculating 2020-09-13 23:12:48 +00:00
Michael Vogel 1ca13876fb
Merge pull request #52 from tobiasd/20200901-it
IT translation update THX Sylke Vicious
2020-09-01 15:11:23 +02:00
Tobias Diekershoff fad8939cb4 IT translation update THX Sylke Vicious 2020-09-01 15:02:27 +02:00
Hypolite Petovan 896a07f9cb
Merge pull request #51 from tobiasd/20200818-lng
EN GB and PL translation updates
2020-08-18 11:54:12 -04:00
Tobias Diekershoff 1451b18460 PL translation updated THX Waldis 2020-08-18 17:51:53 +02:00
Tobias Diekershoff d04b50d2b9 EN GB translation updated THX AndyH3 2020-08-18 17:51:27 +02:00
Hypolite Petovan c41ce95f4e
Merge pull request #50 from annando/user-agent
User agent added
2020-07-17 19:47:34 -04:00
Michael 776851b84f User agent added 2020-07-17 20:41:02 +00:00
Michael Vogel 937fedc2c7
Merge pull request #46 from MrPetovan/release/2.3.1
Release 2.3.1
2020-06-20 22:54:07 +02:00
Hypolite Petovan 0c203131b9 Increase specificity of checks of $param
- Avoids warnings and notices
2020-06-20 12:47:17 -04:00
Hypolite Petovan 7799fb99ed [Composer] Update dependency mrpetovan/Net_Ping to version 1.2 2020-06-20 12:45:18 -04:00
Hypolite Petovan b6933785dd [Composer] Update lock file hash 2020-06-17 15:16:39 -04:00
Tobias Diekershoff 83aa5152aa
Merge pull request #44 from MrPetovan/release/2.3.0
Release 2.3.0
2020-06-17 19:50:29 +02:00
Hypolite Petovan 9ee72bb382 Update container namespace after dependency replacement
- container-interop has been replaced with psr/container
2020-06-17 09:27:20 -04:00
Hypolite Petovan ad6db150c5 Update references to friendica/friendica-directory stable branch 2020-06-15 14:05:26 -04:00
Hypolite Petovan 30bdf306ba Bump version number for 2.3.0 release 2020-06-15 14:05:26 -04:00
Hypolite Petovan 5c9432dd57 [Composer] Replace abandoned leafo/scssphp with scssphp/scssphp 2020-06-15 14:05:21 -04:00
Hypolite Petovan 19631ef206 [Composer] Update dependencies
- Removing sebastian/resource-operations (1.0.0)
  - Removing sebastian/code-unit-reverse-lookup (1.0.1)
  - Removing myclabs/deep-copy (1.8.1)
  - Removing container-interop/container-interop (1.2.0)
  - Removing sebastian/object-enumerator (2.0.1)
  - Updating fxp/composer-asset-plugin (v1.4.4 => v1.4.6)
  - Updating atlas/pdo (1.1.1 => 1.1.2)
  - Updating boronczyk/localization-middleware (1.4 => 1.4.1)
  - Updating byjg/anydataset (4.0.0 => 4.0.1)
  - Updating byjg/anydataset-db (4.0.0 => 4.0.1)
  - Updating byjg/migration (4.0.2 => 4.0.3)
  - Updating gettext/languages (2.5.0 => 2.6.0)
  - Updating gettext/gettext (v4.6.2 => v4.8.2)
  - Updating masterminds/html5 (2.5.0 => 2.7.1)
  - Updating psr/log (1.1.0 => 1.1.3)
  - Updating monolog/monolog (1.24.0 => 1.25.4)
  - Downgrading sebastian/version (2.0.1 => 1.0.6)
  - Downgrading sebastian/recursion-context (2.0.0 => 1.0.5)
  - Downgrading sebastian/exporter (2.0.0 => 1.2.2)
  - Downgrading sebastian/environment (2.0.0 => 1.3.8)
  - Updating symfony/polyfill-ctype (v1.10.0 => v1.17.0)
  - Updating webmozart/assert (1.4.0 => 1.5.0)
  - Updating phpdocumentor/reflection-common (1.0.1 => 2.1.0)
  - Updating phpdocumentor/type-resolver (0.4.0 => 1.0.1)
  - Updating phpdocumentor/reflection-docblock (4.3.0 => 4.3.4)
  - Updating doctrine/instantiator (1.1.0 => 1.3.1)
  - Updating phpspec/prophecy (1.8.0 => v1.10.3)
  - Downgrading phpunit/phpunit-mock-objects (3.4.4 => 2.3.8)
  - Downgrading phpunit/php-token-stream (2.0.2 => 1.4.12)
  - Downgrading phpunit/php-code-coverage (4.0.8 => 2.2.4)
  - Downgrading phpunit/phpunit (5.7.27 => 4.8.36)
  - Updating sarahman/simple-filesystem-cache (1.0.0 => 1.0.1)
  - Updating slim/slim (3.12.0 => 3.12.3)
  - Updating slim/php-view (2.2.0 => 2.2.1)
  - Updating bower-asset/bootstrap.native (2.0.25 => 2.0.27)
  - Updating bower-asset/fontawesome (5.7.2 => 5.13.0)
  - Updating bower-asset/bootstrap (v4.3.1 => v4.5.0)
  - Updating leafo/scssphp (v0.7.7 => v0.7.8)
  - Updating pear/pear_exception (v1.0.0 => v1.0.1)
  - Updating composer/xdebug-handler (1.3.2 => 1.4.2)
  - Updating netresearch/jsonmapper (v1.4.0 => v1.6.0)
  - Updating felixfbecker/advanced-json-rpc (v3.0.3 => v3.1.1)
  - Updating felixfbecker/language-server-protocol (v1.2.0 => v1.4.0)
  - Updating nikic/php-parser (v4.2.1 => v4.5.0)
  - Updating sabre/event (5.0.3 => 5.1.0)
  - Updating sabre/uri (2.1.1 => 2.2.0)
2020-06-15 09:43:08 -04:00
Hypolite Petovan 685772e24e [Composer] Replace abandoned zendframefork/zend-escape with suggested laminas/laminas-escape 2020-06-15 09:43:07 -04:00
Hypolite Petovan deabe4da49 [Composer] Enforce minimum PHP version during composer update
- Downgrade symfony/yaml to version v3.4.42 to satisfy the enforced minimum PHP version
2020-06-15 09:43:07 -04:00
Michael Vogel d509dfbbfd
Merge pull request #43 from tobiasd/master
updated CS, DE, FR and added ar  ca  cs  de  en  en_GB  et  fr  it  ja  nl  pl  ru  th  zh_CN translation
2020-06-15 10:38:29 +02:00
Tobias Diekershoff aeb0a67b12 updated CS, DE, FR and added ar ca cs de en en_GB et fr it ja nl pl ru th zh_CN translation 2020-06-15 09:54:58 +02:00
Michael Vogel 2eca458ec1
Merge pull request #42 from MrPetovan/master
Use byjg/uri dependency instead of adding new URL-handling library
2020-06-14 21:31:21 +02:00
Hypolite Petovan b0a258cd0e Use byjg/uri dependency instead of adding new URL-handling library in PHPRenderer 2020-06-14 15:13:18 -04:00
Hypolite Petovan e082c0535d [Composer] Added core dependency byjg/uri 2020-06-14 15:13:17 -04:00
Michael Vogel b030cfd4e3
Merge pull request #41 from friendica/task/24-add-zrl-parameter-support
Add zrl parameter support
2020-06-14 09:37:05 +02:00
Hypolite Petovan 6cdbcbddb1 [translation] Update translation files after string update 2020-06-13 13:25:05 -04:00
Hypolite Petovan 476f7ae832 Add ZRL parameter support sitewide in templates 2020-06-13 13:21:18 -04:00
Hypolite Petovan 57dd610e73 Add u() and r() method to PhpRenderer
- Add Router dependency
2020-06-13 13:20:49 -04:00
Hypolite Petovan 0cdbd3af2d Add ZrlMiddleware to the stack 2020-06-13 13:17:40 -04:00
Hypolite Petovan 230ceeab29 Add names to routes we need to get URLs for 2020-06-13 13:17:19 -04:00
Hypolite Petovan f47f263fc3 Add ZrlMiddleware class 2020-06-13 13:16:35 -04:00
Hypolite Petovan a402fbdf5d Add Utils\Url class 2020-06-13 13:16:25 -04:00
Hypolite Petovan 10edbea99e Update author email address in file headers 2020-06-13 13:15:53 -04:00
Hypolite Petovan c18551c44c
Merge pull request #35 from AlfredSK/AlfredSK-change-queries-from-open-to-not-closed
Show and count also servers with policy 'approve'
2019-11-01 13:05:27 -04:00
Steffen K9 dfd1a14300
Change the statistics query as well 2019-11-01 17:18:15 +01:00
Steffen K9 778168e0c4
Show also servers with policy 'approve'
Changed the queries to show also servers with register policy 'approval'.
2019-11-01 17:15:26 +01:00
Hypolite Petovan 1e3dab7c10
Merge pull request #32 from tobiasd/20190930-lng
Updated translations of directory from Transifex sources
2019-10-01 07:58:30 -04:00
Tobias Diekershoff 8c602d3566 updated CS, DE, FR and added AR, en_GB, ET, IT, JA, NL, PL, TH, zh_CN translation 2019-09-30 20:38:28 +02:00
Tobias Diekershoff 2e1812a369 added language codes to the settings.php file 2019-09-30 20:35:41 +02:00
Hypolite Petovan a0cfd33521 Fix queries to work with sql_mode=only_full_group_by 2019-05-12 14:00:44 -04:00
Hypolite Petovan 942de39e6d Release 2.2.1 2019-03-04 22:21:00 -05:00
Hypolite Petovan 6ada08b8f7 [Composer] Update dependencies
- Update atlas/pdo (1.1.0 => 1.1.1)
- Update bower-asset/fontawesome (5.7.1 => 5.7.2)
2019-03-04 22:21:00 -05:00
Hypolite Petovan a7a9001f5e [Database v0005] Change server.ssl_state column type to tinyint
- Fix "Data truncated" SQL error with server.ssl_state column type
2019-03-04 22:11:38 -05:00
Hypolite Petovan c0ff5c1cba [Composer] Use stable release for sarahman/simple-filesystem-cache 2019-03-04 22:07:27 -05:00
Hypolite Petovan 2324c4836f Remove unused use statement in Controllers\Console\ExtractStrings 2019-03-04 22:05:23 -05:00
Hypolite Petovan 45fc6315b3 Release 2.2.0 2019-02-03 09:54:31 -05:00
Hypolite Petovan eb8ea589bc [Composer] Updating dependencies
- Updating gettext/gettext (v4.6.1 => v4.6.2)
- Updating masterminds/html5 (2.4.0 => 2.5.0)
- Updating slim/slim (3.11.0 => 3.12.0)
- Updating bower-asset/bootstrap.native (2.0.24 => 2.0.25)
- Updating bower-asset/fontawesome (5.6.1 => 5.7.1)
- Updating bower-asset/bootstrap (v4.1.3 => v4.2.1)
- Updating symfony/yaml (v4.2.1 => v4.2.3)
- Updating composer/xdebug-handler (1.3.1 => 1.3.2)
- Updating nikic/php-parser (v4.1.0 => v4.2.0)
- Updating webmozart/assert (1.3.0 => 1.4.0)
2019-02-03 09:54:23 -05:00
Hypolite Petovan 1bbe4d3289 Add SSL and availability condition in /servers/surprise (#15) 2019-02-03 09:36:53 -05:00
Hypolite Petovan d0102327c0 Add /servers/surprise endpoint 2019-02-03 09:30:31 -05:00
Hypolite Petovan 0e4d5a035f Fix search parameter handling (#23)
- Add default value for search parameter
- Fix filter_var flags for language parameter
2019-02-03 09:20:58 -05:00
Hypolite Petovan b6aaa246f6
Merge pull request #22 from Aditoo17/master
L10n: Update Czech translation
2019-01-27 04:50:15 -05:00
Aditoo17 d45651328e L10n: Update Czech translation 2019-01-27 08:53:00 +01:00
Hypolite Petovan 4da3acb52a Align `/msearch` output to legacy format 2018-12-24 09:45:47 -05:00
Hypolite Petovan 2fcd282e83 Add documentation for /msearch endpoint 2018-12-23 21:23:08 -05:00
Hypolite Petovan 8db05cfa7c Add new POST route /msearch with associated controller 2018-12-23 21:22:52 -05:00
Hypolite Petovan dc67cff8ef [Database v0004] Add fulltext index to profile for interest match 2018-12-23 21:18:09 -05:00
Hypolite Petovan fc447f07c4 Clean code of Api\Search 2018-12-23 21:15:50 -05:00
Hypolite Petovan 31feea1b7a Updated font awesome paths for version 5.6 2018-12-19 05:52:50 -05:00
Hypolite Petovan 8ca7dfb455 Release 2.1.1 2018-12-19 05:40:48 -05:00
Hypolite Petovan d33fb62e35 Add support for RC version (#19) 2018-12-19 04:09:23 -05:00
Hypolite Petovan 503c134df6 [Composer] Updated font-awesome to version 5.6.1 2018-12-19 03:59:45 -05:00
Hypolite Petovan 6c01f2d3e5 [Composer] Update byjg/migration to version 4.0.2 2018-12-19 03:55:30 -05:00
Hypolite Petovan b561a1a49d Updated README about custom pages 2018-11-19 22:47:39 -05:00
Hypolite Petovan b61afa9502 Add support for multi-word custom HTML page name 2018-11-19 22:43:34 -05:00
Hypolite Petovan 6d4354c97f Update composer.lock 2018-11-19 22:42:17 -05:00
Hypolite Petovan 80e9283c41 Release 2.1.0 2018-11-19 22:32:47 -05:00
Hypolite Petovan c2de5cf80a Add de_DE to available locales 2018-11-19 22:32:20 -05:00
Hypolite Petovan aa25c475dc Update translations before release
- Add partial de translations
- Update fr translations
2018-11-19 22:20:56 -05:00
Hypolite Petovan e30065657d Fix missing argument in servers controller 2018-11-19 22:13:47 -05:00
Hypolite Petovan e36805c206 Add Custom Pages feature
- Add config/pages directory
- Add new custom pages controller and route
- Add generated links in the footer
2018-11-19 22:12:09 -05:00
Hypolite Petovan 8c1d4404c7 Add new expected abstract method to Web\BaseController
- Cleanup obsolete use statements
2018-11-19 22:11:22 -05:00
Hypolite Petovan 9f80a14565 Add footer with current version
- Move stats links from header to footer
2018-11-19 08:32:58 -05:00
Hypolite Petovan c0a56882bc Fix FR translation 2018-11-18 11:46:22 -05:00
Hypolite Petovan 2505436796 Add missing translation string in Layout 2018-11-18 11:40:05 -05:00
Hypolite Petovan d2a9099391 Update translation strings
- Update FR translations
2018-11-18 11:35:42 -05:00
Hypolite Petovan 2591f05fb5 Add translation strings to statistics page 2018-11-18 11:35:23 -05:00
Hypolite Petovan 0c3013adeb Add locale support to extract-strings console
- Remove reliance on available locales setting
- Add reliance on translation path setting
- Only create a new translation when a single language is provided
- Remove debug comments
2018-11-18 11:35:04 -05:00
Hypolite Petovan ce075c80e6 Add statistics page
- Closes https://github.com/friendica/friendica-directory/issues/2
2018-11-18 10:38:43 -05:00
Hypolite Petovan e8752c7631 Restore full local name in settings
- Fallback to two-letter language code for translation if locale isn't found
- Rename L10n::langToString to L10n::localeToLanguageString
- Use setlocale() with the selected locale
2018-11-18 10:38:17 -05:00
Hypolite Petovan 012a3f9f8d Add poco retrieval during server poll
- Closes https://github.com/friendica/friendica-directory/issues/7
2018-11-18 10:36:03 -05:00
Hypolite Petovan 26b4299b68 [Composer] Add byjg/webrequest direct dependency 2018-11-18 10:32:42 -05:00
Hypolite Petovan a828e712a3 Fix SQL error in search 2018-11-17 23:48:08 -05:00
Hypolite Petovan 4790067527 Fix SQL error when there's no condition in AccountTypeTabs 2018-11-17 14:15:25 -05:00
Hypolite Petovan b4f7957f1c Add database structure update known issue in doc 2018-11-17 09:47:11 -05:00
Hypolite Petovan dd60b60e99 Pre-release 2.1-rc2 2018-11-17 09:35:07 -05:00
Hypolite Petovan d66b9aa673 Add new translation strings to PO files
- Add FR translations to latest strings
2018-11-17 08:54:28 -05:00
Hypolite Petovan 94fe8cf397 Add Transifex mention to docs/Translation 2018-11-17 08:52:47 -05:00
Hypolite Petovan 4dad98fa04 Add count display for account type tabs
- Add translations strings to AccountTypeTabs
2018-11-17 08:52:23 -05:00
Hypolite Petovan 651849b18e Update type-hint/PHPDoc in PhpRenderer 2018-11-17 08:51:13 -05:00
Hypolite Petovan 3669e69241 Add PHPRenderer->np__() translation function 2018-11-17 08:50:51 -05:00
Hypolite Petovan b3e0a349c9 Fix overriding headers in extract-strings console 2018-11-17 08:28:04 -05:00
Hypolite Petovan 4ca3fd5a32 Add CS translations, thanks Aditoo 2018-11-17 08:21:37 -05:00
Hypolite Petovan 01d476f542 Use only language part of locale for popular languages
- Fixes https://github.com/friendica/friendica-directory/issues/13
2018-11-17 08:02:09 -05:00
Hypolite Petovan b29aafc2d9 Set Composer to --no-dev on install in doc 2018-11-17 07:51:11 -05:00
Hypolite Petovan 1960634797 Remove SET autocommit=0 in base.sql
- Fixes https://github.com/friendica/friendica-directory/issues/12
- Add transactions in SQL migration scripts
2018-11-17 07:50:42 -05:00
Hypolite Petovan b74921b5d8 Pre-release 2.1-rc1 2018-11-16 00:15:17 -05:00
Hypolite Petovan f2f647c6d7 Add documentation about translation 2018-11-16 00:14:03 -05:00
Hypolite Petovan c437eee38e Add base en strings
- Add translated fr strings
2018-11-16 00:01:48 -05:00
Hypolite Petovan fc434f4506 Add extract-strings console 2018-11-16 00:00:19 -05:00
Hypolite Petovan ff9eab39a7 Add translation string to templates
- Lower popular widget lists to 10 elements
- Add language to profile and server display
2018-11-16 00:00:03 -05:00
Hypolite Petovan 5b7bb030de Add Internationalization
- Add Utils/L10n class
- Add translator functions to PHP Renderer
- Refactor web controllers to prevent duplicated code
- Add locale middleware
- Add translation file loading
- Add i18n settings
2018-11-15 23:59:00 -05:00
Hypolite Petovan 13a2068a8b Move Photo Controller to Api 2018-11-15 23:41:32 -05:00
Hypolite Petovan f6426c1e8a [Composer] Add gettext/gettext and boronczyk/localization-middleware dependencies 2018-11-15 23:40:07 -05:00
Hypolite Petovan bc5a0fb590 Add language storage during server and profile polling
- Add index on language field in profile and server tables
2018-11-15 23:39:32 -05:00
Hypolite Petovan 650f0b4009 Remove unused /tag route 2018-11-15 23:32:07 -05:00
Hypolite Petovan 93c2536f18 Add deep merge between default and local settings 2018-11-15 23:30:25 -05:00
Hypolite Petovan 88fcd9610c Remove trailing slashes in path settings 2018-11-15 23:29:52 -05:00
Hypolite Petovan fadb733527 Fix server polling error when body was unexpected JSON 2018-11-15 23:29:17 -05:00
Hypolite Petovan 31156b8643 Increase verbosity of cron queue counts 2018-11-15 23:28:36 -05:00
Hypolite Petovan 4c85d86b7f
Merge pull request #10 from AndyHee/master
Update INSTALL.md
2018-11-14 10:08:46 -05:00
Andy H f6769a6c2a
Update INSTALL.md 2018-11-14 21:57:20 +07:00
Andy H ea3657d15b
Update INSTALL.md 2018-11-14 21:39:47 +07:00
Hypolite Petovan 5f7930c54f Release 2.0.6 2018-11-13 22:02:13 -05:00
Hypolite Petovan 63b10c37f5 Add ProfileAdd subconsole 2018-11-13 22:01:52 -05:00
Hypolite Petovan f9b9557415 Add queue size info logging in Controllers/Cron 2018-11-13 22:01:34 -05:00
Hypolite Petovan 641dc9b8eb Improve DirectoryAdd console output 2018-11-13 22:01:12 -05:00
Hypolite Petovan bbff23caa5 Add debug logging of ProfilePollQueue::add calls 2018-11-13 22:00:50 -05:00
Hypolite Petovan 27c5c14ed7 Fix critical mistake preventing profile to be added to queue 2018-11-13 22:00:29 -05:00
Hypolite Petovan 2cabe10fa3 Add return codes to ProfilePollQueue->add 2018-11-13 22:00:07 -05:00
Hypolite Petovan d231a9bc80 Improve Network::isPublicHost 2018-11-13 21:51:58 -05:00
Andy H c22ce2292d
Merge branch 'master' into master 2018-11-13 20:18:03 +07:00
Andy H 247e444885
Update INSTALL.md
Resolved conflict
2018-11-13 20:13:50 +07:00
Andy H ad8a2b80e0
Update INSTALL.md
Add Nginx configuration line
2018-11-12 21:36:55 +07:00
160 changed files with 10892 additions and 1738 deletions

View File

@ -4,7 +4,7 @@
1. Fork the repository
2. Create a new branch for each feature or improvement
3. Send a pull request from each feature branch to the master branch
3. Send a pull request from each feature branch to the stable branch
It is very important to separate new features or improvements into separate feature branches, and to send a
pull request for each branch. This allows us to review and pull in new features or improvements individually.

View File

@ -17,7 +17,7 @@ Composer is a popular dependency management tool for PHP projects.
cd /path/to
git clone https://github.com/friendica/friendica-directory friendica-directory
cd friendica-directory
composer install
composer install --no-dev
```
### Using a stable release archive
@ -62,6 +62,22 @@ In your Virtual Host file, set your document root as follow:
DocumentRoot /path/to/friendica-directory/public/
```
### Nginx
Add these lines to your nginx config file.
```
root /path/to/friendica-directory/public;
location / {
rewrite ^/(.*) /index.php?$args last;
}
location ~* \.(jpg|jpeg|gif|png|ico|css|js|htm|html|ttf|woff|svg)$ {
expires 30d;
try_files $uri /index.php?$args;
}
```
## 4. Set up the background task
Friendica Directory relies on a background task running every minute to keep the directory up to date.

View File

@ -25,12 +25,23 @@ Please refer to the provided [installation instructions](INSTALL.md).
Please refer to the provided [update instructions](UPDATE.md).
## Custom pages
If you need to add custom HTML pages as required by law to publish any website processing data in some countries, simply add your HTML files in the `config/pages` folder, they will be automatically linked from the footer.
Tips:
- The expected extension is `.html`.
- Underscores in the page file name are replaced by spaces in the page link label.
- Accents aren't supported.
## See also
- [Project Concepts](docs/Concepts.md)
- [Directory Protocol](docs/Protocol.md)
- [Translation](docs/Translation.md)
## Special thanks
- [Beanow](https://github.com/Beanow) for his efforts to spearhead the previous version of the Friendica Directory software.
- [Scott Arciszewski](https://github.com/paragonie-scott) for his inspiration to use Slim and his invaluable Slim app example.
- [Saša Stamenković](https://github.com/umpirsky) for his useful list packages like [umpirsky/language-list](https://github.com/umpirsky/language-list).

View File

@ -39,4 +39,7 @@ cd /path/to/friendica-directory
bin/console dbupdate
```
You're all set!
### Known issues
Before version 2.1, updating the database schema was impossible because the `status` column value of the `migration_version` table was incorrectly set to `partial up` instead of `complete`.
Updating from 2.0.x, changing the value allows to update the database schema.

View File

@ -1 +1 @@
2.0.5
2.4.2

BIN
bin/composer.phar Normal file

Binary file not shown.

View File

@ -15,9 +15,9 @@ require __DIR__ . '/../src/dependencies.php';
(new \Friendica\Directory\Controllers\Cron(
$container->get('atlas'),
$container->get('\Friendica\Directory\Pollers\Profile'),
$container->get('\Friendica\Directory\Pollers\Server'),
$container->get('\Friendica\Directory\Pollers\Directory'),
$container->get(\Friendica\Directory\Pollers\Profile::class),
$container->get(\Friendica\Directory\Pollers\Server::class),
$container->get(\Friendica\Directory\Pollers\Directory::class),
$container->get('logger')
))->execute();

View File

@ -16,27 +16,33 @@
"ext-curl": "*",
"ext-gd": "*",
"ext-json": "*",
"ext-pdo": "*",
"asika/simple-console": "^1.0",
"atlas/pdo": "^1.1",
"byjg/migration": "^2.1",
"fxp/composer-asset-plugin": "^1.4",
"boronczyk/localization-middleware": "^1.4",
"byjg/migration": "^4.0",
"byjg/uri": "^1.0.4",
"gettext/gettext": "^4.6",
"gofabian/negotiation-middleware": "dev-master",
"guzzlehttp/guzzle": "^6.5",
"laminas/laminas-escaper": "^2.6",
"laminas/laminas-zendframework-bridge": "^1.4",
"masterminds/html5": "^2.3",
"monolog/monolog": "^1.17",
"mrpetovan/net_ping": "^1.0",
"sarahman/simple-filesystem-cache": "dev-master",
"mrpetovan/net_ping": "^1.2",
"sarahman/simple-filesystem-cache": "^1.0",
"seld/cli-prompt": "^1.0",
"slim/slim": "^3.1",
"slim/php-view": "^2.0",
"zendframework/zend-escaper": "^2.6",
"fxp/composer-asset-plugin": "^1.4",
"bower-asset/bootstrap.native": "^2.0",
"bower-asset/fontawesome": "^5.5",
"gofabian/negotiation-middleware": "^0.1.3"
"bower-asset/fontawesome": "^5.5"
},
"require-dev": {
"bower-asset/bootstrap": "^4.1",
"phpunit/phpunit": ">=4.8 < 6.0",
"vimeo/psalm": "^2.0",
"leafo/scssphp": "^0.7.7"
"scssphp/scssphp": "^1.1",
"vimeo/psalm": "^2.0"
},
"autoload": {
"psr-4": {
@ -49,6 +55,9 @@
}
},
"config": {
"platform": {
"php": "7.3.0"
},
"process-timeout" : 0,
"autoloader-suffix": "FriendicaDirectory",
"optimize-autoloader": true,
@ -70,7 +79,8 @@
"/composer.*",
"!/vendor",
"public/assets/vendor",
"!public/assets/vendor/fontawesome/web-fonts-with-css",
"!public/assets/vendor/fontawesome/css",
"!public/assets/vendor/fontawesome/webfonts",
"!public/assets/vendor/bootstrap.native/dist"
]
}

2338
composer.lock generated

File diff suppressed because it is too large Load Diff

4
config/.gitignore vendored
View File

@ -1,4 +1,6 @@
# Ignore everything in this directory
*
# Except this file
!.gitignore
!.gitignore
# And the pages subdirectory
!pages

4
config/pages/.gitignore vendored Normal file
View File

@ -0,0 +1,4 @@
# Ignore everything in this directory
*
# Except this file
!.gitignore

View File

@ -1,15 +1,26 @@
# Friendica Directory Protocol
## Surprise
```
GET /servers/surprise
```
Redirects to the base URL of a random server with open registration policy and above 75 health.
## Search
```
GET /search[/account_type]?q=...
GET /search[/account_type]?q=...[&page=...][&limit=...]
Accept: application/json
```
Parameters:
- `account_type`: An arbitrary account type string. Currently supported are `all`, `people` and `forum`. Default is `all`.
URI Parameter:
- `account_type` (optional): An arbitrary account type string. Expected values are `all`, `people`, `news`, `organization` and `group`. Default is `all`.
Query parameters:
- `q`: The search query.
- `page` (optional): The page number, default is 1.
- `limit` (optional): The page size, default is 20, max is 100.
Returns a JSON structure containing a paginated list profiles matching the search query and the optional account type.
@ -33,10 +44,46 @@ Example:
"region": "New York",
"country": "USA",
"profile_url": "https://friendica.mrpetovan.com/profile/hypolite",
"dfrn_request": "https://friendica.mrpetovan.com/dfrn_request/hypolite",
"photo": "https://friendica.mrpetovan.com/photo/27330388315ae4ed2b03e3c116980490-4.jpg?ts=1541567135",
"tags": "videogame gaming boardgame politics philosophy development programming php",
"last_activity": "2018-45"
"last_activity": "2018-45",
"remote_follow": "https://friendica.mrpetovan.com/remote_follow/hypolite",
"subscribe": null
},
...
]
}
```
### Legacy interest match search
```
POST /msearch
Content-Type: application/x-www-form-urlencoded
Accept: application/json
```
Parameters:
- `s`: The search query, a list of tags separated by spaces or commas is expected.
- `p` (optional): The page number, default is 1.
- `n` (optional): The number of items per page, default is 80.
Returns a JSON structure containing a paginated list profiles matching the search query and the optional account type.
Example:
```json
{
"query": "videogame gaming boardgame politics philosophy development programming php",
"page": 1,
"items_page": 100,
"total": 32,
"results": [
{
"name": "Hyp🌧lite Pe☂ov🍃n (he/him)",
"url": "https://friendica.mrpetovan.com/profile/hypolite",
"photo": "https://friendica.mrpetovan.com/photo/27330388315ae4ed2b03e3c116980490-4.jpg?ts=1545521478",
"tags": "videogame gaming boardgame politics philosophy development programming php"
},
...
]

71
docs/Translation.md Normal file
View File

@ -0,0 +1,71 @@
# Friendica Directory Localization
## Overview
The Friendica Directory interface is available in multiple languages.
We are using a gettext-like process to generate and parse translations files, which enables translators to use their usual software to translate strings.
The translations files are located in `src/lang/<locale>/LC_MESSAGES/`:
- The compiled MO file can be generated from a translation software and will be loaded by default.
- The PO file is the main translation interface and can be loaded if the MO file doesn't exist.
## Main scenarios
Thanks for your interest in translating the Friendica Directory interface!
### On Transifex
Please head over to [the Friendica Directory Transifex page](https://www.transifex.com/Friendica/friendica/directory/) where you will need an account to start translating the existing strings.
The translations will be included in the next release.
### Manually
#### Add a new locale
Adding a new locale requires to edit `src/settings.php` to add it to the `$settings['i18n']['locales']` array.
The base translation file can then be generated with `bin/console extract-string <locale>`.
#### Translate existing strings
PO files can be edited with translation software like [Poedit](https://poedit.net).
Please make sure your software is able to compile MO files to update the existing one.
Both PO and MO files should be committed with Git before being submitted in a GitHub pull request.
Please refer to the [GitHub flow documentation](https://help.github.com/articles/github-flow/) if you're unfamiliar with it.
#### Add new translation strings
Once templates/controllers files have been edited with new translation strings, you can run `bin/console extract-strings --all` to update the PO files of all available languages at once.
## Translation functions usage
### In templates
Basic usage:
- `$this->__('Label')` => `Label`
- `$this->__('Label %s', 'test')` => `Label test`
With context, if a base english term can have multiple meanings:
- `$this->p__('noun', 'Search')` => `Recherche`
- `$this->p__('verb', 'Search')` => `Rechercher`
- `$this->p__('noun', 'Search %s', 'test')` => `Recherche test`
- `$this->p__('verb', 'Search %s', 'test')` => `Rechercher test`
With plurals:
- `$this->p__('Label', 'Labels', 1)` => `Label`
- `$this->p__('Label', 'Labels', 3)` => `Labels`
- `$this->p__('%d Label', '%d Labels', 1)` => `1 Label`
- `$this->p__('%d Label', '%d Labels', 3)` => `3 Labels`
- `$this->p__('%d Label', 'Labels %2%s %3%s', 1, 'test', 'test2')` => `Label test test2`
- `$this->p__('%d Label', 'Labels %2%s %3%s', 3, 'test', 'test2')` => `Labels test test2`
### In classes
You will need to add the `l10n` dependency to your class before you can use it (see [Pager](src/classes/Content/Pager.php)).
- `$this->l10n->gettext('Label')`
- `$this->l10n->pgettext('context', 'Label')`
- `$this->l10n->ngettext('Label', 'Labels', 3)`
- `$this->l10n->npgettext('context', 'Label', 'Labels', 3)`

Binary file not shown.

Before

Width:  |  Height:  |  Size: 325 B

After

Width:  |  Height:  |  Size: 2.6 KiB

View File

@ -1,141 +0,0 @@
<?php
namespace Friendica\Directory\Content;
/**
* @author Hypolite Petovan <mrpetovan@gmail.com>
*/
class L10n
{
/**
* @var string
*/
private $lang;
/**
* @var array
*/
private $strings;
/**
* @var string
*/
private $lang_path;
public function __construct(string $language = 'en', string $lang_path = '')
{
$this->lang = $language;
$this->lang_path = $lang_path;
$this->loadTranslationTable();
}
/**
* Loads string translation table
*
* First addon strings are loaded, then globals
*
* Uses an App object shim since all the strings files refer to $a->strings
*
* @param string $lang language code to load
*/
private function loadTranslationTable(): void
{
if (file_exists($this->lang_path . '/' . $this->lang . '/strings.php')) {
$this->strings = include $this->lang_path . '/' . $this->lang . '/strings.php';
}
}
/**
* @brief Return the localized version of a singular/plural string with optional string interpolation
*
* This function takes two english strings as parameters, singular and plural, as
* well as a count. If a localized version exists for the current language, they
* are used instead. Discrimination between singular and plural is done using the
* localized function if any or the default one. Finally, a string interpolation
* is performed using the count as parameter.
*
* Usages:
* - L10n::tt('Like', 'Likes', $count)
* - L10n::tt("%s user deleted", "%s users deleted", count($users))
*
* @param string $singular
* @param string $plural
* @param int $count
* @return string
*/
public function tt(string $singular, string $plural, int $count): string
{
if (!empty($this->strings[$singular])) {
$t = $this->strings[$singular];
if (is_array($t)) {
$plural_function = 'string_plural_select_' . str_replace('-', '_', $this->lang);
if (function_exists($plural_function)) {
$i = $plural_function($count);
} else {
$i = $this->stringPluralSelectDefault($count);
}
// for some languages there is only a single array item
if (!isset($t[$i])) {
$s = $t[0];
} else {
$s = $t[$i];
}
} else {
$s = $t;
}
} elseif ($this->stringPluralSelectDefault($count)) {
$s = $plural;
} else {
$s = $singular;
}
$s = @sprintf($s, $count);
return $s;
}
/**
* @brief Return the localized version of the provided string with optional string interpolation
*
* This function takes a english string as parameter, and if a localized version
* exists for the current language, substitutes it before performing an eventual
* string interpolation (sprintf) with additional optional arguments.
*
* Usages:
* - L10n::t('This is an example')
* - L10n::t('URL %s returned no result', $url)
* - L10n::t('Current version: %s, new version: %s', $current_version, $new_version)
*
* @param string $s
* @param array $vars Variables to interpolate in the translation string
* @return string
*/
public function t($s, ...$vars): string
{
if (empty($s)) {
return '';
}
if (!empty($this->strings[$s])) {
$t = $this->strings[$s];
$s = is_array($t) ? $t[0] : $t;
}
if (count($vars) > 0) {
$s = sprintf($s, ...$vars);
}
return $s;
}
/**
* Provide a fallback which will not collide with a function defined in any language file
*/
private function stringPluralSelectDefault(int $n): bool
{
return $n != 1;
}
}

View File

@ -5,7 +5,7 @@ namespace Friendica\Directory\Content;
/**
* The Pager has two very different output, Minimal and Full, see renderMinimal() and renderFull() for more details.
*
* @author Hypolite Petovan <mrpetovan@gmail.com>
* @author Hypolite Petovan <hypolite@mrpetovan.com>
*/
class Pager
{
@ -24,7 +24,7 @@ class Pager
private $baseQueryString = '';
/**
* @var \Friendica\Directory\Content\L10n
* @var \Gettext\TranslatorInterface
*/
private $l10n;
@ -33,11 +33,11 @@ class Pager
*
* Guesses the page number from the GET parameter 'page'.
*
* @param \Friendica\Directory\Content\L10n $l10n
* @param \Gettext\TranslatorInterface $l10n
* @param \Psr\Http\Message\ServerRequestInterface $request
* @param integer $itemsPerPage An optional number of items per page to override the default value
*/
public function __construct(L10n $l10n, \Psr\Http\Message\ServerRequestInterface $request, int $itemsPerPage = 50)
public function __construct(\Gettext\TranslatorInterface $l10n, \Psr\Http\Message\ServerRequestInterface $request, int $itemsPerPage = 50)
{
$this->l10n = $l10n;
$this->setQueryString($request);
@ -157,7 +157,7 @@ class Pager
* @param integer $itemCount The number of displayed items on the page
* @return array of links
*/
public function renderMinimal(int $itemCount, string $previous_label = 'Previous', string $next_label = 'Next')
public function renderMinimal(int $itemCount)
{
$displayedItemCount = max(0, $itemCount);
@ -165,12 +165,12 @@ class Pager
'class' => 'pager',
'prev' => [
'url' => $this->ensureQueryParameter($this->baseQueryString . '&page=' . ($this->getPage() - 1)),
'text' => $this->l10n->t($previous_label),
'text' => $this->l10n->gettext('Previous'),
'class' => 'previous' . ($this->getPage() == 1 ? ' disabled' : '')
],
'next' => [
'url' => $this->ensureQueryParameter($this->baseQueryString . '&page=' . ($this->getPage() + 1)),
'text' => $this->l10n->t($next_label),
'text' => $this->l10n->gettext('Next'),
'class' => 'next' . ($displayedItemCount <= 0 ? ' disabled' : '')
]
];
@ -208,12 +208,12 @@ class Pager
if ($totalItemCount > $this->getItemsPerPage()) {
$data['first'] = [
'url' => $this->ensureQueryParameter($this->baseQueryString . '&page=1'),
'text' => $this->l10n->t('First'),
'text' => $this->l10n->gettext('First'),
'class' => $this->getPage() == 1 ? 'disabled' : ''
];
$data['prev'] = [
'url' => $this->ensureQueryParameter($this->baseQueryString . '&page=' . ($this->getPage() - 1)),
'text' => $this->l10n->t('Previous'),
'text' => $this->l10n->gettext('Previous'),
'class' => $this->getPage() == 1 ? 'disabled' : ''
];
@ -270,12 +270,12 @@ class Pager
$data['next'] = [
'url' => $this->ensureQueryParameter($this->baseQueryString . '&page=' . ($this->getPage() + 1)),
'text' => $this->l10n->t('Next'),
'text' => $this->l10n->gettext('Next'),
'class' => $this->getPage() == $lastpage ? 'disabled' : ''
];
$data['last'] = [
'url' => $this->ensureQueryParameter($this->baseQueryString . '&page=' . $lastpage),
'text' => $this->l10n->t('Last'),
'text' => $this->l10n->gettext('Last'),
'class' => $this->getPage() == $lastpage ? 'disabled' : ''
];
}

View File

@ -0,0 +1,80 @@
<?php
namespace Friendica\Directory\Controllers\Api;
use Friendica\Directory\Content\Pager;
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\ServerRequestInterface;
/**
* @author Hypolite Petovan <hypolite@mrpetovan.com>
*/
class MatchSearch
{
/**
* @var \Friendica\Directory\Models\Profile
*/
private $profileModel;
/**
* @var \Gettext\TranslatorInterface
*/
private $l10n;
public function __construct(
\Friendica\Directory\Models\Profile $profileModel,
\Gettext\TranslatorInterface $l10n
)
{
$this->profileModel = $profileModel;
$this->l10n = $l10n;
}
public function render(\Slim\Http\Request $request, \Slim\Http\Response $response, array $args): \Slim\Http\Response
{
$perpage = filter_input(INPUT_POST, 'n', FILTER_SANITIZE_NUMBER_INT);
$query = filter_input(INPUT_POST, 's', FILTER_SANITIZE_STRING);
if (!$perpage) {
$perpage = 80;
}
$pager = new Pager($this->l10n, $request, $perpage);
$pager->setPage(filter_input(INPUT_POST, 'p', FILTER_SANITIZE_NUMBER_INT));
$sql_where = "MATCH (p.`tags`) AGAINST (:query)";
// At sign (@) is a reserved symbol in InnoDB full-text search, it can't be escaped
$query = str_replace('@', ' ', $query);
$values = ['query' => $query];
$profiles = $this->profileModel->getListForDisplay(
null,
$pager->getItemsPerPage(),
$pager->getStart(),
$sql_where,
$values,
);
$results = [];
foreach ($profiles as $profile) {
$results[] = [
'name' => $profile['name'],
'url' => $profile['profile_url'],
'photo' => $profile['photo'],
'tags' => $profile['tags'],
];
}
$count = $this->profileModel->getCountForDisplay($sql_where, $values);
$vars = [
'query' => $query,
'page' => $pager->getPage(),
'items_page' => $pager->getItemsPerPage(),
'total' => $count,
'results' => $results
];
return $response->withJson($vars);
}
}

View File

@ -1,12 +1,12 @@
<?php
namespace Friendica\Directory\Controllers\Web;
namespace Friendica\Directory\Controllers\Api;
use Slim\Http\Request;
use Slim\Http\Response;
/**
* @author Hypolite Petovan <mrpetovan@gmail.com>
* @author Hypolite Petovan <hypolite@mrpetovan.com>
*/
class Photo
{
@ -14,12 +14,18 @@ class Photo
* @var \Atlas\Pdo\Connection
*/
private $atlas;
/**
* @var string
*/
private $defaultProfilePictureSmallPath;
public function __construct(
\Atlas\Pdo\Connection $atlas
\Atlas\Pdo\Connection $atlas,
string $defaultProfilePictureSmallPath
)
{
$this->atlas = $atlas;
$this->defaultProfilePictureSmallPath = $defaultProfilePictureSmallPath;
}
public function render(Request $request, Response $response, array $args): Response
@ -30,7 +36,7 @@ class Photo
);
if (!$data) {
$data = file_get_contents('public/images/default-profile-sm.jpg');
$data = file_get_contents($this->defaultProfilePictureSmallPath);
}
//Try and cache our result.

View File

@ -2,43 +2,38 @@
namespace Friendica\Directory\Controllers\Api;
use \Friendica\Directory\Content\Pager;
use PDO;
use Friendica\Directory\Content\Pager;
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\ServerRequestInterface;
/**
* @author Hypolite Petovan <mrpetovan@gmail.com>
* @author Hypolite Petovan <hypolite@mrpetovan.com>
*/
class Search
{
/**
* @var \Atlas\Pdo\Connection
*/
private $atlas;
/**
* @var \Friendica\Directory\Models\Profile
*/
private $profileModel;
/**
* @var \Friendica\Directory\Content\L10n
* @var \Gettext\TranslatorInterface
*/
private $l10n;
public function __construct(
\Atlas\Pdo\Connection $atlas,
\Friendica\Directory\Models\Profile $profileModel,
\Friendica\Directory\Content\L10n $l10n
\Gettext\TranslatorInterface $l10n
)
{
$this->atlas = $atlas;
$this->profileModel = $profileModel;
$this->l10n = $l10n;
}
public function render(\Slim\Http\Request $request, \Slim\Http\Response $response, array $args): \Slim\Http\Response
{
$pager = new Pager($this->l10n, $request, 20);
$limit = min(100, filter_input(INPUT_GET, 'limit', FILTER_SANITIZE_NUMBER_INT) ?: 20);
$pager = new Pager($this->l10n, $request, $limit);
$originalQuery = $query = filter_input(INPUT_GET, 'q');
@ -48,8 +43,10 @@ class Search
$query .= '%';
$sql_where = '`' . $field . '` LIKE :query';
} else {
$sql_where = "MATCH (p.`name`, p.`pdesc`, p.`profile_url`, p.`locality`, p.`region`, p.`country`, p.`tags` )
$sql_where = "MATCH (p.`name`, p.`pdesc`, p.`username`, p.`locality`, p.`region`, p.`country`, p.`tags` )
AGAINST (:query IN BOOLEAN MODE)";
// At sign (@) is a reserved symbol in InnoDB full-text search, it can't be escaped
$query = str_replace('@', ' ', $query);
}
$values = ['query' => $query];
@ -61,19 +58,25 @@ AND `account_type` = :account_type';
$values['account_type'] = $account_type;
}
$profiles = $this->profileModel->getListForDisplay($pager->getItemsPerPage(), $pager->getStart(), $sql_where, $values);
$profiles = $this->profileModel->getListForDisplay(
null,
$pager->getItemsPerPage(),
$pager->getStart(),
$sql_where,
$values,
);
$count = $this->profileModel->getCountForDisplay($sql_where, $values);
$vars = [
'query' => $originalQuery,
'page' => $pager->getPage(),
'query' => $originalQuery,
'field' => $field,
'page' => $pager->getPage(),
'itemsperpage' => $pager->getItemsPerPage(),
'count' => $count,
'profiles' => $profiles
'count' => $count,
'profiles' => $profiles
];
// Render index view
return $response->withJson($vars);
}
}

View File

@ -6,7 +6,7 @@ use Slim\Http\Request;
use Slim\Http\Response;
/**
* @author Hypolite Petovan <mrpetovan@gmail.com>
* @author Hypolite Petovan <hypolite@mrpetovan.com>
*/
class Submit
{
@ -68,7 +68,8 @@ class Submit
['base_url' => $profileUriInfo['server_uri']]
);
$this->profilePollQueueModel->add($url);
$result = $this->profilePollQueueModel->add($url);
$this->logger->debug('Profile queue add URL: ' . $url . ' - ' . $result);
$this->logger->info('Successfully received profile URL');
} catch (\Exception $ex) {

View File

@ -0,0 +1,45 @@
<?php
namespace Friendica\Directory\Controllers\Api;
use Friendica\Directory\Content\Pager;
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\ServerRequestInterface;
/**
* @author Hypolite Petovan <hypolite@mrpetovan.com>
*/
class Surprise
{
/**
* @var \Atlas\Pdo\Connection
*/
private $atlas;
public function __construct(
\Atlas\Pdo\Connection $atlas
)
{
$this->atlas = $atlas;
}
public function render(\Slim\Http\Request $request, \Slim\Http\Response $response, array $args): \Slim\Http\Response
{
$redirectUrl = '';
$sql = 'SELECT `base_url`, server.*
FROM `server`
WHERE `reg_policy` = "REGISTER_OPEN"
AND `health_score` > 75
AND `ssl_state`
AND `available`
ORDER BY `health_score` DESC, RAND()';
$server = $this->atlas->fetchOne($sql);
if ($server) {
$redirectUrl = $server['base_url'];
}
return $response->withRedirect($redirectUrl);
}
}

View File

@ -6,7 +6,7 @@ use Slim\Http\Request;
use Slim\Http\Response;
/**
* @author Hypolite Petovan <mrpetovan@gmail.com>
* @author Hypolite Petovan <hypolite@mrpetovan.com>
*/
class SyncPull
{

View File

@ -7,7 +7,7 @@ use Monolog\Logger;
/**
* Description of Console
*
* @author Hypolite Petovan <mrpetovan@gmail.com>
* @author Hypolite Petovan <hypolite@mrpetovan.com>
*/
class Console extends \Asika\SimpleConsole\Console
{
@ -23,6 +23,7 @@ class Console extends \Asika\SimpleConsole\Console
protected $routes = [
'directory-add' => \Friendica\Directory\Routes\Console\DirectoryAdd::class,
'directory-poll' => \Friendica\Directory\Routes\Console\DirectoryPoll::class,
'profile-add' => \Friendica\Directory\Routes\Console\ProfileAdd::class,
'profile-hide' => \Friendica\Directory\Routes\Console\ProfileHide::class,
'profile-poll' => \Friendica\Directory\Routes\Console\ProfilePoll::class,
'server-hide' => \Friendica\Directory\Routes\Console\ServerHide::class,
@ -30,6 +31,7 @@ class Console extends \Asika\SimpleConsole\Console
'install' => \Friendica\Directory\Routes\Console\Install::class,
'updatedb' => \Friendica\Directory\Routes\Console\UpdateDb::class,
'dbupdate' => \Friendica\Directory\Routes\Console\UpdateDb::class,
'extract-strings' => \Friendica\Directory\Routes\Console\ExtractStrings::class,
];
public function __construct(\Slim\Container $container, ?array $argv = null)

View File

@ -3,7 +3,7 @@
namespace Friendica\Directory\Controllers\Console;
/**
* @author Hypolite Petovan <mrpetovan@gmail.com>
* @author Hypolite Petovan <hypolite@mrpetovan.com>
*/
class DirectoryAdd extends \Asika\SimpleConsole\Console
{
@ -54,17 +54,17 @@ HELP;
$directory_url = $this->getArgument(0);
$result = $this->atlas->perform('INSERT IGNORE INTO `directory_poll_queue` SET
$affected = $this->atlas->fetchAffected('INSERT IGNORE INTO `directory_poll_queue` SET
`directory_url` = :directory_url',
['directory_url' => $directory_url]
);
if (!$result) {
throw new \RuntimeException('Unable to add repository with URL: ' . $directory_url);
if (!$affected) {
$this->out('Directory already exists in the queue.');
} else {
$this->out('Successfully added the directory to the queue.');
}
$this->out('Successfully added the repository to the queue.');
return 0;
}
}

View File

@ -3,7 +3,7 @@
namespace Friendica\Directory\Controllers\Console;
/**
* @author Hypolite Petovan <mrpetovan@gmail.com>
* @author Hypolite Petovan <hypolite@mrpetovan.com>
*/
class DirectoryPoll extends \Asika\SimpleConsole\Console
{

View File

@ -0,0 +1,121 @@
<?php
namespace Friendica\Directory\Controllers\Console;
use Gettext\Merge;
use Gettext\Translations;
/**
* @author Hypolite Petovan <hypolite@mrpetovan.com>
*/
class ExtractStrings extends \Asika\SimpleConsole\Console
{
/**
* @var string
*/
protected $translationPath;
protected $helpOptions = ['h', 'help', '?'];
public function __construct(
string $translationPath,
?array $argv = null
)
{
$this->translationPath = $translationPath;
parent::__construct($argv);
}
protected function getHelp()
{
$help = <<<HELP
console extract-strings - Extract translation strings
Usage
bin/console extract-strings [language] [--all] [--force] [-h|--help|-?] [-v]
Description
Extract translation strings
Options
--all Update PO files for all existing languages
--force Generate po file from scratch, discarding existing translations
-h|--help|-? Show help information
-v Show more debug information.
HELP;
return $help;
}
protected function doExecute()
{
if (count($this->args) > 1) {
throw new \Asika\SimpleConsole\CommandArgsException('Too many arguments');
}
if ($this->getOption('all')) {
$langs = array_map('basename', glob(realpath($this->translationPath) . '/*', GLOB_ONLYDIR));
} else {
$lang = $this->getArgument(0);
if (!$lang) {
throw new \RuntimeException('Missing language argument and --all isn\'t provided');
}
$langs = [$lang];
}
$dir_iterator = new \RecursiveDirectoryIterator(realpath(__DIR__ . '/../../../'), \FilesystemIterator::SKIP_DOTS);
$iterator = new \RecursiveIteratorIterator($dir_iterator, \RecursiveIteratorIterator::SELF_FIRST);
$updatedTranslations = new Translations();
foreach ($iterator as $file) {
/**
* @var \SplFileInfo $file
*/
$extension = $file->getExtension();
if ($extension == 'php' || $extension == 'phtml') {
$translations = Translations::fromPhpCodeFile($file->getPathname());
if (count($translations)) {
$updatedTranslations->mergeWith($translations);
}
}
}
$this->out('Compiled up-to-date translations');
foreach ($langs as $locale) {
$existingTranslations = new Translations();
$stringsPoFile = $this->translationPath . '/' . $locale . '/LC_MESSAGES/strings.po';
if (is_file($stringsPoFile)) {
if (!$this->getOption('force')) {
$this->out('Loading existing ' . $locale . ' translations');
$existingTranslations->addFromPoFile($stringsPoFile);
}
} else {
$this->out('Creating directory ' . dirname($stringsPoFile));
mkdir(dirname($stringsPoFile), 0755, true);
}
$updatedTranslations->setLanguage($locale);
if ($this->getOption('force') || !is_file($stringsPoFile)) {
$existingTranslations = $updatedTranslations;
} else {
$this->out('Merging with existing translations');
$existingTranslations->mergeWith($updatedTranslations, Merge::ADD | Merge::REMOVE | Merge::REFERENCES_THEIRS | Merge::HEADERS_ADD);
}
$poString = $existingTranslations->toPoString();
// Strip absolute path to files
$poString = str_replace(realpath(__DIR__ . '/../../../../') . DIRECTORY_SEPARATOR, '', $poString);
$this->out('Writing ' . realpath($stringsPoFile));
file_put_contents($stringsPoFile, $poString);
}
return 0;
}
}

View File

@ -7,7 +7,7 @@ use Monolog\Logger;
use Seld\CliPrompt\CliPrompt;
/**
* @author Hypolite Petovan <mrpetovan@gmail.com>
* @author Hypolite Petovan <hypolite@mrpetovan.com>
*/
class Install extends \Asika\SimpleConsole\Console
{
@ -31,9 +31,9 @@ class Install extends \Asika\SimpleConsole\Console
protected function getHelp()
{
$help = <<<HELP
console install - Install directory
console install - Install wizard
Usage
bin/console install <server_url> [-h|--help|-?] [-v]
bin/console install [-h|--help|-?] [-v]
Description
Install directory

View File

@ -0,0 +1,86 @@
<?php
namespace Friendica\Directory\Controllers\Console;
use Friendica\Directory\Models\ProfilePollQueue;
/**
* @author Hypolite Petovan <hypolite@mrpetovan.com>
*/
class ProfileAdd extends \Asika\SimpleConsole\Console
{
/**
* @var ProfilePollQueue
*/
protected $profilePollQueueModel;
protected $helpOptions = ['h', 'help', '?'];
public function __construct(
ProfilePollQueue $profilePollQueueModel,
?array $argv = null
)
{
parent::__construct($argv);
$this->profilePollQueueModel = $profilePollQueueModel;
}
protected function getHelp()
{
$help = <<<HELP
console profile-add - Adds provided profile to queue
Usage
bin/console profile-add <profile_url> [-h|--help|-?] [-v]
Description
Adds provided profile to queue
Options
-h|--help|-? Show help information
-v Show more debug information.
HELP;
return $help;
}
protected function doExecute()
{
if (count($this->args) == 0) {
$this->out($this->getHelp());
return 0;
}
if (count($this->args) > 1) {
throw new \Asika\SimpleConsole\CommandArgsException('Too many arguments');
}
$profile_url = $this->getArgument(0);
$result = $this->profilePollQueueModel->add($profile_url);
switch($result) {
case 0: {
$this->out('Successfully added the profile to the queue.');
return 0;
break;
}
case ProfilePollQueue::EMPTY_URL: {
throw new \RuntimeException('Unable to add profile with empty URL');
}
case ProfilePollQueue::MISSING_HOST: {
throw new \RuntimeException('Unable to add profile URL with a missing host');
}
case ProfilePollQueue::PRIVATE_HOST: {
throw new \RuntimeException('Unable to add profile with a private URL');
}
case ProfilePollQueue::ALREADY_EXISTS: {
$this->out('Profile already existing in the queue.');
return 0;
}
default: {
throw new \RuntimeException('Unable to add profile to the queue');
}
}
}
}

View File

@ -5,7 +5,7 @@ namespace Friendica\Directory\Controllers\Console;
use Friendica\Directory\Models\Profile;
/**
* @author Hypolite Petovan <mrpetovan@gmail.com>
* @author Hypolite Petovan <hypolite@mrpetovan.com>
*/
class ProfileHide extends \Asika\SimpleConsole\Console
{

View File

@ -3,7 +3,7 @@
namespace Friendica\Directory\Controllers\Console;
/**
* @author Hypolite Petovan <mrpetovan@gmail.com>
* @author Hypolite Petovan <hypolite@mrpetovan.com>
*/
class ProfilePoll extends \Asika\SimpleConsole\Console
{

View File

@ -3,7 +3,7 @@
namespace Friendica\Directory\Controllers\Console;
/**
* @author Hypolite Petovan <mrpetovan@gmail.com>
* @author Hypolite Petovan <hypolite@mrpetovan.com>
*/
class ServerHide extends \Asika\SimpleConsole\Console
{

View File

@ -3,7 +3,7 @@
namespace Friendica\Directory\Controllers\Console;
/**
* @author Hypolite Petovan <mrpetovan@gmail.com>
* @author Hypolite Petovan <hypolite@mrpetovan.com>
*/
class ServerPoll extends \Asika\SimpleConsole\Console
{

View File

@ -5,7 +5,7 @@ namespace Friendica\Directory\Controllers\Console;
use Monolog\Logger;
/**
* @author Hypolite Petovan <mrpetovan@gmail.com>
* @author Hypolite Petovan <hypolite@mrpetovan.com>
*/
class UpdateDb extends \Asika\SimpleConsole\Console
{
@ -37,12 +37,14 @@ class UpdateDb extends \Asika\SimpleConsole\Console
$help = <<<HELP
console updatedb - Update database schema
Usage
bin/console updatedb <server_url> [-h|--help|-?] [-v]
bin/console updatedb [<version>] [-h|--help|-?] [-v]
Description
Update database schema
Options
<version> Optional target version number, default is the latest version.
Do not use this parameter if you're not sure what you're doing, it will result in data loss!
-h|--help|-? Show help information
-v Show more debug information.
HELP;
@ -56,16 +58,38 @@ HELP;
return 0;
}
if (count($this->args) > 1) {
if (count($this->args) > 2) {
throw new \Asika\SimpleConsole\CommandArgsException('Too many arguments');
}
$this->out('Updating database schema to latest version...');
$currentVersion = $this->migration->getCurrentVersion()['version'];
$this->migration->up();
$this->out('Database schema currently in version ' . $currentVersion);
$this->out('Database schema migrated to version ' . $this->migration->getCurrentVersion()['version']);
if (count($this->args) == 1) {
$this->out('Updating database schema to latest version...');
$this->migration->up();
$this->out('Database schema migrated to version ' . $this->migration->getCurrentVersion()['version']);
return 0;
}
$target = $this->getArgument(1);
if ($target > $currentVersion) {
$this->out('Updating database schema to version ' . $target);
$this->migration->up($target);
$this->out('Database schema migrated up to version ' . $this->migration->getCurrentVersion()['version']);
return 0;
}
if ($target < $currentVersion) {
$this->out('Downgrading database schema to version ' . $target);
$this->migration->down($target);
$this->out('Database schema migrated down to version ' . $this->migration->getCurrentVersion()['version']);
return 0;
}
$this->out('Target version equal to current version, exiting.');
return 0;
}
}

View File

@ -3,7 +3,7 @@
namespace Friendica\Directory\Controllers;
/**
* @author Hypolite Petovan <mrpetovan@gmail.com>
* @author Hypolite Petovan <hypolite@mrpetovan.com>
*/
class Cron
{
@ -93,6 +93,8 @@ class Cron
ORDER BY ISNULL(`last_polled`) DESC'
);
$this->logger->notice('Directories polling queue size: ' . count($directories));
foreach ($directories as $directory) {
if ($time_limit && microtime(true) - $this->startTime > $time_limit) {
break;
@ -133,6 +135,8 @@ class Cron
ORDER BY ISNULL(`last_polled`) DESC, `request_count` DESC'
);
$this->logger->notice('Servers polling queue size: ' . count($servers));
foreach ($servers as $server_queue_item) {
if ($time_limit && microtime(true) - $this->startTime > $time_limit) {
break;
@ -188,6 +192,8 @@ class Cron
ORDER BY RAND() ASC'
);
$this->logger->notice('Profiles polling queue size: ' . count($profiles));
foreach ($profiles as $profile) {
if ($time_limit && microtime(true) - $this->startTime > $time_limit) {
break;

View File

@ -0,0 +1,9 @@
<?php
namespace Friendica\Directory\Controllers\Web;
abstract class BaseController
{
abstract function render(\Slim\Http\Request $request, \Slim\Http\Response $response, array $args): array;
}

View File

@ -2,22 +2,26 @@
namespace Friendica\Directory\Controllers\Web;
use \Friendica\Directory\Content\Pager;
use \Friendica\Directory\Views\Widget\PopularCountries;
use \Friendica\Directory\Views\Widget\PopularTags;
use PDO;
use Friendica\Directory\Content\Pager;
use Friendica\Directory\Views\Widget\PopularCountries;
use Friendica\Directory\Views\Widget\PopularProfileLanguages;
use Friendica\Directory\Views\Widget\PopularTags;
use Slim\Http\Request;
use Slim\Http\Response;
/**
* @author Hypolite Petovan <mrpetovan@gmail.com>
* @author Hypolite Petovan <hypolite@mrpetovan.com>
*/
class Directory
class Directory extends BaseController
{
/**
* @var \Atlas\Pdo\Connection
*/
private $atlas;
/**
* @var \Friendica\Directory\Models\Server
*/
private $serverModel;
/**
* @var \Friendica\Directory\Models\Profile
*/
@ -31,56 +35,65 @@ class Directory
*/
private $renderer;
/**
* @var \Friendica\Directory\Content\L10n
* @var \Gettext\TranslatorInterface
*/
private $l10n;
public function __construct(
\Atlas\Pdo\Connection $atlas,
\Friendica\Directory\Models\Server $serverModel,
\Friendica\Directory\Models\Profile $profileModel,
\Friendica\Directory\Views\Widget\AccountTypeTabs $accountTypeTabs,
\Friendica\Directory\Views\PhpRenderer $renderer,
\Friendica\Directory\Content\L10n $l10n
\Gettext\TranslatorInterface $l10n
)
{
$this->atlas = $atlas;
$this->serverModel = $serverModel;
$this->profileModel = $profileModel;
$this->accountTypeTabs = $accountTypeTabs;
$this->renderer = $renderer;
$this->l10n = $l10n;
}
public function render(Request $request, Response $response, array $args): Response
public function render(Request $request, Response $response, array $args): array
{
$popularTags = new PopularTags($this->atlas, $this->renderer);
$popularCountries = new PopularCountries($this->atlas, $this->renderer);
$popularLanguages = new PopularProfileLanguages($this->atlas, $this->renderer);
$pager = new Pager($this->l10n, $request, 20);
$condition = '';
$sql_where = '';
$values = [];
if (!empty($args['account_type'])) {
$condition = '`account_type` = :account_type';
$sql_where = '`account_type` = :account_type';
$values = ['account_type' => $args['account_type']];
}
$profiles = $this->profileModel->getListForDisplay($pager->getItemsPerPage(), $pager->getStart(), $condition, $values);
$profiles = $this->profileModel->getListForDisplay(
$this->serverModel->getSubscribeUrlByProfile($request->getQueryParam('zrl', '')),
$pager->getItemsPerPage(),
$pager->getStart(),
$sql_where,
$values,
);
$count = $this->profileModel->getCountForDisplay($condition, $values);
$count = $this->profileModel->getCountForDisplay($sql_where, $values);
$vars = [
'title' => $this->l10n->t('People'),
'title' => $this->l10n->gettext('People'),
'profiles' => $profiles,
'pager_full' => $pager->renderFull($count),
'pager_minimal' => $pager->renderMinimal($count),
'accountTypeTabs' => $this->accountTypeTabs->render('directory', $args['account_type'] ?? ''),
'popularTags' => $popularTags->render(),
'popularCountries' => $popularCountries->render(),
'popularLanguages' => $popularLanguages->render(),
];
$content = $this->renderer->fetch('directory.phtml', $vars);
// Render index view
return $this->renderer->render($response, 'layout.phtml', ['baseUrl' => $request->getUri()->getBaseUrl(), 'content' => $content]);
return ['content' => $content];
}
}

View File

@ -0,0 +1,28 @@
<?php
namespace Friendica\Directory\Controllers\Web;
/**
* @author Hypolite Petovan <hypolite@mrpetovan.com>
*/
class Page extends BaseController
{
/**
* @var string
*/
private $pageFile;
public function __construct(
string $pageFile
)
{
$this->pageFile = $pageFile;
}
public function render(\Slim\Http\Request $request, \Slim\Http\Response $response, array $args): array
{
$content = file_get_contents($this->pageFile);
return ['content' => $content];
}
}

View File

@ -3,19 +3,20 @@
namespace Friendica\Directory\Controllers\Web;
use \Friendica\Directory\Content\Pager;
use PDO;
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\ServerRequestInterface;
/**
* @author Hypolite Petovan <mrpetovan@gmail.com>
* @author Hypolite Petovan <hypolite@mrpetovan.com>
*/
class Search
class Search extends BaseController
{
/**
* @var \Atlas\Pdo\Connection
*/
private $atlas;
/**
* @var \Friendica\Directory\Models\Server
*/
private $serverModel;
/**
* @var \Friendica\Directory\Models\Profile
*/
@ -29,66 +30,94 @@ class Search
*/
private $accountTypeTabs;
/**
* @var \Friendica\Directory\Content\L10n
* @var \Gettext\TranslatorInterface
*/
private $l10n;
public function __construct(
\Atlas\Pdo\Connection $atlas,
\Friendica\Directory\Models\Server $serverModel,
\Friendica\Directory\Models\Profile $profileModel,
\Friendica\Directory\Views\Widget\AccountTypeTabs $accountTypeTabs,
\Friendica\Directory\Views\PhpRenderer $renderer,
\Friendica\Directory\Content\L10n $l10n
\Gettext\TranslatorInterface $l10n
)
{
$this->atlas = $atlas;
$this->serverModel = $serverModel;
$this->profileModel = $profileModel;
$this->accountTypeTabs = $accountTypeTabs;
$this->renderer = $renderer;
$this->l10n = $l10n;
}
public function render(\Slim\Http\Request $request, \Slim\Http\Response $response, array $args): \Slim\Http\Response
public function render(\Slim\Http\Request $request, \Slim\Http\Response $response, array $args): array
{
$pager = new Pager($this->l10n, $request, 20);
$limit = min(100, filter_input(INPUT_GET, 'limit', FILTER_SANITIZE_NUMBER_INT) ?: 20);
$originalQuery = $query = filter_input(INPUT_GET, 'q');
$pager = new Pager($this->l10n, $request, $limit);
$field = filter_input(INPUT_GET, 'field', FILTER_SANITIZE_STRING, FILTER_FLAG_STRIP_LOW & FILTER_FLAG_STRIP_HIGH);
$originalQuery = $query = $request->getParam('q', '');
$field = $request->getParam('field', '');
$field = filter_var($field, FILTER_SANITIZE_STRING, FILTER_FLAG_STRIP_LOW | FILTER_FLAG_STRIP_HIGH | FILTER_FLAG_STRIP_BACKTICK);
$fieldName = '';
if ($field) {
$query .= '%';
$sql_where = '`' . $field . '` LIKE :query';
$sql_where = 'p.`' . $field . '` LIKE :query';
switch($field) {
case 'language': $fieldName = $this->l10n->pgettext('field', 'Language'); break;
case 'locality': $fieldName = $this->l10n->pgettext('field', 'Locality'); break;
case 'region' : $fieldName = $this->l10n->pgettext('field', 'Region') ; break;
case 'country' : $fieldName = $this->l10n->pgettext('field', 'Country') ; break;
default: $fieldName = ucfirst($field);
}
} else {
$sql_where = "MATCH (p.`name`, p.`pdesc`, p.`profile_url`, p.`locality`, p.`region`, p.`country`, p.`tags` )
$sql_where = "MATCH (p.`name`, p.`pdesc`, p.`username`, p.`locality`, p.`region`, p.`country`, p.`tags` )
AGAINST (:query IN BOOLEAN MODE)";
// At sign (@) is a reserved symbol in InnoDB full-text search, it can't be escaped
$query = str_replace('@', ' ', $query);
}
$values = ['query' => $query];
$account_type = $args['account_type'] ?? '';
$accountTypeTabs = $this->accountTypeTabs->render('search', $account_type, $sql_where, $values, ['q' => $originalQuery, 'field' => $field]);
if ($account_type) {
$sql_where .= '
AND `account_type` = :account_type';
$values['account_type'] = $account_type;
}
$profiles = $this->profileModel->getListForDisplay($pager->getItemsPerPage(), $pager->getStart(), $sql_where, $values);
$profiles = $this->profileModel->getListForDisplay(
$this->serverModel->getSubscribeUrlByProfile($request->getQueryParam('zrl', '')),
$pager->getItemsPerPage(),
$pager->getStart(),
$sql_where,
$values,
);
$count = $this->profileModel->getCountForDisplay($sql_where, $values);
$vars = [
'query' => $originalQuery,
'count' => $count,
'accountTypeTabs' => $this->accountTypeTabs->render('search', $account_type, ['q' => $originalQuery]),
'profiles' => $profiles,
'query' => $originalQuery,
'field' => $field,
'fieldName' => $fieldName,
'count' => $count,
'accountTypeTabs' => $accountTypeTabs,
'profiles' => $profiles,
'pager_full' => $pager->renderFull($count),
'pager_minimal' => $pager->renderMinimal($count),
];
$content = $this->renderer->fetch('search.phtml', $vars);
// Render index view
return $this->renderer->render($response, 'layout.phtml', ['baseUrl' => $request->getUri()->getBaseUrl(), 'content' => $content, 'noNavSearch' => true]);
return ['content' => $content, 'noNavSearch' => true];
}
}

View File

@ -2,15 +2,16 @@
namespace Friendica\Directory\Controllers\Web;
use \Friendica\Directory\Content\Pager;
use Friendica\Directory\Content\Pager;
use Friendica\Directory\Views\Widget\PopularServerLanguages;
use PDO;
use Slim\Http\Request;
use Slim\Http\Response;
/**
* @author Hypolite Petovan <mrpetovan@gmail.com>
* @author Hypolite Petovan <hypolite@mrpetovan.com>
*/
class Servers
class Servers extends BaseController
{
/**
* @var \Atlas\Pdo\Connection
@ -21,7 +22,7 @@ class Servers
*/
private $renderer;
/**
* @var \Friendica\Directory\Content\L10n
* @var \Gettext\TranslatorInterface
*/
private $l10n;
/**
@ -32,7 +33,7 @@ class Servers
public function __construct(
\Atlas\Pdo\Connection $atlas,
\Friendica\Directory\Views\PhpRenderer $renderer,
\Friendica\Directory\Content\L10n $l10n,
\Gettext\TranslatorInterface $l10n,
\Psr\SimpleCache\CacheInterface $simplecache
)
{
@ -42,11 +43,11 @@ class Servers
$this->simplecache = $simplecache;
}
public function render(Request $request, Response $response): Response
public function render(Request $request, Response $response, array $args): array
{
$stable_version = $this->simplecache->get('stable_version');
if (!$stable_version) {
$stable_version = trim(file_get_contents('https://git.friendi.ca/friendica/friendica/raw/branch/master/VERSION'));
$stable_version = trim(file_get_contents('https://git.friendi.ca/friendica/friendica/raw/branch/stable/VERSION'));
$this->simplecache->set('stable_version', $stable_version);
}
@ -56,45 +57,65 @@ class Servers
$this->simplecache->set('dev_version', $dev_version);
}
$rc_version = str_replace('-dev', '-rc', $dev_version);
$popularLanguages = new PopularServerLanguages($this->atlas, $this->renderer);
$pager = new Pager($this->l10n, $request, 20);
$sql_where = '';
$values = [];
if (!empty($args['language'])) {
$sql_where .= '
AND LEFT(`language`, 2) = LEFT(:language, 2)';
$values['language'] = $args['language'];
}
$stmt = 'SELECT *
FROM `server` s
WHERE `reg_policy` = "REGISTER_OPEN"
WHERE `reg_policy` != "REGISTER_CLOSED"
AND `available`
AND NOT `hidden`
' . $sql_where . '
ORDER BY `health_score` DESC, `ssl_state` DESC, `info` != "" DESC, `last_seen` DESC
LIMIT :start, :limit';
$servers = $this->atlas->fetchAll($stmt, [
$listValues = array_merge($values, [
'start' => [$pager->getStart(), PDO::PARAM_INT],
'limit' => [$pager->getItemsPerPage(), PDO::PARAM_INT]
]);
$servers = $this->atlas->fetchAll($stmt, $listValues);
foreach ($servers as $key => $server) {
$servers[$key]['user_count'] = $this->atlas->fetchValue(
'SELECT COUNT(*) FROM `profile` WHERE `available` AND `server_id` = :server_id',
'SELECT COUNT(*) FROM `profile` WHERE `available` AND NOT `hidden` AND `server_id` = :server_id',
['server_id' => [$server['id'], PDO::PARAM_INT]]
);
}
$stmt = 'SELECT COUNT(*)
FROM `server` s
WHERE `reg_policy` = "REGISTER_OPEN"
WHERE `reg_policy` != "REGISTER_CLOSED"
AND `available`
AND NOT `hidden`';
$count = $this->atlas->fetchValue($stmt);
AND NOT `hidden`
' . $sql_where;
$count = $this->atlas->fetchValue($stmt, $values);
$vars = [
'title' => $this->l10n->t('Public Servers'),
'title' => $this->l10n->gettext('Public Servers'),
'total' => $count,
'language' => $args['language'] ?? null,
'servers' => $servers,
'pager' => $pager->renderFull($count),
'stable_version' => $stable_version,
'rc_version' => $rc_version,
'dev_version' => $dev_version,
'popularLanguages' => $popularLanguages->render(),
];
$content = $this->renderer->fetch('servers.phtml', $vars);
// Render index view
return $this->renderer->render($response, 'layout.phtml', ['baseUrl' => $request->getUri()->getBaseUrl(), 'content' => $content]);
return ['content' => $content];
}
}

View File

@ -0,0 +1,159 @@
<?php
namespace Friendica\Directory\Controllers\Web;
/**
* @author Hypolite Petovan <hypolite@mrpetovan.com>
*/
class Statistics extends BaseController
{
/**
* @var \Atlas\Pdo\Connection
*/
private $connection;
/**
* @var \Psr\SimpleCache\CacheInterface
*/
private $simplecache;
/**
* @var \Friendica\Directory\Views\PhpRenderer
*/
private $renderer;
public function __construct(
\Atlas\Pdo\Connection $atlas,
\Psr\SimpleCache\CacheInterface $simplecache,
\Friendica\Directory\Views\PhpRenderer $renderer
)
{
$this->connection = $atlas;
$this->simplecache = $simplecache;
$this->renderer = $renderer;
}
public function render(\Slim\Http\Request $request, \Slim\Http\Response $response, array $args): array
{
$profilePollQueueCount = $this->connection->fetchValue('SELECT COUNT(*) FROM `profile_poll_queue`');
$profileCounts = $this->connection->fetchOne(
'SELECT
COUNT(*) AS `total`,
SUM(CASE WHEN `available` THEN 1 ELSE 0 END) AS `available`,
SUM(CASE WHEN `language` IS NOT NULL THEN 1 ELSE 0 END) AS `language`
FROM `profile`
WHERE NOT `hidden`');
$stmt = 'SELECT `language`, COUNT(*) AS `total`, COUNT(*) / :total AS `ratio`
FROM `profile`
WHERE `language` IS NOT NULL
AND `available`
AND NOT `hidden`
GROUP BY `language`
ORDER BY `total` DESC';
$profileLanguages = $this->connection->fetchAll($stmt, ['total' => $profileCounts['language']]);
$stable_version = $this->simplecache->get('stable_version');
if (!$stable_version) {
$stable_version = trim(file_get_contents('https://git.friendi.ca/friendica/friendica/raw/branch/stable/VERSION'));
$this->simplecache->set('stable_version', $stable_version);
}
$dev_version = $this->simplecache->get('dev_version');
if (!$dev_version) {
$dev_version = trim(file_get_contents('https://git.friendi.ca/friendica/friendica/raw/branch/develop/VERSION'));
$this->simplecache->set('dev_version', $dev_version);
}
$rc_version = str_replace('-dev', '-rc', $dev_version);
$serverPollQueueCount = $this->connection->fetchValue('SELECT COUNT(*) FROM `server_poll_queue`');
$serverCounts = $this->connection->fetchOne(
'SELECT
COUNT(*) AS `total`,
SUM(CASE WHEN `available` THEN 1 ELSE 0 END) AS `available`,
SUM(CASE WHEN `available` AND `language` IS NOT NULL THEN 1 ELSE 0 END) AS `language`,
SUM(CASE WHEN `available` AND `reg_policy` != "REGISTER_CLOSED" THEN 1 ELSE 0 END) AS `open`,
SUM(CASE WHEN `available` AND `version` IS NOT NULL THEN 1 ELSE 0 END) AS `version`,
SUM(CASE WHEN `available` AND (`version` = :dev_version OR `version` = :rc_version) THEN 1 ELSE 0 END) AS `dev_version`,
SUM(CASE WHEN `available` AND `version` = :stable_version THEN 1 ELSE 0 END) AS `stable_version`,
SUM(CASE WHEN `available` AND `version` != :dev_version AND `version` != :stable_version AND `version` != :rc_version THEN 1 ELSE 0 END) AS `outdated_version`
FROM `server`
WHERE NOT `hidden`', ['dev_version' => $dev_version, 'rc_version' => $rc_version, 'stable_version' => $stable_version]);
$stmt = 'SELECT LEFT(`language`, 2) AS `language`, COUNT(*) AS `total`, COUNT(*) / :total AS `ratio`
FROM `server`
WHERE `language` IS NOT NULL
AND `available`
AND NOT `hidden`
GROUP BY LEFT(`language`, 2)
ORDER BY `total` DESC';
$serverLanguages = $this->connection->fetchAll($stmt, ['total' => $serverCounts['language']]);
$stmt = 'SELECT `version`, COUNT(*) AS `total`, COUNT(*) / :total AS `ratio`
FROM `server`
WHERE `version` IS NOT NULL
AND `available`
AND NOT `hidden`
GROUP BY `version`
ORDER BY `total` DESC';
$serverVersions = $this->connection->fetchAll($stmt, ['total' => $serverCounts['version']]);
$vars = [
'stats' => [
'profile_queue' => [
'total' => $profilePollQueueCount
],
'profile' => [
'total' => $profileCounts['total'],
'ratio' => $profileCounts['total'] / $profilePollQueueCount,
'available' => [
'total' => $profileCounts['available'],
'ratio' => $profileCounts['available'] / $profileCounts['total']
],
'language' => [
'total' => $profileCounts['language'],
'ratio' => $profileCounts['language'] / $profileCounts['available']
],
'languages' => $profileLanguages,
],
'server_queue' => [
'total' => $serverPollQueueCount
],
'server' => [
'total' => $serverCounts['total'],
'ratio' => $serverCounts['total'] / $serverPollQueueCount,
'available' => [
'total' => $serverCounts['available'],
'ratio' => $serverCounts['available'] / $serverCounts['total']
],
'language' => [
'total' => $serverCounts['language'],
'ratio' => $serverCounts['language'] / $serverCounts['available']
],
'open' => [
'total' => $serverCounts['open'],
'ratio' => $serverCounts['open'] / $serverCounts['available']
],
'version' => [
'total' => $serverCounts['version'],
'ratio' => $serverCounts['version'] / $serverCounts['available']
],
'languages' => $serverLanguages,
'versions' => $serverVersions,
],
],
'dev_version' => $dev_version,
'rc_version' => $rc_version,
'stable_version' => $stable_version,
];
$content = $this->renderer->fetch('statistics.phtml', $vars);
// Render index view
return ['content' => $content, 'noNavSearch' => true];
}
}

View File

@ -0,0 +1,40 @@
<?php
namespace Friendica\Directory\Middleware;
use Friendica\Directory\Views\PhpRenderer;
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\ServerRequestInterface;
/**
* The ZRL middleware ensures the Renderer ZRL attribute is set if the query string parameter is present
*
* @author Hypolite Petovan <hypolite@mrpetovan.com>
* @package Friendica\Directory\Middleware
*/
class ZrlMiddleware
{
/**
* @var PhpRenderer
*/
private $phpRenderer;
public function __construct(PhpRenderer $phpRenderer)
{
$this->phpRenderer = $phpRenderer;
}
/**
* @param ServerRequestInterface $request PSR7 request
* @param ResponseInterface $response PSR7 response
* @param callable $next Next middleware
*
* @return ResponseInterface
*/
public function __invoke(ServerRequestInterface $request, ResponseInterface $response, callable $next)
{
$this->phpRenderer->addAttribute('zrl', $request->getQueryParams()['zrl'] ?? null);
return $next($request, $response);
}
}

View File

@ -3,7 +3,7 @@
namespace Friendica\Directory;
/**
* @author Hypolite Petovan <mrpetovan@gmail.com>
* @author Hypolite Petovan <hypolite@mrpetovan.com>
*/
class Model
{

View File

@ -3,10 +3,17 @@
namespace Friendica\Directory\Models;
/**
* @author Hypolite Petovan <mrpetovan@gmail.com>
* @author Hypolite Petovan <hypolite@mrpetovan.com>
*/
class Profile extends \Friendica\Directory\Model
{
const ACCOUNT_TYPE_PERSON = 0;
const ACCOUNT_TYPE_ORGANISATION = 1;
const ACCOUNT_TYPE_NEWS = 2;
const ACCOUNT_TYPE_COMMUNITY = 3;
const ACCOUNT_TYPE_RELAY = 4;
const ACCOUNT_TYPE_DELETED = 127;
public function deleteById(int $profile_id): bool
{
$this->atlas->perform('DELETE FROM `photo` WHERE `profile_id` = :profile_id',
@ -74,7 +81,7 @@ class Profile extends \Friendica\Directory\Model
];
}
public function getListForDisplay(int $limit = 30, int $start = 0, string $condition = '', array $values = []): array
public function getListForDisplay(string $subscribeUrl = null, int $limit = 30, int $start = 0, string $condition = '', array $values = []): array
{
if ($condition) {
$condition = 'AND ' . $condition;
@ -85,19 +92,25 @@ class Profile extends \Friendica\Directory\Model
'limit' => [$limit, \PDO::PARAM_INT]
]);
$stmt = 'SELECT p.`id`, p.`name`, p.`username`, p.`addr`, p.`account_type`, p.`pdesc`,
p.`locality`, p.`region`, p.`country`, p.`profile_url`, p.`dfrn_request`, p.`photo`,
p.`tags`, p.`last_activity`
$stmt = 'SELECT p.`id`, p.`name`, p.`username`, p.`addr`, p.`account_type`, p.`language`,
p.`pdesc`, p.`locality`, p.`region`, p.`country`, p.`profile_url`,
p.`photo`, p.`tags`, p.`last_activity`, s.`version`
FROM `profile` p
JOIN `server` s ON s.`id` = p.`server_id` AND s.`available` AND NOT s.`hidden`
WHERE p.`available`
AND NOT p.`hidden`
' . $condition . '
GROUP BY p.`id`
GROUP BY p.`id`, `filled_fields`, `last_activity`, `updated`
ORDER BY `filled_fields` DESC, `last_activity` DESC, `updated` DESC
LIMIT :start, :limit';
$profiles = $this->atlas->fetchAll($stmt, $values);
array_walk($profiles, function (array &$profile) use ($subscribeUrl) {
$profile['remote_follow'] = version_compare($profile['version'], '2020.03', '>=') ? str_replace('/profile/', '/remote_follow/', $profile['profile_url']) : null;
$profile['subscribe'] = $subscribeUrl ? str_replace('{uri}', urlencode($profile['profile_url']), $subscribeUrl): null;
unset($profile['version']);
});
return $profiles;
}

View File

@ -5,32 +5,41 @@ namespace Friendica\Directory\Models;
use Friendica\Directory\Utils\Network;
/**
* @author Hypolite Petovan <mrpetovan@gmail.com>
* @author Hypolite Petovan <hypolite@mrpetovan.com>
*/
class ProfilePollQueue extends \Friendica\Directory\Model
{
public function add(string $profile_url): bool
const EMPTY_URL = 1;
const MISSING_HOST = 2;
const PRIVATE_HOST = 3;
const ALREADY_EXISTS = 4;
/**
* @param string $profile_url
* @return int 0 on success or error code
*/
public function add(string $profile_url): int
{
$url = trim($profile_url);
if (!$url) {
return false;
return self::EMPTY_URL;
}
$host = parse_url($url, PHP_URL_HOST);
if (!$host) {
return false;
return self::MISSING_HOST;
}
if (Network::isPublicHost($host)) {
return false;
if (!Network::isPublicHost($host)) {
return self::PRIVATE_HOST;
}
$this->atlas->perform(
$affected = $this->atlas->fetchAffected(
'INSERT IGNORE INTO `profile_poll_queue` SET `profile_url` = :profile_url',
['profile_url' => $url]
);
return true;
return ($affected == 1 ? 0 : self::ALREADY_EXISTS);
}
}

View File

@ -3,7 +3,7 @@
namespace Friendica\Directory\Models;
/**
* @author Hypolite Petovan <mrpetovan@gmail.com>
* @author Hypolite Petovan <hypolite@mrpetovan.com>
*/
class Server extends \Friendica\Directory\Model
{
@ -33,10 +33,28 @@ class Server extends \Friendica\Directory\Model
SET `server_id` = :server_id,
`alias` = :alias,
`timestamp` = NOW()
ON DUPLICATE KEY UPDATE `timestamp` = NOW()',
ON DUPLICATE KEY UPDATE
`server_id` = :server_id,
`timestamp` = NOW()',
[
'server_id' => $server_id,
'alias' => strtolower($server_alias)
]);
}
/**
* Returns the complete subscribe URL of the given profile URL if we have it for the related server
*
* @param string $profile_url
* @return mixed|null
*/
public function getSubscribeUrlByProfile(string $profile_url)
{
if (preg_match('#^(.+)/profile/#', $profile_url, $matches)) {
$server = $this->getByUrlAlias($matches[1]);
return $server['subscribe_url'] ?? null;
}
return null;
}
}

View File

@ -2,15 +2,17 @@
namespace Friendica\Directory\Pollers;
use Friendica\Directory\Utils\Network;
/**
* @author Hypolite Petovan <mrpetovan@gmail.com>
* @author Hypolite Petovan <hypolite@mrpetovan.com>
*/
class Directory
{
/**
* @var \Atlas\Pdo\Connection
* @var \GuzzleHttp\ClientInterface
*/
private $atlas;
private $http;
/**
* @var \Friendica\Directory\Models\ProfilePollQueue
*/
@ -28,12 +30,12 @@ class Directory
];
public function __construct(
\Atlas\Pdo\Connection $atlas,
\GuzzleHttp\ClientInterface $http,
\Friendica\Directory\Models\ProfilePollQueue $profilePollQueueModel,
\Psr\Log\LoggerInterface $logger,
array $settings)
{
$this->atlas = $atlas;
$this->http = $http;
$this->profilePollQueueModel = $profilePollQueueModel;
$this->logger = $logger;
$this->settings = array_merge($this->settings, $settings);
@ -60,7 +62,8 @@ class Directory
$profiles = $this->getPullResult($directory_url, $last_polled);
foreach ($profiles as $profile_url) {
$this->profilePollQueueModel->add($profile_url);
$result = $this->profilePollQueueModel->add($profile_url);
$this->logger->debug('Profile queue add URL: ' . $profile_url . ' - ' . $result);
}
$this->logger->info('Successfully pulled ' . count($profiles) . ' profiles');
@ -79,35 +82,7 @@ class Directory
$path = '/sync/pull/since/' . $last_polled;
}
//Prepare the CURL call.
$handle = curl_init();
$options = array(
//Timeouts
CURLOPT_TIMEOUT => max($this->settings['probe_timeout'], 1), //Minimum of 1 second timeout.
CURLOPT_CONNECTTIMEOUT => 1,
//Redirecting
CURLOPT_FOLLOWLOCATION => true,
CURLOPT_MAXREDIRS => 8,
//SSL
CURLOPT_SSL_VERIFYPEER => true,
// CURLOPT_VERBOSE => true,
// CURLOPT_CERTINFO => true,
CURLOPT_SSL_VERIFYHOST => 2,
CURLOPT_PROTOCOLS => CURLPROTO_HTTP | CURLPROTO_HTTPS,
//Basic request
CURLOPT_USERAGENT => 'friendica-directory-probe-1.0',
CURLOPT_RETURNTRANSFER => true,
CURLOPT_URL => $directory_url . $path
);
curl_setopt_array($handle, $options);
$this->logger->info('Pulling profiles from directory URL: ' . $directory_url . $path);
//Probe the site.
$pull_data = curl_exec($handle);
//Done with CURL now.
curl_close($handle);
$pull_data = $this->http->get($directory_url . $path, ['timeout' => max($this->settings['probe_timeout'], 1)])->getBody()->getContents();
$data = json_decode($pull_data, true);

View File

@ -2,13 +2,18 @@
namespace Friendica\Directory\Pollers;
use Friendica\Directory\Utils\Network;
use Friendica\Directory\Models;
use Friendica\Directory\Utils;
use GuzzleHttp\Exception\RequestException;
/**
* @author Hypolite Petovan <mrpetovan@gmail.com>
* @author Hypolite Petovan <hypolite@mrpetovan.com>
*/
class Profile
{
const PROFILE_MISSING_CONFIRM = 2;
const PROFILE_MISSING_NOTIFY = 4;
const PROFILE_MISSING_POLL = 8;
/**
* @var \Atlas\Pdo\Connection
@ -16,12 +21,17 @@ class Profile
private $atlas;
/**
* @var \Friendica\Directory\Models\Server
* @var \GuzzleHttp\ClientInterface
*/
private $http;
/**
* @var Models\Server
*/
private $serverModel;
/**
* @var \Friendica\Directory\Models\Profile
* @var Models\Profile
*/
private $profileModel;
@ -40,13 +50,15 @@ class Profile
public function __construct(
\Atlas\Pdo\Connection $atlas,
\Friendica\Directory\Models\Server $serverModel,
\Friendica\Directory\Models\Profile $profileModel,
\GuzzleHttp\ClientInterface $http,
Models\Server $serverModel,
Models\Profile $profileModel,
\Psr\Log\LoggerInterface $logger,
array $settings
)
{
$this->atlas = $atlas;
$this->http = $http;
$this->serverModel = $serverModel;
$this->profileModel = $profileModel;
$this->logger = $logger;
@ -71,12 +83,12 @@ class Profile
return false;
}
if (!\Friendica\Directory\Utils\Network::isPublicHost($host)) {
if (!Utils\Network::isPublicHost($host)) {
$this->logger->warning('Private/reserved IP in polled profile URL: ' . $profile_uri);
return false;
}
$profileUriInfo = \Friendica\Directory\Models\Profile::extractInfoFromProfileUrl($profile_uri);
$profileUriInfo = Models\Profile::extractInfoFromProfileUrl($profile_uri);
if (!$profileUriInfo) {
$this->logger->warning('Profile URI invalid');
return false;
@ -119,28 +131,42 @@ class Profile
);
}
//Skip the profile scrape?
$noscrape = $server['noscrape_url'];
$available = false;
$params = [];
if ($noscrape) {
//Skip the profile scrape?
if ($server['noscrape_url']) {
$this->logger->debug('Calling ' . $server['noscrape_url'] . '/' . $username);
$params = \Friendica\Directory\Utils\Scrape::retrieveNoScrapeData($server['noscrape_url'] . '/' . $username);
$noscrape = !!$params; //If the result was false, do a scrape after all.
try {
$params = Utils\Scrape::retrieveNoScrapeData($this->http, $server['noscrape_url'] . '/' . $username);
} catch (RequestException $e) {
$this->logger->info('Request failed with error code ' . $e->getCode());
} catch (\Throwable $e) {
$this->logger->warning('Request failed with exception ' . get_class($e));
$this->logger->warning(var_export($e, true));
}
$available = !!$params; //If the result was false, do a scrape after all.
}
$available = true;
if (!$available) {
$this->logger->info('Parsing profile page ' . $profile_uri);
try {
$params = Utils\Scrape::retrieveProfileData($this->http, $profile_uri);
} catch (RequestException $e) {
$this->logger->info('Request failed with error code ' . $e->getCode());
} catch (\Throwable $e) {
$this->logger->warning('Request failed with exception ' . get_class($e));
$this->logger->warning(var_export($e, true));
}
$params['language'] = $server['language'];
if ($noscrape) {
$available = Network::testURL($profile_uri);
$this->logger->debug('Testing ' . $profile_uri . ': ' . ($available?'Success':'Failure'));
} else {
$this->logger->notice('Parsing profile page ' . $profile_uri);
$params = \Friendica\Directory\Utils\Scrape::retrieveProfileData($profile_uri);
$available = !empty($params['fn']);
}
// Empty result is due to an offline site.
if (count($params) < 2) {
if (empty($params) || count($params) < 2) {
//But for sites that are already in bad status. Do a cleanup now.
if ($profile_id && $server['health_score'] < $this->settings['remove_profile_health_threshold']) {
$this->profileModel->deleteById($profile_id);
@ -170,14 +196,37 @@ class Profile
}
// This is most likely a problem with the site configuration. Ignore.
if (self::validateParams($params)) {
if ($error = self::validateParams($params)) {
$this->logger->warning('Poll aborted, parameters invalid.', ['params' => $params]);
if ($error & Profile::PROFILE_MISSING_CONFIRM) {
$this->logger->notice('dfrn-confirm parameter is empty.');
}
if ($error & Profile::PROFILE_MISSING_NOTIFY) {
$this->logger->notice('dfrn-notify parameter is empty.');
}
if ($error & Profile::PROFILE_MISSING_POLL) {
$this->logger->notice('dfrn-poll parameter is empty.');
}
return false;
}
$account_type = 'People';
if (!empty($params['comm'])) {
$account_type = 'Forum';
switch ($params['account-type'] ?? Models\Profile::ACCOUNT_TYPE_PERSON) {
case Models\Profile::ACCOUNT_TYPE_ORGANISATION: $account_type = 'Organization'; break;
case Models\Profile::ACCOUNT_TYPE_NEWS : $account_type = 'News'; break;
case Models\Profile::ACCOUNT_TYPE_COMMUNITY : $account_type = 'Group'; break;
case Models\Profile::ACCOUNT_TYPE_RELAY : $account_type = 'Relay'; break;
case Models\Profile::ACCOUNT_TYPE_DELETED : $account_type = 'Deleted'; break;
case Models\Profile::ACCOUNT_TYPE_PERSON: {
$account_type = 'People';
if (!empty($params['comm'])) {
$account_type = 'Group';
}
break;
}
default: $account_type = 'Unknown'; break;
}
$tags = [];
@ -195,62 +244,68 @@ class Profile
$filled_fields = intval(!empty($params['pdesc'])) * 4 + intval(!empty($params['tags'])) * 2 + intval(!empty($params['locality']) || !empty($params['region']) || !empty($params['country-name']));
$this->logger->debug(var_export($params, true));
$values = [
'profile_id' => $profile_id,
'server_id' => $server['id'],
'username' => $username,
'name' => $params['fn'],
'pdesc' => $params['pdesc'] ?? '',
'locality' => $params['locality'] ?? '',
'region' => $params['region'] ?? '',
'country' => $params['country-name'] ?? '',
'profile_url' => $profile_uri,
'photo' => $params['photo'],
'tags' => implode(' ', $tags),
'addr' => $addr,
'account_type' => $account_type,
'language' => $params['language'] ?? null,
'filled_fields'=> $filled_fields,
'last_activity'=> $params['last-activity'] ?? null,
'available' => [$available, \PDO::PARAM_BOOL],
];
$this->logger->debug(var_export($values, true));
$this->atlas->perform('INSERT INTO `profile` SET
`id` = :profile_id,
`server_id` = :server_id,
`username` = :username,
`name` = :name,
`pdesc` = :pdesc,
`locality` = :locality,
`region` = :region,
`country` = :country,
`profile_url` = :profile_url,
`dfrn_request` = :dfrn_request,
`tags` = :tags,
`addr` = :addr,
`account_type` = :account_type,
`server_id` = :server_id,
`username` = :username,
`name` = :name,
`pdesc` = :pdesc,
`locality` = :locality,
`region` = :region,
`country` = :country,
`profile_url` = :profile_url,
`photo` = :photo,
`tags` = :tags,
`addr` = :addr,
`account_type` = :account_type,
`language` = :language,
`filled_fields` = :filled_fields,
`last_activity` = :last_activity,
`available` = :available,
`available` = :available,
`created` = NOW(),
`updated` = NOW()
ON DUPLICATE KEY UPDATE
`server_id` = :server_id,
`username` = :username,
`name` = :name,
`pdesc` = :pdesc,
`locality` = :locality,
`region` = :region,
`country` = :country,
`profile_url` = :profile_url,
`dfrn_request` = :dfrn_request,
`photo` = :photo,
`tags` = :tags,
`addr` = :addr,
`account_type` = :account_type,
`server_id` = :server_id,
`username` = :username,
`name` = :name,
`pdesc` = :pdesc,
`locality` = :locality,
`region` = :region,
`country` = :country,
`profile_url` = :profile_url,
`photo` = :photo,
`tags` = :tags,
`addr` = :addr,
`account_type` = :account_type,
`language` = :language,
`filled_fields` = :filled_fields,
`last_activity` = :last_activity,
`available` = :available,
`updated` = NOW()',
[
'profile_id' => $profile_id,
'server_id' => $server['id'],
'username' => $username,
'name' => $params['fn'],
'pdesc' => $params['pdesc'] ?? '',
'locality' => $params['locality'] ?? '',
'region' => $params['region'] ?? '',
'country' => $params['country-name'] ?? '',
'profile_url' => $profile_uri,
'dfrn_request' => $params['dfrn-request'] ?? null,
'photo' => $params['photo'],
'tags' => implode(' ', $tags),
'addr' => $addr,
'account_type' => $account_type,
'filled_fields' => $filled_fields,
'last_activity' => $params['last-activity'] ?? null,
'available' => $available,
]
`available` = :available,
`updated` = NOW()',
$values
);
if (!$profile_id) {
@ -274,23 +329,27 @@ class Profile
$status = false;
if ($profile_id) {
$img_str = \Friendica\Directory\Utils\Network::fetchURL($params['photo'], true);
$img = new \Friendica\Directory\Utils\Photo($img_str);
if ($img->getImage()) {
$img->scaleImageSquare(80);
try {
$img_str = $this->http->get($params['photo'])->getBody()->getContents();
$img = new Utils\Photo($img_str);
if ($img->getImage()) {
$img->scaleImageSquare(80);
$this->atlas->perform('INSERT INTO `photo` SET
`profile_id` = :profile_id,
`data` = :data
ON DUPLICATE KEY UPDATE
`data` = :data',
[
'profile_id' => $profile_id,
'data' => $img->imageString()
]
);
$this->atlas->perform('INSERT INTO `photo` SET
`profile_id` = :profile_id,
`data` = :data
ON DUPLICATE KEY UPDATE
`data` = :data',
[
'profile_id' => $profile_id,
'data' => $img->imageString()
]
);
}
$status = true;
} catch (RequestException $e) {
$this->logger->info('Photo retrieval unsuccessful', ['url' => $params['photo'], 'code' => $e->getCode()]);
}
$status = true;
}
$submit_end = microtime(true);
@ -320,23 +379,21 @@ class Profile
return true;
}
/**
* @param array $params
* @return int
*/
private static function validateParams(array $params): int
{
$errors = 0;
if (empty($params['key'])) {
$errors++;
}
if (empty($params['dfrn-request'])) {
$errors++;
}
if (empty($params['dfrn-confirm'])) {
$errors++;
$errors &= self::PROFILE_MISSING_CONFIRM;
}
if (empty($params['dfrn-notify'])) {
$errors++;
$errors &= self::PROFILE_MISSING_NOTIFY;
}
if (empty($params['dfrn-poll'])) {
$errors++;
$errors &= self::PROFILE_MISSING_POLL;
}
return $errors;

View File

@ -2,8 +2,12 @@
namespace Friendica\Directory\Pollers;
use GuzzleHttp\Psr7\Uri;
use GuzzleHttp\Exception\RequestException;
use GuzzleHttp\TransferStats;
/**
* @author Hypolite Petovan <mrpetovan@gmail.com>
* @author Hypolite Petovan <hypolite@mrpetovan.com>
*/
class Server
{
@ -11,6 +15,10 @@ class Server
* @var \Atlas\Pdo\Connection
*/
private $atlas;
/**
* @var \GuzzleHttp\ClientInterface
*/
private $http;
/**
* @var \Friendica\Directory\Models\ProfilePollQueue
*/
@ -38,6 +46,7 @@ class Server
public function __construct(
\Atlas\Pdo\Connection $atlas,
\GuzzleHttp\ClientInterface $http,
\Friendica\Directory\Models\ProfilePollQueue $profilePollQueueModel,
\Friendica\Directory\Models\Server $serverModel,
\Psr\SimpleCache\CacheInterface $simplecache,
@ -45,6 +54,7 @@ class Server
array $settings)
{
$this->atlas = $atlas;
$this->http = $http;
$this->profilePollQueueModel = $profilePollQueueModel;
$this->serverModel = $serverModel;
$this->simplecache = $simplecache;
@ -85,7 +95,9 @@ class Server
$probe_result = $this->getProbeResult($polled_url);
$parse_success = !empty($probe_result['data']);
$parse_success = !empty($probe_result['data']['url']);
$avg_ping = null;
if ($parse_success) {
$base_url = $probe_result['data']['url'];
@ -142,12 +154,17 @@ class Server
$addons = $probe_result['data']['plugins'];
}
if (!empty($probe_result['data']['admin']['profile'])) {
$subscribe = $this->getSubscribeUrl($probe_result['data']['url'], $probe_result['data']['admin']['profile']);
}
$this->atlas->perform(
'UPDATE `server`
SET `available` = 1,
`last_seen` = NOW(),
`base_url` = :base_url,
`name` = :name,
`language` = :language,
`version` = :version,
`addons` = :addons,
`reg_policy` = :reg_policy,
@ -155,33 +172,49 @@ class Server
`admin_name` = :admin_name,
`admin_profile` = :admin_profile,
`noscrape_url` = :noscrape_url,
`subscribe_url` = :subscribe_url,
`ssl_state` = :ssl_state
WHERE `id` = :server_id',
[
'server_id' => $server['id'],
'base_url' => strtolower($probe_result['data']['url']),
'name' => $probe_result['data']['site_name'],
'version' => $probe_result['data']['version'],
'addons' => implode(',', $addons),
'reg_policy' => $probe_result['data']['register_policy'],
'info' => $probe_result['data']['info'],
'admin_name' => $probe_result['data']['admin']['name'],
'admin_profile' => $probe_result['data']['admin']['profile'],
'noscrape_url' => $probe_result['data']['no_scrape_url'] ?? null,
'ssl_state' => $probe_result['ssl_state']
'server_id' => $server['id'],
'base_url' => strtolower($probe_result['data']['url']),
'name' => substr($probe_result['data']['site_name'], 0, 255),
'language' => $probe_result['data']['language'] ?? null,
'version' => $probe_result['data']['version'],
'addons' => implode(',', $addons),
'reg_policy' => $probe_result['data']['register_policy'],
'info' => $probe_result['data']['info'],
'admin_name' => $probe_result['data']['admin']['name'] ?? null,
'admin_profile' => $probe_result['data']['admin']['profile'] ?? null,
'noscrape_url' => $probe_result['data']['no_scrape_url'] ?? null,
'subscribe_url' => $subscribe ?? null,
'ssl_state' => $probe_result['ssl_state']
]
);
//Add the admin to the directory
if (!empty($probe_result['data']['admin']['profile'])) {
$this->profilePollQueueModel->add($probe_result['data']['admin']['profile']);
$result = $this->profilePollQueueModel->add($probe_result['data']['admin']['profile']);
$this->logger->debug('Profile queue add URL: ' . $probe_result['data']['admin']['profile'] . ' - ' . $result);
}
$this->discoverPoco($base_url);
} else {
$this->logger->debug('Parse unsuccessful', ['$polled_url' => $polled_url, '$probe_result' => $probe_result]);
}
if ($server) {
//Get the new health.
$version = $parse_success ? $probe_result['data']['version'] : '';
$health_score = $this->computeHealthScore($server['health_score'], $parse_success, $probe_result['time'], $version, $probe_result['ssl_state']);
$health_score = $this->computeHealthScore(
$server['health_score'],
$parse_success,
$probe_result['time'],
$version,
$probe_result['ssl_state'],
$avg_ping,
$probe_result['data']['info'] ?? null
);
$this->atlas->perform(
'UPDATE `server` SET `health_score` = :health_score WHERE `id` = :server_id',
@ -221,65 +254,50 @@ class Server
private function getProbeResult(string $base_url): array
{
//Prepare the CURL call.
$handle = curl_init();
$options = array(
//Timeouts
CURLOPT_TIMEOUT => max($this->settings['probe_timeout'], 1), //Minimum of 1 second timeout.
CURLOPT_CONNECTTIMEOUT => 1,
//Redirecting
CURLOPT_FOLLOWLOCATION => true,
CURLOPT_MAXREDIRS => 8,
//SSL
CURLOPT_SSL_VERIFYPEER => true,
// CURLOPT_VERBOSE => true,
// CURLOPT_CERTINFO => true,
CURLOPT_SSL_VERIFYHOST => 2,
CURLOPT_PROTOCOLS => CURLPROTO_HTTP | CURLPROTO_HTTPS,
//Basic request
CURLOPT_USERAGENT => 'friendica-directory-probe-1.0',
CURLOPT_RETURNTRANSFER => true,
CURLOPT_URL => $base_url . '/friendica/json'
);
curl_setopt_array($handle, $options);
$curl_info = null;
$options = [
'timeout' => max($this->settings['probe_timeout'], 1),
'on_stats' => function (TransferStats $transferStats) use (&$curl_info) {
$curl_info = $transferStats->getHandlerStats();
}
];
$sslcert_issues = false;
//Probe the site.
$probe_start = microtime(true);
$probe_data = curl_exec($handle);
$probe_end = microtime(true);
$probe_data = null;
try {
//Probe the site.
$probe_data = $this->http->get($base_url . '/friendica/json', $options)->getBody()->getContents();
} catch (RequestException $e) {
if (in_array($e->getHandlerContext()['errno'] ?? 0, [
60, //Could not authenticate certificate with known CA's
83 //Issuer check failed
])) {
$sslcert_issues = true;
//Check for SSL problems.
$curl_statuscode = curl_errno($handle);
$sslcert_issues = in_array($curl_statuscode, array(
60, //Could not authenticate certificate with known CA's
83 //Issuer check failed
));
//When it's the certificate that doesn't work, we probe again without strict SSL.
$options['verify'] = false;
//When it's the certificate that doesn't work.
if ($sslcert_issues) {
//Probe again, without strict SSL.
$options[CURLOPT_SSL_VERIFYPEER] = false;
//Replace the handle.
curl_close($handle);
$handle = curl_init();
curl_setopt_array($handle, $options);
//Probe.
$probe_start = microtime(true);
$probe_data = curl_exec($handle);
$probe_end = microtime(true);
//Store new status.
$curl_statuscode = curl_errno($handle);
$probe_start = microtime(true);
try {
$probe_data = $this->http->get($base_url . '/friendica/json', $options)->getBody()->getContents();
} catch(RequestException $e) {
// Collects 404, 500 errors
$this->logger->info('SSL-non-verified URL probe failed with error code: ' . $e->getCode());
}
} else {
$this->logger->info('SSL-verified URL probe failed with error code: ' . $e->getCode());
}
} catch (\InvalidArgumentException $e) {
$this->logger->error('Invalid argument provided to HTTP client', ['base_url' => $base_url, 'exception' => $e]);
return ['data' => false, 'time' => 0, 'curl_info' => [], 'ssl_state' => null];
}
//Gather more meta.
$time = round(($probe_end - $probe_start) * 1000);
$curl_info = curl_getinfo($handle);
$probe_end = microtime(true);
//Done with CURL now.
curl_close($handle);
$time = round(($probe_end - $probe_start) * 1000);
try {
$data = json_decode($probe_data, true);
@ -299,7 +317,15 @@ class Server
return ['data' => $data, 'time' => $time, 'curl_info' => $curl_info, 'ssl_state' => $ssl_state];
}
private function computeHealthScore(int $original_health, bool $probe_success, int $time = null, string $version = null, int $ssl_state = null): int
private function computeHealthScore(
int $original_health,
bool $probe_success,
?int $time,
?string $version,
?int $ssl_state,
?float $avg_ping,
?string $description
): int
{
//Probe failed, costs you 30 points.
if (!$probe_success) {
@ -348,7 +374,7 @@ class Server
} else {
$stable_version = $this->simplecache->get('stable_version');
if (!$stable_version) {
$stable_version = trim(file_get_contents('https://git.friendi.ca/friendica/friendica/raw/branch/master/VERSION'));
$stable_version = trim(file_get_contents('https://git.friendi.ca/friendica/friendica/raw/branch/stable/VERSION'));
$this->simplecache->set('stable_version', $stable_version);
}
@ -358,14 +384,108 @@ class Server
$this->simplecache->set('dev_version', $dev_version);
}
if ($version == $dev_version) {
$max_health = 95; //Develop can be unstable
$rc_version = str_replace('-dev', '-rc', $dev_version);
if ($version == $dev_version || $version == $rc_version) {
$old_max_health = $max_health;
$new_max_health = 95; //Develop/RC can be unstable
$max_health = min($old_max_health, $new_max_health);
} elseif ($version !== $stable_version) {
$delta = min($delta, 0) - 10; // Losing score as time passes if node isn't updated
}
}
}
// No description available penalty
if (!$description) {
$max_health = min(75, $max_health);
}
// No ping penalty
if (!$avg_ping) {
$max_health -= 5;
}
return max(min($max_health, $original_health + $delta), -100);
}
function discoverPoco($base_url): void
{
$uri = Uri::withQueryValues(new Uri($base_url . '/poco'), ['fields' => 'urls', 'count' => 1000]);
try {
$response = $this->http->request('GET', $uri);
} catch (RequestException $e) {
$this->logger->info('Unsuccessful poco request: ' . $uri);
return;
}
try {
$pocoFetchData = json_decode($response->getBody()->getContents());
} catch (\Throwable $e) {
$this->logger->notice('Invalid JSON string for PoCo URL: ' . $uri);
return;
}
if (!isset($pocoFetchData->entry)) {
$this->logger->notice('Invalid JSON structure for PoCo URL: ' . $uri);
return;
}
foreach($pocoFetchData->entry as $entry) {
if (empty($entry->urls)) {
continue;
}
foreach ($entry->urls as $url) {
if (!empty($url->type) && !empty($url->value) && $url->type == 'profile') {
$result = $this->profilePollQueueModel->add($url->value);
if ($result === 0) {
$this->logger->info('Discovered profile URL ' . $url->value);
}
}
}
}
}
public function getSubscribeUrl($base_url, $profile)
{
$uri = Uri::withQueryValues(new Uri($base_url . '/xrd'), ['uri' => $profile]);
try {
$response = $this->http->request('GET', $uri, ['headers' => ['Accept' => 'application/jrd+json']]);
} catch (RequestException $e) {
$this->logger->info('Unsuccessful xrd request: ' . $uri);
return null;
}
$xrdJsonData = $response->getBody()->getContents();
$this->logger->debug('WebRequest: ' . $uri . ' Status: ' . $response->getStatusCode());
if ($response->getStatusCode() != 200) {
$this->logger->info('Unsuccessful XRD request: ' . $uri);
return null;
}
try {
$xrdData = json_decode($xrdJsonData);
} catch (\Throwable $e) {
$this->logger->notice('Invalid JSON string for XRD URL: ' . $uri);
return null;
}
if (!isset($xrdData->links)) {
$this->logger->notice('Invalid JSON structure for XRD URL: ' . $uri);
return null;
}
foreach ($xrdData->links as $link) {
if ($link->rel == 'http://ostatus.org/schema/1.0/subscribe') {
return $link->template ?? null;
}
}
return null;
}
}

View File

@ -3,7 +3,7 @@
namespace Friendica\Directory\Routes\Console;
/**
* @author Hypolite Petovan <mrpetovan@gmail.com>
* @author Hypolite Petovan <hypolite@mrpetovan.com>
*/
abstract class BaseRoute
{

View File

@ -3,7 +3,7 @@
namespace Friendica\Directory\Routes\Console;
/**
* @author Hypolite Petovan <mrpetovan@gmail.com>
* @author Hypolite Petovan <hypolite@mrpetovan.com>
*/
class DirectoryAdd extends BaseRoute
{

View File

@ -3,7 +3,7 @@
namespace Friendica\Directory\Routes\Console;
/**
* @author Hypolite Petovan <mrpetovan@gmail.com>
* @author Hypolite Petovan <hypolite@mrpetovan.com>
*/
class DirectoryPoll extends BaseRoute
{
@ -11,7 +11,7 @@ class DirectoryPoll extends BaseRoute
{
return (new \Friendica\Directory\Controllers\Console\DirectoryPoll(
$this->container->get('atlas'),
$this->container->get('\Friendica\Directory\Pollers\Directory'),
$this->container->get(\Friendica\Directory\Pollers\Directory::class),
$args
));
}

View File

@ -0,0 +1,17 @@
<?php
namespace Friendica\Directory\Routes\Console;
/**
* @author Hypolite Petovan <hypolite@mrpetovan.com>
*/
class ExtractStrings extends BaseRoute
{
public function __invoke(array $args)
{
return (new \Friendica\Directory\Controllers\Console\ExtractStrings(
$this->container->get('settings')['i18n']['path'],
$args
));
}
}

View File

@ -3,7 +3,7 @@
namespace Friendica\Directory\Routes\Console;
/**
* @author Hypolite Petovan <mrpetovan@gmail.com>
* @author Hypolite Petovan <hypolite@mrpetovan.com>
*/
class Install extends BaseRoute
{

View File

@ -0,0 +1,17 @@
<?php
namespace Friendica\Directory\Routes\Console;
/**
* @author Hypolite Petovan <hypolite@mrpetovan.com>
*/
class ProfileAdd extends BaseRoute
{
public function __invoke(array $args)
{
return (new \Friendica\Directory\Controllers\Console\ProfileAdd(
$this->container->get(\Friendica\Directory\Models\ProfilePollQueue::class),
$args
));
}
}

View File

@ -3,7 +3,7 @@
namespace Friendica\Directory\Routes\Console;
/**
* @author Hypolite Petovan <mrpetovan@gmail.com>
* @author Hypolite Petovan <hypolite@mrpetovan.com>
*/
class ProfileHide extends BaseRoute
{

View File

@ -3,14 +3,14 @@
namespace Friendica\Directory\Routes\Console;
/**
* @author Hypolite Petovan <mrpetovan@gmail.com>
* @author Hypolite Petovan <hypolite@mrpetovan.com>
*/
class ProfilePoll extends BaseRoute
{
public function __invoke(array $args)
{
return (new \Friendica\Directory\Controllers\Console\ProfilePoll(
$this->container->get('\Friendica\Directory\Pollers\Profile'),
$this->container->get(\Friendica\Directory\Pollers\Profile::class),
$args
));
}

View File

@ -3,7 +3,7 @@
namespace Friendica\Directory\Routes\Console;
/**
* @author Hypolite Petovan <mrpetovan@gmail.com>
* @author Hypolite Petovan <hypolite@mrpetovan.com>
*/
class ServerHide extends BaseRoute
{
@ -11,7 +11,7 @@ class ServerHide extends BaseRoute
{
return (new \Friendica\Directory\Controllers\Console\ServerHide(
$this->container->get('atlas'),
$this->container->get('\Friendica\Directory\Models\Server'),
$this->container->get(\Friendica\Directory\Models\Server::class),
$args
));
}

View File

@ -3,7 +3,7 @@
namespace Friendica\Directory\Routes\Console;
/**
* @author Hypolite Petovan <mrpetovan@gmail.com>
* @author Hypolite Petovan <hypolite@mrpetovan.com>
*/
class ServerPoll extends BaseRoute
{
@ -11,7 +11,7 @@ class ServerPoll extends BaseRoute
{
return (new \Friendica\Directory\Controllers\Console\ServerPoll(
$this->container->get('atlas'),
$this->container->get('\Friendica\Directory\Pollers\Server'),
$this->container->get(\Friendica\Directory\Pollers\Server::class),
$args
));
}

View File

@ -3,7 +3,7 @@
namespace Friendica\Directory\Routes\Console;
/**
* @author Hypolite Petovan <mrpetovan@gmail.com>
* @author Hypolite Petovan <hypolite@mrpetovan.com>
*/
class UpdateDb extends BaseRoute
{

View File

@ -3,7 +3,7 @@
namespace Friendica\Directory\Routes\Http;
/**
* @author Hypolite Petovan <mrpetovan@gmail.com>
* @author Hypolite Petovan <hypolite@mrpetovan.com>
*/
abstract class BaseRoute
{

View File

@ -1,20 +0,0 @@
<?php
namespace Friendica\Directory\Routes\Http;
/**
* @author Hypolite Petovan <mrpetovan@gmail.com>
*/
class Directory extends BaseRoute
{
public function __invoke(\Slim\Http\Request $request, \Slim\Http\Response $response, array $args): \Slim\Http\Response
{
return (new \Friendica\Directory\Controllers\Web\Directory(
$this->container->atlas,
$this->container->get('\Friendica\Directory\Models\Profile'),
$this->container->get('\Friendica\Directory\Views\Widget\AccountTypeTabs'),
$this->container->renderer,
$this->container->l10n)
)->render($request, $response, $args);
}
}

View File

@ -0,0 +1,17 @@
<?php
namespace Friendica\Directory\Routes\Http;
/**
* @author Hypolite Petovan <hypolite@mrpetovan.com>
*/
class MatchSearch extends BaseRoute
{
public function __invoke(\Slim\Http\Request $request, \Slim\Http\Response $response, array $args): \Slim\Http\Response
{
return (new \Friendica\Directory\Controllers\Api\MatchSearch(
$this->container->get(\Friendica\Directory\Models\Profile::class),
$this->container->l10n
))->render($request, $response, $args);
}
}

View File

@ -3,14 +3,15 @@
namespace Friendica\Directory\Routes\Http;
/**
* @author Hypolite Petovan <mrpetovan@gmail.com>
* @author Hypolite Petovan <hypolite@mrpetovan.com>
*/
class Photo extends BaseRoute
{
public function __invoke(\Slim\Http\Request $request, \Slim\Http\Response $response, array $args): \Slim\Http\Response
{
return (new \Friendica\Directory\Controllers\Web\Photo(
$this->container->atlas
return (new \Friendica\Directory\Controllers\Api\Photo(
$this->container->atlas,
$this->container->get('defaultProfilePictureSmallPath')
))->render($request, $response, $args);
}
}

View File

@ -3,28 +3,15 @@
namespace Friendica\Directory\Routes\Http;
/**
* @author Hypolite Petovan <mrpetovan@gmail.com>
* @author Hypolite Petovan <hypolite@mrpetovan.com>
*/
class Search extends BaseRoute
{
public function __invoke(\Slim\Http\Request $request, \Slim\Http\Response $response, array $args): \Slim\Http\Response
{
if ($request->getAttribute('negotiation')->getMediaType() == 'application/json') {
$controller = new \Friendica\Directory\Controllers\Api\Search(
$this->container->atlas,
$this->container->get('\Friendica\Directory\Models\Profile'),
$this->container->l10n
);
} else {
$controller = new \Friendica\Directory\Controllers\Web\Search(
$this->container->atlas,
$this->container->get('\Friendica\Directory\Models\Profile'),
$this->container->get('\Friendica\Directory\Views\Widget\AccountTypeTabs'),
$this->container->renderer,
$this->container->l10n
);
}
return $controller->render($request, $response, $args);
return (new \Friendica\Directory\Controllers\Api\Search(
$this->container->get(\Friendica\Directory\Models\Profile::class),
$this->container->l10n
))->render($request, $response, $args);
}
}

View File

@ -1,19 +0,0 @@
<?php
namespace Friendica\Directory\Routes\Http;
/**
* @author Hypolite Petovan <mrpetovan@gmail.com>
*/
class Servers extends BaseRoute
{
public function __invoke(\Slim\Http\Request $request, \Slim\Http\Response $response, array $args): \Slim\Http\Response
{
return (new \Friendica\Directory\Controllers\Web\Servers(
$this->container->atlas,
$this->container->renderer,
$this->container->l10n,
$this->container->simplecache)
)->render($request, $response);
}
}

View File

@ -3,7 +3,7 @@
namespace Friendica\Directory\Routes\Http;
/**
* @author Hypolite Petovan <mrpetovan@gmail.com>
* @author Hypolite Petovan <hypolite@mrpetovan.com>
*/
class Submit extends BaseRoute
{
@ -11,7 +11,7 @@ class Submit extends BaseRoute
{
return (new \Friendica\Directory\Controllers\Api\Submit(
$this->container->atlas,
$this->container->get('\Friendica\Directory\Models\ProfilePollQueue'),
$this->container->get(\Friendica\Directory\Models\ProfilePollQueue::class),
$this->container->logger
))->execute($request, $response);
}

View File

@ -0,0 +1,16 @@
<?php
namespace Friendica\Directory\Routes\Http;
/**
* @author Hypolite Petovan <hypolite@mrpetovan.com>
*/
class Surprise extends BaseRoute
{
public function __invoke(\Slim\Http\Request $request, \Slim\Http\Response $response, array $args): \Slim\Http\Response
{
return (new \Friendica\Directory\Controllers\Api\Surprise(
$this->container->atlas
))->render($request, $response, $args);
}
}

View File

@ -3,7 +3,7 @@
namespace Friendica\Directory\Routes\Http;
/**
* @author Hypolite Petovan <mrpetovan@gmail.com>
* @author Hypolite Petovan <hypolite@mrpetovan.com>
*/
class SyncPull extends BaseRoute
{

View File

@ -0,0 +1,46 @@
<?php
namespace Friendica\Directory\Routes\Web;
use Friendica\Directory\Controllers\Web\BaseController;
/**
* @author Hypolite Petovan <hypolite@mrpetovan.com>
*/
abstract class BaseRoute
{
/**
* @var \Slim\Container
*/
protected $container;
/**
* @var BaseController
*/
protected $controller;
public function __construct(\Slim\Container $container)
{
$this->container = $container;
}
public function __invoke(\Slim\Http\Request $request, \Slim\Http\Response $response, array $args): \Slim\Http\Response
{
$defaults = [
'pages' => glob(__DIR__ . '/../../../../config/pages/*.html'),
'version' => file_get_contents(__DIR__ . '/../../../../VERSION'),
'languages' => $this->container->settings['i18n']['locales'],
'lang' => $request->getAttribute('locale'),
'baseUrl' => $request->getUri()->getBaseUrl(),
'content' => '',
'noNavSearch' => false
];
$values = $this->controller->render($request, $response, $args);
$values = $values + $defaults;
// Render index view
return $this->container->renderer->render($response, 'layout.phtml', $values);
}
}

View File

@ -0,0 +1,23 @@
<?php
namespace Friendica\Directory\Routes\Web;
/**
* @author Hypolite Petovan <hypolite@mrpetovan.com>
*/
class Directory extends BaseRoute
{
public function __construct(\Slim\Container $container)
{
parent::__construct($container);
$this->controller = new \Friendica\Directory\Controllers\Web\Directory(
$this->container->atlas,
$this->container->get(\Friendica\Directory\Models\Server::class),
$this->container->get(\Friendica\Directory\Models\Profile::class),
$this->container->get(\Friendica\Directory\Views\Widget\AccountTypeTabs::class),
$this->container->renderer,
$this->container->l10n
);
}
}

View File

@ -0,0 +1,18 @@
<?php
namespace Friendica\Directory\Routes\Web;
/**
* @author Hypolite Petovan <hypolite@mrpetovan.com>
*/
class Pages extends BaseRoute
{
public function __construct(\Slim\Container $container, $pageFile)
{
parent::__construct($container);
$this->controller = new \Friendica\Directory\Controllers\Web\Page(
$pageFile
);
}
}

View File

@ -0,0 +1,23 @@
<?php
namespace Friendica\Directory\Routes\Web;
/**
* @author Hypolite Petovan <hypolite@mrpetovan.com>
*/
class Search extends BaseRoute
{
public function __construct(\Slim\Container $container)
{
parent::__construct($container);
$this->controller = new \Friendica\Directory\Controllers\Web\Search(
$this->container->atlas,
$this->container->get(\Friendica\Directory\Models\Server::class),
$this->container->get(\Friendica\Directory\Models\Profile::class),
$this->container->get(\Friendica\Directory\Views\Widget\AccountTypeTabs::class),
$this->container->renderer,
$this->container->l10n
);
}
}

View File

@ -0,0 +1,21 @@
<?php
namespace Friendica\Directory\Routes\Web;
/**
* @author Hypolite Petovan <hypolite@mrpetovan.com>
*/
class Servers extends BaseRoute
{
public function __construct(\Slim\Container $container)
{
parent::__construct($container);
$this->controller = new \Friendica\Directory\Controllers\Web\Servers(
$this->container->atlas,
$this->container->renderer,
$this->container->l10n,
$this->container->simplecache
);
}
}

View File

@ -0,0 +1,20 @@
<?php
namespace Friendica\Directory\Routes\Web;
/**
* @author Hypolite Petovan <hypolite@mrpetovan.com>
*/
class Statistics extends BaseRoute
{
public function __construct(\Slim\Container $container)
{
parent::__construct($container);
$this->controller = new \Friendica\Directory\Controllers\Web\Statistics(
$this->container->atlas,
$this->container->simplecache,
$this->container->renderer
);
}
}

215
src/classes/Utils/L10n.php Normal file
View File

@ -0,0 +1,215 @@
<?php
namespace Friendica\Directory\Utils;
use Gettext\Languages\Language;
use Gettext\Translator;
/**
* @author Hypolite Petovan <hypolite@mrpetovan.com>
*/
class L10n
{
const DECIMAL = 1;
const PERCENT = 2;
public static $languages = [
'af' => 'Afrikaans',
'ak' => 'Akan',
'am' => 'አማርኛ',
'ar' => 'العربية',
'as' => 'অসমীয়া',
'az' => 'Azərbaycan',
'be' => 'Беларуская',
'bg' => 'Български',
'bm' => 'Bamanakan',
'bn' => 'বাংলা',
'bo' => 'བོད་སྐད་',
'br' => 'Brezhoneg',
'bs' => 'Bosanski',
'ca' => 'Català',
'cs' => 'Čeština',
'cy' => 'Cymraeg',
'da' => 'Dansk',
'de' => 'Deutsch',
'de_AT' => 'Österreichisches Deutsch',
'de_CH' => 'Schweizer Hochdeutsch',
'dz' => 'རྫོང་ཁ',
'ee' => 'Eʋegbe',
'el' => 'Ελληνικά',
'en' => 'English',
'en_AU' => 'Australian English',
'en_CA' => 'Canadian English',
'en_GB' => 'British English',
'en_US' => 'American English',
'eo' => 'Esperanto',
'es' => 'Español',
'es_ES' => 'Español De España',
'es_MX' => 'Español De México',
'et' => 'Eesti',
'eu' => 'Euskara',
'fa' => 'فارسی',
'ff' => 'Pulaar',
'fi' => 'Suomi',
'fo' => 'Føroyskt',
'fr' => 'Français',
'fr_CA' => 'Français Canadien',
'fr_CH' => 'Français Suisse',
'fy' => 'West-Frysk',
'ga' => 'Gaeilge',
'gd' => 'Gàidhlig',
'gl' => 'Galego',
'gu' => 'ગુજરાતી',
'gv' => 'Gaelg',
'ha' => 'Hausa',
'he' => 'עברית',
'hi' => 'हिन्दी',
'hr' => 'Hrvatski',
'hu' => 'Magyar',
'hy' => 'Հայերեն',
'id' => 'Bahasa Indonesia',
'ig' => 'Igbo',
'ii' => 'ꆈꌠꉙ',
'is' => 'Íslenska',
'it' => 'Italiano',
'ja' => '日本語',
'ka' => 'ქართული',
'ki' => 'Gikuyu',
'kk' => 'Қазақ Тілі',
'kl' => 'Kalaallisut',
'km' => 'ខ្មែរ',
'kn' => 'ಕನ್ನಡ',
'ko' => '한국어',
'ks' => 'کٲشُر',
'kw' => 'Kernewek',
'ky' => 'Кыргызча',
'la' => 'Lingua Latina',
'lb' => 'Lëtzebuergesch',
'lg' => 'Luganda',
'ln' => 'Lingála',
'lo' => 'ລາວ',
'lt' => 'Lietuvių',
'lu' => 'Tshiluba',
'lv' => 'Latviešu',
'mg' => 'Malagasy',
'mk' => 'Македонски',
'ml' => 'മലയാളം',
'mn' => 'Монгол',
'mr' => 'मराठी',
'ms' => 'Bahasa Melayu',
'mt' => 'Malti',
'my' => 'ဗမာ',
'nb' => 'Norsk Bokmål',
'nd' => 'Isindebele',
'ne' => 'नेपाली',
'nl' => 'Nederlands',
'nl_BE' => 'Vlaams',
'nn' => 'Nynorsk',
'no' => 'Norsk',
'om' => 'Oromoo',
'or' => 'ଓଡ଼ିଆ',
'os' => 'Ирон',
'pa' => 'ਪੰਜਾਬੀ',
'pl' => 'Polski',
'ps' => 'پښتو',
'pt' => 'Português',
'pt_BR' => 'Português Do Brasil',
'pt_PT' => 'Português Europeu',
'qu' => 'Runasimi',
'rm' => 'Rumantsch',
'rn' => 'Ikirundi',
'ro' => 'Română',
'ro_MD' => 'Moldovenească',
'ru' => 'Русский',
'rw' => 'Kinyarwanda',
'se' => 'Davvisámegiella',
'sg' => 'Sängö',
'sh' => 'Srpskohrvatski',
'si' => 'සිංහල',
'sk' => 'Slovenčina',
'sl' => 'Slovenščina',
'sn' => 'Chishona',
'so' => 'Soomaali',
'sq' => 'Shqip',
'sr' => 'Српски',
'sv' => 'Svenska',
'sw' => 'Kiswahili',
'ta' => 'தமிழ்',
'te' => 'తెలుగు',
'th' => 'ไทย',
'ti' => 'ትግርኛ',
'tl' => 'Tagalog',
'to' => 'Lea Fakatonga',
'tr' => 'Türkçe',
'ug' => 'ئۇيغۇرچە',
'uk' => 'Українська',
'ur' => 'اردو',
'uz' => 'Oʻzbekcha',
'vi' => 'Tiếng Việt',
'yi' => 'ייִדיש',
'yo' => 'Èdè Yorùbá',
'zh' => '中文',
'zh_Hans' => '简体中文',
'zh_Hant' => '繁體中文',
'zu' => 'Isizulu',
];
public static function localeToLanguageString($locale)
{
$lang = substr($locale, 0, 2);
$foundLocale = false;
$foundLang = false;
foreach(self::$languages as $key => $language) {
if (strtolower($key) == strtolower($lang)) {
$foundLang = $language;
}
if (strtolower($key) == strtolower(str_replace('-', '_', $locale))) {
$foundLocale = $language;
break;
}
}
return $foundLocale ?: $foundLang ?: $locale;
}
/**
* @param float|int $number
* @param int $style
* @return string
*/
public static function formatNumber($number, $style = self::DECIMAL)
{
$locale = localeconv();
switch($style) {
case self::PERCENT:
$number *= 100;
if (\intval($number) == $number) {
$decimals = 0;
} else {
$decimals = 2;
}
$return = number_format($number, $decimals,
$locale['decimal_point'],
$locale['thousands_sep']) . '%';
break;
case self::DECIMAL:
default:
if (\intval($number) == $number) {
$decimals = 0;
} else {
$decimals = 2;
}
$return = number_format($number, $decimals,
$locale['decimal_point'],
$locale['thousands_sep']);
break;
}
return $return;
}
}

View File

@ -11,56 +11,10 @@ namespace Friendica\Directory\Utils;
/**
* Description of Network
*
* @author Hypolite Petovan <mrpetovan@gmail.com>
* @author Hypolite Petovan <hypolite@mrpetovan.com>
*/
class Network
{
public static function fetchURL(string $url, bool $binary = false, int $timeout = 20): string
{
$ch = curl_init($url);
if (!$ch) {
return false;
}
curl_setopt($ch, CURLOPT_HEADER, 0);
curl_setopt($ch, CURLOPT_TIMEOUT, max($timeout, 1)); //Minimum of 1 second timeout.
curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);
curl_setopt($ch, CURLOPT_MAXREDIRS, 8);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
if ($binary) {
curl_setopt($ch, CURLOPT_BINARYTRANSFER, 1);
}
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
$s = curl_exec($ch);
curl_close($ch);
return $s;
}
public static function testURL(string $url, int $timeout = 20): bool
{
$ch = curl_init($url);
if (!$ch) {
return false;
}
curl_setopt($ch, CURLOPT_HEADER , 0);
curl_setopt($ch, CURLOPT_TIMEOUT , max($timeout, 1)); //Minimum of 1 second timeout.
curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);
curl_setopt($ch, CURLOPT_MAXREDIRS , 8);
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, true);
curl_setopt($ch, CURLOPT_NOBODY , true);
curl_exec($ch);
$responseCode = intval(curl_getinfo($ch, CURLINFO_RESPONSE_CODE));
$testSuccess = curl_errno($ch) === 0 && $responseCode < 400;
curl_close($ch);
return $testSuccess;
}
/**
* Check if a hostname is public and non-reserved
*
@ -82,6 +36,13 @@ class Network
return false;
}
// RFC 2606 -continued
$tld = substr($host, strrpos($host, '.'));
if ($tld === '.test' || $tld === '.example' || $tld === '.invalid' || $tld === '.localhost') {
return false;
}
// Private/Reserved IP ranges
if (filter_var($host, FILTER_VALIDATE_IP) && !filter_var($host, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4 | FILTER_FLAG_NO_PRIV_RANGE | FILTER_FLAG_NO_RES_RANGE)) {
return false;
}

View File

@ -3,7 +3,7 @@
namespace Friendica\Directory\Utils;
/**
* @author Hypolite Petovan <mrpetovan@gmail.com>
* @author Hypolite Petovan <hypolite@mrpetovan.com>
*/
class Photo
{

View File

@ -2,8 +2,10 @@
namespace Friendica\Directory\Utils;
use GuzzleHttp\ClientInterface;
/**
* @author Hypolite Petovan <mrpetovan@gmail.com>
* @author Hypolite Petovan <hypolite@mrpetovan.com>
*/
class Scrape
{
@ -12,10 +14,10 @@ class Scrape
* @param string $url
* @return array|false
*/
public static function retrieveNoScrapeData(string $url)
public static function retrieveNoScrapeData(ClientInterface $http, string $url)
{
$submit_noscrape_start = microtime(true);
$data = Network::fetchURL($url);
$data = $http->get($url)->getBody()->getContents();
$submit_noscrape_request_end = microtime(true);
if (empty($data)) {
@ -23,7 +25,7 @@ class Scrape
}
$params = json_decode($data, true);
if (!$params || !count($params)) {
if (empty($params) || !is_array($params)) {
return false;
}
@ -42,7 +44,7 @@ class Scrape
return $params;
}
public static function retrieveProfileData(string $url, int $max_nodes = 3500): array
public static function retrieveProfileData(ClientInterface $http, string $url, int $max_nodes = 3500): array
{
$minNodes = 100; //Lets do at least 100 nodes per type.
@ -56,7 +58,7 @@ class Scrape
$scrape_start = microtime(true);
$params = [];
$html = Network::fetchURL($url, false, $timeout);
$html = $http->get($url, ['timeout' => $timeout])->getBody()->getContents();;
$scrape_fetch_end = microtime(true);
@ -129,7 +131,7 @@ class Scrape
$nodes_left = max(intval($max_nodes), $minNodes);
$items = $dom->getElementsByTagName('*');
$targets = array('fn', 'pdesc', 'photo', 'key', 'locality', 'region', 'postal-code', 'country-name');
$targets = array('fn', 'pdesc', 'photo', 'locality', 'region', 'postal-code', 'country-name');
$targets_left = count($targets);
foreach ($items as $item) {
if (self::attributeContains($item->getAttribute('class'), 'vcard')) {
@ -147,10 +149,6 @@ class Scrape
$params['photo'] = $vcard_element->getAttribute('src');
$targets_left = self::popScrapeTarget($targets, 'photo');
}
if (self::attributeContains($vcard_element->getAttribute('class'), 'key')) {
$params['key'] = $vcard_element->textContent;
$targets_left = self::popScrapeTarget($targets, 'key');
}
if (self::attributeContains($vcard_element->getAttribute('class'), 'locality')) {
$params['locality'] = $vcard_element->textContent;
$targets_left = self::popScrapeTarget($targets, 'locality');

View File

@ -2,10 +2,27 @@
namespace Friendica\Directory\Views;
use Slim\Router;
/**
* Zend-Escaper wrapper for Slim PHP Renderer
*
* @author Hypolite Petovan <mrpetovan@gmail.com>
* @author Hypolite Petovan <hypolite@mrpetovan.com>
*
* @method string escapeHtml(string $value)
* @method string escapeHtmlAttr(string $value)
* @method string escapeCss(string $value)
* @method string escapeJs(string $value)
* @method string escapeUrl(string $value)
* @method string noop(string $original)
* @method string gettext(string $original)
* @method string ngettext(string $original, string $plural, string $value)
* @method string dngettext(string $domain, string $original, string $plural, string $value)
* @method string npgettext(string $context, string $original, string $plural, string $value)
* @method string pgettext(string $context, string $original)
* @method string dgettext(string $domain, string $original)
* @method string dpgettext(string $domain, string $context, string $original)
* @method string dnpgettext(string $domain, string $context, string $original, string $plural, string $value)
*/
class PhpRenderer extends \Slim\Views\PhpRenderer
{
@ -14,13 +31,18 @@ class PhpRenderer extends \Slim\Views\PhpRenderer
*/
private $escaper;
/**
* @var \Friendica\Directory\Content\L10n
* @var \Gettext\TranslatorInterface
*/
private $l10n;
/**
* @var Router
*/
private $router;
public function __construct(
\Zend\Escaper\Escaper $escaper,
\Friendica\Directory\Content\L10n $l10n,
\Gettext\TranslatorInterface $l10n,
Router $router,
string $templatePath = "",
array $attributes = array()
)
@ -29,6 +51,7 @@ class PhpRenderer extends \Slim\Views\PhpRenderer
$this->escaper = $escaper;
$this->l10n = $l10n;
$this->router = $router;
}
public function e(string $value): string
@ -36,28 +59,157 @@ class PhpRenderer extends \Slim\Views\PhpRenderer
return $this->escapeHtml($value);
}
public function escapeHtml(string $value): string
public function __call($name, $arguments)
{
return $this->escaper->escapeHtml($value);
if (method_exists($this->escaper, $name)) {
return $this->escaper->$name(...$arguments);
} elseif (method_exists($this->l10n, $name)) {
return $this->l10n->$name(...$arguments);
} else {
throw new \Exception('Unknown PhpRenderer magic method: ' . $name);
}
}
public function escapeCss(string $value): string
/**
* Returns the translation of a string.
*
* Loose copy of Gettext/gettext global __() function
*
* Usages:
* - $this->__('Label')
* - $this->__('Label %s', $value)
*
* @param $original
* @param array $args
* @return string
*/
public function __(string $original, ...$args)
{
return $this->escaper->escapeCss($value);
$text = $this->l10n->gettext($original);
if (!count($args)) {
return $text;
}
return is_array($args[0]) ? strtr($text, $args[0]) : vsprintf($text, $args);
}
public function escapeJs(string $value): string
/**
* Returns the translation of a string in a specific context.
*
* @param string $context
* @param string $original
* @param array $args
* @return string
*/
function p__(string $context, string $original, ...$args)
{
return $this->escaper->escapeJs($value);
$text = $this->l10n->pgettext($context, $original);
if (!count($args)) {
return $text;
}
return is_array($args[0]) ? strtr($text, $args[0]) : vsprintf($text, $args);
}
public function escapeHtmlAttr(string $value): string
/**
* Returns the translation of a string in a specific domain.
*
* @param string $domain
* @param string $original
* @param array $args
* @return string
*/
function d__(string $domain, string $original, ...$args)
{
return $this->escaper->escapeHtmlAttr($value);
$text = $this->l10n->dgettext($domain, $original);
if (!count($args)) {
return $text;
}
return is_array($args[0]) ? strtr($text, $args[0]) : vsprintf($text, $args);
}
public function escapeUrl(string $value): string
/**
* Returns the singular/plural translation of a string.
*
* Loose copy of Gettext/gettext global n__() function
*
* Usages:
* - $this->n__('Label', 'Labels', 3)
* - $this->n__('%d Label for %s', '%d Labels for %s', 3, $value)
*
* @param string $original
* @param string $plural
* @param int $count
* @param array $args
*
* @return string
*/
function n__(string $original, string $plural, int $count, ...$args)
{
return $this->escaper->escapeUrl($value);
$text = $this->l10n->ngettext($original, $plural, $count);
array_unshift($args, $count);
return !empty($args[1]) && is_array($args[1]) ? strtr($text, $args[1]) : vsprintf($text, $args);
}
/**
* Returns the singular/plural translation of a string in a specific context
*
* Usages:
* - $this->n__('search', 'Label', 'Labels', 3)
* - $this->n__('search', '%d Label for %s', '%d Labels for %s', 3, $value)
*
* @param string $context
* @param string $original
* @param string $plural
* @param int $count
* @param array ...$args
* @return string
*/
function np__(string $context, string $original, string $plural, int $count, array ...$args)
{
$text = $this->l10n->npgettext($context, $original, $plural, $count);
array_unshift($args, $count);
return !empty($args[1]) && is_array($args[1]) ? strtr($text, $args[1]) : vsprintf($text, $args);
}
/**
* Return the URL of the provided route and parameters
*
* @param string $name
* @param array $data
* @param array $queryParams
* @return string
*/
function r(string $name, array $data = [], array $queryParams = [])
{
if ($this->getAttribute('zrl')) {
$queryParams['zrl'] = $this->getAttribute('zrl');
}
return $this->router->pathFor($name, $data, $queryParams);
}
/**
* Add sitewide ZRL support for external URLs
*
* @param string $url
*/
function u(string $url)
{
if ($this->getAttribute('zrl')) {
$uri = new \ByJG\Util\Uri($url);
$uri->withQueryKeyValue('zrl', $this->getAttribute('zrl'), false);
$url = $uri->__toString();
}
return $url;
}
}

View File

@ -3,7 +3,7 @@
namespace Friendica\Directory\Views\Widget;
/**
* @author Hypolite Petovan <mrpetovan@gmail.com>
* @author Hypolite Petovan <hypolite@mrpetovan.com>
*/
class AccountTypeTabs
{
@ -27,28 +27,46 @@ class AccountTypeTabs
$this->router = $router;
}
public function render(string $route_name, string $current_type = '', array $queryParams = []): string
public function render(string $route_name, string $current_type = '', $condition = '', $values = [], array $queryParams = []): string
{
$stmt = '
SELECT DISTINCT(`account_type`) AS `account_type`
FROM `profile`
WHERE `available`
AND NOT `hidden`';
$account_types = $this->connection->fetchAll($stmt);
if ($condition) {
$condition = 'AND ' . $condition;
}
$stmt = 'SELECT `account_type`, COUNT(*) AS `count`
FROM `profile` p
JOIN `server` s ON s.`id` = p.`server_id` AND s.`available` AND NOT s.`hidden`
WHERE p.`available`
AND NOT p.`hidden`
' . $condition . '
GROUP BY p.`account_type`
ORDER BY `count` DESC';
$account_types = $this->connection->fetchAll($stmt, $values);
$tabs = [
[
'title' => 'All',
'title' => $this->renderer->p__('account-type', 'All'),
'link' => $this->router->pathFor($route_name, [], $queryParams),
'active' => empty($current_type)
]
];
foreach ($account_types as $account_type) {
switch ($account_type['account_type']) {
case 'People' : $title = $this->renderer->np__('account-type', 'People (%d)' , 'People (%d)' , $account_type['count']); break;
case 'News' : $title = $this->renderer->np__('account-type', 'News (%d)' , 'News (%d)' , $account_type['count']); break;
case 'Organization': $title = $this->renderer->np__('account-type', 'Organization (%d)', 'Organizations (%d)', $account_type['count']); break;
// Kept for backward compatibility
case 'Forum' :
case 'Group' : $title = $this->renderer->np__('account-type', 'Group (%d)' , 'Groups (%d)' , $account_type['count']); break;
default: $title = $this->renderer->np__('account-type', $account_type['account_type']. ' (%d)', $account_type['account_type']. ' (%d)', $account_type['count']);
}
$tabs[] = [
'title' => $account_type['account_type'],
'title' => $title,
'link' => $this->router->pathFor($route_name, ['account_type' => strtolower($account_type['account_type'])], $queryParams),
'active' => strtolower($account_type['account_type']) == strtolower($current_type)
'active' => strtolower($account_type['account_type']) == strtolower($current_type),
'disabled' => $account_type['count'] == 0
];
}

View File

@ -3,7 +3,7 @@
namespace Friendica\Directory\Views\Widget;
/**
* @author Hypolite Petovan <mrpetovan@gmail.com>
* @author Hypolite Petovan <hypolite@mrpetovan.com>
*/
class PopularCountries
{
@ -30,12 +30,11 @@ FROM `profile`
WHERE `country` != ""
AND `available`
GROUP BY `country`
ORDER BY COUNT(`country`) DESC
LIMIT 20';
ORDER BY `total` DESC
LIMIT 10';
$countries = $this->connection->fetchAll($stmt);
$vars = [
'title' => 'Popular Countries',
'countries' => $countries
];

View File

@ -0,0 +1,41 @@
<?php
namespace Friendica\Directory\Views\Widget;
/**
* @author Hypolite Petovan <hypolite@mrpetovan.com>
*/
class PopularProfileLanguages
{
/**
* @var \Atlas\Pdo\Connection
*/
private $connection;
/**
* @var \Friendica\Directory\Views\PhpRenderer
*/
private $renderer;
public function __construct(\Atlas\Pdo\Connection $connection, \Friendica\Directory\Views\PhpRenderer $renderer)
{
$this->connection = $connection;
$this->renderer = $renderer;
}
public function render(): string
{
$stmt = 'SELECT LEFT(`language`, 2) AS `language`, COUNT(*) AS `total`
FROM `profile`
WHERE `language` IS NOT NULL
GROUP BY LEFT(`language`, 2)
ORDER BY `total` DESC
LIMIT 10';
$languages = $this->connection->fetchAll($stmt);
$vars = [
'languages' => $languages
];
return $this->renderer->fetch('widget/popularprofilelanguages.phtml', $vars);
}
}

View File

@ -0,0 +1,44 @@
<?php
namespace Friendica\Directory\Views\Widget;
/**
* @author Hypolite Petovan <hypolite@mrpetovan.com>
*/
class PopularServerLanguages
{
/**
* @var \Atlas\Pdo\Connection
*/
private $connection;
/**
* @var \Friendica\Directory\Views\PhpRenderer
*/
private $renderer;
public function __construct(\Atlas\Pdo\Connection $connection, \Friendica\Directory\Views\PhpRenderer $renderer)
{
$this->connection = $connection;
$this->renderer = $renderer;
}
public function render(): string
{
$stmt = 'SELECT LEFT(`language`, 2) AS `language`, COUNT(*) AS `total`
FROM `server`
WHERE `reg_policy` != "REGISTER_CLOSED"
AND `available`
AND NOT `hidden`
AND `language` IS NOT NULL
GROUP BY LEFT(`language`, 2)
ORDER BY `total` DESC
LIMIT 10';
$languages = $this->connection->fetchAll($stmt);
$vars = [
'languages' => $languages
];
return $this->renderer->fetch('widget/popularserverlanguages.phtml', $vars);
}
}

View File

@ -3,7 +3,7 @@
namespace Friendica\Directory\Views\Widget;
/**
* @author Hypolite Petovan <mrpetovan@gmail.com>
* @author Hypolite Petovan <hypolite@mrpetovan.com>
*/
class PopularTags
{
@ -24,11 +24,14 @@ class PopularTags
public function render(): string
{
$stmt = 'SELECT `term`, COUNT(*) AS `total` FROM `tag` GROUP BY `term` ORDER BY COUNT(`term`) DESC LIMIT 20';
$stmt = 'SELECT `term`, COUNT(*) AS `total`
FROM `tag`
GROUP BY `term`
ORDER BY `total` DESC
LIMIT 10';
$tags = $this->connection->fetchAll($stmt);
$vars = [
'title' => 'Popular Tags',
'tags' => $tags
];

View File

@ -1,13 +1,13 @@
<?php
use Interop\Container\ContainerInterface;
use Psr\Container\ContainerInterface;
// DIC configuration
// l10n
$container['l10n'] = function (ContainerInterface $c): Friendica\Directory\Content\L10n {
$settings = $c->get('settings')['l10n'];
return new Friendica\Directory\Content\L10n($settings['language'] ?: 'en', $settings['lang_path'] ?: '');
$container['l10n'] = function (ContainerInterface $c): Gettext\TranslatorInterface {
$translator = new Gettext\Translator();
return $translator;
};
// simple cache
@ -25,7 +25,12 @@ $container['escaper'] = function (ContainerInterface $c): Zend\Escaper\Escaper {
// view renderer
$container['renderer'] = function (ContainerInterface $c): Friendica\Directory\Views\PhpRenderer {
$settings = $c->get('settings')['renderer'];
return new Friendica\Directory\Views\PhpRenderer($c->get('escaper'), $c->get('l10n'), $settings['template_path']);
return new Friendica\Directory\Views\PhpRenderer(
$c->get('escaper'),
$c->get('l10n'),
$c->get('router'),
$settings['template_path']
);
};
// monolog
@ -84,54 +89,68 @@ $container['migration'] = function (ContainerInterface $c): ByJG\DbMigration\Mig
return $migration;
};
$container['http'] = function (ContainerInterface $c): GuzzleHttp\ClientInterface {
$version = file_get_contents(__DIR__ . '/../VERSION');
if (!$version || !preg_match('/^\s*\d\.\d\.\d\s*$/', $version)) {
$version = '0.0.0';
}
return new GuzzleHttp\Client(['timeout' => 20, 'headers' => ['User-Agent' => 'FriendicaDirectory/' . trim($version) . ' ' . \GuzzleHttp\default_user_agent()]]);
};
$container['defaultProfilePictureSmallPath'] = __DIR__ . '/../public/assets/images/default-profile-sm.jpg';
// Internal Dependency Injection
$container['\Friendica\Directory\Models\Profile'] = function (ContainerInterface $c): Friendica\Directory\Models\Profile {
$container[\Friendica\Directory\Models\Profile::class] = function (ContainerInterface $c): Friendica\Directory\Models\Profile {
return new Friendica\Directory\Models\Profile($c->get('atlas'));
};
$container['\Friendica\Directory\Models\ProfilePollQueue'] = function (ContainerInterface $c): Friendica\Directory\Models\ProfilePollQueue {
$container[\Friendica\Directory\Models\ProfilePollQueue::class] = function (ContainerInterface $c): Friendica\Directory\Models\ProfilePollQueue {
return new Friendica\Directory\Models\ProfilePollQueue($c->get('atlas'));
};
$container['\Friendica\Directory\Models\Server'] = function (ContainerInterface $c): Friendica\Directory\Models\Server {
$container[\Friendica\Directory\Models\Server::class] = function (ContainerInterface $c): Friendica\Directory\Models\Server {
return new Friendica\Directory\Models\Server($c->get('atlas'));
};
$container['\Friendica\Directory\Pollers\Directory'] = function (ContainerInterface $c): Friendica\Directory\Pollers\Directory {
$container[\Friendica\Directory\Pollers\Directory::class] = function (ContainerInterface $c): Friendica\Directory\Pollers\Directory {
$settings = $c->get('settings')['poller'];
return new Friendica\Directory\Pollers\Directory(
$c->get('atlas'),
$c->get('\Friendica\Directory\Models\ProfilePollQueue'),
$c->get('http'),
$c->get(\Friendica\Directory\Models\ProfilePollQueue::class),
$c->get('logger'),
$settings ?: []
);
};
$container['\Friendica\Directory\Pollers\Profile'] = function (ContainerInterface $c): Friendica\Directory\Pollers\Profile {
$container[\Friendica\Directory\Pollers\Profile::class] = function (ContainerInterface $c): Friendica\Directory\Pollers\Profile {
$settings = $c->get('settings')['poller'];
return new Friendica\Directory\Pollers\Profile(
$c->get('atlas'),
$c->get('\Friendica\Directory\Models\Server'),
$c->get('\Friendica\Directory\Models\Profile'),
$c->get('http'),
$c->get(\Friendica\Directory\Models\Server::class),
$c->get(\Friendica\Directory\Models\Profile::class),
$c->get('logger'),
$settings ?: []
);
};
$container['\Friendica\Directory\Pollers\Server'] = function (ContainerInterface $c): Friendica\Directory\Pollers\Server {
$container[\Friendica\Directory\Pollers\Server::class] = function (ContainerInterface $c): Friendica\Directory\Pollers\Server {
$settings = $c->get('settings')['poller'];
return new Friendica\Directory\Pollers\Server(
$c->get('atlas'),
$c->get('\Friendica\Directory\Models\ProfilePollQueue'),
$c->get('\Friendica\Directory\Models\Server'),
$c->get('http'),
$c->get(\Friendica\Directory\Models\ProfilePollQueue::class),
$c->get(\Friendica\Directory\Models\Server::class),
$c->get('simplecache'),
$c->get('logger'),
$settings ?: []
);
};
$container['\Friendica\Directory\Views\Widget\AccountTypeTabs'] = function (ContainerInterface $c): Friendica\Directory\Views\Widget\AccountTypeTabs {
$container[\Friendica\Directory\Views\Widget\AccountTypeTabs::class] = function (ContainerInterface $c): Friendica\Directory\Views\Widget\AccountTypeTabs {
return new Friendica\Directory\Views\Widget\AccountTypeTabs(
$c->get('atlas'),
$c->get('renderer'),

Binary file not shown.

View File

@ -0,0 +1,381 @@
msgid ""
msgstr ""
"Project-Id-Version: \n"
"Report-Msgid-Bugs-To: \n"
"Last-Translator: abidin toumi <abidin24@tutanota.com>, 2021\n"
"Language-Team: Arabic (https://www.transifex.com/Friendica/teams/12172/ar/)\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"POT-Creation-Date: 2018-11-16T04:17:37+00:00\n"
"PO-Revision-Date: 2018-11-16 20:30+0000\n"
"Language: ar\n"
"Plural-Forms: nplurals=6; plural=n==0 ? 0 : n==1 ? 1 : n==2 ? 2 : n%100>=3 && n%100<=10 ? 3 : n%100>=11 && n%100<=99 ? 4 : 5;\n"
"X-Generator: Poedit 2.2\n"
#: src\classes\Content\Pager.php:168
#: src\classes\Content\Pager.php:216
msgid "Previous"
msgstr "السابقة"
#: src\classes\Content\Pager.php:173
#: src\classes\Content\Pager.php:273
msgid "Next"
msgstr "التالية"
#: src\classes\Content\Pager.php:211
msgid "First"
msgstr "الأولى"
#: src\classes\Content\Pager.php:278
msgid "Last"
msgstr "الأخيرة"
#: src\classes\Controllers\Web\Directory.php:85
msgid "People"
msgstr "أشخاص"
#: src\classes\Controllers\Web\Search.php:72
msgctxt "field"
msgid "Language"
msgstr "اللغة"
#: src\classes\Controllers\Web\Search.php:73
msgctxt "field"
msgid "Locality"
msgstr "المنطقة"
#: src\classes\Controllers\Web\Search.php:74
msgctxt "field"
msgid "Region"
msgstr "الإقليم"
#: src\classes\Controllers\Web\Search.php:75
msgctxt "field"
msgid "Country"
msgstr "البلد"
#: src\classes\Controllers\Web\Servers.php:105
msgid "Public Servers"
msgstr "الخوادم العمومية"
#: src\templates\layout.phtml:4
#: src\templates\layout.phtml:18
msgid "Friendica Directory"
msgstr "دليل فرنديكا"
#: src\templates\layout.phtml:23
#: src\templates\layout.phtml:25
#: src\templates\layout.phtml:43
#: src\templates\layout.phtml:45
#: src\templates\search.phtml:4
#: src\templates\search.phtml:12
msgid "Search terms"
msgstr "مصطلحات البحث"
#: src\templates\layout.phtml:24
#: src\templates\layout.phtml:45
#: src\templates\search.phtml:11
msgctxt "noun"
msgid "Search"
msgstr "ابحث"
#: src\templates\layout.phtml:27
#: src\templates\layout.phtml:47
#: src\templates\search.phtml:14
msgctxt "verb"
msgid "Search"
msgstr "ابحث"
#: src\templates\layout.phtml:33
msgid "Toggle navigation"
msgstr "بدل التصفح"
#: src\templates\layout.phtml:55
msgid "Directory"
msgstr "الدليل"
#: src\templates\layout.phtml:60
msgid "Public servers"
msgstr "الخوادم العمومية"
#: src\templates\search.phtml:19
msgid "%d result for \"%s\""
msgid_plural "%d results for \"%s\""
msgstr[0] "%d نتائج لـ \"%s\""
msgstr[1] "نتيجة %d لـ \"%s\""
msgstr[2] "نتيجتان %d لـ \"%s\""
msgstr[3] "%d نتائج لـ \"%s\""
msgstr[4] "%d نتيجة لـ \"%s\""
msgstr[5] "%dنتيجة لـ \"%s\""
#: src\templates\servers.phtml:7
msgid "Top servers pagination"
msgstr ""
#: src\templates\servers.phtml:17
msgid "Bottom servers pagination"
msgstr ""
#: src\templates\sub\profile.phtml:5
msgid "Filter by locality"
msgstr "رشح حسب المنطقة"
#: src\templates\sub\profile.phtml:11
msgid "Filter by region"
msgstr "رشح حسب الإقليم"
#: src\templates\sub\profile.phtml:17
msgid "Filter by country"
msgstr "رشح حسب الدولة"
#: src\templates\sub\profile.phtml:31
#: src\templates\sub\profile.phtml:35
#: src\templates\sub\profile.phtml:39
msgctxt "verb"
msgid "Follow"
msgstr "تابع"
#: src\templates\layout.phtml:65
#: src\templates\sub\profile.phtml:57
msgid "Language"
msgstr "اللغة"
#: src\templates\sub\profile.phtml:60
#: src\templates\widget\popularserverlanguages.phtml:2
msgid "Filter by language"
msgstr "رشح حسب اللغة"
#: src\templates\sub\profile.phtml:66
msgid "Location"
msgstr "الموقع"
#: src\templates\sub\profile.phtml:79
msgid "Search Tag"
msgstr "ابحث عن وسم"
#: src\templates\sub\profiles.phtml:1
msgid "Account type tabs"
msgstr "ألسنة نوع الحساب"
#: src\templates\sub\profiles.phtml:4
#: src\templates\sub\profiles.phtml:7
msgid "Top %s pagination"
msgstr ""
#: src\templates\sub\profiles.phtml:13
#: src\templates\sub\profiles.phtml:16
msgid "Bottom %s pagination"
msgstr ""
#: src\templates\statistics.phtml:66
#: src\templates\sub\server.phtml:15
msgid "Stable Version"
msgstr "الإصدار المستقر"
#: src\templates\statistics.phtml:68
#: src\templates\sub\server.phtml:17
msgid "Develop Version"
msgstr "الإصدار الاختباري"
#: src\templates\sub\server.phtml:19
msgid "Outdated Version"
msgstr "إصدار قديم"
#: src\templates\sub\server.phtml:69
msgid "Admin"
msgstr "المدير"
#: src\templates\sub\server.phtml:76
msgid "No description provided"
msgstr "لم يقدم أي وصف"
#: src\templates\sub\server.phtml:79
msgid "Visit Server"
msgstr "زُر الخادم"
#: src\templates\widget\popularcountries.phtml:2
msgid "Popular Countries"
msgstr "البلدان الشائعة"
#: src\templates\widget\popularprofilelanguages.phtml:2
msgid "Popular Languages"
msgstr "اللغات الشائعة"
#: src\templates\widget\populartags.phtml:2
msgid "Popular Tags"
msgstr "الوسوم الشائعة"
#: src\templates\sub\server.phtml:44
#: src\templates\sub\server.phtml:45
msgid "Default Language"
msgstr "اللغة الافتراضية"
#: src\templates\sub\server.phtml:49
msgid "Known Users"
msgstr "المستخدِمون المعروفون"
#: src\classes\Views\Widget\AccountTypeTabs.php:48
msgctxt "account-type"
msgid "All"
msgstr "الكل"
#: src\classes\Views\Widget\AccountTypeTabs.php:56
msgctxt "account-type"
msgid "People (%d)"
msgid_plural "People (%d)"
msgstr[0] "أشخاص (%d)"
msgstr[1] "أشخاص (%d)"
msgstr[2] "أشخاص (%d)"
msgstr[3] "أشخاص (%d)"
msgstr[4] "أشخاص (%d)"
msgstr[5] "أشخاص (%d)"
#: src\templates\layout.phtml:97
msgid "Stats"
msgstr "الإحصائيات"
#: src\templates\statistics.phtml:4
msgid "Directory statistics"
msgstr "إحصائيات الدليل"
#: src\templates\statistics.phtml:5
msgid "Profiles"
msgstr "الملفات الشخصية"
#: src\templates\statistics.phtml:6
msgid "This directory knows about <strong>%s distinct potential profile URLs</strong>."
msgstr ""
#: src\templates\statistics.phtml:16
#: src\templates\statistics.phtml:46
msgid "Languages"
msgstr "اللغات"
#: src\templates\statistics.phtml:17
msgid "Out of <strong>%s</strong> profiles reporting their language there are:"
msgstr "من بين <strong>%s</strong> ملفا شخصيا عرف عن لغته هناك:"
#: src\templates\statistics.phtml:27
msgid "Servers"
msgstr "الخوادم"
#: src\templates\statistics.phtml:28
msgid "This directory knows about <strong>%s distinct potential server URLs</strong>."
msgstr ""
#: src\templates\statistics.phtml:29
msgid "Out of those, there are <strong>%s domains (%s)</strong> that have been a Friendica server at least once."
msgstr ""
#: src\templates\statistics.phtml:33
msgid "Out of those, there are:"
msgstr "من بين هؤلاء هناك:"
#: src\templates\statistics.phtml:47
msgid "Out of <strong>%s</strong> servers reporting their language there are:"
msgstr "من بين <strong>%s</strong> خادما عرف عن لغته هناك:"
#: src\templates\statistics.phtml:57
msgid "Versions"
msgstr "الإصدارات"
#: src\templates\statistics.phtml:58
msgid "Out of <strong>%s</strong> servers reporting their version there are:"
msgstr "من بين <strong>%s</strong> خادما عرف عن إصداره هناك:"
#: src\templates\statistics.phtml:7
msgid "Out of those, there are <strong>%s profiles (%s)</strong> that opted in the public directory at least once."
msgstr ""
#: src\templates\statistics.phtml:35
msgid "<strong>%s available servers (%s)</strong>"
msgstr "<strong>%s خادم متوفر (%s)</strong>"
#: src\templates\layout.phtml:91
msgid "Friendica Directory version %s"
msgstr "إصدار دليل فرنديكا %s"
#: src\templates\statistics.phtml:11
msgid "Out of those, there currently are <strong>%s available profiles (%s)</strong>. <a href=\"%s\">Check them out!</a>"
msgstr ""
#: src\templates\statistics.phtml:39
msgid "<strong>%s public servers (%s)</strong> currently open for registration. <a href=\"%s\">Check them out!</a>"
msgstr "يوجد <strong>%s خادم عمومي (%s)</strong> مفتوح للستجيل. <a href=\"%s\">ألق نظرة عليهم!</a>"
#: src\classes\Views\Widget\AccountTypeTabs.php:57
msgctxt "account-type"
msgid "News (%d)"
msgid_plural "News (%d)"
msgstr[0] "أخبار (%d)"
msgstr[1] "أخبار (%d)"
msgstr[2] "أخبار (%d)"
msgstr[3] "أخبار (%d)"
msgstr[4] "أخبار (%d)"
msgstr[5] "أخبار (%d)"
#: src\classes\Views\Widget\AccountTypeTabs.php:58
msgctxt "account-type"
msgid "Organization (%d)"
msgid_plural "Organizations (%d)"
msgstr[0] "منظمات (%d)"
msgstr[1] "منظمات (%d)"
msgstr[2] "منظمات (%d)"
msgstr[3] "منظمات (%d)"
msgstr[4] "منظمات (%d)"
msgstr[5] "منظمات (%d)"
#: src\templates\sub\server.phtml:40
msgid "Health Score"
msgstr "تقييم الصحة"
#: src\templates\sub\server.phtml:52
msgid "%s User"
msgid_plural "%s Users"
msgstr[0] "لا مستخدمين %s"
msgstr[1] "مستخدم %s"
msgstr[2] "مستخدمان %s"
msgstr[3] "%s مستخدمين"
msgstr[4] "%s مستخدما"
msgstr[5] "%s مستخدم"
#: src\templates\sub\server.phtml:54
msgid "None"
msgstr "لا شيء"
#: src\templates\sub\server.phtml:58
#: src\templates\sub\server.phtml:62
msgid "Registration Policy"
msgstr "سياسة التسجيل"
#: src\templates\sub\server.phtml:59
msgid "By Approval"
msgstr "يحتاج الموافقة"
#: src\templates\sub\server.phtml:63
msgid "Open"
msgstr "مفتوح"
#: src\classes\Views\Widget\AccountTypeTabs.php:61
msgctxt "account-type"
msgid "Group (%d)"
msgid_plural "Groups (%d)"
msgstr[0] ""
msgstr[1] ""
msgstr[2] ""
msgstr[3] ""
msgstr[4] ""
msgstr[5] ""
#: src\templates\servers.phtml:3
msgid "Filtered by language:"
msgstr ""
#: src\templates\servers.phtml:3
msgid "Clear language filter"
msgstr ""
#: src\templates\layout.phtml:94
msgid "Source Code on Friendica's Forgejo"
msgstr ""

Binary file not shown.

View File

@ -0,0 +1,357 @@
msgid ""
msgstr ""
"Project-Id-Version: \n"
"Report-Msgid-Bugs-To: \n"
"Last-Translator: Joan Bar <friendica@tutanota.com>, 2019\n"
"Language-Team: Catalan (https://www.transifex.com/Friendica/teams/12172/ca/)\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"POT-Creation-Date: 2018-11-16T04:17:37+00:00\n"
"PO-Revision-Date: 2018-11-16 20:30+0000\n"
"Language: ca\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
"X-Generator: Poedit 2.2\n"
#: src\classes\Content\Pager.php:168
#: src\classes\Content\Pager.php:216
msgid "Previous"
msgstr "prèvia"
#: src\classes\Content\Pager.php:173
#: src\classes\Content\Pager.php:273
msgid "Next"
msgstr "següent"
#: src\classes\Content\Pager.php:211
msgid "First"
msgstr "Primer"
#: src\classes\Content\Pager.php:278
msgid "Last"
msgstr "Ultim"
#: src\classes\Controllers\Web\Directory.php:85
msgid "People"
msgstr "gent"
#: src\classes\Controllers\Web\Search.php:72
msgctxt "field"
msgid "Language"
msgstr "Llengua"
#: src\classes\Controllers\Web\Search.php:73
msgctxt "field"
msgid "Locality"
msgstr "Localitat"
#: src\classes\Controllers\Web\Search.php:74
msgctxt "field"
msgid "Region"
msgstr "Regió"
#: src\classes\Controllers\Web\Search.php:75
msgctxt "field"
msgid "Country"
msgstr "País"
#: src\classes\Controllers\Web\Servers.php:105
msgid "Public Servers"
msgstr "Servidors públics"
#: src\templates\layout.phtml:4
#: src\templates\layout.phtml:18
msgid "Friendica Directory"
msgstr "Directori de Friendica"
#: src\templates\layout.phtml:23
#: src\templates\layout.phtml:25
#: src\templates\layout.phtml:43
#: src\templates\layout.phtml:45
#: src\templates\search.phtml:4
#: src\templates\search.phtml:12
msgid "Search terms"
msgstr "Termes de cerca"
#: src\templates\layout.phtml:24
#: src\templates\layout.phtml:45
#: src\templates\search.phtml:11
msgctxt "noun"
msgid "Search"
msgstr "Buscar"
#: src\templates\layout.phtml:27
#: src\templates\layout.phtml:47
#: src\templates\search.phtml:14
msgctxt "verb"
msgid "Search"
msgstr "Buscar"
#: src\templates\layout.phtml:33
msgid "Toggle navigation"
msgstr "Commuta la navegació"
#: src\templates\layout.phtml:55
msgid "Directory"
msgstr "Directori"
#: src\templates\layout.phtml:60
msgid "Public servers"
msgstr "Servidors públics"
#: src\templates\search.phtml:19
msgid "%d result for \"%s\""
msgid_plural "%d results for \"%s\""
msgstr[0] ""
msgstr[1] ""
#: src\templates\servers.phtml:7
msgid "Top servers pagination"
msgstr "Paginació dels servidors principals"
#: src\templates\servers.phtml:17
msgid "Bottom servers pagination"
msgstr "Paginació dels servidors principals"
#: src\templates\sub\profile.phtml:5
msgid "Filter by locality"
msgstr "Filtra per localitat"
#: src\templates\sub\profile.phtml:11
msgid "Filter by region"
msgstr "Filtra per Regió"
#: src\templates\sub\profile.phtml:17
msgid "Filter by country"
msgstr "Filtra per País"
#: src\templates\sub\profile.phtml:31
#: src\templates\sub\profile.phtml:35
#: src\templates\sub\profile.phtml:39
msgctxt "verb"
msgid "Follow"
msgstr "seguir"
#: src\templates\layout.phtml:65
#: src\templates\sub\profile.phtml:57
msgid "Language"
msgstr "Llenguatge "
#: src\templates\sub\profile.phtml:60
#: src\templates\widget\popularserverlanguages.phtml:2
msgid "Filter by language"
msgstr ""
#: src\templates\sub\profile.phtml:66
msgid "Location"
msgstr "Localitat"
#: src\templates\sub\profile.phtml:79
msgid "Search Tag"
msgstr "Etiqueta de cerca"
#: src\templates\sub\profiles.phtml:1
msgid "Account type tabs"
msgstr "Pestanyes tipus compte"
#: src\templates\sub\profiles.phtml:4
#: src\templates\sub\profiles.phtml:7
msgid "Top %s pagination"
msgstr "Superior%s Paginació"
#: src\templates\sub\profiles.phtml:13
#: src\templates\sub\profiles.phtml:16
msgid "Bottom %s pagination"
msgstr ""
#: src\templates\statistics.phtml:66
#: src\templates\sub\server.phtml:15
msgid "Stable Version"
msgstr ""
#: src\templates\statistics.phtml:68
#: src\templates\sub\server.phtml:17
msgid "Develop Version"
msgstr "Desenvolupar la versió"
#: src\templates\sub\server.phtml:19
msgid "Outdated Version"
msgstr "Versió obsoleta"
#: src\templates\sub\server.phtml:69
msgid "Admin"
msgstr "Administrador"
#: src\templates\sub\server.phtml:76
msgid "No description provided"
msgstr "No es proporciona cap descripció"
#: src\templates\sub\server.phtml:79
msgid "Visit Server"
msgstr "Visita servidor"
#: src\templates\widget\popularcountries.phtml:2
msgid "Popular Countries"
msgstr "Països populars"
#: src\templates\widget\popularprofilelanguages.phtml:2
msgid "Popular Languages"
msgstr "Idiomes populars"
#: src\templates\widget\populartags.phtml:2
msgid "Popular Tags"
msgstr "Etiquetes populars"
#: src\templates\sub\server.phtml:44
#: src\templates\sub\server.phtml:45
msgid "Default Language"
msgstr "Idioma per defecte"
#: src\templates\sub\server.phtml:49
msgid "Known Users"
msgstr "Usuaris coneguts"
#: src\classes\Views\Widget\AccountTypeTabs.php:48
msgctxt "account-type"
msgid "All"
msgstr "tots"
#: src\classes\Views\Widget\AccountTypeTabs.php:56
msgctxt "account-type"
msgid "People (%d)"
msgid_plural "People (%d)"
msgstr[0] ""
msgstr[1] ""
#: src\templates\layout.phtml:97
msgid "Stats"
msgstr "Estadístiques"
#: src\templates\statistics.phtml:4
msgid "Directory statistics"
msgstr "Estadístiques del directori"
#: src\templates\statistics.phtml:5
msgid "Profiles"
msgstr "perfil"
#: src\templates\statistics.phtml:6
msgid "This directory knows about <strong>%s distinct potential profile URLs</strong>."
msgstr "Aquest directori en sap <strong>%s URL de perfil diferents</strong>."
#: src\templates\statistics.phtml:16
#: src\templates\statistics.phtml:46
msgid "Languages"
msgstr "Idiomes"
#: src\templates\statistics.phtml:17
msgid "Out of <strong>%s</strong> profiles reporting their language there are:"
msgstr "Fora de<strong>%s</strong> Els perfils que informen el seu idioma hi ha:"
#: src\templates\statistics.phtml:27
msgid "Servers"
msgstr "Servidors"
#: src\templates\statistics.phtml:28
msgid "This directory knows about <strong>%s distinct potential server URLs</strong>."
msgstr "Aquest directori en sap <strong>%s URL de servidor diferents</strong>."
#: src\templates\statistics.phtml:29
msgid "Out of those, there are <strong>%s domains (%s)</strong> that have been a Friendica server at least once."
msgstr "Daquests, nhi ha <strong>%s dominis (%s)</strong> que han estat un servidor Friendica com a mínim una vegada."
#: src\templates\statistics.phtml:33
msgid "Out of those, there are:"
msgstr "D'aquests, hi ha:"
#: src\templates\statistics.phtml:47
msgid "Out of <strong>%s</strong> servers reporting their language there are:"
msgstr "Fora de <strong>%s</strong> hi ha servidors que informen del seu idioma:"
#: src\templates\statistics.phtml:57
msgid "Versions"
msgstr "Versions"
#: src\templates\statistics.phtml:58
msgid "Out of <strong>%s</strong> servers reporting their version there are:"
msgstr "Fora de<strong>%s</strong> Hi ha servidors que informen de la seva versió:"
#: src\templates\statistics.phtml:7
msgid "Out of those, there are <strong>%s profiles (%s)</strong> that opted in the public directory at least once."
msgstr "Daquests, nhi ha <strong>%s perfil (%s)</strong> que va optar al directori públic almenys una vegada."
#: src\templates\statistics.phtml:35
msgid "<strong>%s available servers (%s)</strong>"
msgstr "<strong>%s servidors disponibles (%s)</strong>"
#: src\templates\layout.phtml:91
msgid "Friendica Directory version %s"
msgstr "Versió del directori Friendica"
#: src\templates\statistics.phtml:11
msgid "Out of those, there currently are <strong>%s available profiles (%s)</strong>. <a href=\"%s\">Check them out!</a>"
msgstr ""
#: src\templates\statistics.phtml:39
msgid "<strong>%s public servers (%s)</strong> currently open for registration. <a href=\"%s\">Check them out!</a>"
msgstr ""
#: src\classes\Views\Widget\AccountTypeTabs.php:57
msgctxt "account-type"
msgid "News (%d)"
msgid_plural "News (%d)"
msgstr[0] ""
msgstr[1] ""
#: src\classes\Views\Widget\AccountTypeTabs.php:58
msgctxt "account-type"
msgid "Organization (%d)"
msgid_plural "Organizations (%d)"
msgstr[0] ""
msgstr[1] ""
#: src\classes\Views\Widget\AccountTypeTabs.php:61
msgctxt "account-type"
msgid "Group (%d)"
msgid_plural "Groups (%d)"
msgstr[0] ""
msgstr[1] ""
#: src\templates\servers.phtml:3
msgid "Filtered by language:"
msgstr ""
#: src\templates\servers.phtml:3
msgid "Clear language filter"
msgstr ""
#: src\templates\sub\server.phtml:40
msgid "Health Score"
msgstr ""
#: src\templates\sub\server.phtml:52
msgid "%s User"
msgid_plural "%s Users"
msgstr[0] ""
msgstr[1] ""
#: src\templates\sub\server.phtml:54
msgid "None"
msgstr ""
#: src\templates\sub\server.phtml:58
#: src\templates\sub\server.phtml:62
msgid "Registration Policy"
msgstr ""
#: src\templates\sub\server.phtml:59
msgid "By Approval"
msgstr ""
#: src\templates\sub\server.phtml:63
msgid "Open"
msgstr ""
#: src\templates\layout.phtml:94
msgid "Source Code on Friendica's Forgejo"
msgstr ""

Binary file not shown.

View File

@ -0,0 +1,369 @@
msgid ""
msgstr ""
"Project-Id-Version: \n"
"Report-Msgid-Bugs-To: \n"
"Last-Translator: Aditoo, 2019\n"
"Language-Team: Czech (https://www.transifex.com/Friendica/teams/12172/cs/)\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"POT-Creation-Date: 2018-11-16T04:17:37+00:00\n"
"PO-Revision-Date: 2018-11-16 20:30+0000\n"
"Language: cs\n"
"Plural-Forms: nplurals=4; plural=(n == 1 && n % 1 == 0) ? 0 : (n >= 2 && n <= 4 && n % 1 == 0) ? 1: (n % 1 != 0 ) ? 2 : 3;\n"
"X-Generator: Poedit 2.2\n"
#: src\classes\Content\Pager.php:168
#: src\classes\Content\Pager.php:216
msgid "Previous"
msgstr "Předchozí"
#: src\classes\Content\Pager.php:173
#: src\classes\Content\Pager.php:273
msgid "Next"
msgstr "Další"
#: src\classes\Content\Pager.php:211
msgid "First"
msgstr "První"
#: src\classes\Content\Pager.php:278
msgid "Last"
msgstr "Poslední"
#: src\classes\Controllers\Web\Directory.php:85
msgid "People"
msgstr "Lidé"
#: src\classes\Controllers\Web\Search.php:72
msgctxt "field"
msgid "Language"
msgstr "Jazyk"
#: src\classes\Controllers\Web\Search.php:73
msgctxt "field"
msgid "Locality"
msgstr "Poloha"
#: src\classes\Controllers\Web\Search.php:74
msgctxt "field"
msgid "Region"
msgstr "Region/kraj"
#: src\classes\Controllers\Web\Search.php:75
msgctxt "field"
msgid "Country"
msgstr "Země"
#: src\classes\Controllers\Web\Servers.php:105
msgid "Public Servers"
msgstr "Veřejné servery"
#: src\templates\layout.phtml:4
#: src\templates\layout.phtml:18
msgid "Friendica Directory"
msgstr "Adresář Friendica"
#: src\templates\layout.phtml:23
#: src\templates\layout.phtml:25
#: src\templates\layout.phtml:43
#: src\templates\layout.phtml:45
#: src\templates\search.phtml:4
#: src\templates\search.phtml:12
msgid "Search terms"
msgstr "Termíny vyhledávání"
#: src\templates\layout.phtml:24
#: src\templates\layout.phtml:45
#: src\templates\search.phtml:11
msgctxt "noun"
msgid "Search"
msgstr "Hledání"
#: src\templates\layout.phtml:27
#: src\templates\layout.phtml:47
#: src\templates\search.phtml:14
msgctxt "verb"
msgid "Search"
msgstr "Hledat"
#: src\templates\layout.phtml:33
msgid "Toggle navigation"
msgstr "Přepínat navigaci"
#: src\templates\layout.phtml:55
msgid "Directory"
msgstr "Adresář"
#: src\templates\layout.phtml:60
msgid "Public servers"
msgstr "Veřejné servery"
#: src\templates\search.phtml:19
msgid "%d result for \"%s\""
msgid_plural "%d results for \"%s\""
msgstr[0] "%d výsledek pro „%s“"
msgstr[1] "%d výsledky pro „%s“"
msgstr[2] "%d výsledku pro „%s“"
msgstr[3] "%d výsledků pro „%s“"
#: src\templates\servers.phtml:7
msgid "Top servers pagination"
msgstr "Horní stránkování serverů"
#: src\templates\servers.phtml:17
msgid "Bottom servers pagination"
msgstr "Spodní stránkování serverů"
#: src\templates\sub\profile.phtml:5
msgid "Filter by locality"
msgstr "Filtrovat dle polohy"
#: src\templates\sub\profile.phtml:11
msgid "Filter by region"
msgstr "Filtrovat dle regionu/kraje"
#: src\templates\sub\profile.phtml:17
msgid "Filter by country"
msgstr "Filtrovat dle země"
#: src\templates\sub\profile.phtml:31
#: src\templates\sub\profile.phtml:35
#: src\templates\sub\profile.phtml:39
msgctxt "verb"
msgid "Follow"
msgstr "Sledovat"
#: src\templates\layout.phtml:65
#: src\templates\sub\profile.phtml:57
msgid "Language"
msgstr "Jazyk"
#: src\templates\sub\profile.phtml:60
#: src\templates\widget\popularserverlanguages.phtml:2
msgid "Filter by language"
msgstr "Filtrovat dle jazyka"
#: src\templates\sub\profile.phtml:66
msgid "Location"
msgstr "Poloha"
#: src\templates\sub\profile.phtml:79
msgid "Search Tag"
msgstr "Hledat podle štítku"
#: src\templates\sub\profiles.phtml:1
msgid "Account type tabs"
msgstr "Záložky typů účtů"
#: src\templates\sub\profiles.phtml:4
#: src\templates\sub\profiles.phtml:7
msgid "Top %s pagination"
msgstr "Horní stránkování %s"
#: src\templates\sub\profiles.phtml:13
#: src\templates\sub\profiles.phtml:16
msgid "Bottom %s pagination"
msgstr "Spodní stránkování %s"
#: src\templates\statistics.phtml:66
#: src\templates\sub\server.phtml:15
msgid "Stable Version"
msgstr "Stabilní verze"
#: src\templates\statistics.phtml:68
#: src\templates\sub\server.phtml:17
msgid "Develop Version"
msgstr "Vývojová verze"
#: src\templates\sub\server.phtml:19
msgid "Outdated Version"
msgstr "Neaktuální verze"
#: src\templates\sub\server.phtml:69
msgid "Admin"
msgstr "Administrátor"
#: src\templates\sub\server.phtml:76
msgid "No description provided"
msgstr "Není zadán žádný popis"
#: src\templates\sub\server.phtml:79
msgid "Visit Server"
msgstr "Navštívit server"
#: src\templates\widget\popularcountries.phtml:2
msgid "Popular Countries"
msgstr "Populární země"
#: src\templates\widget\popularprofilelanguages.phtml:2
msgid "Popular Languages"
msgstr "Populární jazyky"
#: src\templates\widget\populartags.phtml:2
msgid "Popular Tags"
msgstr "Populární štítky"
#: src\templates\sub\server.phtml:44
#: src\templates\sub\server.phtml:45
msgid "Default Language"
msgstr "Výchozí jazyk"
#: src\templates\sub\server.phtml:49
msgid "Known Users"
msgstr "Známí uživatelé"
#: src\classes\Views\Widget\AccountTypeTabs.php:48
msgctxt "account-type"
msgid "All"
msgstr "Vše"
#: src\classes\Views\Widget\AccountTypeTabs.php:56
msgctxt "account-type"
msgid "People (%d)"
msgid_plural "People (%d)"
msgstr[0] "Lidé (%d)"
msgstr[1] "Lidé (%d)"
msgstr[2] "Lidé (%d)"
msgstr[3] "Lidé (%d)"
#: src\templates\layout.phtml:97
msgid "Stats"
msgstr "Statistika"
#: src\templates\statistics.phtml:4
msgid "Directory statistics"
msgstr "Statistika adresáře"
#: src\templates\statistics.phtml:5
msgid "Profiles"
msgstr "Profily"
#: src\templates\statistics.phtml:6
msgid "This directory knows about <strong>%s distinct potential profile URLs</strong>."
msgstr "Tento adresář ví o <strong>%s zřetelných potenciálních profilových URL adresách</strong>."
#: src\templates\statistics.phtml:16
#: src\templates\statistics.phtml:46
msgid "Languages"
msgstr "Jazyky"
#: src\templates\statistics.phtml:17
msgid "Out of <strong>%s</strong> profiles reporting their language there are:"
msgstr "Ze všech <strong>%s</strong> profilů hlásících svůj jazyk je tu:"
#: src\templates\statistics.phtml:27
msgid "Servers"
msgstr "Servery"
#: src\templates\statistics.phtml:28
msgid "This directory knows about <strong>%s distinct potential server URLs</strong>."
msgstr "Tento adresář ví o <strong>%s zřetelných potenciálních serverových URL adresách</strong>."
#: src\templates\statistics.phtml:29
msgid "Out of those, there are <strong>%s domains (%s)</strong> that have been a Friendica server at least once."
msgstr "Z těch je tu <strong>%s domén (%s)</strong>, které byly alespoň jednou serverem Friendica."
#: src\templates\statistics.phtml:33
msgid "Out of those, there are:"
msgstr "Z těch je tu:"
#: src\templates\statistics.phtml:47
msgid "Out of <strong>%s</strong> servers reporting their language there are:"
msgstr "Ze všech <strong>%s</strong> serverů hlásících svůj jazyk je tu:"
#: src\templates\statistics.phtml:57
msgid "Versions"
msgstr "Verze"
#: src\templates\statistics.phtml:58
msgid "Out of <strong>%s</strong> servers reporting their version there are:"
msgstr "Ze všech <strong>%s</strong> serverů hlásících svou verzi je tu:"
#: src\templates\statistics.phtml:7
msgid "Out of those, there are <strong>%s profiles (%s)</strong> that opted in the public directory at least once."
msgstr "Z těch je tu <strong>%s profilů (%s)</strong>, které byly alespoň jednou publikovány ve veřejném adresáři."
#: src\templates\statistics.phtml:35
msgid "<strong>%s available servers (%s)</strong>"
msgstr "<strong>%s dostupných serverů (%s)</strong>"
#: src\templates\layout.phtml:91
msgid "Friendica Directory version %s"
msgstr "Adresář Friendica, verze %s"
#: src\templates\statistics.phtml:11
msgid "Out of those, there currently are <strong>%s available profiles (%s)</strong>. <a href=\"%s\">Check them out!</a>"
msgstr ""
#: src\templates\statistics.phtml:39
msgid "<strong>%s public servers (%s)</strong> currently open for registration. <a href=\"%s\">Check them out!</a>"
msgstr ""
#: src\classes\Views\Widget\AccountTypeTabs.php:57
msgctxt "account-type"
msgid "News (%d)"
msgid_plural "News (%d)"
msgstr[0] ""
msgstr[1] ""
msgstr[2] ""
msgstr[3] ""
#: src\classes\Views\Widget\AccountTypeTabs.php:58
msgctxt "account-type"
msgid "Organization (%d)"
msgid_plural "Organizations (%d)"
msgstr[0] ""
msgstr[1] ""
msgstr[2] ""
msgstr[3] ""
#: src\classes\Views\Widget\AccountTypeTabs.php:61
msgctxt "account-type"
msgid "Group (%d)"
msgid_plural "Groups (%d)"
msgstr[0] ""
msgstr[1] ""
msgstr[2] ""
msgstr[3] ""
#: src\templates\servers.phtml:3
msgid "Filtered by language:"
msgstr ""
#: src\templates\servers.phtml:3
msgid "Clear language filter"
msgstr ""
#: src\templates\sub\server.phtml:40
msgid "Health Score"
msgstr ""
#: src\templates\sub\server.phtml:52
msgid "%s User"
msgid_plural "%s Users"
msgstr[0] ""
msgstr[1] ""
msgstr[2] ""
msgstr[3] ""
#: src\templates\sub\server.phtml:54
msgid "None"
msgstr ""
#: src\templates\sub\server.phtml:58
#: src\templates\sub\server.phtml:62
msgid "Registration Policy"
msgstr ""
#: src\templates\sub\server.phtml:59
msgid "By Approval"
msgstr ""
#: src\templates\sub\server.phtml:63
msgid "Open"
msgstr ""
#: src\templates\layout.phtml:94
msgid "Source Code on Friendica's Forgejo"
msgstr ""

Binary file not shown.

View File

@ -0,0 +1,357 @@
msgid ""
msgstr ""
"Project-Id-Version: \n"
"Report-Msgid-Bugs-To: \n"
"Last-Translator: Anton <dev@atjn.dk>, 2022\n"
"Language-Team: Danish (Denmark) (https://www.transifex.com/Friendica/teams/12172/da_DK/)\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"POT-Creation-Date: 2018-11-16T04:17:37+00:00\n"
"PO-Revision-Date: 2018-11-16 20:30+0000\n"
"Language: da_DK\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
"X-Generator: Poedit 2.2\n"
#: src\classes\Content\Pager.php:168
#: src\classes\Content\Pager.php:216
msgid "Previous"
msgstr "Forrige"
#: src\classes\Content\Pager.php:173
#: src\classes\Content\Pager.php:273
msgid "Next"
msgstr "Næste"
#: src\classes\Content\Pager.php:211
msgid "First"
msgstr "Første"
#: src\classes\Content\Pager.php:278
msgid "Last"
msgstr "Sidste"
#: src\classes\Controllers\Web\Directory.php:85
msgid "People"
msgstr "Mennesker"
#: src\classes\Controllers\Web\Search.php:72
msgctxt "field"
msgid "Language"
msgstr "Sprog"
#: src\classes\Controllers\Web\Search.php:73
msgctxt "field"
msgid "Locality"
msgstr "Lokalitet"
#: src\classes\Controllers\Web\Search.php:74
msgctxt "field"
msgid "Region"
msgstr "Region"
#: src\classes\Controllers\Web\Search.php:75
msgctxt "field"
msgid "Country"
msgstr "Land"
#: src\classes\Controllers\Web\Servers.php:105
msgid "Public Servers"
msgstr "Offentlige servere"
#: src\templates\layout.phtml:4
#: src\templates\layout.phtml:18
msgid "Friendica Directory"
msgstr "Friendica adressebog"
#: src\templates\layout.phtml:23
#: src\templates\layout.phtml:25
#: src\templates\layout.phtml:43
#: src\templates\layout.phtml:45
#: src\templates\search.phtml:4
#: src\templates\search.phtml:12
msgid "Search terms"
msgstr "Søgetermer"
#: src\templates\layout.phtml:24
#: src\templates\layout.phtml:45
#: src\templates\search.phtml:11
msgctxt "noun"
msgid "Search"
msgstr "Søg"
#: src\templates\layout.phtml:27
#: src\templates\layout.phtml:47
#: src\templates\search.phtml:14
msgctxt "verb"
msgid "Search"
msgstr "Søg"
#: src\templates\layout.phtml:33
msgid "Toggle navigation"
msgstr "Skift navigation"
#: src\templates\layout.phtml:55
msgid "Directory"
msgstr "Adressebog"
#: src\templates\layout.phtml:60
msgid "Public servers"
msgstr "Offentlige servere"
#: src\templates\search.phtml:19
msgid "%d result for \"%s\""
msgid_plural "%d results for \"%s\""
msgstr[0] "%d resultat for \"%s\""
msgstr[1] "%d resultater for \"%s\""
#: src\templates\servers.phtml:7
msgid "Top servers pagination"
msgstr "Top servere sideopdeling"
#: src\templates\servers.phtml:17
msgid "Bottom servers pagination"
msgstr "Bundservere sideopdeling"
#: src\templates\sub\profile.phtml:5
msgid "Filter by locality"
msgstr "Filtrer efter lokalitet"
#: src\templates\sub\profile.phtml:11
msgid "Filter by region"
msgstr "Filtrer efter region"
#: src\templates\sub\profile.phtml:17
msgid "Filter by country"
msgstr "Filtrer efter land"
#: src\templates\sub\profile.phtml:31
#: src\templates\sub\profile.phtml:35
#: src\templates\sub\profile.phtml:39
msgctxt "verb"
msgid "Follow"
msgstr "Følg"
#: src\templates\layout.phtml:65
#: src\templates\sub\profile.phtml:57
msgid "Language"
msgstr "Sprog"
#: src\templates\sub\profile.phtml:60
#: src\templates\widget\popularserverlanguages.phtml:2
msgid "Filter by language"
msgstr "Filtrer efter sprog"
#: src\templates\sub\profile.phtml:66
msgid "Location"
msgstr "Placering"
#: src\templates\sub\profile.phtml:79
msgid "Search Tag"
msgstr "Søgetag"
#: src\templates\sub\profiles.phtml:1
msgid "Account type tabs"
msgstr "Kontotypefaner"
#: src\templates\sub\profiles.phtml:4
#: src\templates\sub\profiles.phtml:7
msgid "Top %s pagination"
msgstr "Top %s sideopdeling"
#: src\templates\sub\profiles.phtml:13
#: src\templates\sub\profiles.phtml:16
msgid "Bottom %s pagination"
msgstr "Bund %s sideopdeling"
#: src\templates\statistics.phtml:66
#: src\templates\sub\server.phtml:15
msgid "Stable Version"
msgstr "Stabil version"
#: src\templates\statistics.phtml:68
#: src\templates\sub\server.phtml:17
msgid "Develop Version"
msgstr "Udviklingsversion"
#: src\templates\sub\server.phtml:19
msgid "Outdated Version"
msgstr "Forældet version"
#: src\templates\sub\server.phtml:69
msgid "Admin"
msgstr "Admin"
#: src\templates\sub\server.phtml:76
msgid "No description provided"
msgstr "Ingen beskrivelse givet"
#: src\templates\sub\server.phtml:79
msgid "Visit Server"
msgstr "Besøg server"
#: src\templates\widget\popularcountries.phtml:2
msgid "Popular Countries"
msgstr "Populære lande"
#: src\templates\widget\popularprofilelanguages.phtml:2
msgid "Popular Languages"
msgstr "Populære sprog"
#: src\templates\widget\populartags.phtml:2
msgid "Popular Tags"
msgstr "Populære tags"
#: src\templates\sub\server.phtml:44
#: src\templates\sub\server.phtml:45
msgid "Default Language"
msgstr "Standard sprog"
#: src\templates\sub\server.phtml:49
msgid "Known Users"
msgstr "Kendte brugere"
#: src\classes\Views\Widget\AccountTypeTabs.php:48
msgctxt "account-type"
msgid "All"
msgstr "Alle"
#: src\classes\Views\Widget\AccountTypeTabs.php:56
msgctxt "account-type"
msgid "People (%d)"
msgid_plural "People (%d)"
msgstr[0] "Mennesker (%d)"
msgstr[1] "Mennesker (%d)"
#: src\templates\layout.phtml:97
msgid "Stats"
msgstr "Statistik"
#: src\templates\statistics.phtml:4
msgid "Directory statistics"
msgstr "Adressebog statistik"
#: src\templates\statistics.phtml:5
msgid "Profiles"
msgstr "Profiler"
#: src\templates\statistics.phtml:6
msgid "This directory knows about <strong>%s distinct potential profile URLs</strong>."
msgstr "Denne adressebog kender til <strong>%s særskilte potentielle profil-URL'er</strong>."
#: src\templates\statistics.phtml:16
#: src\templates\statistics.phtml:46
msgid "Languages"
msgstr "Sprog"
#: src\templates\statistics.phtml:17
msgid "Out of <strong>%s</strong> profiles reporting their language there are:"
msgstr "Ud af <strong>%s</strong> profiler som rapporterer deres sprog, er der:"
#: src\templates\statistics.phtml:27
msgid "Servers"
msgstr "Servere"
#: src\templates\statistics.phtml:28
msgid "This directory knows about <strong>%s distinct potential server URLs</strong>."
msgstr "Denne adressebog kender til <strong>%s særskilte potentielle server-URL'er</strong>."
#: src\templates\statistics.phtml:29
msgid "Out of those, there are <strong>%s domains (%s)</strong> that have been a Friendica server at least once."
msgstr "Ud af dem er der <strong>%s domæner (%s)</strong> som har været en Friendica server mindst én gang."
#: src\templates\statistics.phtml:33
msgid "Out of those, there are:"
msgstr "Ud af dem er der:"
#: src\templates\statistics.phtml:47
msgid "Out of <strong>%s</strong> servers reporting their language there are:"
msgstr "Ud af <strong>%s</strong> servere som rapporterer deres sprog, er der:"
#: src\templates\statistics.phtml:57
msgid "Versions"
msgstr "Versioner"
#: src\templates\statistics.phtml:58
msgid "Out of <strong>%s</strong> servers reporting their version there are:"
msgstr "Ud af <strong>%s</strong> servere som rapporterer deres version, er der:"
#: src\templates\statistics.phtml:7
msgid "Out of those, there are <strong>%s profiles (%s)</strong> that opted in the public directory at least once."
msgstr "Ud af dem er der <strong>%s profiler (%s)</strong> som tilvalgte den offentlige adressebog mindst én gang."
#: src\templates\statistics.phtml:35
msgid "<strong>%s available servers (%s)</strong>"
msgstr "<strong>%s tilgængelige servere (%s)</strong>"
#: src\templates\layout.phtml:91
msgid "Friendica Directory version %s"
msgstr "Friendica adressebogsversion %s"
#: src\templates\statistics.phtml:11
msgid "Out of those, there currently are <strong>%s available profiles (%s)</strong>. <a href=\"%s\">Check them out!</a>"
msgstr "Ud af dem er der i øjeblikket <strong>%s tilgængelige profiler (%s)</strong>. <a href=\"%s\">Tjek dem ud!</a>"
#: src\templates\statistics.phtml:39
msgid "<strong>%s public servers (%s)</strong> currently open for registration. <a href=\"%s\">Check them out!</a>"
msgstr "<strong>%s offentlige servere (%s)</strong> åben for registrering lige nu. <a href=\"%s\">Tjek dem ud!</a>"
#: src\classes\Views\Widget\AccountTypeTabs.php:57
msgctxt "account-type"
msgid "News (%d)"
msgid_plural "News (%d)"
msgstr[0] "Nyheder (%d)"
msgstr[1] "Nyheder (%d)"
#: src\classes\Views\Widget\AccountTypeTabs.php:58
msgctxt "account-type"
msgid "Organization (%d)"
msgid_plural "Organizations (%d)"
msgstr[0] "Organisationer (%d)"
msgstr[1] "Organisationer (%d)"
#: src\templates\sub\server.phtml:40
msgid "Health Score"
msgstr "Sundhedsscore"
#: src\templates\sub\server.phtml:52
msgid "%s User"
msgid_plural "%s Users"
msgstr[0] "%s bruger"
msgstr[1] "%s brugere"
#: src\templates\sub\server.phtml:54
msgid "None"
msgstr "Ingen"
#: src\templates\sub\server.phtml:58
#: src\templates\sub\server.phtml:62
msgid "Registration Policy"
msgstr "Registreringspolitik"
#: src\templates\sub\server.phtml:59
msgid "By Approval"
msgstr "Ved godkendelse"
#: src\templates\sub\server.phtml:63
msgid "Open"
msgstr "Åben"
#: src\classes\Views\Widget\AccountTypeTabs.php:61
msgctxt "account-type"
msgid "Group (%d)"
msgid_plural "Groups (%d)"
msgstr[0] ""
msgstr[1] ""
#: src\templates\servers.phtml:3
msgid "Filtered by language:"
msgstr ""
#: src\templates\servers.phtml:3
msgid "Clear language filter"
msgstr ""
#: src\templates\layout.phtml:94
msgid "Source Code on Friendica's Forgejo"
msgstr ""

Binary file not shown.

View File

@ -0,0 +1,357 @@
msgid ""
msgstr ""
"Project-Id-Version: \n"
"Report-Msgid-Bugs-To: \n"
"Last-Translator: Tobias Diekershoff <tobias.diekershoff@gmx.net>, 2020\n"
"Language-Team: German (https://www.transifex.com/Friendica/teams/12172/de/)\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"POT-Creation-Date: 2018-11-16T04:17:37+00:00\n"
"PO-Revision-Date: 2018-11-16 20:30+0000\n"
"Language: de\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
"X-Generator: Poedit 2.2\n"
#: src\classes\Content\Pager.php:168
#: src\classes\Content\Pager.php:216
msgid "Previous"
msgstr "Vorherige"
#: src\classes\Content\Pager.php:173
#: src\classes\Content\Pager.php:273
msgid "Next"
msgstr "Nächste"
#: src\classes\Content\Pager.php:211
msgid "First"
msgstr "Erste"
#: src\classes\Content\Pager.php:278
msgid "Last"
msgstr "Letzte"
#: src\classes\Controllers\Web\Directory.php:85
msgid "People"
msgstr "Leute"
#: src\classes\Controllers\Web\Search.php:72
msgctxt "field"
msgid "Language"
msgstr "Sprache"
#: src\classes\Controllers\Web\Search.php:73
msgctxt "field"
msgid "Locality"
msgstr "Wohnort"
#: src\classes\Controllers\Web\Search.php:74
msgctxt "field"
msgid "Region"
msgstr "Region"
#: src\classes\Controllers\Web\Search.php:75
msgctxt "field"
msgid "Country"
msgstr "Land"
#: src\classes\Controllers\Web\Servers.php:105
msgid "Public Servers"
msgstr "Öffentlicher Server"
#: src\templates\layout.phtml:4
#: src\templates\layout.phtml:18
msgid "Friendica Directory"
msgstr "Friendica Verzeichnis"
#: src\templates\layout.phtml:23
#: src\templates\layout.phtml:25
#: src\templates\layout.phtml:43
#: src\templates\layout.phtml:45
#: src\templates\search.phtml:4
#: src\templates\search.phtml:12
msgid "Search terms"
msgstr "Suchbegriff"
#: src\templates\layout.phtml:24
#: src\templates\layout.phtml:45
#: src\templates\search.phtml:11
msgctxt "noun"
msgid "Search"
msgstr "Suche"
#: src\templates\layout.phtml:27
#: src\templates\layout.phtml:47
#: src\templates\search.phtml:14
msgctxt "verb"
msgid "Search"
msgstr "Suche"
#: src\templates\layout.phtml:33
msgid "Toggle navigation"
msgstr "Schalte Navigation"
#: src\templates\layout.phtml:55
msgid "Directory"
msgstr "Verzeichnis"
#: src\templates\layout.phtml:60
msgid "Public servers"
msgstr "Öffentliche Server"
#: src\templates\search.phtml:19
msgid "%d result for \"%s\""
msgid_plural "%d results for \"%s\""
msgstr[0] "%d Ergebnisse für \"%s\""
msgstr[1] "%d Ergebnisse für \"%s\""
#: src\templates\servers.phtml:7
msgid "Top servers pagination"
msgstr "Höchste Serverwertung"
#: src\templates\servers.phtml:17
msgid "Bottom servers pagination"
msgstr "Niedrigste Serverwertung"
#: src\templates\sub\profile.phtml:5
msgid "Filter by locality"
msgstr "Filter nach Ort"
#: src\templates\sub\profile.phtml:11
msgid "Filter by region"
msgstr "Filter nach Region"
#: src\templates\sub\profile.phtml:17
msgid "Filter by country"
msgstr "Filter nach Land"
#: src\templates\sub\profile.phtml:31
#: src\templates\sub\profile.phtml:35
#: src\templates\sub\profile.phtml:39
msgctxt "verb"
msgid "Follow"
msgstr "Folge"
#: src\templates\layout.phtml:65
#: src\templates\sub\profile.phtml:57
msgid "Language"
msgstr "Sprache"
#: src\templates\sub\profile.phtml:60
#: src\templates\widget\popularserverlanguages.phtml:2
msgid "Filter by language"
msgstr "Filter nach Sprache"
#: src\templates\sub\profile.phtml:66
msgid "Location"
msgstr "Ort"
#: src\templates\sub\profile.phtml:79
msgid "Search Tag"
msgstr "Suchbegriff"
#: src\templates\sub\profiles.phtml:1
msgid "Account type tabs"
msgstr "Konto-Typ Register"
#: src\templates\sub\profiles.phtml:4
#: src\templates\sub\profiles.phtml:7
msgid "Top %s pagination"
msgstr "Top %s pagination"
#: src\templates\sub\profiles.phtml:13
#: src\templates\sub\profiles.phtml:16
msgid "Bottom %s pagination"
msgstr "Bottom %s pagination"
#: src\templates\statistics.phtml:66
#: src\templates\sub\server.phtml:15
msgid "Stable Version"
msgstr "Stabile Version"
#: src\templates\statistics.phtml:68
#: src\templates\sub\server.phtml:17
msgid "Develop Version"
msgstr "Entwickler Version"
#: src\templates\sub\server.phtml:19
msgid "Outdated Version"
msgstr "Abgelaufene Version"
#: src\templates\sub\server.phtml:69
msgid "Admin"
msgstr "Admin"
#: src\templates\sub\server.phtml:76
msgid "No description provided"
msgstr "Keine Beschreibung angeboten"
#: src\templates\sub\server.phtml:79
msgid "Visit Server"
msgstr "Besuche den Server"
#: src\templates\widget\popularcountries.phtml:2
msgid "Popular Countries"
msgstr "Häufige Länder"
#: src\templates\widget\popularprofilelanguages.phtml:2
msgid "Popular Languages"
msgstr "Häufige Sprachen"
#: src\templates\widget\populartags.phtml:2
msgid "Popular Tags"
msgstr "Beliebte Tags"
#: src\templates\sub\server.phtml:44
#: src\templates\sub\server.phtml:45
msgid "Default Language"
msgstr "Standard Sprache"
#: src\templates\sub\server.phtml:49
msgid "Known Users"
msgstr "Bekannte Nutzer"
#: src\classes\Views\Widget\AccountTypeTabs.php:48
msgctxt "account-type"
msgid "All"
msgstr "Alle"
#: src\classes\Views\Widget\AccountTypeTabs.php:56
msgctxt "account-type"
msgid "People (%d)"
msgid_plural "People (%d)"
msgstr[0] "Personen (%d)"
msgstr[1] "Personen (%d)"
#: src\templates\layout.phtml:97
msgid "Stats"
msgstr "Statistik"
#: src\templates\statistics.phtml:4
msgid "Directory statistics"
msgstr "Verzeichnis Statistiken"
#: src\templates\statistics.phtml:5
msgid "Profiles"
msgstr "Profile"
#: src\templates\statistics.phtml:6
msgid "This directory knows about <strong>%s distinct potential profile URLs</strong>."
msgstr "Dieses Verzeichnis kennt <strong>%s unterschiedliche, potentielle Profil URLs</strong>"
#: src\templates\statistics.phtml:16
#: src\templates\statistics.phtml:46
msgid "Languages"
msgstr "Sprachen"
#: src\templates\statistics.phtml:17
msgid "Out of <strong>%s</strong> profiles reporting their language there are:"
msgstr "Von den <strong>%s</strong> Profilen, die ihre Sprache angeben, sind:"
#: src\templates\statistics.phtml:27
msgid "Servers"
msgstr "Server"
#: src\templates\statistics.phtml:28
msgid "This directory knows about <strong>%s distinct potential server URLs</strong>."
msgstr "Dieses Verzeichnis kennt <strong>%s unterschiedliche, potentielle Server URLs</strong>"
#: src\templates\statistics.phtml:29
msgid "Out of those, there are <strong>%s domains (%s)</strong> that have been a Friendica server at least once."
msgstr "Von diesen gibt es <strong>%sDomänen(%s)</strong>, die zumindest einmal Friendica Knoten gewesen sind."
#: src\templates\statistics.phtml:33
msgid "Out of those, there are:"
msgstr "Von diesen sind:"
#: src\templates\statistics.phtml:47
msgid "Out of <strong>%s</strong> servers reporting their language there are:"
msgstr "Von den <strong>%s</strong> Servern, die ihre Spracheinstellung angeben, sind:"
#: src\templates\statistics.phtml:57
msgid "Versions"
msgstr "Versionen"
#: src\templates\statistics.phtml:58
msgid "Out of <strong>%s</strong> servers reporting their version there are:"
msgstr "Von den <strong>%s</strong> Servern, die ihre Version angeben, sind:"
#: src\templates\statistics.phtml:7
msgid "Out of those, there are <strong>%s profiles (%s)</strong> that opted in the public directory at least once."
msgstr "Von diesen gibt es <strong>%sProfile (%s)</strong> die sich, zumindest einmal, für die Aufnahme in das öffentliche Verzeichnis entschlossen haben."
#: src\templates\statistics.phtml:35
msgid "<strong>%s available servers (%s)</strong>"
msgstr "<strong>%sverfügbare Server (%s)</strong>"
#: src\templates\layout.phtml:91
msgid "Friendica Directory version %s"
msgstr "Friendica Verzeichnis Version %s"
#: src\templates\statistics.phtml:11
msgid "Out of those, there currently are <strong>%s available profiles (%s)</strong>. <a href=\"%s\">Check them out!</a>"
msgstr "Von diesen sind derzeit <strong>%sverfügbare Profile (%s)</strong>. <a href=\"%s\">Schaue sie dir an!</a>"
#: src\templates\statistics.phtml:39
msgid "<strong>%s public servers (%s)</strong> currently open for registration. <a href=\"%s\">Check them out!</a>"
msgstr "<strong>%s öffentliche Server (%s)</strong> erlauben derzeit die Anmeldung. <a href=\"%s\">Schau sie dir an!</a>"
#: src\classes\Views\Widget\AccountTypeTabs.php:57
msgctxt "account-type"
msgid "News (%d)"
msgid_plural "News (%d)"
msgstr[0] ""
msgstr[1] ""
#: src\classes\Views\Widget\AccountTypeTabs.php:58
msgctxt "account-type"
msgid "Organization (%d)"
msgid_plural "Organizations (%d)"
msgstr[0] ""
msgstr[1] ""
#: src\classes\Views\Widget\AccountTypeTabs.php:61
msgctxt "account-type"
msgid "Group (%d)"
msgid_plural "Groups (%d)"
msgstr[0] ""
msgstr[1] ""
#: src\templates\servers.phtml:3
msgid "Filtered by language:"
msgstr ""
#: src\templates\servers.phtml:3
msgid "Clear language filter"
msgstr ""
#: src\templates\sub\server.phtml:40
msgid "Health Score"
msgstr ""
#: src\templates\sub\server.phtml:52
msgid "%s User"
msgid_plural "%s Users"
msgstr[0] ""
msgstr[1] ""
#: src\templates\sub\server.phtml:54
msgid "None"
msgstr ""
#: src\templates\sub\server.phtml:58
#: src\templates\sub\server.phtml:62
msgid "Registration Policy"
msgstr ""
#: src\templates\sub\server.phtml:59
msgid "By Approval"
msgstr ""
#: src\templates\sub\server.phtml:63
msgid "Open"
msgstr ""
#: src\templates\layout.phtml:94
msgid "Source Code on Friendica's Forgejo"
msgstr ""

Binary file not shown.

View File

@ -0,0 +1,357 @@
msgid ""
msgstr ""
"Project-Id-Version: \n"
"Report-Msgid-Bugs-To: \n"
"Last-Translator: \n"
"Language-Team: \n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"POT-Creation-Date: 2018-11-16T04:17:37+00:00\n"
"PO-Revision-Date: 2018-11-16 00:01-0500\n"
"Language: en\n"
"Plural-Forms: nplurals=2; plural=n != 1;\n"
"X-Generator: Poedit 2.2\n"
#: src\classes\Content\Pager.php:168
#: src\classes\Content\Pager.php:216
msgid "Previous"
msgstr ""
#: src\classes\Content\Pager.php:173
#: src\classes\Content\Pager.php:273
msgid "Next"
msgstr ""
#: src\classes\Content\Pager.php:211
msgid "First"
msgstr ""
#: src\classes\Content\Pager.php:278
msgid "Last"
msgstr ""
#: src\classes\Controllers\Web\Directory.php:85
msgid "People"
msgstr ""
#: src\classes\Controllers\Web\Search.php:72
msgctxt "field"
msgid "Language"
msgstr ""
#: src\classes\Controllers\Web\Search.php:73
msgctxt "field"
msgid "Locality"
msgstr ""
#: src\classes\Controllers\Web\Search.php:74
msgctxt "field"
msgid "Region"
msgstr ""
#: src\classes\Controllers\Web\Search.php:75
msgctxt "field"
msgid "Country"
msgstr ""
#: src\classes\Controllers\Web\Servers.php:105
msgid "Public Servers"
msgstr ""
#: src\templates\layout.phtml:4
#: src\templates\layout.phtml:18
msgid "Friendica Directory"
msgstr ""
#: src\templates\layout.phtml:23
#: src\templates\layout.phtml:25
#: src\templates\layout.phtml:43
#: src\templates\layout.phtml:45
#: src\templates\search.phtml:4
#: src\templates\search.phtml:12
msgid "Search terms"
msgstr ""
#: src\templates\layout.phtml:24
#: src\templates\layout.phtml:45
#: src\templates\search.phtml:11
msgctxt "noun"
msgid "Search"
msgstr ""
#: src\templates\layout.phtml:27
#: src\templates\layout.phtml:47
#: src\templates\search.phtml:14
msgctxt "verb"
msgid "Search"
msgstr ""
#: src\templates\layout.phtml:33
msgid "Toggle navigation"
msgstr ""
#: src\templates\layout.phtml:55
msgid "Directory"
msgstr ""
#: src\templates\layout.phtml:60
msgid "Public servers"
msgstr ""
#: src\templates\search.phtml:19
msgid "%d result for \"%s\""
msgid_plural "%d results for \"%s\""
msgstr[0] ""
msgstr[1] ""
#: src\templates\servers.phtml:7
msgid "Top servers pagination"
msgstr ""
#: src\templates\servers.phtml:17
msgid "Bottom servers pagination"
msgstr ""
#: src\templates\sub\profile.phtml:5
msgid "Filter by locality"
msgstr ""
#: src\templates\sub\profile.phtml:11
msgid "Filter by region"
msgstr ""
#: src\templates\sub\profile.phtml:17
msgid "Filter by country"
msgstr ""
#: src\templates\sub\profile.phtml:31
#: src\templates\sub\profile.phtml:35
#: src\templates\sub\profile.phtml:39
msgctxt "verb"
msgid "Follow"
msgstr ""
#: src\templates\layout.phtml:65
#: src\templates\sub\profile.phtml:57
msgid "Language"
msgstr ""
#: src\templates\sub\profile.phtml:60
#: src\templates\widget\popularserverlanguages.phtml:2
msgid "Filter by language"
msgstr ""
#: src\templates\sub\profile.phtml:66
msgid "Location"
msgstr ""
#: src\templates\sub\profile.phtml:79
msgid "Search Tag"
msgstr ""
#: src\templates\sub\profiles.phtml:1
msgid "Account type tabs"
msgstr ""
#: src\templates\sub\profiles.phtml:4
#: src\templates\sub\profiles.phtml:7
msgid "Top %s pagination"
msgstr ""
#: src\templates\sub\profiles.phtml:13
#: src\templates\sub\profiles.phtml:16
msgid "Bottom %s pagination"
msgstr ""
#: src\templates\statistics.phtml:66
#: src\templates\sub\server.phtml:15
msgid "Stable Version"
msgstr ""
#: src\templates\statistics.phtml:68
#: src\templates\sub\server.phtml:17
msgid "Develop Version"
msgstr ""
#: src\templates\sub\server.phtml:19
msgid "Outdated Version"
msgstr ""
#: src\templates\sub\server.phtml:69
msgid "Admin"
msgstr ""
#: src\templates\sub\server.phtml:76
msgid "No description provided"
msgstr ""
#: src\templates\sub\server.phtml:79
msgid "Visit Server"
msgstr ""
#: src\templates\widget\popularcountries.phtml:2
msgid "Popular Countries"
msgstr ""
#: src\templates\widget\popularprofilelanguages.phtml:2
msgid "Popular Languages"
msgstr ""
#: src\templates\widget\populartags.phtml:2
msgid "Popular Tags"
msgstr ""
#: src\templates\sub\server.phtml:44
#: src\templates\sub\server.phtml:45
msgid "Default Language"
msgstr ""
#: src\templates\sub\server.phtml:49
msgid "Known Users"
msgstr ""
#: src\classes\Views\Widget\AccountTypeTabs.php:48
msgctxt "account-type"
msgid "All"
msgstr ""
#: src\classes\Views\Widget\AccountTypeTabs.php:56
msgctxt "account-type"
msgid "People (%d)"
msgid_plural "People (%d)"
msgstr[0] ""
msgstr[1] ""
#: src\templates\layout.phtml:97
msgid "Stats"
msgstr ""
#: src\templates\statistics.phtml:4
msgid "Directory statistics"
msgstr ""
#: src\templates\statistics.phtml:5
msgid "Profiles"
msgstr ""
#: src\templates\statistics.phtml:6
msgid "This directory knows about <strong>%s distinct potential profile URLs</strong>."
msgstr ""
#: src\templates\statistics.phtml:16
#: src\templates\statistics.phtml:46
msgid "Languages"
msgstr ""
#: src\templates\statistics.phtml:17
msgid "Out of <strong>%s</strong> profiles reporting their language there are:"
msgstr ""
#: src\templates\statistics.phtml:27
msgid "Servers"
msgstr ""
#: src\templates\statistics.phtml:28
msgid "This directory knows about <strong>%s distinct potential server URLs</strong>."
msgstr ""
#: src\templates\statistics.phtml:29
msgid "Out of those, there are <strong>%s domains (%s)</strong> that have been a Friendica server at least once."
msgstr ""
#: src\templates\statistics.phtml:33
msgid "Out of those, there are:"
msgstr ""
#: src\templates\statistics.phtml:47
msgid "Out of <strong>%s</strong> servers reporting their language there are:"
msgstr ""
#: src\templates\statistics.phtml:57
msgid "Versions"
msgstr ""
#: src\templates\statistics.phtml:58
msgid "Out of <strong>%s</strong> servers reporting their version there are:"
msgstr ""
#: src\templates\statistics.phtml:7
msgid "Out of those, there are <strong>%s profiles (%s)</strong> that opted in the public directory at least once."
msgstr ""
#: src\templates\statistics.phtml:35
msgid "<strong>%s available servers (%s)</strong>"
msgstr ""
#: src\templates\layout.phtml:91
msgid "Friendica Directory version %s"
msgstr ""
#: src\templates\statistics.phtml:11
msgid "Out of those, there currently are <strong>%s available profiles (%s)</strong>. <a href=\"%s\">Check them out!</a>"
msgstr ""
#: src\templates\statistics.phtml:39
msgid "<strong>%s public servers (%s)</strong> currently open for registration. <a href=\"%s\">Check them out!</a>"
msgstr ""
#: src\classes\Views\Widget\AccountTypeTabs.php:57
msgctxt "account-type"
msgid "News (%d)"
msgid_plural "News (%d)"
msgstr[0] ""
msgstr[1] ""
#: src\classes\Views\Widget\AccountTypeTabs.php:58
msgctxt "account-type"
msgid "Organization (%d)"
msgid_plural "Organizations (%d)"
msgstr[0] ""
msgstr[1] ""
#: src\templates\sub\server.phtml:40
msgid "Health Score"
msgstr ""
#: src\templates\sub\server.phtml:52
msgid "%s User"
msgid_plural "%s Users"
msgstr[0] ""
msgstr[1] ""
#: src\templates\sub\server.phtml:54
msgid "None"
msgstr ""
#: src\templates\sub\server.phtml:58
#: src\templates\sub\server.phtml:62
msgid "Registration Policy"
msgstr ""
#: src\templates\sub\server.phtml:59
msgid "By Approval"
msgstr ""
#: src\templates\sub\server.phtml:63
msgid "Open"
msgstr ""
#: src\classes\Views\Widget\AccountTypeTabs.php:61
msgctxt "account-type"
msgid "Group (%d)"
msgid_plural "Groups (%d)"
msgstr[0] ""
msgstr[1] ""
#: src\templates\servers.phtml:3
msgid "Filtered by language:"
msgstr ""
#: src\templates\servers.phtml:3
msgid "Clear language filter"
msgstr ""
#: src\templates\layout.phtml:94
msgid "Source Code on Friendica's Forgejo"
msgstr ""

Binary file not shown.

View File

@ -0,0 +1,357 @@
msgid ""
msgstr ""
"Project-Id-Version: \n"
"Report-Msgid-Bugs-To: \n"
"Last-Translator: Andy H3 <andy@hubup.pro>, 2020\n"
"Language-Team: English (United Kingdom) (https://www.transifex.com/Friendica/teams/12172/en_GB/)\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"POT-Creation-Date: 2018-11-16T04:17:37+00:00\n"
"PO-Revision-Date: 2018-11-16 20:30+0000\n"
"Language: en_GB\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
"X-Generator: Poedit 2.2\n"
#: src\classes\Content\Pager.php:168
#: src\classes\Content\Pager.php:216
msgid "Previous"
msgstr "Previous"
#: src\classes\Content\Pager.php:173
#: src\classes\Content\Pager.php:273
msgid "Next"
msgstr "Next"
#: src\classes\Content\Pager.php:211
msgid "First"
msgstr "First"
#: src\classes\Content\Pager.php:278
msgid "Last"
msgstr "Last"
#: src\classes\Controllers\Web\Directory.php:85
msgid "People"
msgstr "People"
#: src\classes\Controllers\Web\Search.php:72
msgctxt "field"
msgid "Language"
msgstr "Language"
#: src\classes\Controllers\Web\Search.php:73
msgctxt "field"
msgid "Locality"
msgstr "Locality"
#: src\classes\Controllers\Web\Search.php:74
msgctxt "field"
msgid "Region"
msgstr "Region"
#: src\classes\Controllers\Web\Search.php:75
msgctxt "field"
msgid "Country"
msgstr "Country"
#: src\classes\Controllers\Web\Servers.php:105
msgid "Public Servers"
msgstr "Public servers"
#: src\templates\layout.phtml:4
#: src\templates\layout.phtml:18
msgid "Friendica Directory"
msgstr "Friendica directory"
#: src\templates\layout.phtml:23
#: src\templates\layout.phtml:25
#: src\templates\layout.phtml:43
#: src\templates\layout.phtml:45
#: src\templates\search.phtml:4
#: src\templates\search.phtml:12
msgid "Search terms"
msgstr "Search terms"
#: src\templates\layout.phtml:24
#: src\templates\layout.phtml:45
#: src\templates\search.phtml:11
msgctxt "noun"
msgid "Search"
msgstr "Search"
#: src\templates\layout.phtml:27
#: src\templates\layout.phtml:47
#: src\templates\search.phtml:14
msgctxt "verb"
msgid "Search"
msgstr "Search"
#: src\templates\layout.phtml:33
msgid "Toggle navigation"
msgstr "Toggle navigation"
#: src\templates\layout.phtml:55
msgid "Directory"
msgstr "Directory"
#: src\templates\layout.phtml:60
msgid "Public servers"
msgstr "Public servers"
#: src\templates\search.phtml:19
msgid "%d result for \"%s\""
msgid_plural "%d results for \"%s\""
msgstr[0] "%d result for \"%s\""
msgstr[1] "%d results for \"%s\""
#: src\templates\servers.phtml:7
msgid "Top servers pagination"
msgstr "Top servers pagination"
#: src\templates\servers.phtml:17
msgid "Bottom servers pagination"
msgstr "Bottom servers pagination"
#: src\templates\sub\profile.phtml:5
msgid "Filter by locality"
msgstr "Filter by locality"
#: src\templates\sub\profile.phtml:11
msgid "Filter by region"
msgstr "Filter by region"
#: src\templates\sub\profile.phtml:17
msgid "Filter by country"
msgstr "Filter by country"
#: src\templates\sub\profile.phtml:31
#: src\templates\sub\profile.phtml:35
#: src\templates\sub\profile.phtml:39
msgctxt "verb"
msgid "Follow"
msgstr "Follow"
#: src\templates\layout.phtml:65
#: src\templates\sub\profile.phtml:57
msgid "Language"
msgstr "Language"
#: src\templates\sub\profile.phtml:60
#: src\templates\widget\popularserverlanguages.phtml:2
msgid "Filter by language"
msgstr "Filter by language"
#: src\templates\sub\profile.phtml:66
msgid "Location"
msgstr "Location"
#: src\templates\sub\profile.phtml:79
msgid "Search Tag"
msgstr "Search tag"
#: src\templates\sub\profiles.phtml:1
msgid "Account type tabs"
msgstr "Account type tabs"
#: src\templates\sub\profiles.phtml:4
#: src\templates\sub\profiles.phtml:7
msgid "Top %s pagination"
msgstr "Top %s pagination"
#: src\templates\sub\profiles.phtml:13
#: src\templates\sub\profiles.phtml:16
msgid "Bottom %s pagination"
msgstr "Bottom %s pagination"
#: src\templates\statistics.phtml:66
#: src\templates\sub\server.phtml:15
msgid "Stable Version"
msgstr "Stable version"
#: src\templates\statistics.phtml:68
#: src\templates\sub\server.phtml:17
msgid "Develop Version"
msgstr "Develop version"
#: src\templates\sub\server.phtml:19
msgid "Outdated Version"
msgstr "Outdated version"
#: src\templates\sub\server.phtml:69
msgid "Admin"
msgstr "Admin"
#: src\templates\sub\server.phtml:76
msgid "No description provided"
msgstr "No description provided"
#: src\templates\sub\server.phtml:79
msgid "Visit Server"
msgstr "Visit server"
#: src\templates\widget\popularcountries.phtml:2
msgid "Popular Countries"
msgstr "Popular countries"
#: src\templates\widget\popularprofilelanguages.phtml:2
msgid "Popular Languages"
msgstr "Popular languages"
#: src\templates\widget\populartags.phtml:2
msgid "Popular Tags"
msgstr "Popular tags"
#: src\templates\sub\server.phtml:44
#: src\templates\sub\server.phtml:45
msgid "Default Language"
msgstr "Default language"
#: src\templates\sub\server.phtml:49
msgid "Known Users"
msgstr "Known users"
#: src\classes\Views\Widget\AccountTypeTabs.php:48
msgctxt "account-type"
msgid "All"
msgstr "All"
#: src\classes\Views\Widget\AccountTypeTabs.php:56
msgctxt "account-type"
msgid "People (%d)"
msgid_plural "People (%d)"
msgstr[0] "People (%d)"
msgstr[1] "People (%d)"
#: src\templates\layout.phtml:97
msgid "Stats"
msgstr "Stats"
#: src\templates\statistics.phtml:4
msgid "Directory statistics"
msgstr "Directory statistics"
#: src\templates\statistics.phtml:5
msgid "Profiles"
msgstr "Profiles"
#: src\templates\statistics.phtml:6
msgid "This directory knows about <strong>%s distinct potential profile URLs</strong>."
msgstr "This directory knows about <strong>%s distinct potential profile URLs</strong>."
#: src\templates\statistics.phtml:16
#: src\templates\statistics.phtml:46
msgid "Languages"
msgstr "Languages"
#: src\templates\statistics.phtml:17
msgid "Out of <strong>%s</strong> profiles reporting their language there are:"
msgstr "Out of <strong>%s</strong> profiles reporting their language there are:"
#: src\templates\statistics.phtml:27
msgid "Servers"
msgstr "Servers"
#: src\templates\statistics.phtml:28
msgid "This directory knows about <strong>%s distinct potential server URLs</strong>."
msgstr "This directory knows about <strong>%s distinct potential server URLs</strong>."
#: src\templates\statistics.phtml:29
msgid "Out of those, there are <strong>%s domains (%s)</strong> that have been a Friendica server at least once."
msgstr "Out of those, there are <strong>%s domains (%s)</strong> that have been a Friendica server at least once."
#: src\templates\statistics.phtml:33
msgid "Out of those, there are:"
msgstr "Out of those, there are:"
#: src\templates\statistics.phtml:47
msgid "Out of <strong>%s</strong> servers reporting their language there are:"
msgstr "Out of <strong>%s</strong> servers reporting their language there are:"
#: src\templates\statistics.phtml:57
msgid "Versions"
msgstr "Versions"
#: src\templates\statistics.phtml:58
msgid "Out of <strong>%s</strong> servers reporting their version there are:"
msgstr "Out of <strong>%s</strong> servers reporting their version there are:"
#: src\templates\statistics.phtml:7
msgid "Out of those, there are <strong>%s profiles (%s)</strong> that opted in the public directory at least once."
msgstr "Out of those, there are <strong>%s profiles (%s)</strong> that opted in the public directory at least once."
#: src\templates\statistics.phtml:35
msgid "<strong>%s available servers (%s)</strong>"
msgstr "<strong>%s available servers (%s)</strong>"
#: src\templates\layout.phtml:91
msgid "Friendica Directory version %s"
msgstr "Friendica directory version %s"
#: src\templates\statistics.phtml:11
msgid "Out of those, there currently are <strong>%s available profiles (%s)</strong>. <a href=\"%s\">Check them out!</a>"
msgstr "Out of those, there currently are <strong>%s available profiles (%s)</strong>. <a href=\"%s\">Check them out!</a>"
#: src\templates\statistics.phtml:39
msgid "<strong>%s public servers (%s)</strong> currently open for registration. <a href=\"%s\">Check them out!</a>"
msgstr "<strong>%s public servers (%s)</strong> currently open for registration. <a href=\"%s\">Check them out!</a>"
#: src\classes\Views\Widget\AccountTypeTabs.php:57
msgctxt "account-type"
msgid "News (%d)"
msgid_plural "News (%d)"
msgstr[0] ""
msgstr[1] ""
#: src\classes\Views\Widget\AccountTypeTabs.php:58
msgctxt "account-type"
msgid "Organization (%d)"
msgid_plural "Organizations (%d)"
msgstr[0] ""
msgstr[1] ""
#: src\classes\Views\Widget\AccountTypeTabs.php:61
msgctxt "account-type"
msgid "Group (%d)"
msgid_plural "Groups (%d)"
msgstr[0] ""
msgstr[1] ""
#: src\templates\servers.phtml:3
msgid "Filtered by language:"
msgstr ""
#: src\templates\servers.phtml:3
msgid "Clear language filter"
msgstr ""
#: src\templates\sub\server.phtml:40
msgid "Health Score"
msgstr ""
#: src\templates\sub\server.phtml:52
msgid "%s User"
msgid_plural "%s Users"
msgstr[0] ""
msgstr[1] ""
#: src\templates\sub\server.phtml:54
msgid "None"
msgstr ""
#: src\templates\sub\server.phtml:58
#: src\templates\sub\server.phtml:62
msgid "Registration Policy"
msgstr ""
#: src\templates\sub\server.phtml:59
msgid "By Approval"
msgstr ""
#: src\templates\sub\server.phtml:63
msgid "Open"
msgstr ""
#: src\templates\layout.phtml:94
msgid "Source Code on Friendica's Forgejo"
msgstr ""

Binary file not shown.

Some files were not shown because too many files have changed in this diff Show More