Compare commits

...

145 Commits

Author SHA1 Message Date
Matthew Wild
488dc9a3f3 Merge pull request #202 from snikket-im/ka/oauthtweaks
OAuth tweaks
2025-06-05 17:55:56 +01:00
Kim Alvefur
1a65ba6150 Include a software id in oauth client registration
This is supposed to be a unique and persistent identifier for the
software itself, regardless of version or deployment instance.

Generated from the domain name in the comment using uuid_generate_sha1()
2025-06-05 17:52:04 +01:00
Kim Alvefur
9474238dee Declare as a web application in oauth client registration
It is, even if the password grant isn't restricted to that, but if ever
the authorization code flow is implemented, it'll be correct.
2025-06-05 17:52:01 +01:00
Kim Alvefur
60e663316b Declare that oauth client credentials are using POST method
Not enforced by mod_http_oauth2, but could be in the future
2025-06-05 17:50:52 +01:00
Kim Alvefur
770d05c72c Declare use of no response types, since password grant uses none
Needless restriction removed in
https://hg.prosody.im/prosody-modules/rev/ef81c67e1ae7
2025-06-05 17:50:52 +01:00
Kim Alvefur
ea75d8e832 Include requested scopes in oauth client registration
This can be used on the oauth server side to enforce that no additional
scopes are added.
2025-06-05 17:50:52 +01:00
Kim Alvefur
145dda8c19 Include web portal version in oauth client registration
This could be shown in client listings and audit logs, and checked to
ensure old versions stop being used. Not the most relevant for the web
portal as it is closely tied together with the server, but could help
answer questions about where old grants come from.
2025-06-05 17:50:52 +01:00
Matthew Wild
149a79cb2c Merge pull request #203 from snikket-im/make-lint
prosodyclient: Fixes to satisfy mypy
2025-06-05 17:48:23 +01:00
Matthew Wild
69f77020b8 prosodyclient: Fixes to satisfy mypy 2025-06-05 17:46:05 +01:00
Matthew Wild
5ac481a4b4 prosodyclient: Switch to black formatting and remove lint issues 2025-06-05 17:33:22 +01:00
Matthew Wild
56470eec01 Github: Use flake8 target 2025-06-05 17:32:34 +01:00
Matthew Wild
9b4903b230 Makefile: Add lint, format (black), flake8 and mypy targets 2025-06-05 17:29:44 +01:00
Matthew Wild
74c3946609 Bump log level of oauth errors 2025-06-02 11:36:08 +01:00
Matthew Wild
feabed6565 Register as an OAuth client and authenticate token requests
mod_http_oauth2 in prosody-modules was updated to require client
authentication for the password grant, which previously did not need
client authentication.

This means that the first request we make to Prosody will now register as a
client in order to obtain client_id and client_secret.

There is no real security gain from this approach (unlike other grant types,
the password grant does not do redirects which could be intercepted). In the
future, however, some security could be gained by having Prosody restrict
the ability to use the password grant to privileged OAuth clients. This would
prevent third-party OAuth clients from using the password grant which is not
suitable for that purpose.
2025-06-02 11:36:08 +01:00
Jonas Schäfer
af13a3cc47 Merge pull request #197 from snikket-im/z/play-badge-l10n
Serve localized Google Play badges locally
2025-04-13 08:57:16 +02:00
Kim Alvefur
466e3e79b7 Serve localized Google Play badges locally
Fixes #196

Badges downloaded from <https://play.google.com/intl/en_us/badges/> with
a bit of automation to get one per supported language.
2025-04-12 20:23:10 +02:00
Weblate
3f1ce7565b Update translation files
Updated by "Update PO files to match POT (msgmerge)" hook in Weblate.

Translation: Snikket/Web Portal
Translate-URL: http://i18n.sotecware.net/projects/snikket/web-portal/
2024-08-11 19:21:49 +00:00
Matthew Wild
265ca4db8f Merge pull request #191 from snikket-im/fix/issue190
Ignore that users do not have access to metrics
2024-08-11 20:21:32 +01:00
Kim Alvefur
5015c4aa43 fixup: refresh translation code references 2024-08-11 16:36:53 +02:00
Kim Alvefur
465720c5b1 fixup: please flake8 2024-08-11 16:34:06 +02:00
Kim Alvefur
2a8e7ae72b fixup: please mypy 2024-08-11 16:25:17 +02:00
Andrey
449e345ee5 Ignore that users do not have access to metrics
Fixes #190

The templates appear to handle this being False, so that seems the path
of least resistance.
2024-08-11 15:56:40 +02:00
Andrey
51798ecc43 Translated using Weblate (Russian)
Currently translated at 100.0% (373 of 373 strings)

Translation: Snikket/Web Portal
Translate-URL: http://i18n.sotecware.net/projects/snikket/web-portal/ru/
2024-07-18 17:08:03 +00:00
uira
1e15ef5fce Translated using Weblate (Indonesian)
Currently translated at 100.0% (373 of 373 strings)

Translation: Snikket/Web Portal
Translate-URL: http://i18n.sotecware.net/projects/snikket/web-portal/id/
2024-07-18 17:07:58 +00:00
misiek
0f41aa24d8 Translated using Weblate (Polish)
Currently translated at 100.0% (373 of 373 strings)

Translation: Snikket/Web Portal
Translate-URL: http://i18n.sotecware.net/projects/snikket/web-portal/pl/
2024-05-07 17:04:52 +00:00
J👀
15516cdaa5 Translated using Weblate (Spanish)
Currently translated at 100.0% (373 of 373 strings)

Translation: Snikket/Web Portal
Translate-URL: http://i18n.sotecware.net/projects/snikket/web-portal/es/
2024-05-06 05:04:46 +00:00
Rosebud
948e415dbd Translated using Weblate (Chinese (Simplified))
Currently translated at 100.0% (373 of 373 strings)

Translation: Snikket/Web Portal
Translate-URL: http://i18n.sotecware.net/projects/snikket/web-portal/zh_Hans/
2024-05-02 21:15:30 +00:00
Kim Alvefur
a3fcf7d1d4 Translated using Weblate (Swedish)
Currently translated at 100.0% (373 of 373 strings)

Translation: Snikket/Web Portal
Translate-URL: http://i18n.sotecware.net/projects/snikket/web-portal/sv/
2024-05-02 21:15:28 +00:00
Federico
65de73f1fe Translated using Weblate (Italian)
Currently translated at 100.0% (373 of 373 strings)

Translation: Snikket/Web Portal
Translate-URL: http://i18n.sotecware.net/projects/snikket/web-portal/it/
2024-05-02 21:15:28 +00:00
Roberto Resoli
989fe7b5b6 Translated using Weblate (Italian)
Currently translated at 100.0% (373 of 373 strings)

Translation: Snikket/Web Portal
Translate-URL: http://i18n.sotecware.net/projects/snikket/web-portal/it/
2024-05-02 21:15:27 +00:00
Andrey
4bc929e1ce Translated using Weblate (Russian)
Currently translated at 100.0% (373 of 373 strings)

Translation: Snikket/Web Portal
Translate-URL: http://i18n.sotecware.net/projects/snikket/web-portal/ru/
2024-05-02 21:15:27 +00:00
BetaRays
5817b24c48 Translated using Weblate (French)
Currently translated at 100.0% (373 of 373 strings)

Translation: Snikket/Web Portal
Translate-URL: http://i18n.sotecware.net/projects/snikket/web-portal/fr/
2024-05-02 21:15:19 +00:00
Weblate
550526efc9 Update translation files
Updated by "Update PO files to match POT (msgmerge)" hook in Weblate.

Translation: Snikket/Web Portal
Translate-URL: http://i18n.sotecware.net/projects/snikket/web-portal/
2024-04-30 09:56:03 +00:00
Kim Alvefur
2a2e36ade2 Translated using Weblate (Swedish)
Currently translated at 100.0% (370 of 370 strings)

Translation: Snikket/Web Portal
Translate-URL: http://i18n.sotecware.net/projects/snikket/web-portal/sv/
2024-04-30 09:56:01 +00:00
Andrey
22f7d6f36a Translated using Weblate (Russian)
Currently translated at 100.0% (370 of 370 strings)

Translation: Snikket/Web Portal
Translate-URL: http://i18n.sotecware.net/projects/snikket/web-portal/ru/
2024-04-30 09:56:01 +00:00
Matthew Wild
2d42099017 Merge pull request #188 from snikket-im/invitation-ui
Invitation admin UI improvements
2024-04-30 10:55:44 +01:00
Matthew Wild
2ff47c486a Update translation strings 2024-04-30 10:52:52 +01:00
Matthew Wild
338ee0b278 Add 'share' button for browsers supporting Web Share API 2024-04-30 10:48:51 +01:00
Matthew Wild
64c6548a48 Support for optional text notes on invitations 2024-04-29 18:39:06 +01:00
Matthew Wild
8c824149cc Fixes for invitation display
- Reorder columns, from generic to specific
- Fix empty tooltip on invitation types caused by incorrect macro usage
2024-04-29 18:19:07 +01:00
Matthew Wild
607863cfc4 Remove duplicate template macro 2024-04-29 18:00:22 +01:00
Matthew Wild
13c5d44544 Merge pull request #187 from snikket-im/cookie-samesite-attribute
Explicitly set cookie SameSite attribute to Lax
2024-04-29 11:22:21 +01:00
Matthew Wild
6407eb90db Explicitly set cookie SameSite attribute to Lax
With 'Secure' set, it may default to 'None', which we don't need or want.

'Strict' is not suitable for session cookies - the user would see the login
screen when navigating from another site (e.g. hosting dashboard) and we
already have CSRF protection on forms.
2024-04-29 11:18:55 +01:00
Matthew Wild
a8c6b1a70c Merge pull request #186 from snikket-im/cookie-secure-attribute
Add 'secure' attribute to session cookies
2024-04-29 11:09:44 +01:00
Matthew Wild
67c94bb045 Add 'secure' attribute to session cookies 2024-04-29 11:08:30 +01:00
Weblate
f4c1173a34 Update translation files
Updated by "Update PO files to match POT (msgmerge)" hook in Weblate.

Translation: Snikket/Web Portal
Translate-URL: http://i18n.sotecware.net/projects/snikket/web-portal/
2024-04-28 08:40:16 +00:00
Jonas Schäfer
e39b0082b1 Merge pull request #185 from Zash/translate-welcome
Translate welcome message
2024-04-28 10:39:57 +02:00
Kim Alvefur
9eb187a951 Make welcome message translatable 2024-04-27 14:22:39 +02:00
Kim Alvefur
b928e74a74 make extract_translations 2024-04-27 14:21:32 +02:00
Andrey
75c0f504d0 Translated using Weblate (Russian)
Currently translated at 100.0% (368 of 368 strings)

Translation: Snikket/Web Portal
Translate-URL: http://i18n.sotecware.net/projects/snikket/web-portal/ru/
2024-04-23 17:14:06 +00:00
Matthew Wild
7c0310a141 Merge pull request #184 from Zash/really-fix-default-invite-role
Specify a default role in invite form
2024-04-19 14:50:01 +01:00
Kim Alvefur
5e2e645787 Specify a default role in invite form
Actually in the invite form this time
2024-04-19 15:48:40 +02:00
Weblate
9b31894e85 Update translation files
Updated by "Update PO files to match POT (msgmerge)" hook in Weblate.

Translation: Snikket/Web Portal
Translate-URL: http://i18n.sotecware.net/projects/snikket/web-portal/
2024-04-19 13:38:57 +00:00
Kim Alvefur
a4472e1a44 Translated using Weblate (Swedish)
Currently translated at 100.0% (362 of 362 strings)

Translation: Snikket/Web Portal
Translate-URL: http://i18n.sotecware.net/projects/snikket/web-portal/sv/
2024-04-19 13:38:56 +00:00
Matthew Wild
b99cae84de Update translations 2024-04-19 14:38:45 +01:00
Matthew Wild
1cac19e4c9 Merge pull request #183 from Zash/default-invite-role
Specify a default role in invite form
2024-04-19 14:37:22 +01:00
Kim Alvefur
d4883765b2 Specify a default role in invite form
The role creation appears to fail without an error, only refreshing the
page unless a role is selected.
2024-04-19 15:30:20 +02:00
Weblate
041f26274b Update translation files
Updated by "Update PO files to match POT (msgmerge)" hook in Weblate.

