Compare commits

...

222 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
Hypolite Petovan 8d702801a5 Release v2.0.5 2018-11-12 21:51:23 -05:00
Hypolite Petovan 7616249610 Add profile URL test on profile poll 2018-11-12 21:50:50 -05:00
Hypolite Petovan 6797eb72cb Add new Network::testURL method
- Remove obsolete intval() call in Network::fetchURL
2018-11-12 21:50:31 -05:00
Hypolite Petovan 93e0153112 Add test for public host in profile poll queue add 2018-11-12 21:49:49 -05:00
Hypolite Petovan 5207196e1a Fix wrong Network::fetchUrl call in Utils\Scrape 2018-11-12 21:49:06 -05:00
Hypolite Petovan dadcf3239f Fix empty admin profile in server poller 2018-11-12 21:48:40 -05:00
Hypolite Petovan a319f9991c Remove WIP 2018-11-12 21:48:13 -05:00
Andy H ad8a2b80e0
Update INSTALL.md
Add Nginx configuration line
2018-11-12 21:36:55 +07:00
160 changed files with 10905 additions and 1707 deletions

View file

@ -4,7 +4,7 @@
1. Fork the repository 1. Fork the repository
2. Create a new branch for each feature or improvement 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 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. 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 cd /path/to
git clone https://github.com/friendica/friendica-directory friendica-directory git clone https://github.com/friendica/friendica-directory friendica-directory
cd friendica-directory cd friendica-directory
composer install composer install --no-dev
``` ```
### Using a stable release archive ### 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/ 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 ## 4. Set up the background task
Friendica Directory relies on a background task running every minute to keep the directory up to date. 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). 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 ## See also
- [Project Concepts](docs/Concepts.md) - [Project Concepts](docs/Concepts.md)
- [Directory Protocol](docs/Protocol.md) - [Directory Protocol](docs/Protocol.md)
- [Translation](docs/Translation.md)
## Special thanks ## Special thanks
- [Beanow](https://github.com/Beanow) for his efforts to spearhead the previous version of the Friendica Directory software. - [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. - [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 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.4 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( (new \Friendica\Directory\Controllers\Cron(
$container->get('atlas'), $container->get('atlas'),
$container->get('\Friendica\Directory\Pollers\Profile'), $container->get(\Friendica\Directory\Pollers\Profile::class),
$container->get('\Friendica\Directory\Pollers\Server'), $container->get(\Friendica\Directory\Pollers\Server::class),
$container->get('\Friendica\Directory\Pollers\Directory'), $container->get(\Friendica\Directory\Pollers\Directory::class),
$container->get('logger') $container->get('logger')
))->execute(); ))->execute();

View file