Translation: Snikket/Web Portal
Translate-URL: http://i18n.sotecware.net/projects/snikket/web-portal/
2024-04-17 08:41:42 +00:00
Matthew Wild
82db30ffd9 Merge pull request #182 from snikket-im/invitation-improvements
Allow selecting a role when creating an invitation
2024-04-17 09:41:31 +01:00
Matthew Wild
b8684329b4 Fix syntax error in template 2024-04-16 21:30:19 +01:00
Matthew Wild
7e26b5f994 Update translations 2024-04-16 21:22:07 +01:00
Matthew Wild
4bdcb46a8a Allow selecting a role when creating an invitation
Includes some reorganization and prettification of the creation form.
2024-04-16 21:16:06 +01:00
Matthew Wild
ed6f413c18 Don't fail if active user metrics are unavailable 2024-04-16 20:42:48 +01:00
Weblate
f63549ee87 Update translation files
Updated by "Update PO files to match POT (msgmerge)" hook in Weblate.

Translation: Snikket/Web Portal
Translate-URL: http://i18n.sotecware.net/projects/snikket/web-portal/
2024-04-16 14:51:58 +00:00
Matthew Wild
bd71ab1449 Update translations 2024-04-16 15:51:47 +01:00
Matthew Wild
220bf9994b Show invitation hint when user is alone on their instance 2024-04-16 15:09:16 +01:00
Matthew Wild
33d28e5890 Show active user counts in instance metrics 2024-04-16 15:09:16 +01:00
Matthew Wild
f0f0fa15c9 Small clarifications to the invitation type UI 2024-04-16 15:09:16 +01:00
Matthew Wild
30a9a6816f prosodyclient: Skip adding metadata of broken avatars 2024-04-16 15:09:16 +01:00
Sergio Moreno López
970b8fa7f1 Translated using Weblate (Spanish)
Currently translated at 39.6% (143 of 361 strings)

Translation: Snikket/Web Portal
Translate-URL: http://i18n.sotecware.net/projects/snikket/web-portal/es/
2024-04-14 21:02:20 +00:00
Sergio Moreno López
629d725ff5 Translated using Weblate (Spanish)
Currently translated at 36.0% (130 of 361 strings)

Translation: Snikket/Web Portal
Translate-URL: http://i18n.sotecware.net/projects/snikket/web-portal/es/
2024-04-05 21:27:23 +00:00
Sergio Moreno López
6998e66b22 Translated using Weblate (Spanish)
Currently translated at 29.9% (108 of 361 strings)

Translation: Snikket/Web Portal
Translate-URL: http://i18n.sotecware.net/projects/snikket/web-portal/es/
2024-03-31 22:27:22 +00:00
Matthew Wild
c668c4c56a Added translation using Weblate (Spanish) 2024-03-29 10:13:30 +00:00
Matthew Wild
a13fbd87a6 Merge pull request #181 from snikket-im/feature/handle-invalid-avatars-gracefully
Handle broken/incorrect avatar metadata gracefully
2024-03-11 15:30:24 +00:00
Jonas Schäfer
7ffcd76cea Handle broken/incorrect avatar metadata gracefully
Fixes #180.
2024-03-10 10:49:29 +01:00
Federico
bda0f52320 Translated using Weblate (Italian)
Currently translated at 100.0% (361 of 361 strings)

Translation: Snikket/Web Portal
Translate-URL: http://i18n.sotecware.net/projects/snikket/web-portal/it/
2024-01-20 23:01:47 +00:00
Matthew Wild
5efc2a671e Merge pull request #177 from snikket-im/feature/fix-requirements
Fix python requirements to actually work
2024-01-16 16:12:37 +00:00
Jonas Schäfer
1578654816 Fix python requirements to actually work
Didn't do deep research here, just fitted it against the current Docker
image release.
2024-01-16 16:52:47 +01:00
Matthew Wild
e8ab33e12f Merge pull request #175 from snikket-im/fix/html-tag-typo
templates: Fix typo in closing form tag
2024-01-11 15:29:26 +00:00
Matthew Wild
712b0dc502 templates: Fix typo in closing form tag 2024-01-11 15:20:17 +00:00
Matthew Wild
e56c0f9029 github: Strip Generated-By in both places 2024-01-09 15:07:21 +00:00
Weblate
794b48a50b Update translation files
Updated by "Update PO files to match POT (msgmerge)" hook in Weblate.

Translation: Snikket/Web Portal
Translate-URL: http://i18n.sotecware.net/projects/snikket/web-portal/
2024-01-09 14:52:58 +00:00
Matthew Wild
393b30cf5c Remove collapsible flag from column to fix mobile display 2024-01-09 14:52:43 +00:00
Matthew Wild
97198a1da4 Ignore generator name/version when checking if translations are up to date 2024-01-09 14:51:34 +00:00
Weblate
3ba1195fbe Update translation files
Updated by "Update PO files to match POT (msgmerge)" hook in Weblate.

Translation: Snikket/Web Portal
Translate-URL: http://i18n.sotecware.net/projects/snikket/web-portal/
2024-01-09 10:09:07 +00:00
Matthew Wild
121f3eddb5 Merge pull request #174 from snikket-im/fix/password-validation
Fix password validation
2024-01-09 10:08:58 +00:00
Matthew Wild
38ad81b0e2 Validate passwords as early as possible
Prosody now enforces some password policies, including a minimum length of 10
characters. If this fails, we currently show a rather unfriendly error to the
user. By adding this validation, the user should get nicer feedback and never
see that error.

There is a known issue that we don't currently validate all the policies that
Prosody does - for example, Prosody won't accept a password that contains the
username.

Ultimately we should fix the error handling anyway.
2024-01-08 22:58:46 +00:00
Matthew Wild
ec94c64dbc Handle errors on password change 2024-01-08 22:50:36 +00:00
Andrey
28a9a33aa1 Translated using Weblate (Russian)
Currently translated at 100.0% (361 of 361 strings)

Translation: Snikket/Web Portal
Translate-URL: http://i18n.sotecware.net/projects/snikket/web-portal/ru/
2024-01-07 16:01:03 +00:00
Kim Alvefur
97eeb85032 Merge pull request #173 from Zash/fix-scope-restricted
Fix login for restricted users
2024-01-07 12:40:26 +01:00
Kim Alvefur
ceef9f024c Fix login for restricted users
Previously this led to an OAuth grant with an empty scope, which could
not be used for anything.
2024-01-07 00:03:38 +01:00
Andrey
40c8b9cc36 Translated using Weblate (Russian)
Currently translated at 100.0% (361 of 361 strings)

Translation: Snikket/Web Portal
Translate-URL: http://i18n.sotecware.net/projects/snikket/web-portal/ru/
2024-01-06 05:01:03 +00:00
Matthew Wild
95a8ac1387 Enable Russian and Ukranian languages by default 2024-01-04 14:11:27 +00:00
Roberto Resoli
4c6e26e66b Translated using Weblate (Italian)
Currently translated at 100.0% (361 of 361 strings)

Translation: Snikket/Web Portal
Translate-URL: http://i18n.sotecware.net/projects/snikket/web-portal/it/
2024-01-04 10:17:34 +00:00
Andrey
ad2b351a99 Translated using Weblate (Russian)
Currently translated at 100.0% (361 of 361 strings)

Translation: Snikket/Web Portal
Translate-URL: http://i18n.sotecware.net/projects/snikket/web-portal/ru/
2024-01-04 10:17:33 +00:00
pep
3bda1f9863 Translated using Weblate (French)
Currently translated at 100.0% (361 of 361 strings)

Translation: Snikket/Web Portal
Translate-URL: http://i18n.sotecware.net/projects/snikket/web-portal/fr/
2024-01-04 10:17:33 +00:00
Matthew Wild
f46d95db66 Merge pull request #172 from Zash/form-translation-fix
Fix incomplete translation of Announcement form and Limited user role
2024-01-04 09:05:33 +00:00
Kim Alvefur
ddfdd2fd55 Remove stray HTML closing tag
Where is the <form> ???
2024-01-04 09:54:45 +01:00
Kim Alvefur
17d586e384 Fix translation of "Limited"
Strings in Forms must use lazy_gettext aka _l
2024-01-04 09:54:43 +01:00
Kim Alvefur
dbec07d149 Fix translation of AnnouncementForm
Form translation things must use lazy_gettext aka _l
2024-01-04 09:53:03 +01:00
Weblate
ebf142b505 Update translation files
Updated by "Update PO files to match POT (msgmerge)" hook in Weblate.

Translation: Snikket/Web Portal
Translate-URL: http://i18n.sotecware.net/projects/snikket/web-portal/
2024-01-03 16:12:45 +00:00
Matthew Wild
0539d0ab88 translation: Update copyright year to satisfy lint check 2024-01-03 16:12:28 +00:00
Jonas Schäfer
2736bff76b Translated using Weblate (German)
Currently translated at 100.0% (361 of 361 strings)

Translation: Snikket/Web Portal
Translate-URL: http://i18n.sotecware.net/projects/snikket/web-portal/de/
2024-01-03 16:08:48 +00:00
Dmytro Vozniuk
192601f387 Translated using Weblate (Ukrainian)
Currently translated at 100.0% (361 of 361 strings)

Translation: Snikket/Web Portal
Translate-URL: http://i18n.sotecware.net/projects/snikket/web-portal/uk/
2023-12-25 16:00:58 +00:00
Dmytro Vozniuk
bc9cfeabab Translated using Weblate (Russian)
Currently translated at 100.0% (361 of 361 strings)

Translation: Snikket/Web Portal
Translate-URL: http://i18n.sotecware.net/projects/snikket/web-portal/ru/
2023-12-25 16:00:53 +00:00
Dmytro Vozniuk
b770086071 Translated using Weblate (Ukrainian)
Currently translated at 100.0% (361 of 361 strings)

Translation: Snikket/Web Portal
Translate-URL: http://i18n.sotecware.net/projects/snikket/web-portal/uk/
2023-12-24 04:00:53 +00:00
Dmytro Vozniuk
b2c1fdd23b Translated using Weblate (Russian)
Currently translated at 100.0% (361 of 361 strings)

Translation: Snikket/Web Portal
Translate-URL: http://i18n.sotecware.net/projects/snikket/web-portal/ru/
2023-12-24 04:00:52 +00:00
Dmytro Vozniuk
906978556e Translated using Weblate (Ukrainian)
Currently translated at 79.2% (286 of 361 strings)

Translation: Snikket/Web Portal
Translate-URL: http://i18n.sotecware.net/projects/snikket/web-portal/uk/
2023-12-19 13:00:48 +00:00
riccio
274c8e4658 Translated using Weblate (Italian)
Currently translated at 100.0% (361 of 361 strings)

Translation: Snikket/Web Portal
Translate-URL: http://i18n.sotecware.net/projects/snikket/web-portal/it/
2023-12-19 13:00:48 +00:00
Dmytro Vozniuk
257a44dac2 Translated using Weblate (Ukrainian)
Currently translated at 29.6% (107 of 361 strings)

Translation: Snikket/Web Portal
Translate-URL: http://i18n.sotecware.net/projects/snikket/web-portal/uk/
2023-12-17 22:49:22 +00:00
Roberto Resoli
f393a3980b Translated using Weblate (Italian)
Currently translated at 100.0% (361 of 361 strings)

Translation: Snikket/Web Portal
Translate-URL: http://i18n.sotecware.net/projects/snikket/web-portal/it/
2023-12-17 22:49:22 +00:00
riccio
badff7eed8 Translated using Weblate (Italian)
Currently translated at 100.0% (361 of 361 strings)

Translation: Snikket/Web Portal
Translate-URL: http://i18n.sotecware.net/projects/snikket/web-portal/it/
2023-12-17 22:49:22 +00:00
Andrey
384e07c2a9 Translated using Weblate (Russian)
Currently translated at 100.0% (361 of 361 strings)

Translation: Snikket/Web Portal
Translate-URL: http://i18n.sotecware.net/projects/snikket/web-portal/ru/
2023-12-17 22:49:21 +00:00
pep
89724a9712 Translated using Weblate (French)
Currently translated at 100.0% (361 of 361 strings)

Translation: Snikket/Web Portal
Translate-URL: http://i18n.sotecware.net/projects/snikket/web-portal/fr/
2023-12-17 22:49:21 +00:00
misiek
94f4325f40 Translated using Weblate (Polish)
Currently translated at 100.0% (361 of 361 strings)

Translation: Snikket/Web Portal
Translate-URL: http://i18n.sotecware.net/projects/snikket/web-portal/pl/
2023-12-17 22:49:21 +00:00
Matthew Wild
af1285b650 Translated using Weblate (Russian)
Currently translated at 99.4% (359 of 361 strings)

Translation: Snikket/Web Portal
Translate-URL: http://i18n.sotecware.net/projects/snikket/web-portal/ru/
2023-12-16 14:41:14 +00:00
Andrey
52eba53d8e Translated using Weblate (Russian)
Currently translated at 100.0% (361 of 361 strings)

Translation: Snikket/Web Portal
Translate-URL: http://i18n.sotecware.net/projects/snikket/web-portal/ru/
2023-12-16 08:38:56 +00:00
pep
94f240687a Translated using Weblate (French)
Currently translated at 99.7% (360 of 361 strings)

Translation: Snikket/Web Portal
Translate-URL: http://i18n.sotecware.net/projects/snikket/web-portal/fr/
2023-12-16 08:38:56 +00:00
misiek
1b2bdfa881 Translated using Weblate (Polish)
Currently translated at 93.0% (336 of 361 strings)

Translation: Snikket/Web Portal
Translate-URL: http://i18n.sotecware.net/projects/snikket/web-portal/pl/
2023-12-16 08:38:56 +00:00
Kim Alvefur
271f450c86 Translated using Weblate (Swedish)
Currently translated at 100.0% (361 of 361 strings)

Translation: Snikket/Web Portal
Translate-URL: http://i18n.sotecware.net/projects/snikket/web-portal/sv/
2023-12-16 00:19:29 +00:00
Deleted User
6186e8b635 Translated using Weblate (French)
Currently translated at 99.7% (360 of 361 strings)

Translation: Snikket/Web Portal
Translate-URL: http://i18n.sotecware.net/projects/snikket/web-portal/fr/
2023-12-16 00:19:29 +00:00
pep
dfc6c392c3 Translated using Weblate (French)
Currently translated at 99.7% (360 of 361 strings)

Translation: Snikket/Web Portal
Translate-URL: http://i18n.sotecware.net/projects/snikket/web-portal/fr/
2023-12-16 00:19:28 +00:00
uira
0ec9a2ae02 Translated using Weblate (Indonesian)
Currently translated at 100.0% (361 of 361 strings)

Translation: Snikket/Web Portal
Translate-URL: http://i18n.sotecware.net/projects/snikket/web-portal/id/
2023-12-15 16:11:00 +00:00
Weblate
09fcf64818 Update translation files
Updated by "Update PO files to match POT (msgmerge)" hook in Weblate.

Translation: Snikket/Web Portal
Translate-URL: http://i18n.sotecware.net/projects/snikket/web-portal/
2023-12-15 15:05:45 +00:00
Kim Alvefur
c25db5c3ae Translated using Weblate (Swedish)
Currently translated at 100.0% (361 of 361 strings)

Translation: Snikket/Web Portal
Translate-URL: http://i18n.sotecware.net/projects/snikket/web-portal/sv/
2023-12-15 15:05:44 +00:00
Deleted User
c85fff7581 Translated using Weblate (French)
Currently translated at 99.7% (360 of 361 strings)

Translation: Snikket/Web Portal
Translate-URL: http://i18n.sotecware.net/projects/snikket/web-portal/fr/
2023-12-15 15:05:44 +00:00
Link Mauve
039f4b8210 Translated using Weblate (French)
Currently translated at 99.7% (360 of 361 strings)

Translation: Snikket/Web Portal
Translate-URL: http://i18n.sotecware.net/projects/snikket/web-portal/fr/
2023-12-15 15:05:44 +00:00
uira
7be7ee67c2 Translated using Weblate (Indonesian)
Currently translated at 93.0% (336 of 361 strings)

Translation: Snikket/Web Portal
Translate-URL: http://i18n.sotecware.net/projects/snikket/web-portal/id/
2023-12-15 15:05:44 +00:00
Jonas Schäfer
6f5fc14dbc Translated using Weblate (German)
Currently translated at 100.0% (361 of 361 strings)

Translation: Snikket/Web Portal
Translate-URL: http://i18n.sotecware.net/projects/snikket/web-portal/de/
2023-12-15 15:05:44 +00:00
Matthew Wild
65edd3a52b Merge pull request #169 from snikket-im/feature/tweak-user-profile-display
Tweak user profile display
2023-12-15 15:05:36 +00:00
Matthew Wild
ab7149403a Update translations pot (no actual string changes) 2023-12-15 15:04:46 +00:00
Matthew Wild
5b2f3db867 Update user listing in 'edit circle' view to use new user profile layout 2023-12-15 15:00:13 +00:00
Matthew Wild
e12941eab0 Improve user name/JID display
Show the display name more prominently (if one is set), as it is more
"friendly" name. The JID is now displayed instead of just the username, as
this makes it more clear what is being displayed.

The inspiration for this change was the observation that many users on my own
server have the same display name and username, causing a repetitive list
like:

  Jane
  jane

  Robert
  robert

  Sally
  sally

  Steven
  steven

In addition, some accounts do not have a display name set, so it was not
obvious why some people had their name rendered once, and some twice, and why
the capitalization differences.
2023-12-15 14:41:17 +00:00
Andriy Utkin
eda3f4826c Translated using Weblate (Ukrainian)
Currently translated at 6.0% (22 of 361 strings)

Translation: Snikket/Web Portal
Translate-URL: http://i18n.sotecware.net/projects/snikket/web-portal/uk/
2023-12-15 11:56:32 +00:00
pep
61161eb472 Translated using Weblate (French)
Currently translated at 96.9% (350 of 361 strings)

Translation: Snikket/Web Portal
Translate-URL: http://i18n.sotecware.net/projects/snikket/web-portal/fr/
2023-12-15 11:56:31 +00:00
Matthew Wild
325826c19b Merge pull request #168 from snikket-im/fix/rc2-runtime-errors
Fix a couple of runtime errors
2023-12-14 15:35:04 +00:00
Matthew Wild
587839f852 Added translation using Weblate (Ukrainian) 2023-12-14 13:11:43 +00:00
Matthew Wild
7411f4a9e1 prosodyclient: Use empty name if none provided by the server
In parallel, I have updated the server to use the group name for groups with
no name (the MUCs with no name are typically the default auto-created group
MUC).
2023-12-14 12:43:59 +00:00
Matthew Wild
d63ae4768a infra: Fix string to use correct translation function name 2023-12-14 12:43:52 +00:00
Weblate
92a8da724f Update translation files
Updated by "Update PO files to match POT (msgmerge)" hook in Weblate.

Translation: Snikket/Web Portal
Translate-URL: http://i18n.sotecware.net/projects/snikket/web-portal/
2023-12-12 18:25:32 +00:00
Matthew Wild
ea3a081b6c Merge pull request #167 from snikket-im/fix/remove-useless-qr-js
Remove broken/needless JS from certain pages
2023-12-12 18:25:20 +00:00
Matthew Wild
0647ba2601 Remove broken/needless JS from certain pages 2023-12-12 18:24:01 +00:00
Kim Alvefur
2769036f94 Translated using Weblate (Swedish)
Currently translated at 100.0% (361 of 361 strings)

Translation: Snikket/Web Portal
Translate-URL: http://i18n.sotecware.net/projects/snikket/web-portal/sv/
2023-12-09 16:59:59 +00:00
Weblate
c76befad1c Update translation files
Updated by "Update PO files to match POT (msgmerge)" hook in Weblate.

Translation: Snikket/Web Portal
Translate-URL: http://i18n.sotecware.net/projects/snikket/web-portal/
2023-12-08 12:14:28 +00:00
Matthew Wild
74ecfb8653 Merge pull request #166 from snikket-im/feature/admin-users-ui-updates-dec-23
Admin user management UI updates (Dec 23)
2023-12-08 12:14:19 +00:00
64 changed files with 12996 additions and 4908 deletions

View File

@@ -48,7 +48,7 @@ jobs:
pip install flake8 flake8-print
- name: Linting
run: |
python -m flake8 snikket_web
make flake8
translation-check:
runs-on: ubuntu-latest
@@ -66,10 +66,10 @@ jobs:
pip install flask-babel
- name: Linting
run: |
sed -ri '/^"POT-Creation-Date: /d' snikket_web/translations/messages.pot
sed -ri '/^"POT-Creation-Date: /d;/^"Generated-By: /d' snikket_web/translations/messages.pot
git add snikket_web/translations/messages.pot
make extract_translations
sed -ri '/^"POT-Creation-Date: /d' snikket_web/translations/messages.pot
sed -ri '/^"POT-Creation-Date: /d;/^"Generated-By: /d' snikket_web/translations/messages.pot
git diff --exit-code --color -- snikket_web/translations/messages.pot

View File