@ -16,27 +16,33 @@
"ext-curl": "*", "ext-curl": "*",
"ext-gd": "*", "ext-gd": "*",
"ext-json": "*", "ext-json": "*",
"ext-pdo": "*",
"asika/simple-console": "^1.0", "asika/simple-console": "^1.0",
"atlas/pdo": "^1.1", "atlas/pdo": "^1.1",
"byjg/migration": "^2.1", "boronczyk/localization-middleware": "^1.4",
"fxp/composer-asset-plugin": "^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", "masterminds/html5": "^2.3",
"monolog/monolog": "^1.17", "monolog/monolog": "^1.17",
"mrpetovan/net_ping": "^1.0", "mrpetovan/net_ping": "^1.2",
"sarahman/simple-filesystem-cache": "dev-master", "sarahman/simple-filesystem-cache": "^1.0",
"seld/cli-prompt": "^1.0", "seld/cli-prompt": "^1.0",
"slim/slim": "^3.1", "slim/slim": "^3.1",
"slim/php-view": "^2.0", "slim/php-view": "^2.0",
"zendframework/zend-escaper": "^2.6", "fxp/composer-asset-plugin": "^1.4",
"bower-asset/bootstrap.native": "^2.0", "bower-asset/bootstrap.native": "^2.0",
"bower-asset/fontawesome": "^5.5", "bower-asset/fontawesome": "^5.5"
"gofabian/negotiation-middleware": "^0.1.3"
}, },
"require-dev": { "require-dev": {
"bower-asset/bootstrap": "^4.1", "bower-asset/bootstrap": "^4.1",
"phpunit/phpunit": ">=4.8 < 6.0", "phpunit/phpunit": ">=4.8 < 6.0",
"vimeo/psalm": "^2.0", "scssphp/scssphp": "^1.1",
"leafo/scssphp": "^0.7.7" "vimeo/psalm": "^2.0"
}, },
"autoload": { "autoload": {
"psr-4": { "psr-4": {
@ -49,6 +55,9 @@
} }
}, },
"config": { "config": {
"platform": {
"php": "7.3.0"
},
"process-timeout" : 0, "process-timeout" : 0,
"autoloader-suffix": "FriendicaDirectory", "autoloader-suffix": "FriendicaDirectory",
"optimize-autoloader": true, "optimize-autoloader": true,
@ -70,7 +79,8 @@
"/composer.*", "/composer.*",
"!/vendor", "!/vendor",
"public/assets/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" "!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 # Ignore everything in this directory
* *
# Except this file # 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 # 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 ## Search
``` ```
GET /search[/account_type]?q=... GET /search[/account_type]?q=...[&page=...][&limit=...]
Accept: application/json Accept: application/json
``` ```
Parameters: URI Parameter:
- `account_type`: An arbitrary account type string. Currently supported are `all`, `people` and `forum`. Default is `all`. - `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. - `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. 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", "region": "New York",
"country": "USA", "country": "USA",
"profile_url": "https://friendica.mrpetovan.com/profile/hypolite", "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", "photo": "https://friendica.mrpetovan.com/photo/27330388315ae4ed2b03e3c116980490-4.jpg?ts=1541567135",
"tags": "videogame gaming boardgame politics philosophy development programming php", "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. * 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 class Pager
{ {
@ -24,7 +24,7 @@ class Pager
private $baseQueryString = ''; private $baseQueryString = '';
/** /**
* @var \Friendica\Directory\Content\L10n * @var \Gettext\TranslatorInterface
*/ */
private $l10n; private $l10n;
@ -33,11 +33,11 @@ class Pager
* *
* Guesses the page number from the GET parameter 'page'. * 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 \Psr\Http\Message\ServerRequestInterface $request
* @param integer $itemsPerPage An optional number of items per page to override the default value * @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->l10n = $l10n;
$this->setQueryString($request); $this->setQueryString($request);
@ -157,7 +157,7 @@ class Pager
* @param integer $itemCount The number of displayed items on the page * @param integer $itemCount The number of displayed items on the page
* @return array of links * @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); $displayedItemCount = max(0, $itemCount);
@ -165,12 +165,12 @@ class Pager
'class' => 'pager', 'class' => 'pager',
'prev' => [ 'prev' => [
'url' => $this->ensureQueryParameter($this->baseQueryString . '&page=' . ($this->getPage() - 1)), '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' : '') 'class' => 'previous' . ($this->getPage() == 1 ? ' disabled' : '')
], ],
'next' => [ 'next' => [
'url' => $this->ensureQueryParameter($this->baseQueryString . '&page=' . ($this->getPage() + 1)), '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' : '') 'class' => 'next' . ($displayedItemCount <= 0 ? ' disabled' : '')
] ]
]; ];
@ -208,12 +208,12 @@ class Pager
if ($totalItemCount > $this->getItemsPerPage()) { if ($totalItemCount > $this->getItemsPerPage()) {
$data['first'] = [ $data['first'] = [
'url' => $this->ensureQueryParameter($this->baseQueryString . '&page=1'), 'url' => $this->ensureQueryParameter($this->baseQueryString . '&page=1'),
'text' => $this->l10n->t('First'), 'text' => $this->l10n->gettext('First'),
'class' => $this->getPage() == 1 ? 'disabled' : '' 'class' => $this->getPage() == 1 ? 'disabled' : ''
]; ];
$data['prev'] = [ $data['prev'] = [
'url' => $this->ensureQueryParameter($this->baseQueryString . '&page=' . ($this->getPage() - 1)), 'url' => $this->ensureQueryParameter($this->baseQueryString . '&page=' . ($this->getPage() - 1)),
'text' => $this->l10n->t('Previous'), 'text' => $this->l10n->gettext('Previous'),
'class' => $this->getPage() == 1 ? 'disabled' : '' 'class' => $this->getPage() == 1 ? 'disabled' : ''
]; ];
@ -270,12 +270,12 @@ class Pager
$data['next'] = [ $data['next'] = [
'url' => $this->ensureQueryParameter($this->baseQueryString . '&page=' . ($this->getPage() + 1)), 'url' => $this->ensureQueryParameter($this->baseQueryString . '&page=' . ($this->getPage() + 1)),
'text' => $this->l10n->t('Next'), 'text' => $this->l10n->gettext('Next'),
'class' => $this->getPage() == $lastpage ? 'disabled' : '' 'class' => $this->getPage() == $lastpage ? 'disabled' : ''
]; ];
$data['last'] = [ $data['last'] = [
'url' => $this->ensureQueryParameter($this->baseQueryString . '&page=' . $lastpage), 'url' => $this->ensureQueryParameter($this->baseQueryString . '&page=' . $lastpage),
'text' => $this->l10n->t('Last'), 'text' => $this->l10n->gettext('Last'),
'class' => $this->getPage() == $lastpage ? 'disabled' : '' '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 <?php
namespace Friendica\Directory\Controllers\Web; namespace Friendica\Directory\Controllers\Api;
use Slim\Http\Request; use Slim\Http\Request;
use Slim\Http\Response; use Slim\Http\Response;
/** /**
* @author Hypolite Petovan <mrpetovan@gmail.com> * @author Hypolite Petovan <hypolite@mrpetovan.com>
*/ */
class Photo class Photo
{ {
@ -14,12 +14,18 @@ class Photo
* @var \Atlas\Pdo\Connection * @var \Atlas\Pdo\Connection
*/ */
private $atlas; private $atlas;
/**
* @var string
*/
private $defaultProfilePictureSmallPath;
public function __construct( public function __construct(
\Atlas\Pdo\Connection $atlas \Atlas\Pdo\Connection $atlas,
string $defaultProfilePictureSmallPath
) )
{ {
$this->atlas = $atlas; $this->atlas = $atlas;
$this->defaultProfilePictureSmallPath = $defaultProfilePictureSmallPath;
} }
public function render(Request $request, Response $response, array $args): Response public function render(Request $request, Response $response, array $args): Response
@ -30,7 +36,7 @@ class Photo
); );
if (!$data) { if (!$data) {
$data = file_get_contents('public/images/default-profile-sm.jpg'); $data = file_get_contents($this->defaultProfilePictureSmallPath);
} }
//Try and cache our result. //Try and cache our result.

View file

@ -2,43 +2,38 @@
namespace Friendica\Directory\Controllers\Api; namespace Friendica\Directory\Controllers\Api;
use \Friendica\Directory\Content\Pager; use Friendica\Directory\Content\Pager;
use PDO;
use Psr\Http\Message\ResponseInterface; use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\ServerRequestInterface; use Psr\Http\Message\ServerRequestInterface;
/** /**
* @author Hypolite Petovan <mrpetovan@gmail.com> * @author Hypolite Petovan <hypolite@mrpetovan.com>
*/ */
class Search class Search
{ {
/**
* @var \Atlas\Pdo\Connection
*/
private $atlas;
/** /**
* @var \Friendica\Directory\Models\Profile * @var \Friendica\Directory\Models\Profile
*/ */
private $profileModel; private $profileModel;
/** /**
* @var \Friendica\Directory\Content\L10n * @var \Gettext\TranslatorInterface
*/ */
private $l10n; private $l10n;
public function __construct( public function __construct(
\Atlas\Pdo\Connection $atlas,
\Friendica\Directory\Models\Profile $profileModel, \Friendica\Directory\Models\Profile $profileModel,
\Friendica\Directory\Content\L10n $l10n \Gettext\TranslatorInterface $l10n
) )
{ {
$this->atlas = $atlas;
$this->profileModel = $profileModel; $this->profileModel = $profileModel;
$this->l10n = $l10n; $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): \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'); $originalQuery = $query = filter_input(INPUT_GET, 'q');
@ -48,8 +43,10 @@ class Search
$query .= '%'; $query .= '%';
$sql_where = '`' . $field . '` LIKE :query'; $sql_where = '`' . $field . '` LIKE :query';
} else { } 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)"; 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]; $values = ['query' => $query];
@ -61,19 +58,25 @@ AND `account_type` = :account_type';
$values['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); $count = $this->profileModel->getCountForDisplay($sql_where, $values);
$vars = [ $vars = [
'query' => $originalQuery, 'query' => $originalQuery,
'page' => $pager->getPage(), 'field' => $field,
'page' => $pager->getPage(),
'itemsperpage' => $pager->getItemsPerPage(), 'itemsperpage' => $pager->getItemsPerPage(),
'count' => $count, 'count' => $count,
'profiles' => $profiles 'profiles' => $profiles
]; ];
// Render index view
return $response->withJson($vars); return $response->withJson($vars);
} }
} }

View file

@ -6,7 +6,7 @@ use Slim\Http\Request;
use Slim\Http\Response; use Slim\Http\Response;
/** /**
* @author Hypolite Petovan <mrpetovan@gmail.com> * @author Hypolite Petovan <hypolite@mrpetovan.com>
*/ */
class Submit class Submit
{ {
@ -68,7 +68,8 @@ class Submit
['base_url' => $profileUriInfo['server_uri']] ['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'); $this->logger->info('Successfully received profile URL');
} catch (\Exception $ex) { } 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; use Slim\Http\Response;
/** /**
* @author Hypolite Petovan <mrpetovan@gmail.com> * @author Hypolite Petovan <hypolite@mrpetovan.com>
*/ */
class SyncPull class SyncPull
{ {

View file

@ -7,7 +7,7 @@ use Monolog\Logger;
/** /**
* Description of Console * Description of Console
* *
* @author Hypolite Petovan <mrpetovan@gmail.com> * @author Hypolite Petovan <hypolite@mrpetovan.com>
*/ */
class Console extends \Asika\SimpleConsole\Console class Console extends \Asika\SimpleConsole\Console
{ {
@ -23,6 +23,7 @@ class Console extends \Asika\SimpleConsole\Console
protected $routes = [ protected $routes = [
'directory-add' => \Friendica\Directory\Routes\Console\DirectoryAdd::class, 'directory-add' => \Friendica\Directory\Routes\Console\DirectoryAdd::class,
'directory-poll' => \Friendica\Directory\Routes\Console\DirectoryPoll::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-hide' => \Friendica\Directory\Routes\Console\ProfileHide::class,
'profile-poll' => \Friendica\Directory\Routes\Console\ProfilePoll::class, 'profile-poll' => \Friendica\Directory\Routes\Console\ProfilePoll::class,
'server-hide' => \Friendica\Directory\Routes\Console\ServerHide::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, 'install' => \Friendica\Directory\Routes\Console\Install::class,
'updatedb' => \Friendica\Directory\Routes\Console\UpdateDb::class, 'updatedb' => \Friendica\Directory\Routes\Console\UpdateDb::class,
'dbupdate' => \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) public function __construct(\Slim\Container $container, ?array $argv = null)
@ -43,9 +45,7 @@ class Console extends \Asika\SimpleConsole\Console
{ {
$commandList = ''; $commandList = '';
foreach ($this->routes as $command => $class) { foreach ($this->routes as $command => $class) {
$this->out($class); $commandList .= ' ' . $command . "\n";
$commandList .= ' ' . $command . ' ' . $class::description . "\n";
} }
$help = <<<HELP $help = <<<HELP

View file

@ -3,7 +3,7 @@
namespace Friendica\Directory\Controllers\Console; namespace Friendica\Directory\Controllers\Console;
/** /**
* @author Hypolite Petovan <mrpetovan@gmail.com> * @author Hypolite Petovan <hypolite@mrpetovan.com>
*/ */
class DirectoryAdd extends \Asika\SimpleConsole\Console class DirectoryAdd extends \Asika\SimpleConsole\Console
{ {
@ -54,17 +54,17 @@ HELP;
$directory_url = $this->getArgument(0); $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',
['directory_url' => $directory_url] ['directory_url' => $directory_url]
); );
if (!$result) { if (!$affected) {
throw new \RuntimeException('Unable to add repository with URL: ' . $directory_url); $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; return 0;
} }
} }

View file

@ -3,7 +3,7 @@
namespace Friendica\Directory\Controllers\Console; namespace Friendica\Directory\Controllers\Console;
/** /**
* @author Hypolite Petovan <mrpetovan@gmail.com> * @author Hypolite Petovan <hypolite@mrpetovan.com>
*/ */
class DirectoryPoll extends \Asika\SimpleConsole\Console 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; use Seld\CliPrompt\CliPrompt;
/** /**
* @author Hypolite Petovan <mrpetovan@gmail.com> * @author Hypolite Petovan <hypolite@mrpetovan.com>
*/ */
class Install extends \Asika\SimpleConsole\Console class Install extends \Asika\SimpleConsole\Console
{ {
@ -31,9 +31,9 @@ class Install extends \Asika\SimpleConsole\Console
protected function getHelp() protected function getHelp()
{ {
$help = <<<HELP $help = <<<HELP
console install - Install directory console install - Install wizard
Usage Usage
bin/console install <server_url> [-h|--help|-?] [-v] bin/console install [-h|--help|-?] [-v]
Description Description
Install directory 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; use Friendica\Directory\Models\Profile;
/** /**
* @author Hypolite Petovan <mrpetovan@gmail.com> * @author Hypolite Petovan <hypolite@mrpetovan.com>
*/ */
class ProfileHide extends \Asika\SimpleConsole\Console class ProfileHide extends \Asika\SimpleConsole\Console
{ {

View file

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

View file

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

View file

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

View file

@ -5,7 +5,7 @@ namespace Friendica\Directory\Controllers\Console;
use Monolog\Logger; use Monolog\Logger;
/** /**
* @author Hypolite Petovan <mrpetovan@gmail.com> * @author Hypolite Petovan <hypolite@mrpetovan.com>
*/ */
class UpdateDb extends \Asika\SimpleConsole\Console class UpdateDb extends \Asika\SimpleConsole\Console
{ {
@ -37,12 +37,14 @@ class UpdateDb extends \Asika\SimpleConsole\Console
$help = <<<HELP $help = <<<HELP
console updatedb - Update database schema console updatedb - Update database schema
Usage Usage
bin/console updatedb <server_url> [-h|--help|-?] [-v] bin/console updatedb [<version>] [-h|--help|-?] [-v]
Description Description
Update database schema Update database schema
Options 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 -h|--help|-? Show help information
-v Show more debug information. -v Show more debug information.
HELP; HELP;
@ -56,16 +58,38 @@ HELP;
return 0; return 0;
} }
if (count($this->args) > 1) { if (count($this->args) > 2) {
throw new \Asika\SimpleConsole\CommandArgsException('Too many arguments'); 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; return 0;
} }
} }

View file

@ -3,7 +3,7 @@
namespace Friendica\Directory\Controllers; namespace Friendica\Directory\Controllers;
/** /**
* @author Hypolite Petovan <mrpetovan@gmail.com> * @author Hypolite Petovan <hypolite@mrpetovan.com>
*/ */
class Cron class Cron
{ {
@ -93,6 +93,8 @@ class Cron
ORDER BY ISNULL(`last_polled`) DESC' ORDER BY ISNULL(`last_polled`) DESC'
); );
$this->logger->notice('Directories polling queue size: ' . count($directories));
foreach ($directories as $directory) { foreach ($directories as $directory) {
if ($time_limit && microtime(true) - $this->startTime > $time_limit) { if ($time_limit && microtime(true) - $this->startTime > $time_limit) {
break; break;
@ -133,6 +135,8 @@ class Cron
ORDER BY ISNULL(`last_polled`) DESC, `request_count` DESC' ORDER BY ISNULL(`last_polled`) DESC, `request_count` DESC'
); );
$this->logger->notice('Servers polling queue size: ' . count($servers));
foreach ($servers as $server_queue_item) { foreach ($servers as $server_queue_item) {
if ($time_limit && microtime(true) - $this->startTime > $time_limit) { if ($time_limit && microtime(true) - $this->startTime > $time_limit) {
break; break;
@ -188,6 +192,8 @@ class Cron
ORDER BY RAND() ASC' ORDER BY RAND() ASC'
); );
$this->logger->notice('Profiles polling queue size: ' . count($profiles));
foreach ($profiles as $profile) { foreach ($profiles as $profile) {
if ($time_limit && microtime(true) - $this->startTime > $time_limit) { if ($time_limit && microtime(true) - $this->startTime > $time_limit) {
break; 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; namespace Friendica\Directory\Controllers\Web;
use \Friendica\Directory\Content\Pager; use Friendica\Directory\Content\Pager;
use \Friendica\Directory\Views\Widget\PopularCountries; use Friendica\Directory\Views\Widget\PopularCountries;
use \Friendica\Directory\Views\Widget\PopularTags; use Friendica\Directory\Views\Widget\PopularProfileLanguages;
use PDO; use Friendica\Directory\Views\Widget\PopularTags;
use Slim\Http\Request; use Slim\Http\Request;
use Slim\Http\Response; 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 * @var \Atlas\Pdo\Connection
*/ */
private $atlas; private $atlas;
/**
* @var \Friendica\Directory\Models\Server
*/
private $serverModel;
/** /**
* @var \Friendica\Directory\Models\Profile * @var \Friendica\Directory\Models\Profile
*/ */
@ -31,56 +35,65 @@ class Directory
*/ */
private $renderer; private $renderer;
/** /**
* @var \Friendica\Directory\Content\L10n * @var \Gettext\TranslatorInterface
*/ */
private $l10n; private $l10n;
public function __construct( public function __construct(
\Atlas\Pdo\Connection $atlas, \Atlas\Pdo\Connection $atlas,
\Friendica\Directory\Models\Server $serverModel,
\Friendica\Directory\Models\Profile $profileModel, \Friendica\Directory\Models\Profile $profileModel,
\Friendica\Directory\Views\Widget\AccountTypeTabs $accountTypeTabs, \Friendica\Directory\Views\Widget\AccountTypeTabs $accountTypeTabs,
\Friendica\Directory\Views\PhpRenderer $renderer, \Friendica\Directory\Views\PhpRenderer $renderer,
\Friendica\Directory\Content\L10n $l10n \Gettext\TranslatorInterface $l10n
) )
{ {
$this->atlas = $atlas; $this->atlas = $atlas;
$this->serverModel = $serverModel;
$this->profileModel = $profileModel; $this->profileModel = $profileModel;
$this->accountTypeTabs = $accountTypeTabs; $this->accountTypeTabs = $accountTypeTabs;
$this->renderer = $renderer; $this->renderer = $renderer;
$this->l10n = $l10n; $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); $popularTags = new PopularTags($this->atlas, $this->renderer);
$popularCountries = new PopularCountries($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); $pager = new Pager($this->l10n, $request, 20);
$condition = ''; $sql_where = '';
$values = []; $values = [];
if (!empty($args['account_type'])) { if (!empty($args['account_type'])) {
$condition = '`account_type` = :account_type'; $sql_where = '`account_type` = :account_type';
$values = ['account_type' => $args['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 = [ $vars = [
'title' => $this->l10n->t('People'), 'title' => $this->l10n->gettext('People'),
'profiles' => $profiles, 'profiles' => $profiles,
'pager_full' => $pager->renderFull($count), 'pager_full' => $pager->renderFull($count),
'pager_minimal' => $pager->renderMinimal($count), 'pager_minimal' => $pager->renderMinimal($count),
'accountTypeTabs' => $this->accountTypeTabs->render('directory', $args['account_type'] ?? ''), 'accountTypeTabs' => $this->accountTypeTabs->render('directory', $args['account_type'] ?? ''),
'popularTags' => $popularTags->render(), 'popularTags' => $popularTags->render(),
'popularCountries' => $popularCountries->render(), 'popularCountries' => $popularCountries->render(),
'popularLanguages' => $popularLanguages->render(),
]; ];
$content = $this->renderer->fetch('directory.phtml', $vars); $content = $this->renderer->fetch('directory.phtml', $vars);
// Render index view return ['content' => $content];
return $this->renderer->render($response, 'layout.phtml', ['baseUrl' => $request->getUri()->getBaseUrl(), '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; namespace Friendica\Directory\Controllers\Web;
use \Friendica\Directory\Content\Pager; 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 * @var \Atlas\Pdo\Connection
*/ */
private $atlas; private $atlas;
/**
* @var \Friendica\Directory\Models\Server
*/
private $serverModel;
/** /**
* @var \Friendica\Directory\Models\Profile * @var \Friendica\Directory\Models\Profile
*/ */
@ -29,66 +30,94 @@ class Search
*/ */
private $accountTypeTabs; private $accountTypeTabs;
/** /**
* @var \Friendica\Directory\Content\L10n * @var \Gettext\TranslatorInterface
*/ */
private $l10n; private $l10n;
public function __construct( public function __construct(
\Atlas\Pdo\Connection $atlas, \Atlas\Pdo\Connection $atlas,
\Friendica\Directory\Models\Server $serverModel,
\Friendica\Directory\Models\Profile $profileModel, \Friendica\Directory\Models\Profile $profileModel,
\Friendica\Directory\Views\Widget\AccountTypeTabs $accountTypeTabs, \Friendica\Directory\Views\Widget\AccountTypeTabs $accountTypeTabs,
\Friendica\Directory\Views\PhpRenderer $renderer, \Friendica\Directory\Views\PhpRenderer $renderer,
\Friendica\Directory\Content\L10n $l10n \Gettext\TranslatorInterface $l10n
) )
{ {
$this->atlas = $atlas; $this->atlas = $atlas;
$this->serverModel = $serverModel;
$this->profileModel = $profileModel; $this->profileModel = $profileModel;
$this->accountTypeTabs = $accountTypeTabs; $this->accountTypeTabs = $accountTypeTabs;
$this->renderer = $renderer; $this->renderer = $renderer;
$this->l10n = $l10n; $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) { if ($field) {
$query .= '%'; $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 { } 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)"; 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]; $values = ['query' => $query];
$account_type = $args['account_type'] ?? ''; $account_type = $args['account_type'] ?? '';
$accountTypeTabs = $this->accountTypeTabs->render('search', $account_type, $sql_where, $values, ['q' => $originalQuery, 'field' => $field]);
if ($account_type) { if ($account_type) {
$sql_where .= ' $sql_where .= '
AND `account_type` = :account_type'; AND `account_type` = :account_type';
$values['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); $count = $this->profileModel->getCountForDisplay($sql_where, $values);
$vars = [ $vars = [
'query' => $originalQuery, 'query' => $originalQuery,
'count' => $count, 'field' => $field,
'accountTypeTabs' => $this->accountTypeTabs->render('search', $account_type, ['q' => $originalQuery]), 'fieldName' => $fieldName,
'profiles' => $profiles, 'count' => $count,
'accountTypeTabs' => $accountTypeTabs,
'profiles' => $profiles,
'pager_full' => $pager->renderFull($count), 'pager_full' => $pager->renderFull($count),
'pager_minimal' => $pager->renderMinimal($count), 'pager_minimal' => $pager->renderMinimal($count),
]; ];
$content = $this->renderer->fetch('search.phtml', $vars); $content = $this->renderer->fetch('search.phtml', $vars);
// Render index view return ['content' => $content, 'noNavSearch' => true];
return $this->renderer->render($response, 'layout.phtml', ['baseUrl' => $request->getUri()->getBaseUrl(), 'content' => $content, 'noNavSearch' => true]);
} }
} }

View file

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

View file

@ -3,10 +3,17 @@
namespace Friendica\Directory\Models; namespace Friendica\Directory\Models;
/** /**
* @author Hypolite Petovan <mrpetovan@gmail.com> * @author Hypolite Petovan <hypolite@mrpetovan.com>
*/ */
class Profile extends \Friendica\Directory\Model 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 public function deleteById(int $profile_id): bool
{ {
$this->atlas->perform('DELETE FROM `photo` WHERE `profile_id` = :profile_id', $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) { if ($condition) {
$condition = 'AND ' . $condition; $condition = 'AND ' . $condition;
@ -85,19 +92,25 @@ class Profile extends \Friendica\Directory\Model
'limit' => [$limit, \PDO::PARAM_INT] 'limit' => [$limit, \PDO::PARAM_INT]
]); ]);
$stmt = 'SELECT p.`id`, p.`name`, p.`username`, p.`addr`, p.`account_type`, p.`pdesc`, $stmt = 'SELECT p.`id`, p.`name`, p.`username`, p.`addr`, p.`account_type`, p.`language`,
p.`locality`, p.`region`, p.`country`, p.`profile_url`, p.`dfrn_request`, p.`photo`, p.`pdesc`, p.`locality`, p.`region`, p.`country`, p.`profile_url`,
p.`tags`, p.`last_activity` p.`photo`, p.`tags`, p.`last_activity`, s.`version`
FROM `profile` p FROM `profile` p
JOIN `server` s ON s.`id` = p.`server_id` AND s.`available` AND NOT s.`hidden` JOIN `server` s ON s.`id` = p.`server_id` AND s.`available` AND NOT s.`hidden`
WHERE p.`available` WHERE p.`available`
AND NOT p.`hidden` AND NOT p.`hidden`
' . $condition . ' ' . $condition . '
GROUP BY p.`id` GROUP BY p.`id`, `filled_fields`, `last_activity`, `updated`
ORDER BY `filled_fields` DESC, `last_activity` DESC, `updated` DESC ORDER BY `filled_fields` DESC, `last_activity` DESC, `updated` DESC
LIMIT :start, :limit'; LIMIT :start, :limit';
$profiles = $this->atlas->fetchAll($stmt, $values); $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; return $profiles;
} }

View file

@ -2,24 +2,44 @@
namespace Friendica\Directory\Models; 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 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); $url = trim($profile_url);
if (!$url) { if (!$url) {
return false; return self::EMPTY_URL;
} }
$this->atlas->perform( $host = parse_url($url, PHP_URL_HOST);
if (!$host) {
return self::MISSING_HOST;
}
if (!Network::isPublicHost($host)) {
return self::PRIVATE_HOST;
}
$affected = $this->atlas->fetchAffected(
'INSERT IGNORE INTO `profile_poll_queue` SET `profile_url` = :profile_url', 'INSERT IGNORE INTO `profile_poll_queue` SET `profile_url` = :profile_url',
['profile_url' => $url] ['profile_url' => $url]
); );
return true; return ($affected == 1 ? 0 : self::ALREADY_EXISTS);
} }
} }

View file

@ -3,7 +3,7 @@
namespace Friendica\Directory\Models; namespace Friendica\Directory\Models;
/** /**
* @author Hypolite Petovan <mrpetovan@gmail.com> * @author Hypolite Petovan <hypolite@mrpetovan.com>
*/ */
class Server extends \Friendica\Directory\Model class Server extends \Friendica\Directory\Model
{ {
@ -33,10 +33,28 @@ class Server extends \Friendica\Directory\Model
SET `server_id` = :server_id, SET `server_id` = :server_id,
`alias` = :alias, `alias` = :alias,
`timestamp` = NOW() `timestamp` = NOW()
ON DUPLICATE KEY UPDATE `timestamp` = NOW()', ON DUPLICATE KEY UPDATE
`server_id` = :server_id,
`timestamp` = NOW()',
[ [
'server_id' => $server_id, 'server_id' => $server_id,
'alias' => strtolower($server_alias) '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; namespace Friendica\Directory\Pollers;
use Friendica\Directory\Utils\Network;
/** /**
* @author Hypolite Petovan <mrpetovan@gmail.com> * @author Hypolite Petovan <hypolite@mrpetovan.com>
*/ */
class Directory class Directory
{ {
/** /**
* @var \Atlas\Pdo\Connection * @var \GuzzleHttp\ClientInterface
*/ */
private $atlas; private $http;
/** /**
* @var \Friendica\Directory\Models\ProfilePollQueue * @var \Friendica\Directory\Models\ProfilePollQueue
*/ */
@ -28,12 +30,12 @@ class Directory
]; ];
public function __construct( public function __construct(
\Atlas\Pdo\Connection $atlas, \GuzzleHttp\ClientInterface $http,
\Friendica\Directory\Models\ProfilePollQueue $profilePollQueueModel, \Friendica\Directory\Models\ProfilePollQueue $profilePollQueueModel,
\Psr\Log\LoggerInterface $logger, \Psr\Log\LoggerInterface $logger,
array $settings) array $settings)
{ {
$this->atlas = $atlas; $this->http = $http;
$this->profilePollQueueModel = $profilePollQueueModel; $this->profilePollQueueModel = $profilePollQueueModel;
$this->logger = $logger; $this->logger = $logger;
$this->settings = array_merge($this->settings, $settings); $this->settings = array_merge($this->settings, $settings);
@ -60,7 +62,8 @@ class Directory
$profiles = $this->getPullResult($directory_url, $last_polled); $profiles = $this->getPullResult($directory_url, $last_polled);
foreach ($profiles as $profile_url) { 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'); $this->logger->info('Successfully pulled ' . count($profiles) . ' profiles');
@ -79,35 +82,7 @@ class Directory
$path = '/sync/pull/since/' . $last_polled; $path = '/sync/pull/since/' . $last_polled;
} }
//Prepare the CURL call. $pull_data = $this->http->get($directory_url . $path, ['timeout' => max($this->settings['probe_timeout'], 1)])->getBody()->getContents();
$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);
$data = json_decode($pull_data, true); $data = json_decode($pull_data, true);

View file

@ -2,11 +2,18 @@
namespace Friendica\Directory\Pollers; namespace Friendica\Directory\Pollers;
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 class Profile
{ {
const PROFILE_MISSING_CONFIRM = 2;
const PROFILE_MISSING_NOTIFY = 4;
const PROFILE_MISSING_POLL = 8;
/** /**
* @var \Atlas\Pdo\Connection * @var \Atlas\Pdo\Connection
@ -14,12 +21,17 @@ class Profile
private $atlas; private $atlas;
/** /**
* @var \Friendica\Directory\Models\Server * @var \GuzzleHttp\ClientInterface
*/
private $http;
/**
* @var Models\Server
*/ */
private $serverModel; private $serverModel;
/** /**
* @var \Friendica\Directory\Models\Profile * @var Models\Profile
*/ */
private $profileModel; private $profileModel;
@ -38,13 +50,15 @@ class Profile
public function __construct( public function __construct(
\Atlas\Pdo\Connection $atlas, \Atlas\Pdo\Connection $atlas,
\Friendica\Directory\Models\Server $serverModel, \GuzzleHttp\ClientInterface $http,
\Friendica\Directory\Models\Profile $profileModel, Models\Server $serverModel,
Models\Profile $profileModel,
\Psr\Log\LoggerInterface $logger, \Psr\Log\LoggerInterface $logger,
array $settings array $settings
) )
{ {
$this->atlas = $atlas; $this->atlas = $atlas;
$this->http = $http;
$this->serverModel = $serverModel; $this->serverModel = $serverModel;
$this->profileModel = $profileModel; $this->profileModel = $profileModel;
$this->logger = $logger; $this->logger = $logger;
@ -69,12 +83,12 @@ class Profile
return false; 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); $this->logger->warning('Private/reserved IP in polled profile URL: ' . $profile_uri);
return false; return false;
} }
$profileUriInfo = \Friendica\Directory\Models\Profile::extractInfoFromProfileUrl($profile_uri); $profileUriInfo = Models\Profile::extractInfoFromProfileUrl($profile_uri);
if (!$profileUriInfo) { if (!$profileUriInfo) {
$this->logger->warning('Profile URI invalid'); $this->logger->warning('Profile URI invalid');
return false; return false;
@ -117,23 +131,42 @@ class Profile
); );
} }
//Skip the profile scrape? $available = false;
$noscrape = $server['noscrape_url'];
$params = []; $params = [];
if ($noscrape) {
//Skip the profile scrape?
if ($server['noscrape_url']) {
$this->logger->debug('Calling ' . $server['noscrape_url'] . '/' . $username); $this->logger->debug('Calling ' . $server['noscrape_url'] . '/' . $username);
$params = \Friendica\Directory\Utils\Scrape::retrieveNoScrapeData($server['noscrape_url'] . '/' . $username); try {
$noscrape = !!$params; //If the result was false, do a scrape after all. $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.
} }
if (!$noscrape) { if (!$available) {
$this->logger->notice('Parsing profile page ' . $profile_uri); $this->logger->info('Parsing profile page ' . $profile_uri);
$params = \Friendica\Directory\Utils\Scrape::retrieveProfileData($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'];
$available = !empty($params['fn']);
} }
// Empty result is due to an offline site. // 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. //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']) { if ($profile_id && $server['health_score'] < $this->settings['remove_profile_health_threshold']) {
$this->profileModel->deleteById($profile_id); $this->profileModel->deleteById($profile_id);
@ -163,14 +196,37 @@ class Profile
} }
// This is most likely a problem with the site configuration. Ignore. // 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]); $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; return false;
} }
$account_type = 'People'; switch ($params['account-type'] ?? Models\Profile::ACCOUNT_TYPE_PERSON) {
if (!empty($params['comm'])) { case Models\Profile::ACCOUNT_TYPE_ORGANISATION: $account_type = 'Organization'; break;
$account_type = 'Forum'; 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 = []; $tags = [];
@ -188,61 +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'])); $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 $this->atlas->perform('INSERT INTO `profile` SET
`id` = :profile_id, `server_id` = :server_id,
`server_id` = :server_id, `username` = :username,
`username` = :username, `name` = :name,
`name` = :name, `pdesc` = :pdesc,
`pdesc` = :pdesc, `locality` = :locality,
`locality` = :locality, `region` = :region,
`region` = :region, `country` = :country,
`country` = :country, `profile_url` = :profile_url,
`profile_url` = :profile_url, `photo` = :photo,
`dfrn_request` = :dfrn_request, `tags` = :tags,
`tags` = :tags, `addr` = :addr,
`addr` = :addr, `account_type` = :account_type,
`account_type` = :account_type, `language` = :language,
`filled_fields` = :filled_fields, `filled_fields` = :filled_fields,
`last_activity` = :last_activity, `last_activity` = :last_activity,
`available` = 1, `available` = :available,
`created` = NOW(), `created` = NOW(),
`updated` = NOW() `updated` = NOW()
ON DUPLICATE KEY UPDATE ON DUPLICATE KEY UPDATE
`server_id` = :server_id, `server_id` = :server_id,
`username` = :username, `username` = :username,
`name` = :name, `name` = :name,
`pdesc` = :pdesc, `pdesc` = :pdesc,
`locality` = :locality, `locality` = :locality,
`region` = :region, `region` = :region,
`country` = :country, `country` = :country,
`profile_url` = :profile_url, `profile_url` = :profile_url,
`dfrn_request` = :dfrn_request, `photo` = :photo,
`photo` = :photo, `tags` = :tags,
`tags` = :tags, `addr` = :addr,
`addr` = :addr, `account_type` = :account_type,
`account_type` = :account_type, `language` = :language,
`filled_fields` = :filled_fields, `filled_fields` = :filled_fields,
`last_activity` = :last_activity, `last_activity` = :last_activity,
`available` = 1, `available` = :available,
`updated` = NOW()', `updated` = NOW()',
[ $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,
'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,
]
); );
if (!$profile_id) { if (!$profile_id) {
@ -266,23 +329,27 @@ class Profile
$status = false; $status = false;
if ($profile_id) { if ($profile_id) {
$img_str = \Friendica\Directory\Utils\Network::fetchURL($params['photo'], true); try {
$img = new \Friendica\Directory\Utils\Photo($img_str); $img_str = $this->http->get($params['photo'])->getBody()->getContents();
if ($img->getImage()) { $img = new Utils\Photo($img_str);
$img->scaleImageSquare(80); if ($img->getImage()) {
$img->scaleImageSquare(80);
$this->atlas->perform('INSERT INTO `photo` SET $this->atlas->perform('INSERT INTO `photo` SET
`profile_id` = :profile_id, `profile_id` = :profile_id,
`data` = :data `data` = :data
ON DUPLICATE KEY UPDATE ON DUPLICATE KEY UPDATE
`data` = :data', `data` = :data',
[ [
'profile_id' => $profile_id, 'profile_id' => $profile_id,
'data' => $img->imageString() '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); $submit_end = microtime(true);
@ -312,23 +379,21 @@ class Profile
return true; return true;
} }
/**
* @param array $params
* @return int
*/
private static function validateParams(array $params): int private static function validateParams(array $params): int
{ {
$errors = 0; $errors = 0;
if (empty($params['key'])) {
$errors++;
}
if (empty($params['dfrn-request'])) {
$errors++;
}
if (empty($params['dfrn-confirm'])) { if (empty($params['dfrn-confirm'])) {
$errors++; $errors &= self::PROFILE_MISSING_CONFIRM;
} }
if (empty($params['dfrn-notify'])) { if (empty($params['dfrn-notify'])) {
$errors++; $errors &= self::PROFILE_MISSING_NOTIFY;
} }
if (empty($params['dfrn-poll'])) { if (empty($params['dfrn-poll'])) {
$errors++; $errors &= self::PROFILE_MISSING_POLL;
} }
return $errors; return $errors;

View file

@ -2,8 +2,12 @@
namespace Friendica\Directory\Pollers; 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 class Server
{ {
@ -11,6 +15,10 @@ class Server
* @var \Atlas\Pdo\Connection * @var \Atlas\Pdo\Connection
*/ */
private $atlas; private $atlas;
/**
* @var \GuzzleHttp\ClientInterface
*/
private $http;
/** /**
* @var \Friendica\Directory\Models\ProfilePollQueue * @var \Friendica\Directory\Models\ProfilePollQueue
*/ */
@ -38,6 +46,7 @@ class Server
public function __construct( public function __construct(
\Atlas\Pdo\Connection $atlas, \Atlas\Pdo\Connection $atlas,
\GuzzleHttp\ClientInterface $http,
\Friendica\Directory\Models\ProfilePollQueue $profilePollQueueModel, \Friendica\Directory\Models\ProfilePollQueue $profilePollQueueModel,
\Friendica\Directory\Models\Server $serverModel, \Friendica\Directory\Models\Server $serverModel,
\Psr\SimpleCache\CacheInterface $simplecache, \Psr\SimpleCache\CacheInterface $simplecache,
@ -45,6 +54,7 @@ class Server
array $settings) array $settings)
{ {
$this->atlas = $atlas; $this->atlas = $atlas;
$this->http = $http;
$this->profilePollQueueModel = $profilePollQueueModel; $this->profilePollQueueModel = $profilePollQueueModel;
$this->serverModel = $serverModel; $this->serverModel = $serverModel;
$this->simplecache = $simplecache; $this->simplecache = $simplecache;
@ -85,7 +95,9 @@ class Server
$probe_result = $this->getProbeResult($polled_url); $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) { if ($parse_success) {
$base_url = $probe_result['data']['url']; $base_url = $probe_result['data']['url'];
@ -142,12 +154,17 @@ class Server
$addons = $probe_result['data']['plugins']; $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( $this->atlas->perform(
'UPDATE `server` 'UPDATE `server`
SET `available` = 1, SET `available` = 1,
`last_seen` = NOW(), `last_seen` = NOW(),
`base_url` = :base_url, `base_url` = :base_url,
`name` = :name, `name` = :name,
`language` = :language,
`version` = :version, `version` = :version,
`addons` = :addons, `addons` = :addons,
`reg_policy` = :reg_policy, `reg_policy` = :reg_policy,
@ -155,31 +172,49 @@ class Server
`admin_name` = :admin_name, `admin_name` = :admin_name,
`admin_profile` = :admin_profile, `admin_profile` = :admin_profile,
`noscrape_url` = :noscrape_url, `noscrape_url` = :noscrape_url,
`subscribe_url` = :subscribe_url,
`ssl_state` = :ssl_state `ssl_state` = :ssl_state
WHERE `id` = :server_id', WHERE `id` = :server_id',
[ [
'server_id' => $server['id'], 'server_id' => $server['id'],
'base_url' => strtolower($probe_result['data']['url']), 'base_url' => strtolower($probe_result['data']['url']),
'name' => $probe_result['data']['site_name'], 'name' => substr($probe_result['data']['site_name'], 0, 255),
'version' => $probe_result['data']['version'], 'language' => $probe_result['data']['language'] ?? null,
'addons' => implode(',', $addons), 'version' => $probe_result['data']['version'],
'reg_policy' => $probe_result['data']['register_policy'], 'addons' => implode(',', $addons),
'info' => $probe_result['data']['info'], 'reg_policy' => $probe_result['data']['register_policy'],
'admin_name' => $probe_result['data']['admin']['name'], 'info' => $probe_result['data']['info'],
'admin_profile' => $probe_result['data']['admin']['profile'], 'admin_name' => $probe_result['data']['admin']['name'] ?? null,
'noscrape_url' => $probe_result['data']['no_scrape_url'] ?? null, 'admin_profile' => $probe_result['data']['admin']['profile'] ?? null,
'ssl_state' => $probe_result['ssl_state'] '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 //Add the admin to the directory
$this->profilePollQueueModel->add($probe_result['data']['admin']['profile']); if (!empty($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) { if ($server) {
//Get the new health. //Get the new health.
$version = $parse_success ? $probe_result['data']['version'] : ''; $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( $this->atlas->perform(
'UPDATE `server` SET `health_score` = :health_score WHERE `id` = :server_id', 'UPDATE `server` SET `health_score` = :health_score WHERE `id` = :server_id',
@ -219,65 +254,50 @@ class Server
private function getProbeResult(string $base_url): array private function getProbeResult(string $base_url): array
{ {
//Prepare the CURL call. $curl_info = null;
$handle = curl_init();
$options = array( $options = [
//Timeouts 'timeout' => max($this->settings['probe_timeout'], 1),
CURLOPT_TIMEOUT => max($this->settings['probe_timeout'], 1), //Minimum of 1 second timeout. 'on_stats' => function (TransferStats $transferStats) use (&$curl_info) {
CURLOPT_CONNECTTIMEOUT => 1, $curl_info = $transferStats->getHandlerStats();
//Redirecting }
CURLOPT_FOLLOWLOCATION => true, ];
CURLOPT_MAXREDIRS => 8,
//SSL $sslcert_issues = false;
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);
//Probe the site.
$probe_start = microtime(true); $probe_start = microtime(true);
$probe_data = curl_exec($handle); $probe_data = null;
$probe_end = microtime(true); 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. //When it's the certificate that doesn't work, we probe again without strict SSL.
$curl_statuscode = curl_errno($handle); $options['verify'] = false;
$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. $probe_start = microtime(true);
if ($sslcert_issues) { try {
//Probe again, without strict SSL. $probe_data = $this->http->get($base_url . '/friendica/json', $options)->getBody()->getContents();
$options[CURLOPT_SSL_VERIFYPEER] = false; } catch(RequestException $e) {
// Collects 404, 500 errors
//Replace the handle. $this->logger->info('SSL-non-verified URL probe failed with error code: ' . $e->getCode());
curl_close($handle); }
$handle = curl_init(); } else {
curl_setopt_array($handle, $options); $this->logger->info('SSL-verified URL probe failed with error code: ' . $e->getCode());
}
//Probe. } catch (\InvalidArgumentException $e) {
$probe_start = microtime(true); $this->logger->error('Invalid argument provided to HTTP client', ['base_url' => $base_url, 'exception' => $e]);
$probe_data = curl_exec($handle); return ['data' => false, 'time' => 0, 'curl_info' => [], 'ssl_state' => null];
$probe_end = microtime(true);
//Store new status.
$curl_statuscode = curl_errno($handle);
} }
//Gather more meta. $probe_end = microtime(true);
$time = round(($probe_end - $probe_start) * 1000);
$curl_info = curl_getinfo($handle);
//Done with CURL now. $time = round(($probe_end - $probe_start) * 1000);
curl_close($handle);
try { try {
$data = json_decode($probe_data, true); $data = json_decode($probe_data, true);
@ -297,7 +317,15 @@ class Server
return ['data' => $data, 'time' => $time, 'curl_info' => $curl_info, 'ssl_state' => $ssl_state]; 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. //Probe failed, costs you 30 points.
if (!$probe_success) { if (!$probe_success) {
@ -346,7 +374,7 @@ class Server
} else { } else {
$stable_version = $this->simplecache->get('stable_version'); $stable_version = $this->simplecache->get('stable_version');
if (!$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); $this->simplecache->set('stable_version', $stable_version);
} }
@ -356,14 +384,108 @@ class Server
$this->simplecache->set('dev_version', $dev_version); $this->simplecache->set('dev_version', $dev_version);
} }
if ($version == $dev_version) { $rc_version = str_replace('-dev', '-rc', $dev_version);
$max_health = 95; //Develop can be unstable
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) { } elseif ($version !== $stable_version) {
$delta = min($delta, 0) - 10; // Losing score as time passes if node isn't updated $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); 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; namespace Friendica\Directory\Routes\Console;
/** /**
* @author Hypolite Petovan <mrpetovan@gmail.com> * @author Hypolite Petovan <hypolite@mrpetovan.com>
*/ */
abstract class BaseRoute abstract class BaseRoute
{ {

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

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; namespace Friendica\Directory\Routes\Http;
/** /**
* @author Hypolite Petovan <mrpetovan@gmail.com> * @author Hypolite Petovan <hypolite@mrpetovan.com>
*/ */
class Submit extends BaseRoute class Submit extends BaseRoute
{ {
@ -11,7 +11,7 @@ class Submit extends BaseRoute
{ {
return (new \Friendica\Directory\Controllers\Api\Submit( return (new \Friendica\Directory\Controllers\Api\Submit(
$this->container->atlas, $this->container->atlas,
$this->container->get('\Friendica\Directory\Models\ProfilePollQueue'), $this->container->get(\Friendica\Directory\Models\ProfilePollQueue::class),
$this->container->logger $this->container->logger
))->execute($request, $response); ))->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; namespace Friendica\Directory\Routes\Http;
/** /**
* @author Hypolite Petovan <mrpetovan@gmail.com> * @author Hypolite Petovan <hypolite@mrpetovan.com>
*/ */
class SyncPull extends BaseRoute 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,31 +11,10 @@ namespace Friendica\Directory\Utils;
/** /**
* Description of Network * Description of Network
* *
* @author Hypolite Petovan <mrpetovan@gmail.com> * @author Hypolite Petovan <hypolite@mrpetovan.com>
*/ */
class Network 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(intval($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;
}
/** /**
* Check if a hostname is public and non-reserved * Check if a hostname is public and non-reserved
* *
@ -57,6 +36,13 @@ class Network
return false; 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)) { 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; return false;
} }

View file

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

View file

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

View file

@ -2,10 +2,27 @@
namespace Friendica\Directory\Views; namespace Friendica\Directory\Views;
use Slim\Router;
/** /**
* Zend-Escaper wrapper for Slim PHP Renderer * 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 class PhpRenderer extends \Slim\Views\PhpRenderer
{ {
@ -14,13 +31,18 @@ class PhpRenderer extends \Slim\Views\PhpRenderer
*/ */
private $escaper; private $escaper;
/** /**
* @var \Friendica\Directory\Content\L10n * @var \Gettext\TranslatorInterface
*/ */
private $l10n; private $l10n;
/**
* @var Router
*/
private $router;
public function __construct( public function __construct(
\Zend\Escaper\Escaper $escaper, \Zend\Escaper\Escaper $escaper,
\Friendica\Directory\Content\L10n $l10n, \Gettext\TranslatorInterface $l10n,
Router $router,
string $templatePath = "", string $templatePath = "",
array $attributes = array() array $attributes = array()
) )
@ -29,6 +51,7 @@ class PhpRenderer extends \Slim\Views\PhpRenderer
$this->escaper = $escaper; $this->escaper = $escaper;
$this->l10n = $l10n; $this->l10n = $l10n;
$this->router = $router;
} }
public function e(string $value): string public function e(string $value): string
@ -36,28 +59,157 @@ class PhpRenderer extends \Slim\Views\PhpRenderer
return $this->escapeHtml($value); 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; namespace Friendica\Directory\Views\Widget;
/** /**
* @author Hypolite Petovan <mrpetovan@gmail.com> * @author Hypolite Petovan <hypolite@mrpetovan.com>
*/ */
class AccountTypeTabs class AccountTypeTabs
{ {
@ -27,28 +27,46 @@ class AccountTypeTabs
$this->router = $router; $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 = ' if ($condition) {
SELECT DISTINCT(`account_type`) AS `account_type` $condition = 'AND ' . $condition;
FROM `profile` }
WHERE `available`
AND NOT `hidden`'; $stmt = 'SELECT `account_type`, COUNT(*) AS `count`
$account_types = $this->connection->fetchAll($stmt); 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 = [ $tabs = [
[ [
'title' => 'All', 'title' => $this->renderer->p__('account-type', 'All'),
'link' => $this->router->pathFor($route_name, [], $queryParams), 'link' => $this->router->pathFor($route_name, [], $queryParams),
'active' => empty($current_type) 'active' => empty($current_type)
] ]
]; ];
foreach ($account_types as $account_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[] = [ $tabs[] = [
'title' => $account_type['account_type'], 'title' => $title,
'link' => $this->router->pathFor($route_name, ['account_type' => strtolower($account_type['account_type'])], $queryParams), '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; namespace Friendica\Directory\Views\Widget;
/** /**
* @author Hypolite Petovan <mrpetovan@gmail.com> * @author Hypolite Petovan <hypolite@mrpetovan.com>
*/ */
class PopularCountries class PopularCountries
{ {
@ -30,12 +30,11 @@ FROM `profile`
WHERE `country` != "" WHERE `country` != ""
AND `available` AND `available`
GROUP BY `country` GROUP BY `country`
ORDER BY COUNT(`country`) DESC ORDER BY `total` DESC
LIMIT 20'; LIMIT 10';
$countries = $this->connection->fetchAll($stmt); $countries = $this->connection->fetchAll($stmt);
$vars = [ $vars = [
'title' => 'Popular Countries',
'countries' => $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; namespace Friendica\Directory\Views\Widget;
/** /**
* @author Hypolite Petovan <mrpetovan@gmail.com> * @author Hypolite Petovan <hypolite@mrpetovan.com>
*/ */
class PopularTags class PopularTags
{ {
@ -24,11 +24,14 @@ class PopularTags
public function render(): string 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); $tags = $this->connection->fetchAll($stmt);
$vars = [ $vars = [
'title' => 'Popular Tags',
'tags' => $tags 'tags' => $tags
]; ];

View file

@ -1,13 +1,13 @@
<?php <?php
use Interop\Container\ContainerInterface; use Psr\Container\ContainerInterface;
// DIC configuration // DIC configuration
// l10n // l10n
$container['l10n'] = function (ContainerInterface $c): Friendica\Directory\Content\L10n { $container['l10n'] = function (ContainerInterface $c): Gettext\TranslatorInterface {
$settings = $c->get('settings')['l10n']; $translator = new Gettext\Translator();
return new Friendica\Directory\Content\L10n($settings['language'] ?: 'en', $settings['lang_path'] ?: ''); return $translator;
}; };
// simple cache // simple cache
@ -25,7 +25,12 @@ $container['escaper'] = function (ContainerInterface $c): Zend\Escaper\Escaper {
// view renderer // view renderer
$container['renderer'] = function (ContainerInterface $c): Friendica\Directory\Views\PhpRenderer { $container['renderer'] = function (ContainerInterface $c): Friendica\Directory\Views\PhpRenderer {
$settings = $c->get('settings')['renderer']; $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 // monolog
@ -84,54 +89,68 @@ $container['migration'] = function (ContainerInterface $c): ByJG\DbMigration\Mig
return $migration; 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 // 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')); 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')); 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')); 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']; $settings = $c->get('settings')['poller'];
return new Friendica\Directory\Pollers\Directory( return new Friendica\Directory\Pollers\Directory(
$c->get('atlas'), $c->get('http'),
$c->get('\Friendica\Directory\Models\ProfilePollQueue'), $c->get(\Friendica\Directory\Models\ProfilePollQueue::class),
$c->get('logger'), $c->get('logger'),
$settings ?: [] $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']; $settings = $c->get('settings')['poller'];
return new Friendica\Directory\Pollers\Profile( return new Friendica\Directory\Pollers\Profile(
$c->get('atlas'), $c->get('atlas'),
$c->get('\Friendica\Directory\Models\Server'), $c->get('http'),
$c->get('\Friendica\Directory\Models\Profile'), $c->get(\Friendica\Directory\Models\Server::class),
$c->get(\Friendica\Directory\Models\Profile::class),
$c->get('logger'), $c->get('logger'),
$settings ?: [] $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']; $settings = $c->get('settings')['poller'];
return new Friendica\Directory\Pollers\Server( return new Friendica\Directory\Pollers\Server(
$c->get('atlas'), $c->get('atlas'),
$c->get('\Friendica\Directory\Models\ProfilePollQueue'), $c->get('http'),
$c->get('\Friendica\Directory\Models\Server'), $c->get(\Friendica\Directory\Models\ProfilePollQueue::class),
$c->get(\Friendica\Directory\Models\Server::class),
$c->get('simplecache'), $c->get('simplecache'),
$c->get('logger'), $c->get('logger'),
$settings ?: [] $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( return new Friendica\Directory\Views\Widget\AccountTypeTabs(
$c->get('atlas'), $c->get('atlas'),
$c->get('renderer'), $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