@@ -5,6 +5,8 @@ generated_css_files = $(patsubst snikket_web/scss/%.scss,snikket_web/static/css/
translation_basepath = snikket_web/translations
pot_file = $(translation_basepath)/messages.pot
black_formatted_py = snikket_web/prosodyclient.py
PYTHON3 ?= python3
SCSSC ?= sassc --load-path snikket_web/scss/
@@ -34,4 +36,20 @@ compile_translations:
-pybabel compile -d $(translation_basepath)
.PHONY: lint
lint: format flake8
.PHONY: format
format:
$(PYTHON3) -m black $(black_formatted_py)
.PHONY: flake8
flake8:
$(PYTHON3) -m flake8 --exclude=$(subst $(space),$(comma),$(strip $(black_formatted_py))) snikket_web
$(PYTHON3) -m flake8 --ignore=E501,W503 $(black_formatted_py)
.PHONY: mypy
mypy:
$(PYTHON3) -m mypy --python-version 3.11 snikket_web
.PHONY: build_css clean update_translations compile_translations extract_translations force_update_translations

View File

@@ -1,9 +1,10 @@
aiohttp~=3.6
quart~=0.17,<0.18
flask-wtf~=1.0
aiohttp~=3.8,<3.9
quart~=0.18,<0.19
flask-wtf~=1.1,<1.2
hsluv~=5.0
flask-babel~=1.0
email-validator~=1.1
flask-babel~=2.0,<3
email-validator~=1.3
environ-config~=20.0
wtforms~=3.0
wtforms~=3.0,<4
typing-extensions
werkzeug~=2.2,<3

View File

@@ -158,7 +158,9 @@ class AppConfig:
"id",
"it",
"pl",
"ru",
"sv",
"uk",
"zh_Hans_CN",
], converter=autosplit)
apple_store_url = environ.var(
@@ -210,6 +212,8 @@ def create_app() -> quart.Quart:
app.config["PRIVACY_URI"] = config.privacy_uri
app.config["ABUSE_EMAIL"] = config.abuse_email
app.config["SECURITY_EMAIL"] = config.security_email
app.config["SESSION_COOKIE_SECURE"] = True
app.config["SESSION_COOKIE_SAMESITE"] = "Lax"
app.context_processor(proc)
app.register_error_handler(

View File

@@ -76,10 +76,11 @@ class EditUserForm(BaseForm):
role = wtforms.RadioField(
_l("Access Level"),
choices=[
("prosody:restricted", _("Limited")),
("prosody:restricted", _l("Limited")),
("prosody:registered", _l("Normal user")),
("prosody:admin", _l("Administrator")),
],
default="prosody:registered",
)
action_save = wtforms.SubmitField(
@@ -290,6 +291,20 @@ class InvitePost(BaseForm):
default="account",
)
role = wtforms.RadioField(
_l("Access Level"),
choices=[
("prosody:restricted", _l("Limited")),
("prosody:registered", _l("Normal user")),
("prosody:admin", _l("Administrator")),
],
default="prosody:registered",
)
note = wtforms.StringField(
_l("Comment (optional)"),
)
action_create_invite = wtforms.SubmitField(
_l("New invitation link")
)
@@ -369,12 +384,16 @@ async def create_invite() -> typing.Union[str, werkzeug.Response]:
if form.type_.data == "group":
invite = await client.create_group_invite(
group_ids=form.circles.data,
role_names=[form.role.data],
ttl=form.lifetime.data,
note=form.note.data,
)
else:
invite = await client.create_account_invite(
group_ids=form.circles.data,
role_names=[form.role.data],
ttl=form.lifetime.data,
note=form.note.data,
)
await flash(
_("Invitation created"),
@@ -733,21 +752,21 @@ def get_system_stats() -> typing.MutableMapping[
class AnnouncementForm(BaseForm):
text = wtforms.StringField(
_("Message contents"),
_l("Message contents"),
widget=wtforms.widgets.TextArea(),
validators=[wtforms.validators.DataRequired()],
)
online_only = wtforms.BooleanField(
_("Only send to online users"),
_l("Only send to online users"),
)
action_post_all = wtforms.SubmitField(
_("Post to all users"),
_l("Post to all users"),
)
action_send_preview = wtforms.SubmitField(
_("Send preview to yourself"),
_l("Send preview to yourself"),
)
@@ -812,6 +831,11 @@ async def system() -> typing.Union[str, werkzeug.Response]:
except KeyError:
pass
try:
metrics["users"] = prosody_metrics["users"]
except KeyError:
pass
for k in list(metrics.keys()):
if metrics[k] is None:
# so that defaulting in jinja works

View File

@@ -53,7 +53,7 @@ def flatten(a: typing.Iterable, levels: int = 1) -> typing.Iterable:
def circle_name(c: typing.Any) -> str:
if c.id_ == "default" and c.name == "default":
return _("Main")
return _l("Main")
return c.name

View File

@@ -47,10 +47,20 @@ def apple_store_badge() -> str:
return url_for("static", filename="img/apple/en.svg")
def play_store_badge() -> str:
locale = selected_locale()
filename = "{}_badge_web_generic.png".format(locale)
static_path = pathlib.Path(__file__).parent / "static" / "img" / "google"
if (static_path / filename).exists():
return url_for("static", filename="img/google/{}".format(filename))
return url_for("static", filename="img/google/en_badge_web_generic.png")
@bp.context_processor
def context() -> typing.Dict[str, typing.Any]:
return {
"apple_store_badge": apple_store_badge,
"play_store_badge": play_store_badge,
}
@@ -116,6 +126,10 @@ class RegisterForm(BaseForm):
password = wtforms.PasswordField(
_l("Password"),
validators=[
wtforms.validators.InputRequired(),
wtforms.validators.Length(min=10),
],
)
password_confirm = wtforms.PasswordField(
@@ -184,6 +198,10 @@ async def register(id_: str) -> typing.Union[str, werkzeug.Response]:
class ResetForm(BaseForm):
password = wtforms.PasswordField(
_l("Password"),
validators=[
wtforms.validators.InputRequired(),
wtforms.validators.Length(min=10),
],
)
password_confirm = wtforms.PasswordField(

File diff suppressed because it is too large Load Diff

View File

@@ -259,6 +259,13 @@ div.form.layout-expanded {
margin: 0;
}
fieldset.descriptive-radio-selection {
p {
margin-top: 0;
margin-bottom: $w-s2;
}
}
input[type="radio"] + label, input[type="checkbox"] + label {
font-weight: inherit;
color: inherit;
@@ -363,6 +370,10 @@ div.form.layout-expanded {
margin-left: 0.25em;
}
.radio-button-ext {
margin-left: 0.5rem;
}
div.select-wrap {
display: block;
border-bottom: $w-s4 solid $primary-500;
@@ -981,19 +992,18 @@ div.profile-card {
}
}
/* clipboard button */
/* clipboard and share buttons */
.copy-to-clipboard {
.copy-to-clipboard, .share-button {
cursor: pointer;
font-style: normal;
text-decoration: none;
}
body.no-copy .copy-to-clipboard {
body.no-copy .copy-to-clipboard, body.no-share .share-button {
display: none !important;
}
/* magic */
pre.guru-meditation {
@@ -1304,4 +1314,11 @@ pre.guru-meditation {
margin-left: 0.5em;
}
.user-display-name {
font-size: 110%;
}
.user-jid {
font-size: 90%;
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 28 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.2 KiB

View File

@@ -148,6 +148,11 @@ licensed under the terms of the Apache 2.0 License -->
<path d="M0 0h24v24H0V0z" fill="none" />
<path d="M16 11c1.66 0 2.99-1.34 2.99-3S17.66 5 16 5s-3 1.34-3 3 1.34 3 3 3zm-8 0c1.66 0 2.99-1.34 2.99-3S9.66 5 8 5 5 6.34 5 8s1.34 3 3 3zm0 2c-2.33 0-7 1.17-7 3.5V18c0 .55.45 1 1 1h12c.55 0 1-.45 1-1v-1.5c0-2.33-4.67-3.5-7-3.5zm8 0c-.29 0-.62.02-.97.05.02.01.03.03.04.04 1.14.83 1.93 1.94 1.93 3.41V18c0 .35-.07.69-.18 1H22c.55 0 1-.45 1-1v-1.5c0-2.33-4.67-3.5-7-3.5z" />
</symbol>
<!-- from: social/person/materialiconsround/24px.svg -->
<symbol id="icon-person" viewBox="0 0 24 24">
<path d="M0 0h24v24H0V0z" fill="none" />
<path d="M12 12c2.21 0 4-1.79 4-4s-1.79-4-4-4-4 1.79-4 4 1.79 4 4 4zm0 2c-2.67 0-8 1.34-8 4v1c0 .55.45 1 1 1h14c.55 0 1-.45 1-1v-1c0-2.66-5.33-4-8-4z" />
</symbol>
<!-- from: social/group_add/materialiconsround/24px.svg -->
<symbol id="icon-create_group" viewBox="0 0 24 24">
<path d="M0 0h24v24H0V0z" fill="none" />
@@ -188,4 +193,9 @@ licensed under the terms of the Apache 2.0 License -->
<g><rect fill="none" height="24" width="24" /><rect fill="none" height="24" width="24" /></g>
<g><g><path d="M21,8c-1.45,0-2.26,1.44-1.93,2.51l-3.55,3.56c-0.3-0.09-0.74-0.09-1.04,0l-2.55-2.55C12.27,10.45,11.46,9,10,9 c-1.45,0-2.27,1.44-1.93,2.52l-4.56,4.55C2.44,15.74,1,16.55,1,18c0,1.1,0.9,2,2,2c1.45,0,2.26-1.44,1.93-2.51l4.55-4.56 c0.3,0.09,0.74,0.09,1.04,0l2.55,2.55C12.73,16.55,13.54,18,15,18c1.45,0,2.27-1.44,1.93-2.52l3.56-3.55 C21.56,12.26,23,11.45,23,10C23,8.9,22.1,8,21,8z" /><polygon points="15,9 15.94,6.93 18,6 15.94,5.07 15,3 14.08,5.07 12,6 14.08,6.93" /><polygon points="3.5,11 4,9 6,8.5 4,8 3.5,6 3,8 1,8.5 3,9" /></g></g>
</symbol>
<!-- from: social/share/materialiconsround/24px.svg -->
<symbol id="icon-share" viewBox="0 0 24 24">
<path d="M0 0h24v24H0V0z" fill="none" />
<path d="M18 16.08c-.76 0-1.44.3-1.96.77L8.91 12.7c.05-.23.09-.46.09-.7s-.04-.47-.09-.7l7.05-4.11c.54.5 1.25.81 2.04.81 1.66 0 3-1.34 3-3s-1.34-3-3-3-3 1.34-3 3c0 .24.04.47.09.7L8.04 9.81C7.5 9.31 6.79 9 6 9c-1.66 0-3 1.34-3 3s1.34 3 3 3c.79 0 1.5-.31 2.04-.81l7.12 4.16c-.05.21-.08.43-.08.65 0 1.61 1.31 2.92 2.92 2.92s2.92-1.31 2.92-2.92-1.31-2.92-2.92-2.92z" />
</symbol>
</defs></svg>

Before

Width:  |  Height:  |  Size: 19 KiB

After

Width:  |  Height:  |  Size: 19 KiB

View File

@@ -1,19 +1,57 @@
{% from "library.j2" import form_button, render_errors %}
{% from "library.j2" import form_button,
render_errors,
access_level_description, access_level_icon,
invite_type_description, invite_type_icon
%}
<form method="POST" action="{{ url_for(".create_invite") }}">
{{- invite_form.csrf_token -}}
<div class="form layout-expanded">
<h2 class="form-title">{% trans %}Create new invitation{% endtrans %}</h2>
<p class="form-descr weak">{% trans %}Create a new invitation link to invite more users to your Snikket service by clicking the button below.{% endtrans %}</p>
<!-- Invitation type -->
<div class="f-ebox">
<fieldset>{#- -#}
<fieldset class="descriptive-radio-selection">{#- -#}
<legend>{{ invite_form.type_.label.text }}</legend>
{{- invite_form.type_ -}}
<p>{% trans %}Choose whether this invitation link will allow more than one person to join.{% endtrans %}</p>
{%- for invite_type in invite_form.type_ -%}
<div class="radio-button-ext">
{{ invite_type }}<label for="{{ invite_type.id }}">
{%- trans title=invite_type.label.text, icon=invite_type_icon(invite_type.data), description=invite_type_description(invite_type.data) -%}
<span class="invite-type">{{ title }}{{ icon }}</span><p>{{ description }}</p>
{%- endtrans -%}
</label>
</div>
{%- endfor -%}
</fieldset>
</div>
<!-- Access level -->
<div class="f-ebox">
<fieldset class="descriptive-radio-selection">{#- -#}
<legend>{{ invite_form.role.label.text }}</legend>
<p>{% trans %}The access level of a user determines what interactions are allowed for them on your Snikket service.{% endtrans %}</p>
{%- for level in invite_form.role -%}
<div class="radio-button-ext">
{{ level }}<label for="{{ level.id }}">
{%- trans title=level.label.text, icon=access_level_icon(level.data), description=access_level_description(level.data) -%}
<span class="access-level">{{ title }}{{ icon }}</span><p>{{ description }}</p>
{%- endtrans -%}
</label>
</div>
{%- endfor -%}
</fieldset>
</div>
<!-- Valid for -->
<div class="f-ebox">
{{ invite_form.lifetime.label }}
<div class="select-wrap">{{ invite_form.lifetime }}</div>
</div>
<!-- Invite to circle -->
<div class="f-ebox">
{#
NOTE: This is for when/if we ever support multi-group invites.
@@ -27,6 +65,13 @@
<div class="select-wrap">{{ invite_form.circles }}</div>
{%- call render_errors(invite_form.circles) -%}{%- endcall -%}
</div>
<!-- Comment -->
<div class="f-ebox">
{{ invite_form.note.label }}
{{ invite_form.note }}
</div>
<div class="f-bbox">
{%- call form_button("create_link", invite_form.action_create_invite, class="primary") %}{% endcall -%}
</div>

View File

@@ -1,5 +1,5 @@
{% extends "admin_app.html" %}
{% from "library.j2" import form_button, standard_button, value_or_hint, custom_form_button, clipboard_button, icon %}
{% from "library.j2" import form_button, standard_button, value_or_hint, custom_form_button, clipboard_button, icon, render_user with context %}
{% block head_lead %}
{{ super() }}
{% include "copy-snippet.html" %}
@@ -47,7 +47,7 @@
<tbody>
{%- for chat in circle_chats -%}
<tr>
<td class="collapsible">{% call value_or_hint(chat.name) %}{% endcall %}</td>
<td>{% call value_or_hint(chat.name) %}{% endcall %}</td>
<td class="nowrap">
{%- call custom_form_button("delete", form.action_remove_group_chat.name, chat.id_, class="primary danger", slim=True) -%}
{% trans name=chat.name %}Delete group chat '{{ name }}'{% endtrans %}
@@ -71,7 +71,6 @@
<div class="el-2 elevated"><table>
<thead>
<th>{% trans %}Login name{% endtrans %}</th>
<th class="collapsible">{% trans %}Display name{% endtrans %}</th>
<th>{% trans %}Actions{% endtrans %}</th>
</thead>
<tbody>
@@ -79,13 +78,12 @@
<tr>
<td>
{%- if member -%}
{{ localpart }}
{%- call render_user(member) -%}{%- endcall -%}
{%- else -%}
{{ localpart }}
<span class="with-tooltip above" data-tooltip="{% trans %}The user has been deleted from the server.{% endtrans %}"><em> ({% trans %}deleted{% endtrans %})</em></span>
{%- endif -%}
</td>
<td class="collapsible">{% call value_or_hint(member.display_name) %}{% endcall %}</td>
<td class="nowrap">
{%- call custom_form_button("remove_user", form.action_remove_user.name, member.localpart, class="primary danger", slim=True) -%}
{% trans username=member.localpart %}Remove user {{ username }} from circle{% endtrans %}

View File

@@ -1,5 +1,5 @@
{% extends "admin_app.html" %}
{% from "library.j2" import showuri, form_button, standard_button, extract_circle_name, invite_type_description %}
{% from "library.j2" import showuri, form_button, standard_button, extract_circle_name, invite_type_name, invite_type_description %}
{% block head_lead %}
{{ super() }}
{% include "copy-snippet.html" %}
@@ -13,9 +13,10 @@
<dt>{% trans %}Valid until{% endtrans %}</dt>
<dd>{{ invite.expires | format_date }}</dd>
<dt><label for="link-field">{% trans %}Link{% endtrans %}</label></dt>
<dd>{% call showuri(invite.landing_page, id_="link-field") %}{% endcall %}</dd>
<dd>{% call showuri(invite.landing_page, id_="link-field") %}{% trans %}Invitation to Snikket{% endtrans %}{% endcall %}</dd>
<dt>{% trans %}Invitation type{% endtrans %}</dt>
<dd>{% call invite_type_description(invite) %}{% endcall %}</dd>
{% set invite_type = invite.reusable and "group" or "account" %}
<dd><span class="with-tooltip above" data-tooltip="{% call invite_type_description(invite_type) %}{% endcall %}">{% call invite_type_name(invite_type) %}{% endcall %}</span></dd>
{%- set ngroups = invite.group_ids | length -%}
{%- if ngroups > 1 -%}
{#- not supported via the web UI, but we should still display it properly -#}

View File

@@ -1,21 +1,5 @@
{% extends "admin_app.html" %}
{% from "library.j2" import box, form_button, standard_button, icon %}
{% macro access_level_description(role, caller=None) %}
{%- if role == "prosody:restricted" -%}
{% trans %}Limited users can interact with users on the same Snikket service and be members of circles.{% endtrans %}
{%- elif role == "prosody:registered" -%}
{% trans %}Like limited users and can also interact with users on other Snikket services.{% endtrans %}
{%- elif role == "prosody:admin" -%}
{% trans %}Like normal users and can access the admin panel in the web portal.{% endtrans %}
{%- endif -%}
{% endmacro %}
{% macro access_level_icon(role, caller=None) %}
{%- if role == "prosody:restricted" -%}
{% call icon("lock") %}{% endcall %}
{%- elif role == "prosody:admin" -%}
{% call icon("admin") %}{% endcall %}
{%- endif -%}
{% endmacro %}
{% from "library.j2" import box, form_button, standard_button, icon, access_level_description, access_level_icon %}
{% block content %}
<h1>{% trans user_name=target_user.localpart %}Edit user {{ user_name }}{% endtrans %}</h1>
<form method="POST">{{ form.csrf_token }}<div class="form layout-expanded">

View File

@@ -1,5 +1,5 @@
{% extends "admin_app.html" %}
{% from "library.j2" import action_button, icon, clipboard_button, form_button, custom_form_button, extract_circle_name, invite_type_name, invite_type_description %}
{% from "library.j2" import action_button, icon, clipboard_button, share_button, form_button, custom_form_button, extract_circle_name, invite_type_name, invite_type_description %}
{% block head_lead %}
{{ super() }}
{% include "copy-snippet.html" %}
@@ -18,17 +18,18 @@
<col/>
<thead>
<tr>
<th>{% trans %}Expires{% endtrans %}</th>
<th class="collapsible">{% trans %}Type{% endtrans %}</th>
<th class="collapsible">{% trans %}Circle{% endtrans %}</th>
<th>{% trans %}Expires{% endtrans %}</th>
<th>{% trans %}Comment{% endtrans %}</th>
<th>{% trans %}Actions{% endtrans %}</th>
</tr>
</thead>
<tbody>
{% for invite in invites %}
{% set invite_type = invite.reusable and "group" or "account" %}
<tr>
<td>{{ (invite.expires - now) | format_timedelta(add_direction=True) }}</td>
<td class="collapsible"><span class="with-tooltip above" data-tooltip="{% call invite_type_description(invite) %}{% endcall %}">{% call invite_type_name(invite) %}{% endcall %}</span></td>
<td class="collapsible"><span class="with-tooltip above" data-tooltip="{% call invite_type_description(invite_type) %}{% endcall %}">{% call invite_type_name(invite_type) %}{% endcall %}</span></td>
<td class="collapsible">
{#- -#}
<ul class="inline">
@@ -38,6 +39,8 @@
</ul>
{#- -#}
</td>
<td>{{ (invite.expires - now) | format_timedelta(add_direction=True) }}</td>
<td>{% if invite.note is not none %}{{ invite.note }}{% endif %}</td>
<td class="nowrap">
{%- call action_button("more", url_for(".edit_invite", id_=invite.id_), class="secondary") -%}
{% trans %}Show invite details{% endtrans %}
@@ -45,6 +48,9 @@
{%- call clipboard_button(invite.landing_page, class="primary") -%}
{% trans %}Copy invite link to clipboard{% endtrans %}
{%- endcall -%}
{%- call share_button("Invitation to Snikket", invite.landing_page, class="primary") -%}
{% trans %}Share invitation link{% endtrans %}
{%- endcall -%}
{%- call custom_form_button("remove_link", form.action_revoke.name, invite.id_, class="secondary danger", slim=True) -%}
{% trans %}Delete invitation{% endtrans %}
{%- endcall -%}

View File

@@ -15,7 +15,7 @@
<dt>{% trans %}Valid until{% endtrans %}</dt>
<dd>{{ reset_link.expires | format_date }}</dd>
<dt><label for="link-field">{% trans %}Link{% endtrans %}</label></dt>
<dd>{% call showuri(reset_link.landing_page, id_="link-field") %}{% endcall %}</dd>
<dd>{% call showuri(reset_link.landing_page, id_="link-field") %}Reset your Snikket password{% endcall %}</dd>
</dd>
<div class="f-bbox">
{%- call custom_form_button("remove_link", form.action_revoke.name, reset_link.id_, class="secondary danger") -%}

View File

@@ -76,13 +76,20 @@
<em>{% trans %}unknown{% endtrans %}</em>
{%- endif -%}
</dd>
<dt>{% trans %}Connected devices{% endtrans %}</dt>
<dt>{% trans %}Active users{% endtrans %}</dt>
<dd>
<ul>
{%- if metrics.prosody_devices | default(None) is not none -%}
{{ metrics.prosody_devices }}
<li>{% trans %}Connected now:{% endtrans %} {{ metrics.prosody_devices }}</li>
{%- else -%}
<em>{% trans %}unknown{% endtrans %}</em>
<li><em>{% trans %}unknown{% endtrans %}</em></li>
{%- endif -%}
{%- if metrics.users | default(None) is not none -%}
<li>{% trans %}Past 24 hours:{% endtrans %} {{ metrics.users.active_1d }}</li>
<li>{% trans %}Past 7 days:{% endtrans %} {{ metrics.users.active_7d }}</li>
<li>{% trans %}Past 30 days:{% endtrans %} {{ metrics.users.active_30d }}</li>
{%- endif -%}
</ul>
</dd>
</dl>
</div>

View File

@@ -27,7 +27,6 @@
{%- call action_button("edit", url_for(".edit_user", localpart=user.localpart), class="primary") -%}
{% trans user_name=user.localpart %}Edit user {{ user_name }}{% endtrans %}
{%- endcall -%}
</form>
</td>
</tr>
{% endfor %}

View File

@@ -16,5 +16,5 @@
<meta name="msapplication-TileColor" content="#fbd308">
<meta name="theme-color" content="#fbd308">
</head>
<body{% if body_id | default(False) %} id="{{ body_id }}"{% endif %} class="{% if is_in_debug_mode %}debug{% endif %}{% if body_class | default(False) %} {{ body_class }}{% endif %}"{% if onload | default(False) %} onload="{{ onload }}"{% endif %}>{% block body %}{% endblock %}</body>
<body{% if body_id | default(False) %} id="{{ body_id }}"{% endif %} class="{% if is_in_debug_mode %}debug{% endif %}{% if body_class | default(False) %} {{ body_class }}{% endif %} no-copy no-share"{% if onload | default(False) %} onload="{{ onload }}"{% endif %}>{% block body %}{% endblock %}</body>
</html>

View File

@@ -115,8 +115,63 @@ var copy_to_clipboard_btn = function(el) {
});
};
var copy_to_clipboard_btn = function(el) {
var text = el.dataset.cliptext;
if (!text) {
console.error('copy_to_clipboard used on element without text to copy');
}
copyTextToClipboard(text, el, function(success) {
var existing_result_el = document.getElementById("clipboard-result");
if (existing_result_el !== null) {
existing_result_el.parentNode.removeChild(existing_result_el);
}
var icon = "done";
if (!success) {
icon = "cancel";
}
var icon_bak = get_current_icon(el.firstChild);
change_icon(el.firstChild, icon);
setTimeout(function() {
change_icon(el.firstChild, icon_bak);
el.blur();
}, 1500);
});
};
var share_url_btn = function(el) {
let data = {
"title": el.dataset.shareTitle,
"url": el.dataset.shareUrl,
}
let icon_bak = get_current_icon(el.firstChild);
new Promise(function (resolve, reject) {
if(!navigator.canShare || !navigator.canShare(data)) {
return reject();
}
return resolve(navigator.share(data));
}).then(function () {
// Success
change_icon(el.firstChild, "done");
}, function () {
// Failure
change_icon(el.firstChild, "cancel");
}).finally(function () {
// Either way, clear status icon after 1.5s
setTimeout(function() {
change_icon(el.firstChild, icon_bak);
el.blur();
}, 1500);
});
}
window.addEventListener('load', function() {
document.body.classList.remove("no-copy");
if(navigator.share) {
document.body.classList.remove("no-share");
}
});
</script>

View File

@@ -7,7 +7,6 @@
{% block head_lead %}
{{ super() }}
<title>{% trans %}Reset your password | Snikket{% endtrans %}</title>
<script async type="text/javascript" src="{{ url_for("static", filename="js/qrcode.min.js") }}"></script>
{% endblock %}
{% block content %}
<form method="POST"><div class="form layout-expanded">
@@ -27,9 +26,4 @@
{%- call form_button("passwd", form.action_reset, class="primary") -%}{%- endcall -%}
</div>
</div></form>
<script type="text/javascript">
var onload = function() {
apply_qr_code(document.getElementById("qr-uri"));
};
</script>
{% endblock %}

View File

@@ -32,7 +32,7 @@
{%- endif -%}
<div class="install-buttons">
<ul>
<li><a href="{{ play_store_url }}"><img alt='{% trans %}Get it on Google Play{% endtrans %}' src='https://play.google.com/intl/en_us/badges/static/images/badges/en_badge_web_generic.png' class="play"/></a></li>
<li><a href="{{ play_store_url }}"><img alt='{% trans %}Get it on Google Play{% endtrans %}' src='{{ play_store_badge() }}' class="play"/></a></li>
{%- if apple_store_url -%}
<li><a href="{{ apple_store_url }}" class="popover" data-popover-id="apple-popover"><img alt='{% trans %}Download on the App Store{% endtrans %}' src="{{ apple_store_badge() }}" class="apple"></a></li>
{%- endif -%}
@@ -134,7 +134,6 @@
var onload = function() {
apply_qr_code(document.getElementById("qr-invite-page"));
apply_qr_code(document.getElementById("qr-uri"));
var popover_as = document.getElementsByClassName("popover");
for (var i = 0; i < popover_as.length; ++i) {
var a = popover_as[i];

View File

@@ -25,10 +25,10 @@
{%- endif -%}
</div>
<div class="user-info-container">
<div class="user-localpart">{{- user.localpart -}}</div>
{%- if user.display_name %}
<div class="user-display-name">{{- user.display_name -}}</div>
{%- endif %}
<div class="user-jid"><span class="user-jid-localpart">{{- user.localpart -}}</span><span class="user-jid-at">@</span><span class="user-jid-domain">{{- config["SNIKKET_DOMAIN"] -}}</span></div>
</div>
</div>
{%- endmacro -%}
@@ -38,7 +38,10 @@
<em>—</em>
{%- else -%}
<div><input type="text" {% if id_ %}id="{{ id_ }}" {% endif %}readonly="readonly" value="{{ uri }}"></div>
<div>{% call clipboard_button(uri, show_label=True) %}{% trans %}Copy link{% endtrans %}{% endcall %}</div>
<div>
{% call clipboard_button(uri, show_label=True) %}{% trans %}Copy link{% endtrans %}{% endcall %}
{% call share_button(caller() if caller is not none else None, uri, show_label=True) %}{% trans %}Share{% endtrans %}{% endcall %}
</div>
{%- endif -%}
{% endmacro %}
@@ -82,7 +85,7 @@
{% macro clipboard_button(data, show_label=False, caller=None, class=None) -%}
{%- set label = caller() -%}
<a class="button{% if class %} {{ class }}{% endif %}"
<a class="button copy-to-clipboard{% if class %} {{ class }}{% endif %}"
href="#"
{% if not show_label %}
aria-label="{{ label }}"
@@ -97,6 +100,24 @@
</a>
{%- endmacro %}
{% macro share_button(title, url, show_label=False, caller=None, class=None) -%}
{%- set label = caller() -%}
<a class="button share-button{% if class %} {{ class }}{% endif %}"
href="#"
{% if not show_label %}
aria-label="{{ label }}"
title="{{ label }}"
{% endif %}
data-share-title="{{ title }}"
data-share-url="{{ url }}"
onclick="share_url_btn(this); return false;">
{%- call icon("share") %}{% endcall -%}
{%- if show_label %}
<span>{{ label }}</span>
{% endif -%}
</a>
{%- endmacro %}
{% macro render_errors(field, caller=None) -%}
{%- set error_list = field.errors if field.errors is not mapping else (field.errors.values() | flatten | list) -%}
{%- if error_list -%}
@@ -132,18 +153,44 @@
{%- endif -%}
{% endmacro %}
{%- macro invite_type_name(invite_info, caller=None) -%}
{%- if invite_info.reusable -%}
{% trans %}Group{% endtrans %}
{%- else -%}
{%- macro invite_type_name(invite_type, caller=None) -%}
{%- if invite_type == "account" -%}
{% trans %}Individual{% endtrans %}
{%- else -%}
{% trans %}Group{% endtrans %}
{%- endif -%}
{%- endmacro -%}
{%- macro invite_type_description(invite_info, caller=None) -%}
{%- if invite_info.reusable -%}
{% trans %}Can be used multiple times to create accounts on this Snikket service.{% endtrans %}
{%- else -%}
{% trans %}Can be used once to create an account on this Snikket service.{% endtrans %}
{% macro access_level_description(role, caller=None) %}
{%- if role == "prosody:restricted" -%}
{% trans %}Limited users can interact with users on the same Snikket service and be members of circles.{% endtrans %}
{%- elif role == "prosody:registered" -%}
{% trans %}Like limited users and can also interact with users on other Snikket services.{% endtrans %}
{%- elif role == "prosody:admin" -%}
{% trans %}Like normal users and can access the admin panel in the web portal.{% endtrans %}
{%- endif -%}
{%- endmacro -%}
{% endmacro %}
{% macro access_level_icon(role, caller=None) %}
{%- if role == "prosody:restricted" -%}
{% call icon("lock") %}{% endcall %}
{%- elif role == "prosody:admin" -%}
{% call icon("admin") %}{% endcall %}
{%- endif -%}
{% endmacro %}
{% macro invite_type_description(invite_type, caller=None) %}
{%- if invite_type == "account" -%}
{% trans %}Invite a single person (invitation link can only be used once).{% endtrans %}
{%- elif invite_type == "group" -%}
{% trans %}Invite a group of people (invitation link can be used multiple times).{% endtrans %}
{%- endif -%}
{% endmacro %}
{% macro invite_type_icon(invite_type, caller=None) %}
{%- if invite_type == "account" -%}
{% call icon("person") %}{% endcall %}
{%- elif invite_type == "group" -%}
{% call icon("people") %}{% endcall %}
{%- endif -%}
{% endmacro %}

View File

@@ -30,7 +30,7 @@
<div class="f-bbox">
{%- call form_button("login", form.action_signin, class="primary") -%}{% endcall -%}
</div>
</from>
</form>
<script type="text/javascript">
var domainCheck = function() {
var form = document.getElementById("login-form");

View File

@@ -6,8 +6,13 @@
{% include "copy-snippet.html" %}
{% endblock %}
{% block content %}
<h1>{% trans %}Welcome!{% endtrans %}</h1>
<p>{% trans user_name=user_info.display_name %}Welcome home, {{ user_name }}.{% endtrans %}</p>
{% if user_info.is_admin and metrics.users and metrics.users.active_1d <= 1 %}
<aside class="box hint">
<header>{% trans %}Welcome to Snikket!{% endtrans %}</header>
<p>{% trans %}Now your Snikket instance is up and running, the next step is to invite people to join it. Family, friends, colleagues... you choose!{% endtrans %}</p>
<a href="/admin/invitations">{% trans %}Create new invitation{% endtrans %}</a>
</aside>
{% endif %}
<nav class="welcome">
<ul>
<li class="wide">

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

Binary file not shown.

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -1,21 +1,21 @@
# Translations template for PROJECT.
# Copyright (C) 2023 ORGANIZATION
# Copyright (C) 2025 ORGANIZATION
# This file is distributed under the same license as the PROJECT project.
# FIRST AUTHOR <EMAIL@ADDRESS>, 2023.
# FIRST AUTHOR <EMAIL@ADDRESS>, 2025.
#
#, fuzzy
msgid ""
msgstr ""
"Project-Id-Version: PROJECT VERSION\n"
"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n"
"POT-Creation-Date: 2023-12-08 12:08+0000\n"
"POT-Creation-Date: 2025-04-12 20:21+0200\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=utf-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Generated-By: Babel 2.13.1\n"
"Generated-By: Babel 2.17.0\n"
#: snikket_web/admin.py:69 snikket_web/templates/admin_delete_user.html:10
#: snikket_web/templates/admin_edit_circle.html:73
@@ -23,229 +23,234 @@ msgid "Login name"
msgstr ""
#: snikket_web/admin.py:73 snikket_web/templates/admin_delete_user.html:12
#: snikket_web/templates/admin_edit_circle.html:74 snikket_web/user.py:63
#: snikket_web/user.py:69
msgid "Display name"
msgstr ""
#: snikket_web/admin.py:77 snikket_web/templates/admin_edit_user.html:53
#: snikket_web/admin.py:77 snikket_web/admin.py:295
#: snikket_web/templates/admin_edit_user.html:37
msgid "Access Level"
msgstr ""
#: snikket_web/admin.py:79
#: snikket_web/admin.py:79 snikket_web/admin.py:297
msgid "Limited"
msgstr ""
#: snikket_web/admin.py:80
#: snikket_web/admin.py:80 snikket_web/admin.py:298
msgid "Normal user"
msgstr ""
#: snikket_web/admin.py:81
#: snikket_web/admin.py:81 snikket_web/admin.py:299
msgid "Administrator"
msgstr ""
#: snikket_web/admin.py:86
#: snikket_web/admin.py:87
msgid "Update user"
msgstr ""
#: snikket_web/admin.py:90
#: snikket_web/admin.py:91
msgid "Restore account"
msgstr ""
#: snikket_web/admin.py:94
#: snikket_web/admin.py:95
msgid "Unlock account"
msgstr ""
#: snikket_web/admin.py:98
#: snikket_web/admin.py:99
msgid "Create password reset link"
msgstr ""
#: snikket_web/admin.py:116
#: snikket_web/admin.py:117
msgid "Password reset link created"
msgstr ""
#: snikket_web/admin.py:128
#: snikket_web/admin.py:129
msgid "User account restored"
msgstr ""
#: snikket_web/admin.py:133
#: snikket_web/admin.py:134
msgid "User account unlocked"
msgstr ""
#: snikket_web/admin.py:140
#: snikket_web/admin.py:141
msgid "Could not restore user account"
msgstr ""
#: snikket_web/admin.py:145
#: snikket_web/admin.py:146
msgid "Could not unlock user account"
msgstr ""
#: snikket_web/admin.py:157
#: snikket_web/admin.py:158
msgid "User information updated."
msgstr ""
#: snikket_web/admin.py:179
#: snikket_web/admin.py:180
msgid "Delete user permanently"
msgstr ""
#: snikket_web/admin.py:192
#: snikket_web/admin.py:193
msgid "User deleted"
msgstr ""
#: snikket_web/admin.py:230
#: snikket_web/admin.py:231
msgid "Password reset link not found"
msgstr ""
#: snikket_web/admin.py:242
#: snikket_web/admin.py:243
msgid "Password reset link deleted"
msgstr ""
#: snikket_web/admin.py:262
#: snikket_web/admin.py:263
msgid "Invite to circle"
msgstr ""
#: snikket_web/admin.py:268
#: snikket_web/admin.py:269
msgid "At least one circle must be selected"
msgstr ""
#: snikket_web/admin.py:273
#: snikket_web/admin.py:274
msgid "Valid for"
msgstr ""
#: snikket_web/admin.py:275
#: snikket_web/admin.py:276
msgid "One hour"
msgstr ""
#: snikket_web/admin.py:276
#: snikket_web/admin.py:277
msgid "Twelve hours"
msgstr ""
#: snikket_web/admin.py:277
#: snikket_web/admin.py:278
msgid "One day"
msgstr ""
#: snikket_web/admin.py:278
#: snikket_web/admin.py:279
msgid "One week"
msgstr ""
#: snikket_web/admin.py:279
#: snikket_web/admin.py:280
msgid "Four weeks"
msgstr ""
#: snikket_web/admin.py:285 snikket_web/templates/admin_edit_invite.html:17
#: snikket_web/admin.py:286 snikket_web/templates/admin_edit_invite.html:17
msgid "Invitation type"
msgstr ""
#: snikket_web/admin.py:287 snikket_web/templates/library.j2:139
#: snikket_web/admin.py:288 snikket_web/templates/library.j2:158
msgid "Individual"
msgstr ""
#: snikket_web/admin.py:288 snikket_web/templates/library.j2:137
#: snikket_web/admin.py:289 snikket_web/templates/library.j2:160
msgid "Group"
msgstr ""
#: snikket_web/admin.py:294
#: snikket_web/admin.py:305
msgid "Comment (optional)"
msgstr ""
#: snikket_web/admin.py:309
msgid "New invitation link"
msgstr ""
#: snikket_web/admin.py:356
#: snikket_web/admin.py:371
msgid "Revoke"
msgstr ""
#: snikket_web/admin.py:380
#: snikket_web/admin.py:399
msgid "Invitation created"
msgstr ""
#: snikket_web/admin.py:396
#: snikket_web/admin.py:415
msgid "No such invitation exists"
msgstr ""
#: snikket_web/admin.py:411
#: snikket_web/admin.py:430
msgid "Invitation revoked"
msgstr ""
#: snikket_web/admin.py:428 snikket_web/admin.py:476
#: snikket_web/admin.py:447 snikket_web/admin.py:495
#: snikket_web/templates/admin_delete_circle.html:10
#: snikket_web/templates/admin_edit_circle.html:44
msgid "Name"
msgstr ""
#: snikket_web/admin.py:433 snikket_web/templates/admin_circles.html:47
#: snikket_web/admin.py:452 snikket_web/templates/admin_circles.html:47
msgid "Create circle"
msgstr ""
#: snikket_web/admin.py:463
#: snikket_web/admin.py:482
msgid "Circle created"
msgstr ""
#: snikket_web/admin.py:481
#: snikket_web/admin.py:500
msgid "Select user"
msgstr ""
#: snikket_web/admin.py:486
#: snikket_web/admin.py:505
msgid "Update circle"
msgstr ""
#: snikket_web/admin.py:492
#: snikket_web/admin.py:511
msgid "Add user"
msgstr ""
#: snikket_web/admin.py:510 snikket_web/admin.py:609 snikket_web/admin.py:657
#: snikket_web/admin.py:529 snikket_web/admin.py:628 snikket_web/admin.py:676
msgid "No such circle exists"
msgstr ""
#: snikket_web/admin.py:547
#: snikket_web/admin.py:566
msgid "Circle data updated"
msgstr ""
#: snikket_web/admin.py:557
#: snikket_web/admin.py:576
msgid "User added to circle"
msgstr ""
#: snikket_web/admin.py:566
#: snikket_web/admin.py:585
msgid "User removed from circle"
msgstr ""
#: snikket_web/admin.py:575
#: snikket_web/admin.py:594
msgid "Chat removed from circle"
msgstr ""
#: snikket_web/admin.py:593
#: snikket_web/admin.py:612
msgid "Delete circle permanently"
msgstr ""
#: snikket_web/admin.py:620
#: snikket_web/admin.py:639
msgid "Circle deleted"
msgstr ""
#: snikket_web/admin.py:634
#: snikket_web/admin.py:653
msgid "Group chat name"
msgstr ""
#: snikket_web/admin.py:639
#: snikket_web/admin.py:658
msgid "Create group chat"
msgstr ""
#: snikket_web/admin.py:669
#: snikket_web/admin.py:688
msgid "New group chat added to circle"
msgstr ""
#: snikket_web/admin.py:736
#: snikket_web/admin.py:755
msgid "Message contents"
msgstr ""
#: snikket_web/admin.py:742
#: snikket_web/admin.py:761
msgid "Only send to online users"
msgstr ""
#: snikket_web/admin.py:746
#: snikket_web/admin.py:765
msgid "Post to all users"
msgstr ""
#: snikket_web/admin.py:750
#: snikket_web/admin.py:769
msgid "Send preview to yourself"
msgstr ""
#: snikket_web/admin.py:772
#: snikket_web/admin.py:791
msgid "Announcement sent!"
msgstr ""
@@ -265,7 +270,7 @@ msgstr ""
msgid "Yesterday"
msgstr ""
#: snikket_web/infra.py:105
#: snikket_web/infra.py:104
#, python-format
msgid "%(time)s ago"
msgstr ""
@@ -276,60 +281,60 @@ msgid ""
"contact your Snikket operator."
msgstr ""
#: snikket_web/invite.py:114
#: snikket_web/invite.py:124
msgid "Username"
msgstr ""
#: snikket_web/invite.py:118 snikket_web/invite.py:186 snikket_web/main.py:43
#: snikket_web/invite.py:128 snikket_web/invite.py:200 snikket_web/main.py:43
msgid "Password"
msgstr ""
#: snikket_web/invite.py:122 snikket_web/invite.py:190
#: snikket_web/invite.py:136 snikket_web/invite.py:208
msgid "Confirm password"
msgstr ""
#: snikket_web/invite.py:126 snikket_web/invite.py:194
#: snikket_web/invite.py:140 snikket_web/invite.py:212
msgid "The passwords must match."
msgstr ""
#: snikket_web/invite.py:131
#: snikket_web/invite.py:145
msgid "Create account"
msgstr ""
#: snikket_web/invite.py:158
#: snikket_web/invite.py:172
msgid "That username is already taken."
msgstr ""
#: snikket_web/invite.py:162 snikket_web/invite.py:227
#: snikket_web/invite.py:176 snikket_web/invite.py:245
msgid "Registration was declined for unknown reasons."
msgstr ""
#: snikket_web/invite.py:166
#: snikket_web/invite.py:180
msgid "The username is not valid."
msgstr ""
#: snikket_web/invite.py:199 snikket_web/templates/user_home.html:32
#: snikket_web/invite.py:217 snikket_web/templates/user_home.html:37
#: snikket_web/templates/user_passwd.html:29
msgid "Change password"
msgstr ""
#: snikket_web/invite.py:246
#: snikket_web/invite.py:264
msgid "Account data file"
msgstr ""
#: snikket_web/invite.py:250
#: snikket_web/invite.py:268
msgid "Import data"
msgstr ""
#: snikket_web/invite.py:271
#: snikket_web/invite.py:289
#, python-format
msgid ""
"The account data you tried to import is in an unknown format. Please "
"upload an XML file in XEP-0227 format (provided format: %(mimetype)s)."
msgstr ""
#: snikket_web/invite.py:291 snikket_web/templates/unauth.html:18
#: snikket_web/user.py:178
#: snikket_web/invite.py:309 snikket_web/templates/unauth.html:18
#: snikket_web/user.py:192
msgid "Error"
msgstr ""
@@ -357,73 +362,73 @@ msgstr ""
msgid "New password"
msgstr ""
#: snikket_web/user.py:39
#: snikket_web/user.py:42
msgid "Confirm new password"
msgstr ""
#: snikket_web/user.py:43
#: snikket_web/user.py:47
msgid "The new passwords must match."
msgstr ""
#: snikket_web/user.py:50
#: snikket_web/user.py:56
msgid "Sign out"
msgstr ""
#: snikket_web/user.py:55
#: snikket_web/user.py:61
msgid "Nobody"
msgstr ""
#: snikket_web/user.py:56
#: snikket_web/user.py:62
msgid "Friends only"
msgstr ""
#: snikket_web/user.py:57
#: snikket_web/user.py:63
msgid "Everyone"
msgstr ""
#: snikket_web/user.py:67
#: snikket_web/user.py:73
msgid "Avatar"
msgstr ""
#: snikket_web/user.py:71
#: snikket_web/user.py:77
msgid "Profile visibility"
msgstr ""
#: snikket_web/user.py:76
#: snikket_web/user.py:82
msgid "Update profile"
msgstr ""
#: snikket_web/user.py:82
#: snikket_web/user.py:88
msgid "Account data"
msgstr ""
#: snikket_web/user.py:86
#: snikket_web/user.py:92
msgid "Upload"
msgstr ""
#: snikket_web/user.py:111
#: snikket_web/user.py:125
msgid "Incorrect password."
msgstr ""
#: snikket_web/user.py:115
#: snikket_web/user.py:129
msgid "Password changed"
msgstr ""
#: snikket_web/user.py:123
#: snikket_web/user.py:138
msgid ""
"The chosen avatar is too big. To be able to upload larger avatars, please"
" use the app."
msgstr ""
#: snikket_web/user.py:170
#: snikket_web/user.py:184
msgid "Profile updated"
msgstr ""
#: snikket_web/user.py:184
#: snikket_web/user.py:198
msgid "Export"
msgstr ""
#: snikket_web/user.py:202
#: snikket_web/user.py:216
msgid "You currently have no account data to export."
msgstr ""
@@ -544,8 +549,8 @@ msgstr ""
#: snikket_web/templates/admin_circles.html:15
#: snikket_web/templates/admin_edit_circle.html:45
#: snikket_web/templates/admin_edit_circle.html:75
#: snikket_web/templates/admin_invites.html:24
#: snikket_web/templates/admin_edit_circle.html:74
#: snikket_web/templates/admin_invites.html:25
#: snikket_web/templates/admin_users.html:10
msgid "Actions"
msgstr ""
@@ -603,16 +608,42 @@ msgstr ""
msgid "Create invitation"
msgstr ""
#: snikket_web/templates/admin_create_invite_form.html:5
#: snikket_web/templates/admin_create_invite_form.html:9
#: snikket_web/templates/user_home.html:13
msgid "Create new invitation"
msgstr ""
#: snikket_web/templates/admin_create_invite_form.html:6
#: snikket_web/templates/admin_create_invite_form.html:10
msgid ""
"Create a new invitation link to invite more users to your Snikket service"
" by clicking the button below."
msgstr ""
#: snikket_web/templates/admin_create_invite_form.html:16
msgid ""
"Choose whether this invitation link will allow more than one person to "
"join."
msgstr ""
#: snikket_web/templates/admin_create_invite_form.html:21
#, python-format
msgid "<span class=\"invite-type\">%(title)s%(icon)s</span><p>%(description)s</p>"
msgstr ""
#: snikket_web/templates/admin_create_invite_form.html:34
#: snikket_web/templates/admin_edit_user.html:38
msgid ""
"The access level of a user determines what interactions are allowed for "
"them on your Snikket service."
msgstr ""
#: snikket_web/templates/admin_create_invite_form.html:38
#, python-format
msgid ""
"<span class=\"access-"
"level\">%(title)s%(icon)s</span><p>%(description)s</p>"
msgstr ""
#: snikket_web/templates/admin_debug_user.html:8
#, python-format
msgid "Debug information for %(user_name)s"
@@ -678,7 +709,7 @@ msgid "Delete user %(user_name)s"
msgstr ""
#: snikket_web/templates/admin_delete_user.html:6
#: snikket_web/templates/admin_edit_user.html:74
#: snikket_web/templates/admin_edit_user.html:58
msgid "Delete user"
msgstr ""
@@ -742,37 +773,37 @@ msgstr ""
msgid "All members of the circle will see each other in their contact list."
msgstr ""
#: snikket_web/templates/admin_edit_circle.html:85
#: snikket_web/templates/admin_edit_circle.html:84
msgid "The user has been deleted from the server."
msgstr ""
#: snikket_web/templates/admin_edit_circle.html:85
#: snikket_web/templates/library.j2:131
#: snikket_web/templates/admin_edit_circle.html:84
#: snikket_web/templates/library.j2:152
msgid "deleted"
msgstr ""
#: snikket_web/templates/admin_edit_circle.html:91
#: snikket_web/templates/admin_edit_circle.html:89
#, python-format
msgid "Remove user %(username)s from circle"
msgstr ""
#: snikket_web/templates/admin_edit_circle.html:99
#: snikket_web/templates/admin_edit_circle.html:97
msgid "This circle currently has no members."
msgstr ""
#: snikket_web/templates/admin_edit_circle.html:101
#: snikket_web/templates/admin_edit_circle.html:99
msgid "Invite more members"
msgstr ""
#: snikket_web/templates/admin_edit_circle.html:104
#: snikket_web/templates/admin_edit_circle.html:102
msgid "Add existing user"
msgstr ""
#: snikket_web/templates/admin_edit_circle.html:115
#: snikket_web/templates/admin_edit_circle.html:113
msgid "All users added"
msgstr ""
#: snikket_web/templates/admin_edit_circle.html:116
#: snikket_web/templates/admin_edit_circle.html:114
msgid "All users on this service are already in this circle."
msgstr ""
@@ -790,147 +821,129 @@ msgstr ""
msgid "Link"
msgstr ""
#: snikket_web/templates/admin_edit_invite.html:22
#: snikket_web/templates/admin_edit_invite.html:16
msgid "Invitation to Snikket"
msgstr ""
#: snikket_web/templates/admin_edit_invite.html:23
#: snikket_web/templates/admin_home.html:19
msgid "Circles"
msgstr ""
#: snikket_web/templates/admin_edit_invite.html:23
#: snikket_web/templates/admin_edit_invite.html:24
msgid "Users joining via this invitation will be added to the following circles:"
msgstr ""
#: snikket_web/templates/admin_edit_invite.html:29
#: snikket_web/templates/admin_invites.html:23
#: snikket_web/templates/admin_edit_invite.html:30
#: snikket_web/templates/admin_invites.html:22
msgid "Circle"
msgstr ""
#: snikket_web/templates/admin_edit_invite.html:35
#: snikket_web/templates/admin_edit_invite.html:36
msgid "The user will not be added to any circle and will have no contacts."
msgstr ""
#: snikket_web/templates/admin_edit_invite.html:40
#: snikket_web/templates/admin_edit_invite.html:41
msgid "Contact"
msgstr ""
#: snikket_web/templates/admin_edit_invite.html:41
#: snikket_web/templates/admin_edit_invite.html:42
#, python-format
msgid "The user will get added as contact of %(peer_jid)s."
msgstr ""
#: snikket_web/templates/admin_edit_invite.html:43
#: snikket_web/templates/admin_edit_invite.html:44
msgid "Created"
msgstr ""
#: snikket_web/templates/admin_edit_invite.html:48
#: snikket_web/templates/admin_edit_invite.html:49
msgid "Return to invitation list"
msgstr ""
#: snikket_web/templates/admin_edit_user.html:5
msgid ""
"Limited users can interact with users on the same Snikket service and be "
"members of circles."
msgstr ""
#: snikket_web/templates/admin_edit_user.html:7
msgid ""
"Like limited users and can also interact with users on other Snikket "
"services."
msgstr ""
#: snikket_web/templates/admin_edit_user.html:9
msgid "Like normal users and can access the admin panel in the web portal."
msgstr ""
#: snikket_web/templates/admin_edit_user.html:20
#: snikket_web/templates/admin_edit_user.html:4
#: snikket_web/templates/admin_users.html:28
#, python-format
msgid "Edit user %(user_name)s"
msgstr ""
#: snikket_web/templates/admin_edit_user.html:24
#: snikket_web/templates/admin_edit_user.html:8
msgid "This user account is pending deletion"
msgstr ""
#: snikket_web/templates/admin_edit_user.html:25
#: snikket_web/templates/admin_edit_user.html:9
#, python-format
msgid ""
"The owner of the account sent a deletion request on %(date)s using their "
"app."
msgstr ""
#: snikket_web/templates/admin_edit_user.html:26
#: snikket_web/templates/admin_edit_user.html:10
#, python-format
msgid ""
"The account has been locked, and will be automatically deleted "
"permanently in %(time)s."
msgstr ""
#: snikket_web/templates/admin_edit_user.html:28
#: snikket_web/templates/admin_edit_user.html:12
msgid ""
"If this was a mistake, you can cancel the deletion and restore the "
"account."
msgstr ""
#: snikket_web/templates/admin_edit_user.html:34
#: snikket_web/templates/admin_edit_user.html:18
msgid "This user account is locked"
msgstr ""
#: snikket_web/templates/admin_edit_user.html:35
#: snikket_web/templates/admin_edit_user.html:19
msgid ""
"The user will not be able to log in to their account until it is unlocked"
" again."
msgstr ""
#: snikket_web/templates/admin_edit_user.html:41
#: snikket_web/templates/admin_edit_user.html:25
msgid "Edit user"
msgstr ""
#: snikket_web/templates/admin_edit_user.html:46
#: snikket_web/templates/admin_edit_user.html:30
msgid "The login name cannot be changed."
msgstr ""
#: snikket_web/templates/admin_edit_user.html:54
msgid ""
"The access level of a user determines what interactions are allowed for "
"them on your Snikket service."
msgstr ""
#: snikket_web/templates/admin_edit_user.html:61
#: snikket_web/templates/admin_edit_user.html:45
#, python-format
msgid "<strong>%(title)s%(icon)s</strong><p>%(description)s</p>"
msgstr ""
#: snikket_web/templates/admin_edit_user.html:71
#: snikket_web/templates/admin_edit_user.html:55
msgid "Return to user list"
msgstr ""
#: snikket_web/templates/admin_edit_user.html:79
#: snikket_web/templates/admin_edit_user.html:63
msgid "Further actions"
msgstr ""
#: snikket_web/templates/admin_edit_user.html:81
#: snikket_web/templates/admin_edit_user.html:65
msgid "Reset password"
msgstr ""
#: snikket_web/templates/admin_edit_user.html:84
#: snikket_web/templates/admin_edit_user.html:68
msgid ""
"If the user has lost their password, you can use the button below to "
"create a special link which allows to change the password of the account,"
" once."
msgstr ""
#: snikket_web/templates/admin_edit_user.html:89
#: snikket_web/templates/admin_edit_user.html:73
msgid "Debug information"
msgstr ""
#: snikket_web/templates/admin_edit_user.html:91
#: snikket_web/templates/admin_edit_user.html:75
msgid ""
"In some cases, extended information about the user account and the "
"connected devices is necessary to troubleshoot issues. The button below "
"reveals this (sensitive) information."
msgstr ""
#: snikket_web/templates/admin_edit_user.html:95
#: snikket_web/templates/admin_edit_user.html:79
msgid "Show debug information"
msgstr ""
@@ -1003,26 +1016,34 @@ msgid "Pending invitations"
msgstr ""
#: snikket_web/templates/admin_invites.html:21
msgid "Expires"
msgstr ""
#: snikket_web/templates/admin_invites.html:22
msgid "Type"
msgstr ""
#: snikket_web/templates/admin_invites.html:43
msgid "Show invite details"
#: snikket_web/templates/admin_invites.html:23
msgid "Expires"
msgstr ""
#: snikket_web/templates/admin_invites.html:24
msgid "Comment"
msgstr ""
#: snikket_web/templates/admin_invites.html:46
msgid "Copy invite link to clipboard"
msgid "Show invite details"
msgstr ""
#: snikket_web/templates/admin_invites.html:49
msgid "Copy invite link to clipboard"
msgstr ""
#: snikket_web/templates/admin_invites.html:52
msgid "Share invitation link"
msgstr ""
#: snikket_web/templates/admin_invites.html:55
msgid "Delete invitation"
msgstr ""
#: snikket_web/templates/admin_invites.html:57
#: snikket_web/templates/admin_invites.html:63
msgid "Currently, there are no pending invitations."
msgstr ""
@@ -1060,7 +1081,7 @@ msgstr ""
#: snikket_web/templates/admin_system.html:60
#: snikket_web/templates/admin_system.html:68
#: snikket_web/templates/admin_system.html:76
#: snikket_web/templates/admin_system.html:84
#: snikket_web/templates/admin_system.html:85
msgid "unknown"
msgstr ""
@@ -1108,14 +1129,30 @@ msgid "Storage used by shared files"
msgstr ""
#: snikket_web/templates/admin_system.html:79
msgid "Connected devices"
msgid "Active users"
msgstr ""
#: snikket_web/templates/admin_system.html:83
msgid "Connected now:"
msgstr ""
#: snikket_web/templates/admin_system.html:88
msgid "Past 24 hours:"
msgstr ""
#: snikket_web/templates/admin_system.html:89
msgid "Past 7 days:"
msgstr ""
#: snikket_web/templates/admin_system.html:90
msgid "Past 30 days:"
msgstr ""
#: snikket_web/templates/admin_system.html:97
msgid "Broadcast message"
msgstr ""
#: snikket_web/templates/admin_system.html:92
#: snikket_web/templates/admin_system.html:99
msgid ""
"This form allows you to send a message to all users currently online on "
"your Snikket server. Use it wisely."
@@ -1260,11 +1297,11 @@ msgstr ""
msgid "Reset your password | Snikket"
msgstr ""
#: snikket_web/templates/invite_reset.html:15
#: snikket_web/templates/invite_reset.html:14
msgid "Reset your password online"
msgstr ""
#: snikket_web/templates/invite_reset.html:16
#: snikket_web/templates/invite_reset.html:15
msgid ""
"To reset your password online, fill out the fields below and confirm "
"using the button."
@@ -1358,7 +1395,7 @@ msgid "Your address"
msgstr ""
#: snikket_web/templates/invite_success.html:15
#: snikket_web/templates/user_home.html:21
#: snikket_web/templates/user_home.html:26
msgid "Copy address"
msgstr ""
@@ -1544,20 +1581,40 @@ msgstr ""
msgid " (Restricted)"
msgstr ""
#: snikket_web/templates/library.j2:41
#: snikket_web/templates/library.j2:42
msgid "Copy link"
msgstr ""
#: snikket_web/templates/library.j2:104
#: snikket_web/templates/library.j2:43
msgid "Share"
msgstr ""
#: snikket_web/templates/library.j2:125
msgid "Invalid input"
msgstr ""
#: snikket_web/templates/library.j2:145
msgid "Can be used multiple times to create accounts on this Snikket service."
#: snikket_web/templates/library.j2:166
msgid ""
"Limited users can interact with users on the same Snikket service and be "
"members of circles."
msgstr ""
#: snikket_web/templates/library.j2:147
msgid "Can be used once to create an account on this Snikket service."
#: snikket_web/templates/library.j2:168
msgid ""
"Like limited users and can also interact with users on other Snikket "
"services."
msgstr ""
#: snikket_web/templates/library.j2:170
msgid "Like normal users and can access the admin panel in the web portal."
msgstr ""
#: snikket_web/templates/library.j2:184
msgid "Invite a single person (invitation link can only be used once)."
msgstr ""
#: snikket_web/templates/library.j2:186
msgid "Invite a group of people (invitation link can be used multiple times)."
msgstr ""
#: snikket_web/templates/login.html:5
@@ -1620,41 +1677,42 @@ msgstr ""
msgid "Operation successful"
msgstr ""
#: snikket_web/templates/user_home.html:9
msgid "Welcome!"
#: snikket_web/templates/user_home.html:11
msgid "Welcome to Snikket!"
msgstr ""
#: snikket_web/templates/user_home.html:10
#, python-format
msgid "Welcome home, %(user_name)s."
#: snikket_web/templates/user_home.html:12
msgid ""
"Now your Snikket instance is up and running, the next step is to invite "
"people to join it. Family, friends, colleagues... you choose!"
msgstr ""
#: snikket_web/templates/user_home.html:14
#: snikket_web/templates/user_home.html:19
msgid "Your account"
msgstr ""
#: snikket_web/templates/user_home.html:20
#: snikket_web/templates/user_home.html:25
msgid "Your XMPP address"
msgstr ""
#: snikket_web/templates/user_home.html:31
#: snikket_web/templates/user_home.html:36
msgid "Edit profile"
msgstr ""
#: snikket_web/templates/user_home.html:33
#: snikket_web/templates/user_home.html:38
#: snikket_web/templates/user_manage_data.html:4
msgid "Manage your data"
msgstr ""
#: snikket_web/templates/user_home.html:39
#: snikket_web/templates/user_home.html:44
msgid "Your Snikket"
msgstr ""
#: snikket_web/templates/user_home.html:41
#: snikket_web/templates/user_home.html:46
msgid "Manage users, invitations and circles of your Snikket service."
msgstr ""
#: snikket_web/templates/user_home.html:43
#: snikket_web/templates/user_home.html:48
msgid "Admin panel"
msgstr ""

File diff suppressed because it is too large Load Diff

Binary file not shown.

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

Binary file not shown.

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -32,16 +32,22 @@ class ChangePasswordForm(BaseForm):
new_password = wtforms.PasswordField(
_l("New password"),
validators=[wtforms.validators.InputRequired()]
validators=[
wtforms.validators.InputRequired(),
wtforms.validators.Length(min=10),
]
)
new_password_confirm = wtforms.PasswordField(
_l("Confirm new password"),
validators=[wtforms.validators.InputRequired(),
wtforms.validators.EqualTo(
"new_password",
_l("The new passwords must match.")
)]
validators=[
wtforms.validators.InputRequired(),
wtforms.validators.EqualTo(
"new_password",
_l("The new passwords must match.")
),
wtforms.validators.Length(min=10),
]
)
@@ -91,7 +97,15 @@ class ImportAccountDataForm(BaseForm):
@client.require_session()
async def index() -> str:
user_info = await client.get_user_info()
return await render_template("user_home.html", user_info=user_info)
try:
metrics = await client.get_system_metrics()
except (werkzeug.exceptions.Unauthorized, werkzeug.exceptions.Forbidden):
metrics = {}
return await render_template(
"user_home.html",
user_info=user_info,
metrics=metrics,
)
@bp.route('/passwd', methods=["GET", "POST"])

View File

@@ -27,6 +27,7 @@ navigation/cancel:cancel
navigation/more_vert:more
social/groups:groups
social/people:people
social/person:person
social/group_add:create_group
social/person_add:add_user
social/person_remove:remove_user
@@ -35,3 +36,4 @@ image/edit:edit
action/admin_panel_settings:admin
content/link:link
content/insights:insights
social/share:share