From 0a2091a36648ca6020c96df13c14628e0d2b0dfa Mon Sep 17 00:00:00 2001 From: Fioddor Superconcentrado Date: Sat, 21 Aug 2021 20:08:16 +0200 Subject: [PATCH 01/57] container: Don't fail if there's no fbx network Display debugging info if the reason for failure is different. Signed-off-by: Fioddor Superconcentrado [sunil: Indentation, fix pylint message] Signed-off-by: Sunil Mohan Adapa Reviewed-by: Sunil Mohan Adapa --- container | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/container b/container index c02e6b7b2..8b5298d3b 100755 --- a/container +++ b/container @@ -812,8 +812,16 @@ def _destroy(distribution): connection_name = f'fbx-{distribution}-shared' logger.info('Removing Network Manager connection %s', connection_name) - subprocess.run(['sudo', 'nmcli', 'connection', 'delete', connection_name], - stdout=subprocess.DEVNULL) + result = subprocess.run( + ['sudo', 'nmcli', 'connection', 'delete', connection_name], + capture_output=True) + if result.returncode not in (0, 10): + # nmcli failed and not due to 'Connection, device, or access point does + # not exist.' See + # https://developer-old.gnome.org/NetworkManager/stable/nmcli.html + logger.error('nmcli returned code %d', result.returncode) + logger.error('Error message:\n%s', result.stderr.decode()) + logger.error('Output:\n%s', result.stdout.decode()) logger.info('Keeping downloaded image: %s', _get_compressed_image_path(distribution)) From 426cdefd719388c725070dfd393d14afa2fdefa0 Mon Sep 17 00:00:00 2001 From: Fioddor Superconcentrado Date: Sun, 22 Aug 2021 17:45:12 +0200 Subject: [PATCH 02/57] container: freedombox-develop callable from anywhere Signed-off-by: Fioddor Superconcentrado --- HACKING.md | 7 +++---- container | 2 +- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/HACKING.md b/HACKING.md index a0cc9e4cd..1b69d3ab0 100644 --- a/HACKING.md +++ b/HACKING.md @@ -115,10 +115,9 @@ directory: guest$ cd /freedombox ``` -Run the development version of FreedomBox Service (Plinth) from your source -directory in the container using the following command. This command -continuously deploys your code changes into the container providing a -quick feedback cycle during development. +Run the development version of FreedomBox Service in the container using the +following command. This command continuously deploys your code changes into the +container providing a quick feedback cycle during development. ```bash guest$ freedombox-develop diff --git a/container b/container index 8b5298d3b..14ac23f60 100755 --- a/container +++ b/container @@ -164,7 +164,7 @@ sudo apt-mark unhold freedombox sudo DEBIAN_FRONTEND=noninteractive apt-get install --yes ncurses-term \ sshpass bash-completion -echo 'alias freedombox-develop="sudo -u plinth /freedombox/run --develop"' \ +echo 'alias freedombox-develop="cd /freedombox; sudo -u plinth /freedombox/run --develop"' \ >> /home/fbx/.bashrc # Make some pytest related files and directories writable to the fbx user From 1cff5cf581925c5a3d07c0b38dca1f5cea5749e2 Mon Sep 17 00:00:00 2001 From: Burak Yavuz Date: Tue, 31 Aug 2021 05:37:09 +0000 Subject: [PATCH 03/57] Translated using Weblate (Turkish) Currently translated at 100.0% (1513 of 1513 strings) --- plinth/locale/tr/LC_MESSAGES/django.po | 187 ++++++++++--------------- 1 file changed, 77 insertions(+), 110 deletions(-) diff --git a/plinth/locale/tr/LC_MESSAGES/django.po b/plinth/locale/tr/LC_MESSAGES/django.po index 060346a33..15eac51fe 100644 --- a/plinth/locale/tr/LC_MESSAGES/django.po +++ b/plinth/locale/tr/LC_MESSAGES/django.po @@ -7,7 +7,7 @@ msgstr "" "Project-Id-Version: \n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2021-08-30 19:29-0400\n" -"PO-Revision-Date: 2021-04-22 21:32+0000\n" +"PO-Revision-Date: 2021-09-01 16:33+0000\n" "Last-Translator: Burak Yavuz \n" "Language-Team: Turkish \n" @@ -16,7 +16,7 @@ msgstr "" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=n > 1;\n" -"X-Generator: Weblate 4.7-dev\n" +"X-Generator: Weblate 4.8.1-dev\n" #: doc/dev/_templates/layout.html:11 msgid "Page source" @@ -2074,92 +2074,82 @@ msgstr "" "ayarlayabilirsiniz." #: plinth/modules/email_server/__init__.py:49 -#, fuzzy -#| msgid "Chat Server" msgid "Email Server" -msgstr "Sohbet Sunucusu" +msgstr "E-posta Sunucusu" #: plinth/modules/email_server/__init__.py:81 msgid "Powered by Postfix, Dovecot & Rspamd" -msgstr "" +msgstr "Postfix, Dovecot ve Rspamd tarafından desteklenmektedir" #: plinth/modules/email_server/aliases/__init__.py:66 #: plinth/modules/email_server/aliases/__init__.py:73 msgid "The alias was taken" -msgstr "" +msgstr "Kod adı alındı" #: plinth/modules/email_server/aliases/models.py:13 msgid "Must be at least 2 characters long" -msgstr "" +msgstr "En az 2 karakter uzunluğunda olmak zorundadır" #: plinth/modules/email_server/aliases/models.py:15 msgid "Contains illegal characters" -msgstr "" +msgstr "Geçersiz karakterler içeriyor" #: plinth/modules/email_server/aliases/models.py:17 msgid "Must start and end with a-z or 0-9" -msgstr "" +msgstr "A-z veya 0-9 ile başlamalı ve bitmelidir" #: plinth/modules/email_server/aliases/models.py:19 msgid "Cannot be a number" -msgstr "" +msgstr "Sayı olamaz" #: plinth/modules/email_server/audit/home.py:23 #: plinth/modules/email_server/audit/home.py:32 -#, fuzzy -#| msgid "Directory does not exist." msgid "User does not exist" -msgstr "Dizin mevcut değil." +msgstr "Kullanıcı mevcut değil" #: plinth/modules/email_server/audit/ldap.py:66 msgid "Postfix-Dovecot SASL integration" -msgstr "" +msgstr "Postfix-Dovecot SASL bütünleştirmesi" #: plinth/modules/email_server/audit/ldap.py:67 msgid "Postfix alias maps" -msgstr "" +msgstr "Postfix kod adı eşlemeleri" #: plinth/modules/email_server/audit/ldap.py:68 msgid "Postfix local recipient maps" -msgstr "" +msgstr "Postfix yerel alıcı eşlemeleri" #: plinth/modules/email_server/audit/rcube.py:31 -#, fuzzy -#| msgid "unavailable" msgid "RoundCube availability" -msgstr "kullanılamaz" +msgstr "RoundCube kullanılabilirliği" #: plinth/modules/email_server/audit/rcube.py:32 msgid "RoundCube configured for FreedomBox email" -msgstr "" +msgstr "RoundCube, FreedomBox e-postası için yapılandırıldı" #: plinth/modules/email_server/audit/spam.py:86 msgid "Inbound and outbound mail filters" -msgstr "" +msgstr "Gelen ve giden posta süzgeçleri" #: plinth/modules/email_server/audit/tls.py:80 msgid "Postfix TLS parameters" -msgstr "" +msgstr "Postfix TLS parametreleri" #: plinth/modules/email_server/audit/tls.py:81 msgid "Postfix uses a TLS certificate" -msgstr "" +msgstr "Postfix bir TLS sertifikası kullanır" #: plinth/modules/email_server/audit/tls.py:90 -#, fuzzy -#| msgid "No certificate" msgid "Has a TLS certificate" -msgstr "Sertifika yok" +msgstr "TLS sertifikası var" #: plinth/modules/email_server/forms.py:7 -#, fuzzy -#| msgid "Domain" msgid "domain" -msgstr "Etki Alanı" +msgstr "etki alanı" #: plinth/modules/email_server/forms.py:14 msgid "New alias (without @domain)" -msgstr "" +msgstr "Yeni kod adı (@domain olmadan)" #: plinth/modules/email_server/manifest.py:6 #: plinth/modules/roundcube/__init__.py:55 @@ -2168,46 +2158,36 @@ msgid "Roundcube" msgstr "Roundcube" #: plinth/modules/email_server/manifest.py:12 -#, fuzzy -#| msgid "Mozilla Thunderbird" msgid "Thunderbird" -msgstr "Mozilla Thunderbird" +msgstr "Thunderbird" #: plinth/modules/email_server/manifest.py:27 msgid "K-9 Mail" -msgstr "" +msgstr "K-9 Mail" #: plinth/modules/email_server/manifest.py:40 msgid "FairEmail" -msgstr "" +msgstr "FairEmail" #: plinth/modules/email_server/templates/email_alias.html:12 msgid "You have no email aliases." -msgstr "" +msgstr "E-posta kod adlarınız yok." #: plinth/modules/email_server/templates/email_alias.html:21 -#, fuzzy -#| msgid "Disabled" msgid "Disable selected" -msgstr "Etkisizleştirildi" +msgstr "Seçileni etkisizleştir" #: plinth/modules/email_server/templates/email_alias.html:24 -#, fuzzy -#| msgid "cable is connected" msgid "Enable selected" -msgstr "kablo bağlı" +msgstr "Seçileni etkinleştir" #: plinth/modules/email_server/templates/email_alias.html:27 -#, fuzzy -#| msgid "Deleted selected snapshots" msgid "Delete selected" -msgstr "Seçilen anlık görüntüler silindi" +msgstr "Seçileni sil" #: plinth/modules/email_server/templates/email_alias.html:31 -#, fuzzy -#| msgid "Create a new backup" msgid "Create a new email alias" -msgstr "Yeni bir yedek oluşturun" +msgstr "Yeni bir e-posta kod adı oluşturun" #: plinth/modules/email_server/templates/email_alias.html:38 #: plinth/modules/tahoe/templates/tahoe-post-setup.html:59 @@ -2216,7 +2196,7 @@ msgstr "Ekle" #: plinth/modules/email_server/templates/email_domains.html:19 msgid "New value" -msgstr "" +msgstr "Yeni değer" #: plinth/modules/email_server/templates/email_domains.html:31 #: plinth/modules/email_server/templates/email_security.html:23 @@ -2229,83 +2209,67 @@ msgstr "Güncelle" #: plinth/modules/email_server/templates/email_form_base.html:12 msgid "There was a problem with your request. Please try again." -msgstr "" +msgstr "İsteğiniz ile ilgili bir sorun oldu. Lütfen tekrar deneyin." #: plinth/modules/email_server/templates/email_security.html:13 msgid "Postfix TLS" -msgstr "" +msgstr "Postfix TLS" #: plinth/modules/email_server/templates/email_security.html:18 msgid "Dovecot TLS" -msgstr "" +msgstr "Dovecot TLS" #: plinth/modules/email_server/templates/email_server.html:14 msgid "Visit Rspamd administration interface" -msgstr "" +msgstr "Rspamd yönetim arayüzünü ziyaret edin" #: plinth/modules/email_server/templates/email_server.html:18 -#, fuzzy -#| msgid "Service Type" msgid "Service Alert" -msgstr "Hizmet Türü" +msgstr "Hizmet Uyarısı" #: plinth/modules/email_server/templates/email_server.html:38 msgid "Repair" -msgstr "" +msgstr "Onar" #: plinth/modules/email_server/templates/my_mail.html:15 -#, fuzzy -#| msgid "Path is not a directory." msgid "You do not have a home directory." -msgstr "Yol bir dizin değil." +msgstr "Giriş dizininiz yok." #: plinth/modules/email_server/templates/my_mail.html:17 msgid "Create one to begin receiving emails." -msgstr "" +msgstr "E-posta almaya başlamak için bir tane oluşturun." #: plinth/modules/email_server/templates/my_mail.html:22 -#, fuzzy -#| msgid "Updated media directory" msgid "Create home directory" -msgstr "Güncellenmiş ortam dizini" +msgstr "Giriş dizini oluştur" #: plinth/modules/email_server/templates/tls_form.html:10 -#, fuzzy -#| msgid "Current status:" msgid "Keep current settings" -msgstr "Güncel durum:" +msgstr "Şu anki ayarları koru" #: plinth/modules/email_server/templates/tls_form.html:19 -#, fuzzy -#| msgid "Let's Encrypt" msgid "Use Let's Encrypt" -msgstr "Let's Encrypt" +msgstr "Let's Encrypt'ı kullan" #: plinth/modules/email_server/templates/tls_form.html:24 -#, fuzzy -#| msgid "Domain name set" msgid "Common name" -msgstr "Etki alanı adı ayarlandı" +msgstr "Ortak ad" #: plinth/modules/email_server/templates/tls_form.html:38 msgid "Use custom values" -msgstr "" +msgstr "Özel değerler kullan" #: plinth/modules/email_server/templates/tls_form.html:43 -#, fuzzy -#| msgid "Certificate Status" msgid "Certificate path" -msgstr "Sertifika Durumu" +msgstr "Sertifika yolu" #: plinth/modules/email_server/templates/tls_form.html:53 -#, fuzzy -#| msgid "Private key of this machine" msgid "Private key path" -msgstr "Bu makinenin özel anahtarı" +msgstr "Özel anahtar yolu" #: plinth/modules/email_server/templates/tls_form.html:67 msgid "Use system default" -msgstr "" +msgstr "Sistem varsayılanını kullan" #: plinth/modules/email_server/views.py:24 #: plinth/modules/email_server/views.py:32 plinth/templates/base.html:110 @@ -2314,12 +2278,12 @@ msgstr "Giriş" #: plinth/modules/email_server/views.py:25 msgid "My Mail" -msgstr "" +msgstr "Postam" #: plinth/modules/email_server/views.py:26 #: plinth/modules/email_server/views.py:33 msgid "My Aliases" -msgstr "" +msgstr "Kod Adlarım" #: plinth/modules/email_server/views.py:27 #: plinth/modules/networks/templates/connection_show.html:259 @@ -2335,23 +2299,19 @@ msgstr "Etki Alanları" #: plinth/modules/email_server/views.py:104 #, python-brace-format msgid "Internal error in {0}" -msgstr "" +msgstr "{0} içinde dahili hata" #: plinth/modules/email_server/views.py:107 msgid "Check syslog for more information" -msgstr "" +msgstr "Daha fazla bilgi için syslog'u gözden geçirin" #: plinth/modules/email_server/views.py:181 -#, fuzzy -#| msgid "Enable damage" msgid "Enabled aliases" -msgstr "Hasarı etkinleştir" +msgstr "Etkinleştirilmiş kod adları" #: plinth/modules/email_server/views.py:182 -#, fuzzy -#| msgid "Disabled" msgid "Disabled aliases" -msgstr "Etkisizleştirildi" +msgstr "Etkisizleştirilmiş kod adları" #: plinth/modules/firewall/__init__.py:33 #, python-brace-format @@ -6189,12 +6149,6 @@ msgstr "" "sağlayan FreedomBox uygulamasında bildirilen %(count)s güvenlik açığı var." #: plinth/modules/security/templates/security_report.html:19 -#, fuzzy -#| msgid "" -#| "The following table lists the current reported number, and historical " -#| "count, of security vulnerabilities for each installed app. More " -#| "information on the vulnerabilities can be found on the Debian Security Bug Tracker." msgid "" "The following table lists the current reported number of security " "vulnerabilities for each installed app. More information on the " @@ -6202,9 +6156,9 @@ msgid "" "debian.org/tracker/\">Debian Security Bug Tracker." msgstr "" "Aşağıdaki tablo, yüklü her uygulama için bildirilen güvenlik açıklarının şu " -"anki sayısını ve geçmiş sayısını listeler. Güvenlik açıkları hakkında daha " -"fazla bilgi Debian " -"Security Hata Tracker sitesinde bulunabilir." +"anki sayısını listeler. Güvenlik açıkları hakkında daha fazla bilgi Debian Security Hata " +"Tracker sitesinde bulunabilir." #: plinth/modules/security/templates/security_report.html:28 msgid "" @@ -8241,6 +8195,11 @@ msgid "" "Administration interface and produced web pages are suitable for mobile " "devices." msgstr "" +"WordPress, web siteleri ve bloglar oluşturmanın ve yönetmenin yaygın bir " +"yoludur. İçerik, görsel bir arayüz kullanılarak yönetilebilir. Web " +"sayfalarının düzeni ve işlevselliği özelleştirilebilir. Görünüm temalar " +"kullanılarak seçilebilir. Yönetim arayüzü ve üretilen web sayfaları mobil " +"cihazlara uygundur." #: plinth/modules/wordpress/__init__.py:42 #, python-brace-format @@ -8250,6 +8209,11 @@ msgid "" "the correct domain name. Enable permalinks in administrator interface for " "better URLs to your pages and posts." msgstr "" +"Siteyi aşağıda herkese açık hale getirmeden önce uygulamayı ziyaret ederek " +"WordPress kurulumunu çalıştırmanız gerekir. Doğru etki alanı adıyla " +"{box_name} cihazına erişirken kurulum çalıştırılmak zorundadır. " +"Sayfalarınıza ve yazılarınıza daha iyi URL'ler sağlamak için yönetici " +"arayüzünde kalıcı bağlantıları etkinleştirin." #: plinth/modules/wordpress/__init__.py:47 msgid "" @@ -8257,6 +8221,9 @@ msgid "" "during setup. Bookmark the admin page " "to reach administration interface in the future." msgstr "" +"WordPress'in kendi kullanıcı hesapları vardır. İlk yönetici hesabı kurulum " +"sırasında oluşturulur. Gelecekte yönetim arayüzüne erişmek için yönetici sayfası yerini işaretleyin." #: plinth/modules/wordpress/__init__.py:51 msgid "" @@ -8264,31 +8231,31 @@ msgid "" "from administrator interface. Additional plugins or themes may be installed " "and upgraded at your own risk." msgstr "" +"Büyük bir sürüm yükseltmesinden sonra, yönetici arayüzünden veritabanı " +"yükseltmesini el ile çalıştırmanız gerekir. Ek eklentiler veya temalar kendi " +"sorumluluğunuzda yüklenebilir ve yükseltilebilir." #: plinth/modules/wordpress/__init__.py:69 #: plinth/modules/wordpress/manifest.py:6 -#, fuzzy -#| msgid "Address" msgid "WordPress" -msgstr "Adres" +msgstr "WordPress" #: plinth/modules/wordpress/__init__.py:70 -#, fuzzy -#| msgid "Wiki and Blog" msgid "Website and Blog" -msgstr "Viki ve Blog" +msgstr "Web Sitesi ve Blog" #: plinth/modules/wordpress/forms.py:14 -#, fuzzy -#| msgid "public access" msgid "Public access" -msgstr "herkese açık erişim" +msgstr "Herkese açık erişim" #: plinth/modules/wordpress/forms.py:15 msgid "" "Allow all visitors. Disabling allows only administrators to view the " "WordPress site or blog. Enable only after performing initial WordPress setup." msgstr "" +"Tüm ziyaretçilere izin verin. Etkisizleştirmek, sadece yöneticilerin " +"WordPress sitesini veya blogunu görüntülemesine izin verir. Sadece ilk " +"WordPress kurulumunu gerçekleştirdikten sonra etkinleştirin." #: plinth/modules/zoph/__init__.py:33 #, python-brace-format From 40f2638fde92c307ed4583a7d7952750922bf8bb Mon Sep 17 00:00:00 2001 From: Michael Breidenbach Date: Tue, 31 Aug 2021 15:49:09 +0000 Subject: [PATCH 04/57] Translated using Weblate (Swedish) Currently translated at 100.0% (1513 of 1513 strings) --- plinth/locale/sv/LC_MESSAGES/django.po | 188 ++++++++++--------------- 1 file changed, 78 insertions(+), 110 deletions(-) diff --git a/plinth/locale/sv/LC_MESSAGES/django.po b/plinth/locale/sv/LC_MESSAGES/django.po index 6ef1a2261..66c2d228f 100644 --- a/plinth/locale/sv/LC_MESSAGES/django.po +++ b/plinth/locale/sv/LC_MESSAGES/django.po @@ -8,7 +8,7 @@ msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2021-08-30 19:29-0400\n" -"PO-Revision-Date: 2021-04-22 21:32+0000\n" +"PO-Revision-Date: 2021-09-01 16:33+0000\n" "Last-Translator: Michael Breidenbach \n" "Language-Team: Swedish \n" @@ -17,7 +17,7 @@ msgstr "" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=n != 1;\n" -"X-Generator: Weblate 4.7-dev\n" +"X-Generator: Weblate 4.8.1-dev\n" #: doc/dev/_templates/layout.html:11 msgid "Page source" @@ -2072,92 +2072,82 @@ msgstr "" "din domän på systemet Konfigurera sidan." #: plinth/modules/email_server/__init__.py:49 -#, fuzzy -#| msgid "Chat Server" msgid "Email Server" -msgstr "Chat-Server" +msgstr "E-postserver" #: plinth/modules/email_server/__init__.py:81 msgid "Powered by Postfix, Dovecot & Rspamd" -msgstr "" +msgstr "Drivs av Postfix, Dovecot och Rspamd" #: plinth/modules/email_server/aliases/__init__.py:66 #: plinth/modules/email_server/aliases/__init__.py:73 msgid "The alias was taken" -msgstr "" +msgstr "Aliasnamnet togs" #: plinth/modules/email_server/aliases/models.py:13 msgid "Must be at least 2 characters long" -msgstr "" +msgstr "Måste vara minst 2 tecken lång" #: plinth/modules/email_server/aliases/models.py:15 msgid "Contains illegal characters" -msgstr "" +msgstr "Innehåller otillåtna tecken" #: plinth/modules/email_server/aliases/models.py:17 msgid "Must start and end with a-z or 0-9" -msgstr "" +msgstr "Måste börja och sluta med a-z eller 0-9" #: plinth/modules/email_server/aliases/models.py:19 msgid "Cannot be a number" -msgstr "" +msgstr "Kan inte vara ett nummer" #: plinth/modules/email_server/audit/home.py:23 #: plinth/modules/email_server/audit/home.py:32 -#, fuzzy -#| msgid "Directory does not exist." msgid "User does not exist" -msgstr "Katalogen finns inte." +msgstr "Användaren finns inte" #: plinth/modules/email_server/audit/ldap.py:66 msgid "Postfix-Dovecot SASL integration" -msgstr "" +msgstr "Integrering av Postfix-Dovecot SASL" #: plinth/modules/email_server/audit/ldap.py:67 msgid "Postfix alias maps" -msgstr "" +msgstr "Postfix alias kartor" #: plinth/modules/email_server/audit/ldap.py:68 msgid "Postfix local recipient maps" -msgstr "" +msgstr "Postfix lokala mottagarkartor" #: plinth/modules/email_server/audit/rcube.py:31 -#, fuzzy -#| msgid "unavailable" msgid "RoundCube availability" -msgstr "otillgängligt" +msgstr "RoundCube tillgänglighet" #: plinth/modules/email_server/audit/rcube.py:32 msgid "RoundCube configured for FreedomBox email" -msgstr "" +msgstr "RoundCube konfigurerad för FreedomBox e-post" #: plinth/modules/email_server/audit/spam.py:86 msgid "Inbound and outbound mail filters" -msgstr "" +msgstr "Filter för inkommande och utgående e-post" #: plinth/modules/email_server/audit/tls.py:80 msgid "Postfix TLS parameters" -msgstr "" +msgstr "Postfix TLS-parametrar" #: plinth/modules/email_server/audit/tls.py:81 msgid "Postfix uses a TLS certificate" -msgstr "" +msgstr "Postfix använder ett TLS -certifikat" #: plinth/modules/email_server/audit/tls.py:90 -#, fuzzy -#| msgid "No certificate" msgid "Has a TLS certificate" -msgstr "Inget certifikat" +msgstr "Har ett TLS-certifikat" #: plinth/modules/email_server/forms.py:7 -#, fuzzy -#| msgid "Domain" msgid "domain" -msgstr "Domän" +msgstr "domain" #: plinth/modules/email_server/forms.py:14 msgid "New alias (without @domain)" -msgstr "" +msgstr "Nytt alias (utan @domän)" #: plinth/modules/email_server/manifest.py:6 #: plinth/modules/roundcube/__init__.py:55 @@ -2166,46 +2156,36 @@ msgid "Roundcube" msgstr "Roundcube" #: plinth/modules/email_server/manifest.py:12 -#, fuzzy -#| msgid "Mozilla Thunderbird" msgid "Thunderbird" -msgstr "Mozilla Thunderbird" +msgstr "Thunderbird" #: plinth/modules/email_server/manifest.py:27 msgid "K-9 Mail" -msgstr "" +msgstr "K-9 Mail" #: plinth/modules/email_server/manifest.py:40 msgid "FairEmail" -msgstr "" +msgstr "FairEmail" #: plinth/modules/email_server/templates/email_alias.html:12 msgid "You have no email aliases." -msgstr "" +msgstr "Du har inga e-postalias." #: plinth/modules/email_server/templates/email_alias.html:21 -#, fuzzy -#| msgid "Disabled" msgid "Disable selected" -msgstr "Inaktiverad" +msgstr "Inaktivera markerad" #: plinth/modules/email_server/templates/email_alias.html:24 -#, fuzzy -#| msgid "cable is connected" msgid "Enable selected" -msgstr "kabeln är ansluten" +msgstr "Aktivera markerad" #: plinth/modules/email_server/templates/email_alias.html:27 -#, fuzzy -#| msgid "Deleted selected snapshots" msgid "Delete selected" -msgstr "Borttagna markerade ögonblicksbilder" +msgstr "Tabort valda" #: plinth/modules/email_server/templates/email_alias.html:31 -#, fuzzy -#| msgid "Create a new backup" msgid "Create a new email alias" -msgstr "Skapa en ny säkerhetskopia" +msgstr "Skapa ett nytt e -postalias" #: plinth/modules/email_server/templates/email_alias.html:38 #: plinth/modules/tahoe/templates/tahoe-post-setup.html:59 @@ -2214,7 +2194,7 @@ msgstr "Lägg till" #: plinth/modules/email_server/templates/email_domains.html:19 msgid "New value" -msgstr "" +msgstr "Nytt värde" #: plinth/modules/email_server/templates/email_domains.html:31 #: plinth/modules/email_server/templates/email_security.html:23 @@ -2227,83 +2207,67 @@ msgstr "Uppdatera" #: plinth/modules/email_server/templates/email_form_base.html:12 msgid "There was a problem with your request. Please try again." -msgstr "" +msgstr "Ett problem uppstod med din begäran. Var god försök igen." #: plinth/modules/email_server/templates/email_security.html:13 msgid "Postfix TLS" -msgstr "" +msgstr "Postfix TLS" #: plinth/modules/email_server/templates/email_security.html:18 msgid "Dovecot TLS" -msgstr "" +msgstr "Dovecot TLS" #: plinth/modules/email_server/templates/email_server.html:14 msgid "Visit Rspamd administration interface" -msgstr "" +msgstr "Besök Rspamd administrationsgränssnitt" #: plinth/modules/email_server/templates/email_server.html:18 -#, fuzzy -#| msgid "Service Type" msgid "Service Alert" -msgstr "Servicetype" +msgstr "Servicevarning" #: plinth/modules/email_server/templates/email_server.html:38 msgid "Repair" -msgstr "" +msgstr "Reparera" #: plinth/modules/email_server/templates/my_mail.html:15 -#, fuzzy -#| msgid "Path is not a directory." msgid "You do not have a home directory." -msgstr "Sökvägen är inte en katalog." +msgstr "Du har ingen arbetskatalog." #: plinth/modules/email_server/templates/my_mail.html:17 msgid "Create one to begin receiving emails." -msgstr "" +msgstr "Skapa en för att börja ta emot e-post." #: plinth/modules/email_server/templates/my_mail.html:22 -#, fuzzy -#| msgid "Updated media directory" msgid "Create home directory" -msgstr "Uppdaterad mediekatalog" +msgstr "Skapa arbetskatalog" #: plinth/modules/email_server/templates/tls_form.html:10 -#, fuzzy -#| msgid "Current status:" msgid "Keep current settings" -msgstr "Nuvarande status:" +msgstr "Behåll nuvarande inställningar" #: plinth/modules/email_server/templates/tls_form.html:19 -#, fuzzy -#| msgid "Let's Encrypt" msgid "Use Let's Encrypt" -msgstr "Låt oss kryptera" +msgstr "Använd Let's Encrypt" #: plinth/modules/email_server/templates/tls_form.html:24 -#, fuzzy -#| msgid "Domain name set" msgid "Common name" -msgstr "Domännamn inställt" +msgstr "Vanligt namn" #: plinth/modules/email_server/templates/tls_form.html:38 msgid "Use custom values" -msgstr "" +msgstr "Använd anpassade värden" #: plinth/modules/email_server/templates/tls_form.html:43 -#, fuzzy -#| msgid "Certificate Status" msgid "Certificate path" -msgstr "Certifikatets status" +msgstr "Sökväg för certifikat" #: plinth/modules/email_server/templates/tls_form.html:53 -#, fuzzy -#| msgid "Private key of this machine" msgid "Private key path" -msgstr "Maskinens privata nyckel" +msgstr "Sökväg för den privata nyckeln" #: plinth/modules/email_server/templates/tls_form.html:67 msgid "Use system default" -msgstr "" +msgstr "Använd systemstandard" #: plinth/modules/email_server/views.py:24 #: plinth/modules/email_server/views.py:32 plinth/templates/base.html:110 @@ -2312,12 +2276,12 @@ msgstr "Hem" #: plinth/modules/email_server/views.py:25 msgid "My Mail" -msgstr "" +msgstr "Min e-post" #: plinth/modules/email_server/views.py:26 #: plinth/modules/email_server/views.py:33 msgid "My Aliases" -msgstr "" +msgstr "Mina alias" #: plinth/modules/email_server/views.py:27 #: plinth/modules/networks/templates/connection_show.html:259 @@ -2333,23 +2297,19 @@ msgstr "Domäner" #: plinth/modules/email_server/views.py:104 #, python-brace-format msgid "Internal error in {0}" -msgstr "" +msgstr "Internt fel i {0}" #: plinth/modules/email_server/views.py:107 msgid "Check syslog for more information" -msgstr "" +msgstr "Kontrollera syslog för mer information" #: plinth/modules/email_server/views.py:181 -#, fuzzy -#| msgid "Enable damage" msgid "Enabled aliases" -msgstr "Aktivera skador" +msgstr "Aktiverade alias" #: plinth/modules/email_server/views.py:182 -#, fuzzy -#| msgid "Disabled" msgid "Disabled aliases" -msgstr "Inaktiverad" +msgstr "Inaktiverade alias" #: plinth/modules/firewall/__init__.py:33 #, python-brace-format @@ -6173,22 +6133,16 @@ msgstr "" "server." #: plinth/modules/security/templates/security_report.html:19 -#, fuzzy -#| msgid "" -#| "The following table lists the current reported number, and historical " -#| "count, of security vulnerabilities for each installed app. More " -#| "information on the vulnerabilities can be found on the Debian Security Bug Tracker." msgid "" "The following table lists the current reported number of security " "vulnerabilities for each installed app. More information on the " "vulnerabilities can be found on the Debian Security Bug Tracker." msgstr "" -"I följande tabell visas det aktuella rapporterade antalet och det historiska " -"antalet säkerhetsbrister för varje installerad app. Mer information om " -"sårbarheterna finns på Debian Security Bug Tracker." +"I följande tabell visas det aktuella antalet rapporterade säkerhetsbrister " +"för varje installerad app. Mer information om sårbarheterna finns på Debian Security Bug " +"Tracker." #: plinth/modules/security/templates/security_report.html:28 msgid "" @@ -8216,6 +8170,11 @@ msgid "" "Administration interface and produced web pages are suitable for mobile " "devices." msgstr "" +"WordPress är ett populärt sätt att skapa och hantera webbplatser och " +"bloggar. Innehållet kan hanteras med hjälp av ett visuellt gränssnitt. " +"Webbplatsernas layout och funktionalitet kan anpassas. Utseende kan väljas " +"med hjälp av teman. Administrationsgränssnittet och producerade webbsidor är " +"lämpliga för mobila enheter." #: plinth/modules/wordpress/__init__.py:42 #, python-brace-format @@ -8225,6 +8184,11 @@ msgid "" "the correct domain name. Enable permalinks in administrator interface for " "better URLs to your pages and posts." msgstr "" +"Du måste köra WordPress-installationen genom att besöka appen innan du gör " +"webbplatsen tillgänglig för allmänheten nedan. Installationen måste köras " +"när du går in på {box_name} med rätt domännamn. Aktivera permalänkar i " +"administratörsgränssnittet för bättre webbadresser till dina sidor och " +"inlägg." #: plinth/modules/wordpress/__init__.py:47 msgid "" @@ -8232,6 +8196,10 @@ msgid "" "during setup. Bookmark the admin page " "to reach administration interface in the future." msgstr "" +"WordPress har sina egna användarkonton. Det första administratörskontot " +"skapas under installationen. Lägg admin-" +"sidan som ett bokmärke för att nå administrationsgränssnittet i " +"framtiden." #: plinth/modules/wordpress/__init__.py:51 msgid "" @@ -8239,23 +8207,20 @@ msgid "" "from administrator interface. Additional plugins or themes may be installed " "and upgraded at your own risk." msgstr "" +"Efter en uppgradering av en större version måste du manuellt köra " +"databasuppgradering från administratörsgränssnittet. Ytterligare plugins " +"eller teman kan installeras och uppgraderas på egen risk." #: plinth/modules/wordpress/__init__.py:69 #: plinth/modules/wordpress/manifest.py:6 -#, fuzzy -#| msgid "Address" msgid "WordPress" -msgstr "Adress" +msgstr "WordPress" #: plinth/modules/wordpress/__init__.py:70 -#, fuzzy -#| msgid "Wiki and Blog" msgid "Website and Blog" -msgstr "Wiki och Blogg" +msgstr "Webbplats och blogg" #: plinth/modules/wordpress/forms.py:14 -#, fuzzy -#| msgid "public access" msgid "Public access" msgstr "Publik tillgång" @@ -8264,6 +8229,9 @@ msgid "" "Allow all visitors. Disabling allows only administrators to view the " "WordPress site or blog. Enable only after performing initial WordPress setup." msgstr "" +"Tillåt alla besökare. Om du inaktiverar detta kan endast administratörer se " +"WordPress-webbplatsen eller bloggen. Aktivera endast efter den första " +"installationen av WordPress." #: plinth/modules/zoph/__init__.py:33 #, python-brace-format From 6cc2120e4e29d4e03a84319159e0276f17fd9eba Mon Sep 17 00:00:00 2001 From: Andrij Mizyk Date: Tue, 31 Aug 2021 08:37:51 +0000 Subject: [PATCH 05/57] Translated using Weblate (Ukrainian) Currently translated at 66.8% (1012 of 1513 strings) --- plinth/locale/uk/LC_MESSAGES/django.po | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/plinth/locale/uk/LC_MESSAGES/django.po b/plinth/locale/uk/LC_MESSAGES/django.po index 441d01c99..b42fa5348 100644 --- a/plinth/locale/uk/LC_MESSAGES/django.po +++ b/plinth/locale/uk/LC_MESSAGES/django.po @@ -8,7 +8,7 @@ msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2021-08-30 19:29-0400\n" -"PO-Revision-Date: 2021-08-20 21:20+0000\n" +"PO-Revision-Date: 2021-09-01 16:33+0000\n" "Last-Translator: Andrij Mizyk \n" "Language-Team: Ukrainian \n" @@ -18,7 +18,7 @@ msgstr "" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=3; plural=n%10==1 && n%100!=11 ? 0 : n%10>=2 && n" "%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2;\n" -"X-Generator: Weblate 4.8-dev\n" +"X-Generator: Weblate 4.8.1-dev\n" #: doc/dev/_templates/layout.html:11 msgid "Page source" @@ -1648,6 +1648,9 @@ msgid "" "24h), it may be hard for others to find you on the Internet. This will " "prevent others from finding services which are provided by this {box_name}." msgstr "" +"Якщо ваш надавач Інтернету періодично змінює вашу IP-адресу (наприклад, " +"кожних 24год.), то іншим може бути важко знайти вас в Інтернеті. Це " +"перешкоджатиме іншим пошук сервісів, що надаються цим {box_name}." #: plinth/modules/dynamicdns/__init__.py:33 msgid "" @@ -1698,6 +1701,7 @@ msgstr "" #: plinth/modules/dynamicdns/forms.py:45 msgid "Use this option if your provider uses self signed certificates." msgstr "" +"Використовуйте цю опцію, якщо ваш провайдер використовує власні сертифікати." #: plinth/modules/dynamicdns/forms.py:48 msgid "" @@ -1707,7 +1711,7 @@ msgstr "" #: plinth/modules/dynamicdns/forms.py:51 msgid "Leave this field empty if you want to keep your current password." -msgstr "" +msgstr "Залишіть це поле порожнім, якщо хочете зберегти поточний пароль." #: plinth/modules/dynamicdns/forms.py:54 #, python-brace-format @@ -1919,7 +1923,7 @@ msgstr "" #: plinth/modules/ejabberd/forms.py:27 plinth/modules/matrixsynapse/forms.py:22 msgid "Automatically manage audio/video call setup" -msgstr "" +msgstr "Автоматично керувати налаштуваннями авдіо/відео дзвінків" #: plinth/modules/ejabberd/forms.py:29 #, python-brace-format @@ -2002,7 +2006,7 @@ msgstr "Сервер чату" #: plinth/modules/email_server/__init__.py:81 msgid "Powered by Postfix, Dovecot & Rspamd" -msgstr "" +msgstr "Працює на Postfix, Dovecot та Rspamd" #: plinth/modules/email_server/aliases/__init__.py:66 #: plinth/modules/email_server/aliases/__init__.py:73 @@ -2019,11 +2023,11 @@ msgstr "" #: plinth/modules/email_server/aliases/models.py:17 msgid "Must start and end with a-z or 0-9" -msgstr "" +msgstr "Має починатися і закінчуватися на a-z або 0-9" #: plinth/modules/email_server/aliases/models.py:19 msgid "Cannot be a number" -msgstr "" +msgstr "Не може бути числом" #: plinth/modules/email_server/audit/home.py:23 #: plinth/modules/email_server/audit/home.py:32 @@ -2046,7 +2050,7 @@ msgstr "" #: plinth/modules/email_server/audit/rcube.py:31 msgid "RoundCube availability" -msgstr "" +msgstr "Доступність RoundCube" #: plinth/modules/email_server/audit/rcube.py:32 msgid "RoundCube configured for FreedomBox email" From d49d7f9b412f7828a8cd1579d5f0655e7237fb0a Mon Sep 17 00:00:00 2001 From: Fioddor Superconcentrado Date: Wed, 1 Sep 2021 09:09:15 +0200 Subject: [PATCH 06/57] lintian: Overrides for remove-on-upgrade dpkg conffiles flag Signed-off-by: Fioddor Superconcentrado [sunil: Limit the overrides to just the flag not understood by lintian] Signed-off-by: Sunil Mohan Adapa Reviewed-by: Sunil Mohan Adapa --- debian/freedombox.lintian-overrides | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/debian/freedombox.lintian-overrides b/debian/freedombox.lintian-overrides index 549015397..e934a69f3 100644 --- a/debian/freedombox.lintian-overrides +++ b/debian/freedombox.lintian-overrides @@ -18,3 +18,12 @@ freedombox binary: web-application-works-only-with-apache # Temporary workaround for https://bugs.debian.org/992465 freedombox: systemd-service-file-outside-lib usr/lib/systemd/system/*.service + +# Since dpkg 1.20.6, there is support for a flag called remove-on-upgrade in +# DEBIAN/conffiles. debhelper 13.5+ will generate conffiles with flag from +# .maintscript instead of using postinst scripts. Lintian does not yet +# understand this flag and generates several errors. Ignore all errors related +# to this flag until lintian is fixed. +freedombox: conffile-is-not-in-package remove-on-upgrade * +freedombox: non-etc-file-marked-as-conffile remove-on-upgrade * +freedombox: relative-conffile remove-on-upgrade * From 4109d087bfae3435fb89dd412fdef6bbcfa06391 Mon Sep 17 00:00:00 2001 From: Tiago Zaniquelli Date: Fri, 30 Jul 2021 09:59:53 -0300 Subject: [PATCH 07/57] plinth: remove diagnose command [fioddor: squash fixing commit] Signed-off-by: Fioddor Superconcentrado Reviewed-by: Fioddor Superconcentrado --- plinth/__main__.py | 26 -------------------------- 1 file changed, 26 deletions(-) diff --git a/plinth/__main__.py b/plinth/__main__.py index c8fdbce75..171bcd36c 100644 --- a/plinth/__main__.py +++ b/plinth/__main__.py @@ -2,7 +2,6 @@ # SPDX-License-Identifier: AGPL-3.0-or-later import argparse -import importlib import logging import sys @@ -36,8 +35,6 @@ def parse_arguments(): parser.add_argument( '--setup-no-install', default=False, nargs='*', help='run setup tasks without installing packages and exit') - parser.add_argument('--diagnose', action='store_true', default=False, - help='run diagnostic tests and exit') parser.add_argument('--list-dependencies', default=False, nargs='*', help='list package dependencies for essential modules') parser.add_argument('--list-modules', default=False, nargs='*', @@ -84,26 +81,6 @@ def list_modules(modules_type): sys.exit() -def run_diagnostics_and_exit(): - """Run diagostics on all modules and exit.""" - module = importlib.import_module('plinth.modules.diagnostics.diagnostics') - error_code = 0 - try: - module.run_on_all_modules() - except Exception as exception: - logger.exception('Error running diagnostics - %s', exception) - error_code = 2 - - for module, results in module.current_results['results'].items(): - for test, result_value in results: - print('{result_value}: {module}: {test}'.format( - result_value=result_value, test=test, module=module)) - if result_value != 'passed': - error_code = 1 - - sys.exit(error_code) - - def adapt_config(arguments): """Give commandline arguments precedence over config entries""" for argument_name in precedence_commandline_arguments: @@ -160,9 +137,6 @@ def main(): if arguments.list_modules is not False: list_modules(arguments.list_modules) - if arguments.diagnose: - run_diagnostics_and_exit() - setup.run_setup_in_background() glib.run() From 956b17da062715990024684be6c969c4e40d21c7 Mon Sep 17 00:00:00 2001 From: Joseph Nuthalapati Date: Sat, 10 Jul 2021 10:12:48 +0530 Subject: [PATCH 08/57] apache: Drop support for SSLv3, TLSv1 and TLSv1.1 - As recommended by Mozilla SSL Configuration Generator for 'intermediate' compatibility configuration: https://ssl-config.mozilla.org/ - As recommended by IETF RFC 7525: https://datatracker.ietf.org/doc/html/rfc7525#section-3.1.1 - As recommended by NIST: Guidelines for the Selection, Configuration, and Use of Transport Layer Security (TLS) Implementations: https://nvlpubs.nist.gov/nistpubs/SpecialPublications/NIST.SP.800-52r2.pdf - The following are now the client version requirements for FreedomBox web interface: Firefox: 27, Android: 4.4.2, Chrome: 31, Edge: 12, IE: 11 (Win7), Java: 8u31, OpenSSL: 1.0.1, Opera: 20, Safari: 9 Signed-off-by: Joseph Nuthalapati [sunil: Drop SSLv2, it is not valid anymore as per Apache manual] [sunil: More detailed commit message and comments] Signed-off-by: Sunil Mohan Adapa Reviewed-by: Sunil Mohan Adapa --- .../freedombox-tls-site-macro.conf | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/data/etc/apache2/conf-available/freedombox-tls-site-macro.conf b/data/etc/apache2/conf-available/freedombox-tls-site-macro.conf index 8d5549b34..168dec448 100644 --- a/data/etc/apache2/conf-available/freedombox-tls-site-macro.conf +++ b/data/etc/apache2/conf-available/freedombox-tls-site-macro.conf @@ -1,6 +1,6 @@ - # mod_gnutls default options. See /etc/apache2/site-available/default-tls.conf + # mod_gnutls default options. See /etc/apache2/sites-available/default-tls.conf ServerAdmin webmaster@localhost @@ -11,15 +11,16 @@ CustomLog ${APACHE_LOG_DIR}/ssl_access.log combined GnuTLSEnable On + # Automatically obtained certificates from Let's Encrypt GnuTLSCertificateFile /etc/letsencrypt/live/$domain/fullchain.pem GnuTLSKeyFile /etc/letsencrypt/live/$domain/privkey.pem - # See http://www.outoforder.cc/projects/apache/mod_gnutls/docs/#GnuTLSPriorities - GnuTLSPriorities NORMAL + # See http://www.outoforder.cc/projects/httpd/mod_gnutls/docs/#GnuTLSPriorities + GnuTLSPriorities NORMAL:-VERS-SSL3.0:-VERS-TLS1.0:-VERS-TLS1.1 - # mod_ssl default options. See /etc/apache2/site-available/default-ssl.conf + # mod_ssl default options. See /etc/apache2/sites-available/default-ssl.conf ServerAdmin webmaster@localhost @@ -30,6 +31,13 @@ CustomLog ${APACHE_LOG_DIR}/access.log combined SSLEngine on + + # Disable TLS1.1 and below. Client support: Firefox: 27, Android: + # 4.4.2, Chrome: 31, Edge: 12, IE: 11 (Win7), Java: 8u31, OpenSSL: + # 1.0.1, Opera: 20, Safari: 9. See: + # https://wiki.mozilla.org/Security/Server_Side_TLS + SSLProtocol all -SSLv3 -TLSv1 -TLSv1.1 + # Automatically obtained certificates from Let's Encrypt SSLCertificateFile /etc/letsencrypt/live/$domain/fullchain.pem SSLCertificateKeyFile /etc/letsencrypt/live/$domain/privkey.pem From 5669a6088d7b1509bf8f9b48a7a01fe000388221 Mon Sep 17 00:00:00 2001 From: ikmaak Date: Thu, 2 Sep 2021 07:14:56 +0000 Subject: [PATCH 09/57] Translated using Weblate (Dutch) Currently translated at 96.4% (1460 of 1513 strings) --- plinth/locale/nl/LC_MESSAGES/django.po | 28 ++++++++------------------ 1 file changed, 8 insertions(+), 20 deletions(-) diff --git a/plinth/locale/nl/LC_MESSAGES/django.po b/plinth/locale/nl/LC_MESSAGES/django.po index 83c6ab5ea..27d41aa21 100644 --- a/plinth/locale/nl/LC_MESSAGES/django.po +++ b/plinth/locale/nl/LC_MESSAGES/django.po @@ -8,7 +8,7 @@ msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2021-08-30 19:29-0400\n" -"PO-Revision-Date: 2021-04-22 21:32+0000\n" +"PO-Revision-Date: 2021-09-03 07:35+0000\n" "Last-Translator: ikmaak \n" "Language-Team: Dutch \n" @@ -17,7 +17,7 @@ msgstr "" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=n != 1;\n" -"X-Generator: Weblate 4.7-dev\n" +"X-Generator: Weblate 4.8.1-dev\n" "X-Language: nl_NL\n" "X-Source-Language: C\n" @@ -2084,10 +2084,8 @@ msgstr "" "de Instellingen pagina." #: plinth/modules/email_server/__init__.py:49 -#, fuzzy -#| msgid "Chat Server" msgid "Email Server" -msgstr "Chatserver" +msgstr "E-mailserver" #: plinth/modules/email_server/__init__.py:81 msgid "Powered by Postfix, Dovecot & Rspamd" @@ -2116,10 +2114,8 @@ msgstr "" #: plinth/modules/email_server/audit/home.py:23 #: plinth/modules/email_server/audit/home.py:32 -#, fuzzy -#| msgid "Directory does not exist." msgid "User does not exist" -msgstr "Map bestaat niet." +msgstr "Gebruiker bestaat niet" #: plinth/modules/email_server/audit/ldap.py:66 msgid "Postfix-Dovecot SASL integration" @@ -2134,10 +2130,8 @@ msgid "Postfix local recipient maps" msgstr "" #: plinth/modules/email_server/audit/rcube.py:31 -#, fuzzy -#| msgid "unavailable" msgid "RoundCube availability" -msgstr "niet beschikbaar" +msgstr "RoundCube-beschikbaarheid" #: plinth/modules/email_server/audit/rcube.py:32 msgid "RoundCube configured for FreedomBox email" @@ -2156,16 +2150,12 @@ msgid "Postfix uses a TLS certificate" msgstr "" #: plinth/modules/email_server/audit/tls.py:90 -#, fuzzy -#| msgid "No certificate" msgid "Has a TLS certificate" -msgstr "Geen certificaat" +msgstr "Heeft een TLS-certificaat" #: plinth/modules/email_server/forms.py:7 -#, fuzzy -#| msgid "Domain" msgid "domain" -msgstr "Domein" +msgstr "domein" #: plinth/modules/email_server/forms.py:14 msgid "New alias (without @domain)" @@ -2282,10 +2272,8 @@ msgid "Create home directory" msgstr "Mediamap bijgewerkt" #: plinth/modules/email_server/templates/tls_form.html:10 -#, fuzzy -#| msgid "Current status:" msgid "Keep current settings" -msgstr "Huidige status:" +msgstr "Huidige instellingen behouden" #: plinth/modules/email_server/templates/tls_form.html:19 #, fuzzy From 4e1b70b6bfe755f9c2cb3f9894349ca62bb5d071 Mon Sep 17 00:00:00 2001 From: ikmaak Date: Fri, 3 Sep 2021 08:51:50 +0000 Subject: [PATCH 10/57] Translated using Weblate (Dutch) Currently translated at 97.5% (1476 of 1513 strings) --- plinth/locale/nl/LC_MESSAGES/django.po | 77 +++++++------------------- 1 file changed, 20 insertions(+), 57 deletions(-) diff --git a/plinth/locale/nl/LC_MESSAGES/django.po b/plinth/locale/nl/LC_MESSAGES/django.po index 27d41aa21..bfb30bdca 100644 --- a/plinth/locale/nl/LC_MESSAGES/django.po +++ b/plinth/locale/nl/LC_MESSAGES/django.po @@ -8,7 +8,7 @@ msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2021-08-30 19:29-0400\n" -"PO-Revision-Date: 2021-09-03 07:35+0000\n" +"PO-Revision-Date: 2021-09-04 09:34+0000\n" "Last-Translator: ikmaak \n" "Language-Team: Dutch \n" @@ -2168,10 +2168,8 @@ msgid "Roundcube" msgstr "Roundcube" #: plinth/modules/email_server/manifest.py:12 -#, fuzzy -#| msgid "Mozilla Thunderbird" msgid "Thunderbird" -msgstr "Mozilla Thunderbird" +msgstr "Thunderbird" #: plinth/modules/email_server/manifest.py:27 msgid "K-9 Mail" @@ -2186,28 +2184,20 @@ msgid "You have no email aliases." msgstr "" #: plinth/modules/email_server/templates/email_alias.html:21 -#, fuzzy -#| msgid "Disabled" msgid "Disable selected" -msgstr "Uitgeschakeld" +msgstr "Selectie uitschakelen" #: plinth/modules/email_server/templates/email_alias.html:24 -#, fuzzy -#| msgid "cable is connected" msgid "Enable selected" -msgstr "kabel is verbonden" +msgstr "Selectie inschakelen" #: plinth/modules/email_server/templates/email_alias.html:27 -#, fuzzy -#| msgid "Deleted selected snapshots" msgid "Delete selected" -msgstr "Verwijderde geselecteerde snapshots" +msgstr "Verwijder selectie" #: plinth/modules/email_server/templates/email_alias.html:31 -#, fuzzy -#| msgid "Create a new backup" msgid "Create a new email alias" -msgstr "Maak een nieuwe back-up" +msgstr "Nieuwe e-mailalias maken" #: plinth/modules/email_server/templates/email_alias.html:38 #: plinth/modules/tahoe/templates/tahoe-post-setup.html:59 @@ -2215,10 +2205,8 @@ msgid "Add" msgstr "Toevoegen" #: plinth/modules/email_server/templates/email_domains.html:19 -#, fuzzy -#| msgid "Backups" msgid "New value" -msgstr "Back-ups" +msgstr "Nieuwe waarde" #: plinth/modules/email_server/templates/email_domains.html:31 #: plinth/modules/email_server/templates/email_security.html:23 @@ -2276,32 +2264,24 @@ msgid "Keep current settings" msgstr "Huidige instellingen behouden" #: plinth/modules/email_server/templates/tls_form.html:19 -#, fuzzy -#| msgid "Let's Encrypt" msgid "Use Let's Encrypt" -msgstr "Let's Encrypt" +msgstr "Gebruik Let's Encrypt" #: plinth/modules/email_server/templates/tls_form.html:24 -#, fuzzy -#| msgid "Domain name set" msgid "Common name" -msgstr "Domeinnaam ingesteld" +msgstr "Algemene naam" #: plinth/modules/email_server/templates/tls_form.html:38 msgid "Use custom values" msgstr "" #: plinth/modules/email_server/templates/tls_form.html:43 -#, fuzzy -#| msgid "Certificate Status" msgid "Certificate path" -msgstr "Certificaatstatus" +msgstr "Pad van certificaat" #: plinth/modules/email_server/templates/tls_form.html:53 -#, fuzzy -#| msgid "Private key of this machine" msgid "Private key path" -msgstr "Privésleutel van deze machine" +msgstr "Pad van privésleutel" #: plinth/modules/email_server/templates/tls_form.html:67 msgid "Use system default" @@ -2342,16 +2322,12 @@ msgid "Check syslog for more information" msgstr "" #: plinth/modules/email_server/views.py:181 -#, fuzzy -#| msgid "Enable damage" msgid "Enabled aliases" -msgstr "Inschakelen Schade" +msgstr "Aktieve aliassen" #: plinth/modules/email_server/views.py:182 -#, fuzzy -#| msgid "Disabled" msgid "Disabled aliases" -msgstr "Uitgeschakeld" +msgstr "Inactieve aliassen" #: plinth/modules/firewall/__init__.py:33 #, python-brace-format @@ -6215,23 +6191,16 @@ msgstr "" "de kerndiensten en gebruikersinterface voor een FreedomBox server levert." #: plinth/modules/security/templates/security_report.html:19 -#, fuzzy -#| msgid "" -#| "The following table lists the current reported number, and historical " -#| "count, of security vulnerabilities for each installed app. More " -#| "information on the vulnerabilities can be found on the Debian Security Bug Tracker." msgid "" "The following table lists the current reported number of security " "vulnerabilities for each installed app. More information on the " "vulnerabilities can be found on the Debian Security Bug Tracker." msgstr "" -"In de volgende tabel wordt het huidige gerapporteerde aantal en het " -"historische aantal beveiligingsproblemen voor elke geïnstalleerde toepassing " -"weergegeven. Meer informatie over de kwetsbaarheden is te vinden op de Debian Security Bug " -"Tracker." +"In de volgende tabel wordt het huidige gerapporteerde aantal " +"beveiligingsproblemen voor elke geïnstalleerde toepassing weergegeven. Meer " +"informatie over de kwetsbaarheden is te vinden op de Debian Security Bug Tracker." #: plinth/modules/security/templates/security_report.html:28 msgid "" @@ -8300,22 +8269,16 @@ msgstr "" #: plinth/modules/wordpress/__init__.py:69 #: plinth/modules/wordpress/manifest.py:6 -#, fuzzy -#| msgid "Address" msgid "WordPress" -msgstr "Adres" +msgstr "WordPress" #: plinth/modules/wordpress/__init__.py:70 -#, fuzzy -#| msgid "Wiki and Blog" msgid "Website and Blog" -msgstr "Wiki en Blog" +msgstr "Website en Blog" #: plinth/modules/wordpress/forms.py:14 -#, fuzzy -#| msgid "public access" msgid "Public access" -msgstr "openbare toegang" +msgstr "Openbare toegang" #: plinth/modules/wordpress/forms.py:15 msgid "" From beaf431ee65961d591f0aaea8854d8c7353e4fb4 Mon Sep 17 00:00:00 2001 From: Joseph Nuthalapati Date: Fri, 3 Sep 2021 18:14:27 +0530 Subject: [PATCH 11/57] mediawiki: Backup and restore uploaded files Fixes #1747 MediaWiki on Debian seems to store all uploaded files under images/ folder. The administrator can enable additional file types. In the default configuration, only image files are permitted. Manually tested that backup/restore works irrespective of file type. Signed-off-by: Joseph Nuthalapati Reviewed-by: Sunil Mohan Adapa --- plinth/modules/mediawiki/manifest.py | 4 +- .../modules/mediawiki/tests/mediawiki.feature | 9 ++- .../mediawiki/tests/test_functional.py | 60 +++++++++++++++---- 3 files changed, 56 insertions(+), 17 deletions(-) diff --git a/plinth/modules/mediawiki/manifest.py b/plinth/modules/mediawiki/manifest.py index 1612300d4..ff8e5142f 100644 --- a/plinth/modules/mediawiki/manifest.py +++ b/plinth/modules/mediawiki/manifest.py @@ -15,7 +15,9 @@ backup = { 'files': ['/etc/mediawiki/FreedomBoxSettings.php'] }, 'data': { - 'directories': ['/var/lib/mediawiki-db/'] + 'directories': [ + '/var/lib/mediawiki-db/', '/var/lib/mediawiki/images/' + ] }, 'services': ['mediawiki-jobrunner'] } diff --git a/plinth/modules/mediawiki/tests/mediawiki.feature b/plinth/modules/mediawiki/tests/mediawiki.feature index fb2bc6f71..bf35af7d8 100644 --- a/plinth/modules/mediawiki/tests/mediawiki.feature +++ b/plinth/modules/mediawiki/tests/mediawiki.feature @@ -67,12 +67,15 @@ Scenario: Upload SVG image @backups Scenario: Backup and restore mediawiki Given the mediawiki application is enabled + And I ensure that there is Noise.png image with credentials admin and whatever123 When I create a backup of the mediawiki app data with name test_mediawiki - When I enable mediawiki public registrations - And I delete the mediawiki main page + And I enable mediawiki public registrations + And I delete Noise.png image with credentials admin and whatever123 + And I delete the mediawiki main page with credentials admin and whatever123 And I restore the mediawiki app data backup with name test_mediawiki Then the mediawiki main page should be restored - Then the mediawiki site should allow creating accounts + And there should be Noise.png image + And the mediawiki site should allow creating accounts Scenario: Disable mediawiki application Given the mediawiki application is enabled diff --git a/plinth/modules/mediawiki/tests/test_functional.py b/plinth/modules/mediawiki/tests/test_functional.py index 48654a0ac..4498de92f 100644 --- a/plinth/modules/mediawiki/tests/test_functional.py +++ b/plinth/modules/mediawiki/tests/test_functional.py @@ -6,10 +6,10 @@ Functional, browser based tests for mediawiki app. import pathlib from urllib.parse import urlparse -from pytest_bdd import given, parsers, scenarios, then, when - +import requests from plinth.tests import functional from plinth.tests.functional import config +from pytest_bdd import given, parsers, scenarios, then, when scenarios('mediawiki.feature') @@ -76,9 +76,18 @@ def login_to_mediawiki_with_credentials(session_browser, username, password): _login_with_credentials(session_browser, username, password) -@when('I delete the mediawiki main page') -def mediawiki_delete_main_page(session_browser): - _delete_main_page(session_browser) +@when( + parsers.parse('I delete the mediawiki main page with credentials ' + '{username:w} and {password:w}')) +def mediawiki_delete_main_page(session_browser, username, password): + _delete_main_page(session_browser, username, password) + + +@when( + parsers.parse('I delete {image:S} image with credentials ' + '{username:w} and {password:w}')) +def delete_image(session_browser, username, password, image): + _delete_image(session_browser, username, password, image) @then('the mediawiki main page should be restored') @@ -94,10 +103,17 @@ def upload_image(session_browser, username, password, image): _upload_image(session_browser, username, password, image) +@given( + parsers.parse('I ensure that there is {image:S} image with credentials ' + '{username:w} and {password:w}')) +def ensure_image_exists(session_browser, username, password, image): + if not _image_exists(session_browser, image): + _upload_image(session_browser, username, password, image) + + @then(parsers.parse('there should be {image:S} image')) def uploaded_image_should_be_available(session_browser, image): - uploaded_image = _get_uploaded_image(session_browser, image) - assert image.lower() == uploaded_image.lower() + assert _image_exists(session_browser, image) def _enable_public_registrations(browser): @@ -179,7 +195,7 @@ def _login_with_credentials(browser, username, password): args=['t-upload']) -def _upload_image(browser, username, password, image): +def _upload_image(browser, username, password, image, ignore_warnings=True): """Upload an image to MediaWiki. Idempotent.""" functional.visit(browser, '/mediawiki') _login(browser, username, password) @@ -187,25 +203,43 @@ def _upload_image(browser, username, password, image): # Upload file functional.visit(browser, '/mediawiki/Special:Upload') file_path = pathlib.Path(__file__).parent - file_path /= '../../../../static/themes/default/img/' + image + file_path /= '../../../../static/themes/default/img/' + image.lower() browser.attach_file('wpUploadFile', str(file_path.resolve())) + if ignore_warnings: # allow uploading file with the same name + browser.find_by_name('wpIgnoreWarning').first.click() functional.submit(browser, element=browser.find_by_name('wpUpload')[0]) +def _delete_image(browser, username, password, image): + """Delete an image from MediaWiki.""" + _login(browser, username, password) + path = f'/mediawiki/index.php?title=File:{image}&action=delete' + functional.visit(browser, path) + delete_button = browser.find_by_id('mw-filedelete-submit') + functional.submit(browser, element=delete_button) + + def _get_number_of_uploaded_images(browser): functional.visit(browser, '/mediawiki/Special:ListFiles') return len(browser.find_by_css('.TablePager_col_img_timestamp')) -def _get_uploaded_image(browser, image): +def _image_exists(browser, image): + """Check whether the given image exists.""" functional.visit(browser, '/mediawiki/Special:ListFiles') elements = browser.find_link_by_partial_href(image) - return elements[0].value + if not elements: # Necessary but insufficient check. + # Special:ListFiles also shows deleted images. + return False + + # The second hyperlink is a direct link to the image. + response = requests.get(elements[1]['href'], verify=False) + return response.status_code != 404 -def _delete_main_page(browser): +def _delete_main_page(browser, username, password): """Delete the mediawiki main page.""" - _login(browser, 'admin', 'whatever123') + _login(browser, username, password) functional.visit(browser, '/mediawiki/index.php?title=Main_Page&action=delete') with functional.wait_for_page_update(browser): From 7fe5cf172b0ba6bc283b3e8492c0ffb79ecfe153 Mon Sep 17 00:00:00 2001 From: Joseph Nuthalapati Date: Fri, 3 Sep 2021 18:19:08 +0530 Subject: [PATCH 12/57] mediawiki: Bump version number for 1.35 upgrade We already did a version bump (8 to 9) to run the maintenance script `update.php` for Debian testing. However, this had no effect on Debian stable installations (essentially idempotent since no version change happened then). Bumping version to 10 to ensure that update.php script runs for FreedomBoxes on Debian Bullseye. Signed-off-by: Joseph Nuthalapati Reviewed-by: Sunil Mohan Adapa --- plinth/modules/mediawiki/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plinth/modules/mediawiki/__init__.py b/plinth/modules/mediawiki/__init__.py index 99befe518..4c1762a32 100644 --- a/plinth/modules/mediawiki/__init__.py +++ b/plinth/modules/mediawiki/__init__.py @@ -18,7 +18,7 @@ from plinth.modules.firewall.components import Firewall from . import manifest -version = 9 +version = 10 managed_packages = ['mediawiki', 'imagemagick', 'php-sqlite3'] From 06ded9fe484931d6ff39aa702d2208ec16438956 Mon Sep 17 00:00:00 2001 From: Sunil Mohan Adapa Date: Sat, 4 Sep 2021 12:41:05 -0700 Subject: [PATCH 13/57] mediawiki: tests: functional: Fix races after flipping flags When flags are updated and mediawiki is interface is loaded, somehow, it does not always happen that the page immediate loaded reflects the changes. So, wait for the change to reflect. Signed-off-by: Sunil Mohan Adapa --- .../mediawiki/tests/test_functional.py | 33 +++++++++++-------- 1 file changed, 19 insertions(+), 14 deletions(-) diff --git a/plinth/modules/mediawiki/tests/test_functional.py b/plinth/modules/mediawiki/tests/test_functional.py index 4498de92f..3c9c50577 100644 --- a/plinth/modules/mediawiki/tests/test_functional.py +++ b/plinth/modules/mediawiki/tests/test_functional.py @@ -153,30 +153,35 @@ def _set_admin_password(browser, password): functional.submit(browser, form_class='form-configuration') -def _verify_create_account_link(browser): +def _is_create_account_available(browser): + """Load the create account page and return whether creating is allowed.""" functional.visit(browser, '/mediawiki/index.php/Special:CreateAccount') - assert functional.eventually(browser.is_element_present_by_id, - args=['wpCreateaccount']) + return browser.is_element_present_by_id('wpCreateaccount') + + +def _verify_create_account_link(browser): + assert functional.eventually(_is_create_account_available, args=[browser]) def _verify_no_create_account_link(browser): - functional.visit(browser, '/mediawiki/index.php/Special:CreateAccount') - assert functional.eventually(browser.is_element_not_present_by_id, - args=['wpCreateaccount']) + assert functional.eventually( + lambda: not _is_create_account_available(browser)) + + +def _is_anonymouse_read_allowed(browser): + """Load the main page and check if anonymous reading is allowed.""" + functional.visit(browser, '/mediawiki') + return browser.is_element_present_by_id('ca-nstab-main') def _verify_anonymous_reads_edits_link(browser): - functional.visit(browser, '/mediawiki') - assert functional.eventually(browser.is_element_present_by_id, - args=['ca-nstab-main']) + assert functional.eventually(_is_anonymouse_read_allowed, args=[browser]) def _verify_no_anonymous_reads_edits_link(browser): - functional.visit(browser, '/mediawiki') - assert functional.eventually(browser.is_element_not_present_by_id, - args=['ca-nstab-main']) - assert functional.eventually(browser.is_element_present_by_id, - args=['ca-nstab-special']) + assert functional.eventually( + lambda: not _is_anonymouse_read_allowed(browser)) + assert browser.is_element_present_by_id('ca-nstab-special') def _login(browser, username, password): From 94ce316f41a7a3cdb2cfd5ee25c41b8d845110f6 Mon Sep 17 00:00:00 2001 From: Joseph Nuthalapati Date: Sat, 4 Sep 2021 20:30:07 +0530 Subject: [PATCH 14/57] mediawiki: Enable a subset of default extensions Full list of default extensions in MediaWiki https://www.mediawiki.org/wiki/Bundled_extensions_and_skins This initial set of extensions are chosen from: - extensions that I've used myself over the past 3 years - testing done in #1267 Extensions that are advanced features for administrators, meant for spam control, advanced tags or suitable only to a specific kind of user are not included. Fixes #1382 More extensions can be enabled in the future if they're generally useful. Signed-off-by: Joseph Nuthalapati Reviewed-by: Sunil Mohan Adapa --- .../data/etc/mediawiki/FreedomBoxStaticSettings.php | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/plinth/modules/mediawiki/data/etc/mediawiki/FreedomBoxStaticSettings.php b/plinth/modules/mediawiki/data/etc/mediawiki/FreedomBoxStaticSettings.php index 5bda4c474..6026cfd6d 100644 --- a/plinth/modules/mediawiki/data/etc/mediawiki/FreedomBoxStaticSettings.php +++ b/plinth/modules/mediawiki/data/etc/mediawiki/FreedomBoxStaticSettings.php @@ -44,3 +44,11 @@ $wgDefaultSkin = "timeless"; # Domain Name $wgServer = "https://freedombox.local"; + +# Enable default extensions +wfLoadExtension( 'Cite' ); +wfLoadExtension( 'Interwiki' ); +wfLoadExtension( 'MultimediaViewer' ); +wfLoadExtension( 'Renameuser' ); +wfLoadExtension( 'VisualEditor' ); +wfLoadExtension( 'WikiEditor' ); From 7524d60822dec2f199604d6d0d70aefc4f3bac6c Mon Sep 17 00:00:00 2001 From: ikmaak Date: Sat, 4 Sep 2021 12:54:20 +0000 Subject: [PATCH 15/57] Translated using Weblate (Dutch) Currently translated at 98.4% (1489 of 1513 strings) --- plinth/locale/nl/LC_MESSAGES/django.po | 28 +++++++++++++------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/plinth/locale/nl/LC_MESSAGES/django.po b/plinth/locale/nl/LC_MESSAGES/django.po index bfb30bdca..14da9e9f2 100644 --- a/plinth/locale/nl/LC_MESSAGES/django.po +++ b/plinth/locale/nl/LC_MESSAGES/django.po @@ -8,7 +8,7 @@ msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2021-08-30 19:29-0400\n" -"PO-Revision-Date: 2021-09-04 09:34+0000\n" +"PO-Revision-Date: 2021-09-05 13:34+0000\n" "Last-Translator: ikmaak \n" "Language-Team: Dutch \n" @@ -2089,28 +2089,28 @@ msgstr "E-mailserver" #: plinth/modules/email_server/__init__.py:81 msgid "Powered by Postfix, Dovecot & Rspamd" -msgstr "" +msgstr "Maakt gebruik van Postfix, Dovecot & Rspamd" #: plinth/modules/email_server/aliases/__init__.py:66 #: plinth/modules/email_server/aliases/__init__.py:73 msgid "The alias was taken" -msgstr "" +msgstr "Deze alias is al in gebruik" #: plinth/modules/email_server/aliases/models.py:13 msgid "Must be at least 2 characters long" -msgstr "" +msgstr "Moet ten minste 2 tekens lang zijn" #: plinth/modules/email_server/aliases/models.py:15 msgid "Contains illegal characters" -msgstr "" +msgstr "Bevat ongeldige tekens" #: plinth/modules/email_server/aliases/models.py:17 msgid "Must start and end with a-z or 0-9" -msgstr "" +msgstr "Moet beginnen en eindigen met a-z of 0-9" #: plinth/modules/email_server/aliases/models.py:19 msgid "Cannot be a number" -msgstr "" +msgstr "Kan geen getal zijn" #: plinth/modules/email_server/audit/home.py:23 #: plinth/modules/email_server/audit/home.py:32 @@ -2119,7 +2119,7 @@ msgstr "Gebruiker bestaat niet" #: plinth/modules/email_server/audit/ldap.py:66 msgid "Postfix-Dovecot SASL integration" -msgstr "" +msgstr "Postfix-Dovecot SASL-integratie" #: plinth/modules/email_server/audit/ldap.py:67 msgid "Postfix alias maps" @@ -2135,7 +2135,7 @@ msgstr "RoundCube-beschikbaarheid" #: plinth/modules/email_server/audit/rcube.py:32 msgid "RoundCube configured for FreedomBox email" -msgstr "" +msgstr "RoundCube geconfigureerd voor FreedomBox-e-mail" #: plinth/modules/email_server/audit/spam.py:86 msgid "Inbound and outbound mail filters" @@ -2143,11 +2143,11 @@ msgstr "" #: plinth/modules/email_server/audit/tls.py:80 msgid "Postfix TLS parameters" -msgstr "" +msgstr "Postfix TLS-parameters" #: plinth/modules/email_server/audit/tls.py:81 msgid "Postfix uses a TLS certificate" -msgstr "" +msgstr "Postfix gebruikt een TLS-certificaat" #: plinth/modules/email_server/audit/tls.py:90 msgid "Has a TLS certificate" @@ -2173,11 +2173,11 @@ msgstr "Thunderbird" #: plinth/modules/email_server/manifest.py:27 msgid "K-9 Mail" -msgstr "" +msgstr "K-9 Mail" #: plinth/modules/email_server/manifest.py:40 msgid "FairEmail" -msgstr "" +msgstr "FairEmail" #: plinth/modules/email_server/templates/email_alias.html:12 msgid "You have no email aliases." @@ -2223,7 +2223,7 @@ msgstr "" #: plinth/modules/email_server/templates/email_security.html:13 msgid "Postfix TLS" -msgstr "" +msgstr "Postfix TLS" #: plinth/modules/email_server/templates/email_security.html:18 msgid "Dovecot TLS" From 9a5a362f5020b0e9735b464b62727e8cc87e2042 Mon Sep 17 00:00:00 2001 From: James Valleroy Date: Sun, 5 Sep 2021 11:35:02 -0400 Subject: [PATCH 16/57] container: Update stable image for bullseye Signed-off-by: James Valleroy [sunil: Use the bullseye/ directory for more URL stability] Signed-off-by: Sunil Mohan Adapa Reviewed-by: Sunil Mohan Adapa --- container | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/container b/container index 14ac23f60..c0a945667 100755 --- a/container +++ b/container @@ -130,7 +130,7 @@ from urllib.request import urlopen URLS = { 'stable': 'https://ftp.freedombox.org/pub/freedombox/hardware/' - 'amd64/stable/freedombox-stable-free_buster_all-amd64.img.xz', + 'amd64/bullseye/freedombox-bullseye-free_all-amd64.img.xz', 'testing': 'https://ftp.freedombox.org/pub/freedombox/hardware/' 'amd64/testing/freedombox-testing-free_dev_all-amd64.img.xz', 'unstable': 'https://ftp.freedombox.org/pub/freedombox/hardware/' From 60ef19b6ec6cab5743bd8d5d09d77e818dd1c14e Mon Sep 17 00:00:00 2001 From: Joseph Nuthalapati Date: Sun, 5 Sep 2021 10:21:27 +0530 Subject: [PATCH 17/57] mediawiki: Switch to MediaWiki 2020 logo Signed-off-by: Joseph Nuthalapati [sunil: Added copyright year] [sunil: Scaled the images to sizes commonly used for FreedomBox logos] Signed-off-by: Sunil Mohan Adapa Reviewed-by: Sunil Mohan Adapa --- debian/copyright | 8 +- static/themes/default/icons/mediawiki.png | Bin 34177 -> 28223 bytes static/themes/default/icons/mediawiki.svg | 475 ++++++++++++++++++---- 3 files changed, 408 insertions(+), 75 deletions(-) diff --git a/debian/copyright b/debian/copyright index dc737e4d1..1c2aa540e 100644 --- a/debian/copyright +++ b/debian/copyright @@ -65,7 +65,6 @@ Files: static/themes/default/icons/diaspora.png static/themes/default/icons/ejabberd.png static/themes/default/icons/ejabberd.svg static/themes/default/icons/matrixsynapse.svg - static/themes/default/icons/mediawiki.svg static/themes/default/icons/privoxy.png static/themes/default/icons/privoxy.svg static/themes/default/icons/radicale.svg @@ -82,7 +81,6 @@ Comment: Placed into public domain by authors (or) Do not meet the threshold of originality Tango Desktop Project https://commons.wikimedia.org/wiki/File:Ejabberd_icon.png - https://commons.wikimedia.org/wiki/File:MediaWiki-notext.svg https://radicale.org/css/logo.svg https://github.com/resiprocate/resiprocate/blob/master/resip/stack/doc/reSIProcate-logo.svg License: public-domain @@ -137,6 +135,12 @@ Copyright: 2017 Kishan Raval Comment: https://github.com/thekishanraval/Logos License: GPL-3+ +Files: static/themes/default/icons/mediawiki.png + static/themes/default/icons/mediawiki.svg +Copyright: 2020 Serhio Magpie +Comment: https://commons.wikimedia.org/wiki/File:MediaWiki-2020-icon.svg +License: CC-BY-SA-4.0 + Files: static/themes/default/icons/minetest.png Copyright: 2011 erlehmann License: CC-BY-SA-3.0 diff --git a/static/themes/default/icons/mediawiki.png b/static/themes/default/icons/mediawiki.png index f172eddfe48de9377fc2526b083ec3889ad8e903..79b1b8fed590e9dfecf4683fa2cc7555b14dac25 100644 GIT binary patch literal 28223 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 literal 34177 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%0AD%vVeBK>?$f9PkTY++=U^$Ty%PH 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 diff --git a/static/themes/default/icons/mediawiki.svg b/static/themes/default/icons/mediawiki.svg index 44cd4b399..8bc9feee4 100644 --- a/static/themes/default/icons/mediawiki.svg +++ b/static/themes/default/icons/mediawiki.svg @@ -1,49 +1,25 @@ - - - - + inkscape:version="1.0.2 (e86c870879, 2021-01-15)" + inkscape:export-filename="/home/bunny/work/freedombox/freedombox/static/themes/default/icons/mediawiki.png" + inkscape:export-xdpi="48" + inkscape:export-ydpi="48"> + id="metadata66"> @@ -54,54 +30,407 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + id="petals" + class="petals" + style="fill:url(#linearGradient416)" + transform="matrix(5.1487299,0,0,5.1487817,-1.1574957,-0.05808295)"> + class="petal" + d="m 33.6,14.8 a 16.938,16.938 0 0 0 3.116,11.142 11.457,11.457 0 0 0 6.858,4.3 3.033,3.033 0 0 0 2.385,-0.713 11.924,11.924 0 0 0 3.634,-10.837 c -1.257,-7.54 -6.19,-12.43 -12.4,-17 A 32.468,32.468 0 0 0 33.6,14.8 Z" + id="path13" + style="fill:url(#linearGradient368)" /> + class="petal" + d="m 25.117,20.232 a 16.938,16.938 0 0 0 5.893,9.956 11.457,11.457 0 0 0 7.738,2.381 3.033,3.033 0 0 0 2.119,-1.306 11.924,11.924 0 0 0 0.705,-11.409 C 38.406,12.9 32.376,9.449 25.2,6.642 a 32.468,32.468 0 0 0 -0.083,13.59 z" + id="path15" + style="fill:url(#linearGradient370)" /> + class="petal" + d="m 18.329,27.677 a 16.937,16.937 0 0 0 8.271,8.092 11.457,11.457 0 0 0 8.09,0.3 3.033,3.033 0 0 0 1.709,-1.81 11.924,11.924 0 0 0 -2.271,-11.2 c -4.859,-5.9 -11.576,-7.67 -19.237,-8.523 a 32.466,32.466 0 0 0 3.438,13.141 z" + id="path17" + style="fill:url(#linearGradient372)" /> + class="petal" + d="M 13.7,36.626 A 16.938,16.938 0 0 0 23.781,42.3 11.457,11.457 0 0 0 31.672,40.493 3.033,3.033 0 0 0 32.854,38.3 11.924,11.924 0 0 0 27.761,28.067 C 21.54,23.624 14.594,23.655 6.974,24.813 A 32.468,32.468 0 0 0 13.7,36.626 Z" + id="path19" + style="fill:url(#linearGradient374)" /> + class="petal" + d="m 11.543,46.468 a 16.938,16.938 0 0 0 11.208,2.873 11.457,11.457 0 0 0 7.155,-3.788 3.034,3.034 0 0 0 0.575,-2.422 11.924,11.924 0 0 0 -7.568,-8.566 C 15.753,31.884 9.052,33.711 1.99,36.8 a 32.468,32.468 0 0 0 9.553,9.668 z" + id="path21" + style="fill:url(#linearGradient376)" /> + class="petal" + d="m 12.008,56.532 a 16.938,16.938 0 0 0 11.569,-0.126 11.457,11.457 0 0 0 5.931,-5.51 3.033,3.033 0 0 0 -0.072,-2.488 11.924,11.924 0 0 0 -9.527,-6.315 C 12.3,41.356 6.3,44.855 0.279,49.669 a 32.467,32.467 0 0 0 11.729,6.863 z" + id="path23" + style="fill:url(#linearGradient378)" /> + class="petal" + d="M 15.062,66.134 A 16.938,16.938 0 0 0 26.2,63.018 11.457,11.457 0 0 0 30.5,56.16 3.033,3.033 0 0 0 29.787,53.775 11.924,11.924 0 0 0 18.95,50.141 c -7.54,1.257 -12.43,6.19 -17,12.4 a 32.468,32.468 0 0 0 13.112,3.593 z" + id="path25" + style="fill:url(#linearGradient380)" /> + class="petal" + d="m 20.5,74.618 a 16.938,16.938 0 0 0 9.956,-5.893 11.457,11.457 0 0 0 2.381,-7.738 3.033,3.033 0 0 0 -1.306,-2.119 11.925,11.925 0 0 0 -11.409,-0.705 c -6.958,3.166 -10.4,9.2 -13.212,16.376 a 32.466,32.466 0 0 0 13.59,0.079 z" + id="path27" + style="fill:url(#linearGradient382)" /> + + + + + + + + + + + + + + + + From 1e2ee690d73754aa1df22d614b51f0e48d2d9772 Mon Sep 17 00:00:00 2001 From: James Valleroy Date: Sun, 5 Sep 2021 20:04:26 -0400 Subject: [PATCH 18/57] backups: Add functional test to disable schedule backups This test is at the end so that it leaves scheduled backups disabled while other tests are running. Helps #2058. Tests: - Ran functional tests for backups. All tests passed. - Confirmed that scheduled backups are disabled after backup tests are complete. Signed-off-by: James Valleroy Reviewed-by: Sunil Mohan Adapa --- plinth/modules/backups/tests/backups.feature | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/plinth/modules/backups/tests/backups.feature b/plinth/modules/backups/tests/backups.feature index f27910145..d3695483f 100644 --- a/plinth/modules/backups/tests/backups.feature +++ b/plinth/modules/backups/tests/backups.feature @@ -29,3 +29,8 @@ Scenario: Set a schedule for a repository Given the backup schedule is set to disable for 1 daily, 2 weekly and 3 monthly at 2:00 without app names When I set the backup schedule to enable for 10 daily, 20 weekly and 30 monthly at 15:00 without app firewall Then the schedule should be set to enable for 10 daily, 20 weekly and 30 monthly at 15:00 without app firewall + +Scenario: Disable schedule for a repository + Given the backup schedule is set to enable for 10 daily, 20 weekly and 30 monthly at 15:00 without app firewall + When I set the backup schedule to disable for 1 daily, 2 weekly and 3 monthly at 2:00 without app names + Then the schedule should be set to disable for 1 daily, 2 weekly and 3 monthly at 2:00 without app names From 06d8aade234f465046195cde1c0e5104a76a1df4 Mon Sep 17 00:00:00 2001 From: fliu <10025-fliu@users.noreply.salsa.debian.org> Date: Sat, 21 Aug 2021 21:23:58 +0000 Subject: [PATCH 19/57] email: Fix self.critical not callable error --- plinth/modules/email_server/audit/models.py | 18 +++++++++--------- .../email_server/templates/email_server.html | 4 ++-- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/plinth/modules/email_server/audit/models.py b/plinth/modules/email_server/audit/models.py index de4b3e77d..173e77630 100644 --- a/plinth/modules/email_server/audit/models.py +++ b/plinth/modules/email_server/audit/models.py @@ -19,7 +19,7 @@ class Diagnosis: """Class constructor""" self.title = title self.action = action - self.critical = [] + self.critical_errors = [] self.errors = [] def to_json(self): @@ -29,7 +29,7 @@ class Diagnosis: 'title': self.title, 'action': self.action, 'errors': self.errors, - 'critical': self.critical + 'critical_errors': self.critical_errors } @classmethod @@ -47,15 +47,15 @@ class Diagnosis: title = translate(title) or title result = cls(title, action=valid_dict['action']) result.errors.extend(valid_dict['errors']) - result.critical.extend(valid_dict['critical']) + result.critical_errors.extend(valid_dict['critical_errors']) return result def critical(self, message_fmt, *args): """Append a message to the critical errors list""" if args: - self.critical.append(message_fmt % args) + self.critical_errors.append(message_fmt % args) else: - self.critical.append(message_fmt) + self.critical_errors.append(message_fmt) def error(self, message_fmt, *args): """Append a message to the errors list""" @@ -69,7 +69,7 @@ class Diagnosis: if log: self.write_logs() - if self.critical: + if self.critical_errors: return [self.title, 'error'] elif self.errors: return [self.title, 'failed'] @@ -79,19 +79,19 @@ class Diagnosis: @property def has_failed(self): """True if the diagnosis has failed or contains an error""" - return (self.critical or self.errors) + return (self.critical_errors or self.errors) def write_logs(self): """Log errors and failures""" logger.debug('Ran audit: %s', self.title) - for message in self.critical: + for message in self.critical_errors: logger.critical(message) for message in self.errors: logger.error(message) def sorting_key(self): """The key function for list.sort""" - return (-len(self.critical), -len(self.errors), self.title) + return (-len(self.critical_errors), -len(self.errors), self.title) class MainCfDiagnosis(Diagnosis): diff --git a/plinth/modules/email_server/templates/email_server.html b/plinth/modules/email_server/templates/email_server.html index 78c1fd575..d4eab825f 100644 --- a/plinth/modules/email_server/templates/email_server.html +++ b/plinth/modules/email_server/templates/email_server.html @@ -21,7 +21,7 @@
  • {{ model.title }} - {% if model.critical %} + {% if model.critical_errors %} {% trans "error" %} {% elif model.errors %} {% trans "failed" %} @@ -41,7 +41,7 @@ {% endif %}
      - {% for message in model.critical %} + {% for message in model.critical_errors %}
    • {{ message }}
    • {% endfor %} {% for message in model.errors %} From 63746ce39f1772e4b6c34931189e7a70d9ee743d Mon Sep 17 00:00:00 2001 From: fliu <10025-fliu@users.noreply.salsa.debian.org> Date: Sat, 21 Aug 2021 21:26:16 +0000 Subject: [PATCH 20/57] email: postconf.get_many_unsafe: batch query --- plinth/modules/email_server/postconf.py | 20 ++++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) diff --git a/plinth/modules/email_server/postconf.py b/plinth/modules/email_server/postconf.py index cba3a007b..31826ddf5 100644 --- a/plinth/modules/email_server/postconf.py +++ b/plinth/modules/email_server/postconf.py @@ -61,10 +61,26 @@ def get_many(key_list): return get_many_unsafe(key_list) -def get_many_unsafe(key_iterator): +def get_many_unsafe(key_iterator, flag=''): result = {} + args = ['/sbin/postconf'] + if flag: + args.append(flag) + + number_of_keys = 0 for key in key_iterator: - result[key] = get_unsafe(key) + args.append(key) + number_of_keys += 1 + + stdout = _run(args) + for line in filter(None, stdout.split('\n')): + key, sep, value = line.partition('=') + if not sep: + raise ValueError('Invalid output detected') + result[key.strip()] = value.strip() + + if len(result) != number_of_keys: + raise ValueError('Some keys were missing from the output') return result From 41c86e0ba3d77ac4beac832db2ec119402c3c45d Mon Sep 17 00:00:00 2001 From: fliu <10025-fliu@users.noreply.salsa.debian.org> Date: Sat, 21 Aug 2021 21:28:43 +0000 Subject: [PATCH 21/57] email: configure postfix domain names --- plinth/modules/email_server/__init__.py | 1 + plinth/modules/email_server/audit/domain.py | 152 +++++++++++++++++++- plinth/modules/email_server/views.py | 2 +- 3 files changed, 149 insertions(+), 6 deletions(-) diff --git a/plinth/modules/email_server/__init__.py b/plinth/modules/email_server/__init__.py index 305a10bf3..51851baa2 100644 --- a/plinth/modules/email_server/__init__.py +++ b/plinth/modules/email_server/__init__.py @@ -145,6 +145,7 @@ def setup(helper, old_version=None): """Installs and configures module""" helper.install(packages) helper.install(packages_bloat, skip_recommends=True) + helper.call('post', audit.domain.repair) helper.call('post', audit.ldap.repair) helper.call('post', audit.spam.repair) helper.call('post', audit.tls.repair) diff --git a/plinth/modules/email_server/audit/domain.py b/plinth/modules/email_server/audit/domain.py index 77f1951cb..bdd432e69 100644 --- a/plinth/modules/email_server/audit/domain.py +++ b/plinth/modules/email_server/audit/domain.py @@ -13,8 +13,10 @@ import time from types import SimpleNamespace from django.core.exceptions import ValidationError -from plinth.errors import ActionError +from django.utils.translation import ugettext_lazy as _ from plinth.actions import superuser_run +from plinth.errors import ActionError +from plinth.modules.config import get_domainname from . import models from plinth.modules.email_server import interproc, postconf @@ -29,13 +31,153 @@ class ClientError(RuntimeError): def get(): - # Stub - return [models.Diagnosis('Email domains')] + translation_table = [ + (check_postfix_domains, _('Postfix domain name config')), + ] + results = [] + with postconf.mutex.lock_all(): + for check, title in translation_table: + results.append(check(title)) + return results def repair(): - # Stub - raise RuntimeError() + superuser_run('email_server', ['-i', 'domain', 'set_up']) + + +def repair_component(action_name): + allowed_actions = {'set_up': ['postfix']} + if action_name not in allowed_actions: + return + superuser_run('email_server', ['-i', 'domain', action_name]) + return allowed_actions[action_name] + + +def action_set_up(): + with postconf.mutex.lock_all(): + fix_postfix_domains(check_postfix_domains()) + + +def check_postfix_domains(title=''): + diagnosis = models.MainCfDiagnosis(title, action='set_up') + domain = get_domainname() or 'localhost' + postconf_keys = (k for k in managed_keys if not k.startswith('_')) + conf = postconf.get_many_unsafe(postconf_keys, flag='-x') + + dest_set = set(postconf.parse_maps(conf['mydestination'])) + deletion_set = set() + + temp = _amend_mailname(domain) + if temp is not None: + diagnosis.error('Set /etc/mailname to %s', temp) + diagnosis.flag('_mailname', temp) + + # Amend $mydomain and conf['mydomain'] + temp = _amend_mydomain(conf['mydomain'], domain) + if temp is not None: + diagnosis.error('Set $mydomain to %s', temp) + diagnosis.flag('mydomain', temp) + deletion_set.add(conf['mydomain']) + conf['mydomain'] = temp + + # Amend $myhostname and conf['myhostname'] + temp = _amend_myhostname(conf['myhostname'], conf['mydomain']) + if temp is not None: + diagnosis.error('Set $myhostname to %s', temp) + diagnosis.flag('myhostname', temp) + deletion_set.add(conf['myhostname']) + conf['myhostname'] = temp + + # Delete old domain names + deletion_set.discard('localhost') + dest_set.difference_update(deletion_set) + + # Amend $mydestination + temp = _amend_mydestination(dest_set, conf['mydomain'], conf['myhostname'], + diagnosis.error) + if temp is not None: + diagnosis.flag('mydestination', temp) + elif len(deletion_set) > 0: + corrected_value = ', '.join(sorted(dest_set)) + diagnosis.error('Update $mydestination') + diagnosis.flag('mydestination', corrected_value) + + return diagnosis + + +def _amend_mailname(domain): + with open('/etc/mailname', 'r') as fd: + mailname = fd.readline().strip() + + # If mailname is not localhost, refresh it + if mailname != 'localhost': + temp = _change_to_domain_name(mailname, domain, False) + if temp != mailname: + return temp + + return None + + +def _amend_mydomain(conf_value, domain): + temp = _change_to_domain_name(conf_value, domain, False) + if temp != conf_value: + return temp + + return None + + +def _amend_myhostname(conf_value, mydomain): + if conf_value != mydomain: + if not conf_value.endswith('.' + mydomain): + return mydomain + + return None + + +def _amend_mydestination(dest_set, mydomain, myhostname, error): + addition_set = set() + if mydomain not in dest_set: + error('Value of $mydomain is not in $mydestination') + addition_set.add('$mydomain') + addition_set.add('$myhostname') + if myhostname not in dest_set: + error('Value of $myhostname is not in $mydestination') + addition_set.add('$mydomain') + addition_set.add('$myhostname') + if 'localhost' not in dest_set: + error('localhost is not in $mydestination') + addition_set.add('localhost') + + if addition_set: + addition_set.update(dest_set) + return ', '.join(sorted(addition_set)) + + return None + + +def _change_to_domain_name(value, domain, allow_old_fqdn): + # Detect invalid values + if not value or '.' not in value: + return domain + + if not allow_old_fqdn and value != domain: + return domain + else: + return value + + +def fix_postfix_domains(diagnosis): + diagnosis.apply_changes(_apply_domain_changes) + + +def _apply_domain_changes(conf_dict): + for key, value in conf_dict.items(): + if key.startswith('_'): + update = globals()['su_set' + key] + update(value) + + post = {k: v for (k, v) in conf_dict.items() if not k.startswith('_')} + postconf.set_many_unsafe(post) def get_domain_config(): diff --git a/plinth/modules/email_server/views.py b/plinth/modules/email_server/views.py index 57492845a..81ff70dd5 100644 --- a/plinth/modules/email_server/views.py +++ b/plinth/modules/email_server/views.py @@ -85,7 +85,7 @@ class EmailServerView(TabMixin, AppView): """Server configuration page""" app_id = 'email_server' template_name = 'email_server.html' - audit_modules = ('tls', 'rcube') + audit_modules = ('domain', 'tls', 'rcube') def get_context_data(self, *args, **kwargs): dlist = [] From a8df4bcdb7f73218e2dfab57bbdd62517308edc8 Mon Sep 17 00:00:00 2001 From: Seyed mohammad ali Hosseinifard Date: Mon, 6 Sep 2021 10:46:27 +0000 Subject: [PATCH 22/57] Translated using Weblate (Persian) Currently translated at 18.4% (279 of 1513 strings) --- plinth/locale/fa/LC_MESSAGES/django.po | 59 ++++++++++++-------------- 1 file changed, 26 insertions(+), 33 deletions(-) diff --git a/plinth/locale/fa/LC_MESSAGES/django.po b/plinth/locale/fa/LC_MESSAGES/django.po index d3f53752a..46c0ff95e 100644 --- a/plinth/locale/fa/LC_MESSAGES/django.po +++ b/plinth/locale/fa/LC_MESSAGES/django.po @@ -8,8 +8,8 @@ msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2021-08-30 19:29-0400\n" -"PO-Revision-Date: 2021-01-18 12:32+0000\n" -"Last-Translator: ikmaak \n" +"PO-Revision-Date: 2021-09-07 11:34+0000\n" +"Last-Translator: Seyed mohammad ali Hosseinifard \n" "Language-Team: Persian \n" "Language: fa\n" @@ -17,7 +17,7 @@ msgstr "" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=n != 1;\n" -"X-Generator: Weblate 4.5-dev\n" +"X-Generator: Weblate 4.8.1-dev\n" #: doc/dev/_templates/layout.html:11 msgid "Page source" @@ -31,7 +31,7 @@ msgstr "FreedomBox" #: plinth/daemon.py:103 #, python-brace-format msgid "Service {service_name} is running" -msgstr "" +msgstr "سرویس {service_name} در حال اجراست" #: plinth/daemon.py:130 #, fuzzy, python-brace-format @@ -54,10 +54,8 @@ msgid "Cannot connect to {host}:{port}" msgstr "نمی‌توان به {host}:{port} وصل شد" #: plinth/forms.py:39 -#, fuzzy -#| msgid "Invalid domain name" msgid "Select a domain name to be used with this application" -msgstr "نام دامنه معتبر نیست" +msgstr "نام دامنه‌ای را برای استفاده با این برنامه انتخاب کنید" #: plinth/forms.py:41 msgid "" @@ -108,7 +106,7 @@ msgstr "سرور وب" #: plinth/modules/apache/__init__.py:50 #, python-brace-format msgid "{box_name} Web Interface (Plinth)" -msgstr "" +msgstr "رابط وب {box_name} (Plinth)" #: plinth/modules/apache/components.py:122 #, fuzzy, python-brace-format @@ -168,10 +166,9 @@ msgstr "" #: plinth/modules/backups/__init__.py:207 #: plinth/modules/backups/__init__.py:254 #: plinth/modules/storage/__init__.py:323 -#, fuzzy, python-brace-format -#| msgid "About {box_name}" +#, python-brace-format msgid "Go to {app_name}" -msgstr "دربارهٔ {box_name}" +msgstr "رفتن به {app_name}" #: plinth/modules/backups/__init__.py:242 #, python-brace-format @@ -182,12 +179,12 @@ msgstr "" #: plinth/modules/backups/__init__.py:250 msgid "Error During Backup" -msgstr "" +msgstr "خظا هنگام پشتیبان‌گیری" #: plinth/modules/backups/forms.py:33 #, python-brace-format msgid "{app} (No data to backup)" -msgstr "" +msgstr "{app} (داده‌ای برای پشتیبان‌گیری نیست)" #: plinth/modules/backups/forms.py:53 msgid "Enable scheduled backups" @@ -247,10 +244,8 @@ msgid "Apps to include in the backup" msgstr "" #: plinth/modules/backups/forms.py:98 -#, fuzzy -#| msgid "Create Connection" msgid "Repository" -msgstr "ساختن اتصال" +msgstr "مخزن" #: plinth/modules/backups/forms.py:100 #: plinth/modules/backups/templates/backups_delete.html:17 @@ -8140,14 +8135,12 @@ msgid "Launch web client" msgstr "" #: plinth/templates/clients-button.html:25 -#, fuzzy -#| msgid "BitTorrent Web Client (Deluge)" msgid "Client Apps" -msgstr "برنامهٔ تحت وب بیت‌تورنت (Deluge)" +msgstr "برنامه‌های کارخواه" #: plinth/templates/clients.html:17 msgid "Web" -msgstr "" +msgstr "وب" #: plinth/templates/clients.html:42 msgid "Desktop" @@ -8155,11 +8148,11 @@ msgstr "" #: plinth/templates/clients.html:53 msgid "GNU/Linux" -msgstr "" +msgstr "گنو/لینوکس" #: plinth/templates/clients.html:55 msgid "Windows" -msgstr "" +msgstr "ویندوز" #: plinth/templates/clients.html:57 msgid "macOS" @@ -8184,11 +8177,11 @@ msgstr "" #: plinth/templates/clients.html:104 msgid "Package" -msgstr "" +msgstr "بسته" #: plinth/templates/clients.html:111 msgid "Debian:" -msgstr "" +msgstr "دبیان:" #: plinth/templates/clients.html:114 msgid "Homebrew:" @@ -8234,11 +8227,11 @@ msgstr "" #: plinth/templates/index.html:140 msgid "Source Code" -msgstr "" +msgstr "کد منبع" #: plinth/templates/index.html:143 plinth/templates/toolbar.html:19 msgid "Donate" -msgstr "" +msgstr "کمک مالی" #: plinth/templates/index.html:147 #, fuzzy @@ -8247,11 +8240,11 @@ msgstr "FreedomBox" #: plinth/templates/index.html:154 msgid "IRC Chatroom" -msgstr "" +msgstr "گپ سرای آی‌آرسی" #: plinth/templates/index.html:159 msgid "Mailing list" -msgstr "" +msgstr "فهرست پستی" #: plinth/templates/internal-zone.html:11 #, python-format @@ -8259,6 +8252,8 @@ msgid "" "%(service_name)s is available only on internal networks or when the " "client is connected to %(box_name)s through VPN." msgstr "" +"%(service_name)s تنها در شبکه‌های داخلی یا هنگامی که کارخواه از " +"طریق وی‌پی‌ان به %(box_name)s متصل شده است در دسترس می‌باشد." #: plinth/templates/internal-zone.html:17 msgid "Currently there are no network interfaces configured as internal." @@ -8273,13 +8268,11 @@ msgstr "" #: plinth/templates/messages.html:11 msgid "Close" -msgstr "" +msgstr "بستن" #: plinth/templates/notifications-dropdown.html:11 -#, fuzzy -#| msgid "No certficate" msgid "Notifications" -msgstr "بدون گواهی دیجیتال" +msgstr "اعلان‌ها" #: plinth/templates/port-forwarding-info.html:8 msgid "Port Forwarding" @@ -8310,7 +8303,7 @@ msgstr "" #: plinth/templates/port-forwarding-info.html:37 msgid "Protocol" -msgstr "" +msgstr "پروتکل" #: plinth/templates/port-forwarding-info.html:38 msgid "From Router/WAN Ports" From abbec6b8e9cdf802efd163e50a256dc40a9a990f Mon Sep 17 00:00:00 2001 From: Veiko Aasa Date: Thu, 9 Sep 2021 22:16:40 +0300 Subject: [PATCH 23/57] gitweb: tests: Fix test failures if initial default branch is not master Since Git 2.28, it is possible to change initial default branch name using the configuration option init.defaultBranch. Closes #2101. Signed-off-by: Veiko Aasa Reviewed-by: Sunil Mohan Adapa --- plinth/modules/gitweb/tests/test_actions.py | 29 +++++++++------------ 1 file changed, 13 insertions(+), 16 deletions(-) diff --git a/plinth/modules/gitweb/tests/test_actions.py b/plinth/modules/gitweb/tests/test_actions.py index 2f1bc062c..26ccad990 100644 --- a/plinth/modules/gitweb/tests/test_actions.py +++ b/plinth/modules/gitweb/tests/test_actions.py @@ -17,7 +17,6 @@ REPO_DATA = { 'description': '', 'owner': '', 'access': 'private', - 'default_branch': 'master', } @@ -65,9 +64,11 @@ def test_create_repo(call_action): 'create-repo', '--name', REPO_NAME, '--description', '', '--owner', '', '--is-private', '--keep-ownership' ]) + repo = json.loads(call_action(['repo-info', '--name', REPO_NAME])) + default_branch = repo.pop('default_branch') - assert json.loads(call_action(['repo-info', '--name', - REPO_NAME])) == REPO_DATA + assert repo == REPO_DATA + assert len(default_branch) > 0 def test_change_repo_medatada(call_action, existing_repo): @@ -77,7 +78,6 @@ def test_change_repo_medatada(call_action, existing_repo): 'description': 'description2', 'owner': 'owner2', 'access': 'public', - 'default_branch': 'master', } call_action([ @@ -89,9 +89,10 @@ def test_change_repo_medatada(call_action, existing_repo): call_action([ 'set-repo-access', '--name', REPO_NAME, '--access', new_data['access'] ]) + repo = json.loads(call_action(['repo-info', '--name', REPO_NAME])) + del repo['default_branch'] - assert json.loads(call_action(['repo-info', '--name', - REPO_NAME])) == new_data + assert repo == new_data def test_rename_repository(call_action, existing_repo): @@ -101,21 +102,17 @@ def test_rename_repository(call_action, existing_repo): call_action(['rename-repo', '--oldname', REPO_NAME, '--newname', new_name]) with pytest.raises(RuntimeError, match='Repository not found'): call_action(['repo-info', '--name', REPO_NAME]) + repo = json.loads(call_action(['repo-info', '--name', new_name])) - assert json.loads(call_action(['repo-info', '--name', new_name])) == { - **REPO_DATA, - **{ - 'name': new_name - } - } + assert repo['name'] == new_name def test_get_branches(call_action, existing_repo): """Test getting all the branches of the repository.""" - assert json.loads(call_action(['get-branches', '--name', REPO_NAME])) == { - "default_branch": "master", - "branches": [] - } + result = json.loads(call_action(['get-branches', '--name', REPO_NAME])) + + assert 'default_branch' in result + assert result['branches'] == [] def test_delete_repository(call_action, existing_repo): From ff741e3a85e1af03a455f83dbcf50b415055f5ec Mon Sep 17 00:00:00 2001 From: Artem Date: Thu, 9 Sep 2021 07:18:19 +0000 Subject: [PATCH 24/57] Translated using Weblate (Russian) Currently translated at 88.5% (1340 of 1513 strings) --- plinth/locale/ru/LC_MESSAGES/django.po | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/plinth/locale/ru/LC_MESSAGES/django.po b/plinth/locale/ru/LC_MESSAGES/django.po index 378a1820e..e23030e49 100644 --- a/plinth/locale/ru/LC_MESSAGES/django.po +++ b/plinth/locale/ru/LC_MESSAGES/django.po @@ -8,8 +8,8 @@ msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2021-08-30 19:29-0400\n" -"PO-Revision-Date: 2021-02-01 18:42+0000\n" -"Last-Translator: Алексей Докучаев \n" +"PO-Revision-Date: 2021-09-10 07:34+0000\n" +"Last-Translator: Artem \n" "Language-Team: Russian \n" "Language: ru\n" @@ -18,7 +18,7 @@ msgstr "" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=3; plural=n%10==1 && n%100!=11 ? 0 : n%10>=2 && n" "%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2;\n" -"X-Generator: Weblate 4.5-dev\n" +"X-Generator: Weblate 4.8.1-dev\n" #: doc/dev/_templates/layout.html:11 msgid "Page source" @@ -2253,7 +2253,7 @@ msgstr "Тип службы" #: plinth/modules/email_server/templates/email_server.html:38 msgid "Repair" -msgstr "" +msgstr "Исправление" #: plinth/modules/email_server/templates/my_mail.html:15 #, fuzzy From ae4d973db22a61698cc890e657f936560705881f Mon Sep 17 00:00:00 2001 From: Sunil Mohan Adapa Date: Thu, 9 Sep 2021 14:51:41 -0700 Subject: [PATCH 25/57] d/lintian-overrides: Drop override for a removed tag As of lintian 2.105.0, the tag systemd-service-file-outside-lib has been removed. Drop the override to avoid a lintian error. Signed-off-by: Sunil Mohan Adapa Reviewed-by: James Valleroy --- debian/freedombox.lintian-overrides | 3 --- 1 file changed, 3 deletions(-) diff --git a/debian/freedombox.lintian-overrides b/debian/freedombox.lintian-overrides index e934a69f3..3f0a1c2f5 100644 --- a/debian/freedombox.lintian-overrides +++ b/debian/freedombox.lintian-overrides @@ -16,9 +16,6 @@ freedombox binary: non-standard-apache2-configuration-name * # that requires significant effort. freedombox binary: web-application-works-only-with-apache -# Temporary workaround for https://bugs.debian.org/992465 -freedombox: systemd-service-file-outside-lib usr/lib/systemd/system/*.service - # Since dpkg 1.20.6, there is support for a flag called remove-on-upgrade in # DEBIAN/conffiles. debhelper 13.5+ will generate conffiles with flag from # .maintscript instead of using postinst scripts. Lintian does not yet From 8f8c7354d84e3aa59cab8d5d219c197dd2d76a48 Mon Sep 17 00:00:00 2001 From: Sunil Mohan Adapa Date: Thu, 9 Sep 2021 14:58:12 -0700 Subject: [PATCH 26/57] d/lintian-overrides: Override message for not supporting sysvinit Signed-off-by: Sunil Mohan Adapa Reviewed-by: James Valleroy --- debian/freedombox.lintian-overrides | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/debian/freedombox.lintian-overrides b/debian/freedombox.lintian-overrides index 3f0a1c2f5..b35a80e11 100644 --- a/debian/freedombox.lintian-overrides +++ b/debian/freedombox.lintian-overrides @@ -24,3 +24,9 @@ freedombox binary: web-application-works-only-with-apache freedombox: conffile-is-not-in-package remove-on-upgrade * freedombox: non-etc-file-marked-as-conffile remove-on-upgrade * freedombox: relative-conffile remove-on-upgrade * + +# FreedomBox adds systemd security features for other daemons in Debian. There +# is no equivalent for this in sysvinit. Sometimes, FreedomBox provide service +# files for packages that do not provide them. FreedomBox does not support +# sysvinit so no init.d equivalent is provided. +freedombox: package-supports-alternative-init-but-no-init.d-script usr/lib/systemd/system/* From bdc1965b6c876fc82eaf13cfcb0976122e67c377 Mon Sep 17 00:00:00 2001 From: Sunil Mohan Adapa Date: Thu, 9 Sep 2021 15:04:51 -0700 Subject: [PATCH 27/57] d/lintian-overrides: Add override for manual outside .../doc/ Signed-off-by: Sunil Mohan Adapa Reviewed-by: James Valleroy --- debian/freedombox-doc-en.lintian-overrides | 5 +++++ debian/freedombox-doc-es.lintian-overrides | 5 +++++ debian/freedombox.lintian-overrides | 3 +++ 3 files changed, 13 insertions(+) create mode 100644 debian/freedombox-doc-en.lintian-overrides create mode 100644 debian/freedombox-doc-es.lintian-overrides diff --git a/debian/freedombox-doc-en.lintian-overrides b/debian/freedombox-doc-en.lintian-overrides new file mode 100644 index 000000000..52c996b2e --- /dev/null +++ b/debian/freedombox-doc-en.lintian-overrides @@ -0,0 +1,5 @@ +# Debian Policy Manual, Section 12.3: Any files that are used or read by +# programs but are also useful as stand alone documentation should be installed +# elsewhere, such as under /usr/share/package/, and then included via symbolic +# links in /usr/share/doc/package. +freedombox-doc-en: package-contains-documentation-outside-usr-share-doc usr/share/freedombox/manual/* diff --git a/debian/freedombox-doc-es.lintian-overrides b/debian/freedombox-doc-es.lintian-overrides new file mode 100644 index 000000000..0078711c3 --- /dev/null +++ b/debian/freedombox-doc-es.lintian-overrides @@ -0,0 +1,5 @@ +# Debian Policy Manual, Section 12.3: Any files that are used or read by +# programs but are also useful as stand alone documentation should be installed +# elsewhere, such as under /usr/share/package/, and then included via symbolic +# links in /usr/share/doc/package. +freedombox-doc-es: package-contains-documentation-outside-usr-share-doc usr/share/freedombox/manual/* diff --git a/debian/freedombox.lintian-overrides b/debian/freedombox.lintian-overrides index b35a80e11..71564c06f 100644 --- a/debian/freedombox.lintian-overrides +++ b/debian/freedombox.lintian-overrides @@ -30,3 +30,6 @@ freedombox: relative-conffile remove-on-upgrade * # files for packages that do not provide them. FreedomBox does not support # sysvinit so no init.d equivalent is provided. freedombox: package-supports-alternative-init-but-no-init.d-script usr/lib/systemd/system/* + +# Not documentation +freedombox: package-contains-documentation-outside-usr-share-doc usr/share/plinth/static/jslicense.html From 34987c77bd6b57c1a67eed7d5114bfb8924721b0 Mon Sep 17 00:00:00 2001 From: Sunil Mohan Adapa Date: Thu, 9 Sep 2021 19:27:19 -0700 Subject: [PATCH 28/57] d/lintian-overrides: Drop workaround for remove-on-upgrade dpkg flag As of Lintian 2.105.0, remove-on-upgrade and other flags in DEBIAN/conffiles is properly handled. False errors are no longer thrown. Drop the workaround introduced for this purpose. Signed-off-by: Sunil Mohan Adapa Reviewed-by: James Valleroy --- debian/freedombox.lintian-overrides | 9 --------- 1 file changed, 9 deletions(-) diff --git a/debian/freedombox.lintian-overrides b/debian/freedombox.lintian-overrides index 71564c06f..c7a85c29c 100644 --- a/debian/freedombox.lintian-overrides +++ b/debian/freedombox.lintian-overrides @@ -16,15 +16,6 @@ freedombox binary: non-standard-apache2-configuration-name * # that requires significant effort. freedombox binary: web-application-works-only-with-apache -# Since dpkg 1.20.6, there is support for a flag called remove-on-upgrade in -# DEBIAN/conffiles. debhelper 13.5+ will generate conffiles with flag from -# .maintscript instead of using postinst scripts. Lintian does not yet -# understand this flag and generates several errors. Ignore all errors related -# to this flag until lintian is fixed. -freedombox: conffile-is-not-in-package remove-on-upgrade * -freedombox: non-etc-file-marked-as-conffile remove-on-upgrade * -freedombox: relative-conffile remove-on-upgrade * - # FreedomBox adds systemd security features for other daemons in Debian. There # is no equivalent for this in sysvinit. Sometimes, FreedomBox provide service # files for packages that do not provide them. FreedomBox does not support From e8c4e732eaf8f6686f9d5ef78fda2a1e3360d68b Mon Sep 17 00:00:00 2001 From: Sunil Mohan Adapa Date: Wed, 1 Sep 2021 16:46:07 -0700 Subject: [PATCH 29/57] apache: Drop support for GnuTLS - We have switched to mod_ssl long time ago and are no longer using mod_gnutls. - It is additional effort configure and test mod_gnutls. Signed-off-by: Sunil Mohan Adapa Reviewed-by: James Valleroy --- .../freedombox-tls-site-macro.conf | 20 ------------------- .../apache2/sites-available/plinth-ssl.conf | 2 +- 2 files changed, 1 insertion(+), 21 deletions(-) diff --git a/data/etc/apache2/conf-available/freedombox-tls-site-macro.conf b/data/etc/apache2/conf-available/freedombox-tls-site-macro.conf index 168dec448..9f8389408 100644 --- a/data/etc/apache2/conf-available/freedombox-tls-site-macro.conf +++ b/data/etc/apache2/conf-available/freedombox-tls-site-macro.conf @@ -1,25 +1,5 @@ - # mod_gnutls default options. See /etc/apache2/sites-available/default-tls.conf - - - ServerAdmin webmaster@localhost - ServerName $domain - DocumentRoot /var/www/html - - ErrorLog ${APACHE_LOG_DIR}/error.log - CustomLog ${APACHE_LOG_DIR}/ssl_access.log combined - - GnuTLSEnable On - - # Automatically obtained certificates from Let's Encrypt - GnuTLSCertificateFile /etc/letsencrypt/live/$domain/fullchain.pem - GnuTLSKeyFile /etc/letsencrypt/live/$domain/privkey.pem - # See http://www.outoforder.cc/projects/httpd/mod_gnutls/docs/#GnuTLSPriorities - GnuTLSPriorities NORMAL:-VERS-SSL3.0:-VERS-TLS1.0:-VERS-TLS1.1 - - - # mod_ssl default options. See /etc/apache2/sites-available/default-ssl.conf diff --git a/data/etc/apache2/sites-available/plinth-ssl.conf b/data/etc/apache2/sites-available/plinth-ssl.conf index 0b8a9a627..a7831936c 100644 --- a/data/etc/apache2/sites-available/plinth-ssl.conf +++ b/data/etc/apache2/sites-available/plinth-ssl.conf @@ -6,7 +6,7 @@ ## ## Requires the following Apache modules to be enabled: ## mod_rewrite -## mod_ssl or mod_gnutls +## mod_ssl ## RewriteEngine on From 857ab0afe174ffdcb58bde76a5152fab35df87b9 Mon Sep 17 00:00:00 2001 From: Sunil Mohan Adapa Date: Wed, 1 Sep 2021 17:16:38 -0700 Subject: [PATCH 30/57] apache: Enable and prioritize HTTP/2 protocol - Enabling the module automatically sets 'Protocols h2 h2c http/1.1' in shipped module configuration. - HTTP/2 is given higher priority over HTTP/1.1 for supported clients. - Clients not supporting HTTP/2 continue to work with HTTP/1.1. - Clients work by using APLN extension in TLS to figure out that server supports HTTP/2 and use it. - HTTP/2 improves performance. - Recommended by Mozilla's SSL configurator: https://ssl-config.mozilla.org/. Signed-off-by: Sunil Mohan Adapa Reviewed-by: James Valleroy --- actions/apache | 3 +++ plinth/modules/apache/__init__.py | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/actions/apache b/actions/apache index 6b64c95c6..fc0c06e29 100755 --- a/actions/apache +++ b/actions/apache @@ -126,6 +126,9 @@ def subcommand_setup(arguments): # Disable /server-status page to avoid leaking private info. webserver.disable('status', kind='module') + # Enable HTTP/2 protocol + webserver.enable('http2', kind='module') + # switch to mod_ssl from mod_gnutls webserver.disable('gnutls', kind='module') webserver.enable('ssl', kind='module') diff --git a/plinth/modules/apache/__init__.py b/plinth/modules/apache/__init__.py index b093c5c59..87e7dbbc5 100644 --- a/plinth/modules/apache/__init__.py +++ b/plinth/modules/apache/__init__.py @@ -14,7 +14,7 @@ from plinth.modules.firewall.components import Firewall from plinth.modules.letsencrypt.components import LetsEncrypt from plinth.utils import format_lazy, is_valid_user_name -version = 8 +version = 9 is_essential = True From ae541ca752c666347e0bf28c04e9409cd6efb0b9 Mon Sep 17 00:00:00 2001 From: Sunil Mohan Adapa Date: Thu, 2 Sep 2021 16:45:53 -0700 Subject: [PATCH 31/57] apache: Setup Mozilla recommended configuration - TLS configuration as recommended by Mozilla's SSL Configuration Generator with 'Intermediate' configuration. See: https://wiki.mozilla.org/Security/Server_Side_TLS - Disable ciphers that are weak or without forward secrecy. - Allow client to choose ciphers as they will know best if they have support for hardware-accelerated AES. - TLS session tickets (RFC 5077) require restarting web server with an appropriate frequency. See: https://httpd.apache.org/docs/current/mod/mod_ssl.html#sslsessiontickets - Send OCSP responses to the client and reduce their round trips. - No need to increment apache app version number as it has already been incremented in this release cycle for enabling HTTP/2 module. Tests: - FreedomBox interface is reachable with the changes. - ssllabs.com gives an A+ rating on a server with these changes. - All ciphers are shown as secure. - Forward Secrecy rating is ROBUST. - OCSP stapling shows as enabled. - Client support seems to match the expected after dropping <= TLS1.1. - Session resumption with tickets shows as disabled. Signed-off-by: Sunil Mohan Adapa Reviewed-by: James Valleroy --- actions/apache | 4 +++ .../apache2/conf-available/freedombox.conf | 33 +++++++++++++++++++ 2 files changed, 37 insertions(+) diff --git a/actions/apache b/actions/apache index fc0c06e29..2fbfb2403 100755 --- a/actions/apache +++ b/actions/apache @@ -129,6 +129,10 @@ def subcommand_setup(arguments): # Enable HTTP/2 protocol webserver.enable('http2', kind='module') + # Enable shared object cache needed for OSCP stapling. Needed by + # mod_ssl. + webserver.enable('socache_shmcb', kind='module') + # switch to mod_ssl from mod_gnutls webserver.disable('gnutls', kind='module') webserver.enable('ssl', kind='module') diff --git a/data/etc/apache2/conf-available/freedombox.conf b/data/etc/apache2/conf-available/freedombox.conf index 255f1becf..a2feaf114 100644 --- a/data/etc/apache2/conf-available/freedombox.conf +++ b/data/etc/apache2/conf-available/freedombox.conf @@ -1,3 +1,36 @@ +## SPDX-License-Identifier: AGPL-3.0-or-later +## +## DO NOT EDIT. If you do, FreedomBox will not automatically upgrade. +## +## Apache configuration managed by FreedomBox. If customization is needed, +## create a new configuration file with higher priority and override directives. +## + +## +## TLS configuration as recommended by Mozilla's SSL Configuration Generator +## with 'Intermediate' configuration. See: +## https://wiki.mozilla.org/Security/Server_Side_TLS +## + + # Disable ciphers that are weak or without forward secrecy. + SSLCipherSuite ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384 + + # Allow client to choose ciphers as they will know best if they have support + # for hardware-accelerated AES. + SSLHonorCipherOrder off + + # TLS session tickets (RFC 5077) require restarting web server with an + # appropriate frequency. See: + # https://httpd.apache.org/docs/current/mod/mod_ssl.html#sslsessiontickets + SSLSessionTickets off + + # Send OCSP responses to the client and reduce their round trips. + + SSLUseStapling On + SSLStaplingCache "shmcb:${APACHE_RUN_DIR}/ssl_stapling(32768)" + + + ## ## Enable HSTS, even for subdomains. ## From 0e22eb6c6e0b20bb84403e5fae4d9b26728057d5 Mon Sep 17 00:00:00 2001 From: Sunil Mohan Adapa Date: Wed, 8 Sep 2021 05:35:52 -0700 Subject: [PATCH 32/57] container: Fix the update command for new web server Closes: #2109. We moved from Nginx to Apache on ftp.freedombox.org. This changed the datetime format in the index pages we were relying on to find the difference with local image. Update this datetime format. Tests: - Run ./container update with an old image already in the .container directory. New image will be downloaded and verified. - Run ./container update immediately after downloading the latest image. No new download is done. Signed-off-by: Sunil Mohan Adapa Reviewed-by: James Valleroy --- container | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/container b/container index c0a945667..068de7962 100755 --- a/container +++ b/container @@ -928,9 +928,8 @@ def _get_latest_image_timestamp(distribution): url = URLS[distribution] response = urlopen(url[0:url.rindex('/')]) page_contents = response.read().decode() - str_time = re.findall(r'\d{2}-[A-Z][a-z]{2}-\d{4} \d{2}:\d{2}', - page_contents)[0] - return datetime.datetime.strptime(str_time, '%d-%b-%Y %H:%M').timestamp() + str_time = re.findall(r'\d{4}-\d{2}-\d{2} \d{2}:\d{2}', page_contents)[0] + return datetime.datetime.strptime(str_time, '%Y-%m-%d %H:%M').timestamp() def _is_update_required(distribution): From f4792a6d88ae0e68736765338a0c5dae6f821293 Mon Sep 17 00:00:00 2001 From: ikmaak Date: Sun, 12 Sep 2021 09:03:48 +0000 Subject: [PATCH 33/57] Translated using Weblate (Dutch) Currently translated at 99.0% (1498 of 1513 strings) --- plinth/locale/nl/LC_MESSAGES/django.po | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/plinth/locale/nl/LC_MESSAGES/django.po b/plinth/locale/nl/LC_MESSAGES/django.po index 14da9e9f2..1db3aa311 100644 --- a/plinth/locale/nl/LC_MESSAGES/django.po +++ b/plinth/locale/nl/LC_MESSAGES/django.po @@ -8,7 +8,7 @@ msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2021-08-30 19:29-0400\n" -"PO-Revision-Date: 2021-09-05 13:34+0000\n" +"PO-Revision-Date: 2021-09-13 10:44+0000\n" "Last-Translator: ikmaak \n" "Language-Team: Dutch \n" @@ -17,7 +17,7 @@ msgstr "" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=n != 1;\n" -"X-Generator: Weblate 4.8.1-dev\n" +"X-Generator: Weblate 4.9-dev\n" "X-Language: nl_NL\n" "X-Source-Language: C\n" @@ -2139,7 +2139,7 @@ msgstr "RoundCube geconfigureerd voor FreedomBox-e-mail" #: plinth/modules/email_server/audit/spam.py:86 msgid "Inbound and outbound mail filters" -msgstr "" +msgstr "Filters voor inkomende en uitgaande e-mail" #: plinth/modules/email_server/audit/tls.py:80 msgid "Postfix TLS parameters" @@ -2159,7 +2159,7 @@ msgstr "domein" #: plinth/modules/email_server/forms.py:14 msgid "New alias (without @domain)" -msgstr "" +msgstr "Nieuwe alias (zonder @domein)" #: plinth/modules/email_server/manifest.py:6 #: plinth/modules/roundcube/__init__.py:55 @@ -2181,7 +2181,7 @@ msgstr "FairEmail" #: plinth/modules/email_server/templates/email_alias.html:12 msgid "You have no email aliases." -msgstr "" +msgstr "Je hebt geen e-mailaliassen." #: plinth/modules/email_server/templates/email_alias.html:21 msgid "Disable selected" @@ -2227,7 +2227,7 @@ msgstr "Postfix TLS" #: plinth/modules/email_server/templates/email_security.html:18 msgid "Dovecot TLS" -msgstr "" +msgstr "Dovecot TLS" #: plinth/modules/email_server/templates/email_server.html:14 msgid "Visit Rspamd administration interface" @@ -2241,7 +2241,7 @@ msgstr "Dienst Type" #: plinth/modules/email_server/templates/email_server.html:38 msgid "Repair" -msgstr "" +msgstr "Herstellen" #: plinth/modules/email_server/templates/my_mail.html:15 #, fuzzy @@ -2273,7 +2273,7 @@ msgstr "Algemene naam" #: plinth/modules/email_server/templates/tls_form.html:38 msgid "Use custom values" -msgstr "" +msgstr "Aangepaste waarden gebruiken" #: plinth/modules/email_server/templates/tls_form.html:43 msgid "Certificate path" @@ -2285,7 +2285,7 @@ msgstr "Pad van privésleutel" #: plinth/modules/email_server/templates/tls_form.html:67 msgid "Use system default" -msgstr "" +msgstr "Systeem standaard gebruiken" #: plinth/modules/email_server/views.py:24 #: plinth/modules/email_server/views.py:32 plinth/templates/base.html:110 @@ -2299,7 +2299,7 @@ msgstr "" #: plinth/modules/email_server/views.py:26 #: plinth/modules/email_server/views.py:33 msgid "My Aliases" -msgstr "" +msgstr "Mijn aliassen" #: plinth/modules/email_server/views.py:27 #: plinth/modules/networks/templates/connection_show.html:259 @@ -2315,7 +2315,7 @@ msgstr "Domeinen" #: plinth/modules/email_server/views.py:104 #, python-brace-format msgid "Internal error in {0}" -msgstr "" +msgstr "Interne fout in {0}" #: plinth/modules/email_server/views.py:107 msgid "Check syslog for more information" From 42c2bcfde74ae7d20e41d2693745e21a004a11c7 Mon Sep 17 00:00:00 2001 From: James Valleroy Date: Mon, 6 Sep 2021 18:04:38 -0400 Subject: [PATCH 34/57] avahi: Convert functional tests to non-BDD python format Signed-off-by: James Valleroy [sunil: Add markers] Signed-off-by: Sunil Mohan Adapa Reviewed-by: Sunil Mohan Adapa --- plinth/modules/avahi/tests/avahi.feature | 18 ------------- plinth/modules/avahi/tests/test_functional.py | 25 +++++++++++++++++-- 2 files changed, 23 insertions(+), 20 deletions(-) delete mode 100644 plinth/modules/avahi/tests/avahi.feature diff --git a/plinth/modules/avahi/tests/avahi.feature b/plinth/modules/avahi/tests/avahi.feature deleted file mode 100644 index 0ab01d1cb..000000000 --- a/plinth/modules/avahi/tests/avahi.feature +++ /dev/null @@ -1,18 +0,0 @@ -# SPDX-License-Identifier: AGPL-3.0-or-later - -@system @essential @avahi -Feature: Avahi Service Discovery - Configure service discovery. - -Background: - Given I'm a logged in user - -Scenario: Disable avahi application - Given the avahi application is enabled - When I disable the avahi application - Then the avahi service should not be running - -Scenario: Enable avahi application - Given the avahi application is disabled - When I enable the avahi application - Then the avahi service should be running diff --git a/plinth/modules/avahi/tests/test_functional.py b/plinth/modules/avahi/tests/test_functional.py index 687c73c4f..114dd0328 100644 --- a/plinth/modules/avahi/tests/test_functional.py +++ b/plinth/modules/avahi/tests/test_functional.py @@ -3,6 +3,27 @@ Functional, browser based tests for avahi app. """ -from pytest_bdd import scenarios +import pytest +from plinth.tests import functional -scenarios('avahi.feature') +pytestmark = [pytest.mark.system, pytest.mark.essential, pytest.mark.avahi] + + +@pytest.fixture(scope='module', autouse=True) +def fixture_background(session_browser): + """Login and install the app.""" + functional.login(session_browser) + functional.install(session_browser, 'avahi') + yield + functional.app_disable(session_browser, 'avahi') + + +def test_enable_disable(session_browser): + """Test enabling the app.""" + functional.app_disable(session_browser, 'avahi') + + functional.app_enable(session_browser, 'avahi') + assert functional.service_is_running(session_browser, 'avahi') + + functional.app_disable(session_browser, 'avahi') + assert functional.service_is_not_running(session_browser, 'avahi') From f8277c09caed1e94cb781552035eebc7ff68b76f Mon Sep 17 00:00:00 2001 From: James Valleroy Date: Mon, 6 Sep 2021 18:32:53 -0400 Subject: [PATCH 35/57] cockpit: Convert functional tests to non-BDD python format Signed-off-by: James Valleroy [sunil: Add markers] Signed-off-by: Sunil Mohan Adapa Reviewed-by: Sunil Mohan Adapa --- plinth/modules/cockpit/tests/cockpit.feature | 19 ------------- .../modules/cockpit/tests/test_functional.py | 27 +++++++++++++++++-- 2 files changed, 25 insertions(+), 21 deletions(-) delete mode 100644 plinth/modules/cockpit/tests/cockpit.feature diff --git a/plinth/modules/cockpit/tests/cockpit.feature b/plinth/modules/cockpit/tests/cockpit.feature deleted file mode 100644 index 3b8fe6518..000000000 --- a/plinth/modules/cockpit/tests/cockpit.feature +++ /dev/null @@ -1,19 +0,0 @@ -# SPDX-License-Identifier: AGPL-3.0-or-later - -@system -Feature: Server Administration - Run server administration application - Cockpit. - -Background: - Given I'm a logged in user - Given the cockpit application is installed - -Scenario: Enable cockpit application - Given the cockpit application is disabled - When I enable the cockpit application - Then the cockpit site should be available - -Scenario: Disable cockpit application - Given the cockpit application is enabled - When I disable the cockpit application - Then the cockpit site should not be available diff --git a/plinth/modules/cockpit/tests/test_functional.py b/plinth/modules/cockpit/tests/test_functional.py index df5a79a27..ccf3d0244 100644 --- a/plinth/modules/cockpit/tests/test_functional.py +++ b/plinth/modules/cockpit/tests/test_functional.py @@ -3,6 +3,29 @@ Functional, browser based tests for cockpit app. """ -from pytest_bdd import scenarios +import pytest +from plinth.tests import functional -scenarios('cockpit.feature') +pytestmark = [pytest.mark.system, pytest.mark.essential, pytest.mark.cockpit] + + +@pytest.fixture(scope='module', autouse=True) +def fixture_background(session_browser): + """Login and install the app.""" + functional.login(session_browser) + functional.install(session_browser, 'cockpit') + yield + functional.app_disable(session_browser, 'cockpit') + + +def test_enable_disable(session_browser): + """Test enabling the app.""" + functional.app_disable(session_browser, 'cockpit') + + functional.app_enable(session_browser, 'cockpit') + assert functional.service_is_running(session_browser, 'cockpit') + assert functional.is_available(session_browser, 'cockpit') + + functional.app_disable(session_browser, 'cockpit') + assert functional.service_is_not_running(session_browser, 'cockpit') + assert not functional.is_available(session_browser, 'cockpit') From 00bbae33ffab324f40f7d43c7e5b62520cacddf0 Mon Sep 17 00:00:00 2001 From: James Valleroy Date: Mon, 6 Sep 2021 18:41:58 -0400 Subject: [PATCH 36/57] i2p: Convert functional tests to non-BDD python format Signed-off-by: James Valleroy [sunil: Add markers] Signed-off-by: Sunil Mohan Adapa Reviewed-by: Sunil Mohan Adapa --- plinth/modules/i2p/tests/i2p.feature | 19 --------------- plinth/modules/i2p/tests/test_functional.py | 27 +++++++++++++++++++-- 2 files changed, 25 insertions(+), 21 deletions(-) delete mode 100644 plinth/modules/i2p/tests/i2p.feature diff --git a/plinth/modules/i2p/tests/i2p.feature b/plinth/modules/i2p/tests/i2p.feature deleted file mode 100644 index 8d53106cc..000000000 --- a/plinth/modules/i2p/tests/i2p.feature +++ /dev/null @@ -1,19 +0,0 @@ -# SPDX-License-Identifier: AGPL-3.0-or-later - -@apps @i2p -Feature: I2P Anonymity Network - Manage I2P configuration. - -Background: - Given I'm a logged in user - Given the i2p application is installed - -Scenario: Enable i2p application - Given the i2p application is disabled - When I enable the i2p application - Then the i2p service should be running - -Scenario: Disable i2p application - Given the i2p application is enabled - When I disable the i2p application - Then the i2p service should not be running diff --git a/plinth/modules/i2p/tests/test_functional.py b/plinth/modules/i2p/tests/test_functional.py index 20926e817..cde14aa8d 100644 --- a/plinth/modules/i2p/tests/test_functional.py +++ b/plinth/modules/i2p/tests/test_functional.py @@ -3,6 +3,29 @@ Functional, browser based tests for i2p app. """ -from pytest_bdd import scenarios +import pytest +from plinth.tests import functional -scenarios('i2p.feature') +pytestmark = [pytest.mark.apps, pytest.mark.i2p] + + +@pytest.fixture(scope='module', autouse=True) +def fixture_background(session_browser): + """Login and install the app.""" + functional.login(session_browser) + functional.install(session_browser, 'i2p') + yield + functional.app_disable(session_browser, 'i2p') + + +def test_enable_disable(session_browser): + """Test enabling the app.""" + functional.app_disable(session_browser, 'i2p') + + functional.app_enable(session_browser, 'i2p') + assert functional.service_is_running(session_browser, 'i2p') + assert functional.is_available(session_browser, 'i2p') + + functional.app_disable(session_browser, 'i2p') + assert functional.service_is_not_running(session_browser, 'i2p') + assert not functional.is_available(session_browser, 'i2p') From dc1f6ef732394ef0a273d6902fb59a64f1989889 Mon Sep 17 00:00:00 2001 From: James Valleroy Date: Mon, 6 Sep 2021 19:44:44 -0400 Subject: [PATCH 37/57] infinoted: Convert functional tests to non-BDD python format Signed-off-by: James Valleroy [sunil: Add markers] Signed-off-by: Sunil Mohan Adapa Reviewed-by: Sunil Mohan Adapa --- .../modules/infinoted/tests/infinoted.feature | 19 -------------- .../infinoted/tests/test_functional.py | 25 +++++++++++++++++-- 2 files changed, 23 insertions(+), 21 deletions(-) delete mode 100644 plinth/modules/infinoted/tests/infinoted.feature diff --git a/plinth/modules/infinoted/tests/infinoted.feature b/plinth/modules/infinoted/tests/infinoted.feature deleted file mode 100644 index 9687a6b9c..000000000 --- a/plinth/modules/infinoted/tests/infinoted.feature +++ /dev/null @@ -1,19 +0,0 @@ -# SPDX-License-Identifier: AGPL-3.0-or-later - -@apps @infinoted -Feature: Infinoted Collaborative Text Editor - Run Gobby Server - Infinoted - -Background: - Given I'm a logged in user - Given the infinoted application is installed - -Scenario: Enable infinoted application - Given the infinoted application is disabled - When I enable the infinoted application - Then the infinoted service should be running - -Scenario: Disable infinoted application - Given the infinoted application is enabled - When I disable the infinoted application - Then the infinoted service should not be running diff --git a/plinth/modules/infinoted/tests/test_functional.py b/plinth/modules/infinoted/tests/test_functional.py index be32e536e..eab534960 100644 --- a/plinth/modules/infinoted/tests/test_functional.py +++ b/plinth/modules/infinoted/tests/test_functional.py @@ -3,6 +3,27 @@ Functional, browser based tests for infinoted app. """ -from pytest_bdd import scenarios +import pytest +from plinth.tests import functional -scenarios('infinoted.feature') +pytestmark = [pytest.mark.apps, pytest.mark.infinoted] + + +@pytest.fixture(scope='module', autouse=True) +def fixture_background(session_browser): + """Login and install the app.""" + functional.login(session_browser) + functional.install(session_browser, 'infinoted') + yield + functional.app_disable(session_browser, 'infinoted') + + +def test_enable_disable(session_browser): + """Test enabling the app.""" + functional.app_disable(session_browser, 'infinoted') + + functional.app_enable(session_browser, 'infinoted') + assert functional.service_is_running(session_browser, 'infinoted') + + functional.app_disable(session_browser, 'infinoted') + assert functional.service_is_not_running(session_browser, 'infinoted') From fa6aed9c7a6412bdfd11d717cae6256963005bc7 Mon Sep 17 00:00:00 2001 From: James Valleroy Date: Mon, 6 Sep 2021 20:02:21 -0400 Subject: [PATCH 38/57] minetest: Convert functional tests to non-BDD python format Signed-off-by: James Valleroy [sunil: Add markers] Signed-off-by: Sunil Mohan Adapa Reviewed-by: Sunil Mohan Adapa --- .../modules/minetest/tests/minetest.feature | 19 -------------- .../modules/minetest/tests/test_functional.py | 25 +++++++++++++++++-- 2 files changed, 23 insertions(+), 21 deletions(-) delete mode 100644 plinth/modules/minetest/tests/minetest.feature diff --git a/plinth/modules/minetest/tests/minetest.feature b/plinth/modules/minetest/tests/minetest.feature deleted file mode 100644 index 58bd863ce..000000000 --- a/plinth/modules/minetest/tests/minetest.feature +++ /dev/null @@ -1,19 +0,0 @@ -# SPDX-License-Identifier: AGPL-3.0-or-later - -@apps @minetest -Feature: Minetest Block Sandbox - Run the Minetest server - -Background: - Given I'm a logged in user - Given the minetest application is installed - -Scenario: Enable minetest application - Given the minetest application is disabled - When I enable the minetest application - Then the minetest service should be running - -Scenario: Disable minetest application - Given the minetest application is enabled - When I disable the minetest application - Then the minetest service should not be running diff --git a/plinth/modules/minetest/tests/test_functional.py b/plinth/modules/minetest/tests/test_functional.py index 741bc2b22..8bad3e900 100644 --- a/plinth/modules/minetest/tests/test_functional.py +++ b/plinth/modules/minetest/tests/test_functional.py @@ -3,6 +3,27 @@ Functional, browser based tests for minetest app. """ -from pytest_bdd import scenarios +import pytest +from plinth.tests import functional -scenarios('minetest.feature') +pytestmark = [pytest.mark.apps, pytest.mark.minetest] + + +@pytest.fixture(scope='module', autouse=True) +def fixture_background(session_browser): + """Login and install the app.""" + functional.login(session_browser) + functional.install(session_browser, 'minetest') + yield + functional.app_disable(session_browser, 'minetest') + + +def test_enable_disable(session_browser): + """Test enabling the app.""" + functional.app_disable(session_browser, 'minetest') + + functional.app_enable(session_browser, 'minetest') + assert functional.service_is_running(session_browser, 'minetest') + + functional.app_disable(session_browser, 'minetest') + assert functional.service_is_not_running(session_browser, 'minetest') From b91d2d8c7fff44e4243e07bf715a1a30075cfa8f Mon Sep 17 00:00:00 2001 From: James Valleroy Date: Mon, 6 Sep 2021 20:42:10 -0400 Subject: [PATCH 39/57] minidlna: Convert functional tests to non-BDD python format Signed-off-by: James Valleroy [sunil: Add markers] Signed-off-by: Sunil Mohan Adapa Reviewed-by: Sunil Mohan Adapa --- .../modules/minidlna/tests/minidlna.feature | 19 -------------- .../modules/minidlna/tests/test_functional.py | 25 +++++++++++++++++-- 2 files changed, 23 insertions(+), 21 deletions(-) delete mode 100644 plinth/modules/minidlna/tests/minidlna.feature diff --git a/plinth/modules/minidlna/tests/minidlna.feature b/plinth/modules/minidlna/tests/minidlna.feature deleted file mode 100644 index 22aa22308..000000000 --- a/plinth/modules/minidlna/tests/minidlna.feature +++ /dev/null @@ -1,19 +0,0 @@ -# SPDX-License-Identifier: AGPL-3.0-or-later - -@apps @minidlna -Feature: minidlna Simple Media Server - Run miniDLNA media server - -Background: - Given I'm a logged in user - And the minidlna application is installed - -Scenario: Enable minidlna application - Given the minidlna application is disabled - When I enable the minidlna application - Then the minidlna service should be running - -Scenario: Disable minidlna application - Given the minidlna application is enabled - When I disable the minidlna application - Then the minidlna service should not be running diff --git a/plinth/modules/minidlna/tests/test_functional.py b/plinth/modules/minidlna/tests/test_functional.py index 2b44dbc18..85d106ce9 100644 --- a/plinth/modules/minidlna/tests/test_functional.py +++ b/plinth/modules/minidlna/tests/test_functional.py @@ -3,6 +3,27 @@ Functional, browser based tests for minidlna app. """ -from pytest_bdd import scenarios +import pytest +from plinth.tests import functional -scenarios('minidlna.feature') +pytestmark = [pytest.mark.apps, pytest.mark.minidlna] + + +@pytest.fixture(scope='module', autouse=True) +def fixture_background(session_browser): + """Login and install the app.""" + functional.login(session_browser) + functional.install(session_browser, 'minidlna') + yield + functional.app_disable(session_browser, 'minidlna') + + +def test_enable_disable(session_browser): + """Test enabling the app.""" + functional.app_disable(session_browser, 'minidlna') + + functional.app_enable(session_browser, 'minidlna') + assert functional.service_is_running(session_browser, 'minidlna') + + functional.app_disable(session_browser, 'minidlna') + assert functional.service_is_not_running(session_browser, 'minidlna') From c2844d358e3991dd2a175c0c47f56cefb5c10b24 Mon Sep 17 00:00:00 2001 From: James Valleroy Date: Mon, 6 Sep 2021 20:52:42 -0400 Subject: [PATCH 40/57] performance: Convert functional tests to non-BDD python format Signed-off-by: James Valleroy [sunil: Add markers] Signed-off-by: Sunil Mohan Adapa Reviewed-by: Sunil Mohan Adapa --- .../performance/tests/performance.feature | 20 --------------- .../performance/tests/test_functional.py | 25 +++++++++++++++++-- 2 files changed, 23 insertions(+), 22 deletions(-) delete mode 100644 plinth/modules/performance/tests/performance.feature diff --git a/plinth/modules/performance/tests/performance.feature b/plinth/modules/performance/tests/performance.feature deleted file mode 100644 index cbae2c492..000000000 --- a/plinth/modules/performance/tests/performance.feature +++ /dev/null @@ -1,20 +0,0 @@ -# SPDX-License-Identifier: AGPL-3.0-or-later - -@system @performance -Feature: Performance - system monitoring - Run the Performance Co-Pilot app. - -Background: - Given I'm a logged in user - And advanced mode is on - And the performance application is installed - -Scenario: Enable performance application - Given the performance application is disabled - When I enable the performance application - Then the performance service should be running - -Scenario: Disable performance application - Given the performance application is enabled - When I disable the performance application - Then the performance service should not be running diff --git a/plinth/modules/performance/tests/test_functional.py b/plinth/modules/performance/tests/test_functional.py index eed641b09..3f7e7a70e 100644 --- a/plinth/modules/performance/tests/test_functional.py +++ b/plinth/modules/performance/tests/test_functional.py @@ -3,6 +3,27 @@ Functional, browser based tests for performance app. """ -from pytest_bdd import scenarios +import pytest +from plinth.tests import functional -scenarios('performance.feature') +pytestmark = [pytest.mark.system, pytest.mark.performance] + + +@pytest.fixture(scope='module', autouse=True) +def fixture_background(session_browser): + """Login and install the app.""" + functional.login(session_browser) + functional.install(session_browser, 'performance') + yield + functional.app_disable(session_browser, 'performance') + + +def test_enable_disable(session_browser): + """Test enabling the app.""" + functional.app_disable(session_browser, 'performance') + + functional.app_enable(session_browser, 'performance') + assert functional.service_is_running(session_browser, 'performance') + + functional.app_disable(session_browser, 'performance') + assert functional.service_is_not_running(session_browser, 'performance') From 9cc69ed32c7d2bbcda4cf55d802c631b26b9b544 Mon Sep 17 00:00:00 2001 From: James Valleroy Date: Tue, 7 Sep 2021 11:01:46 -0400 Subject: [PATCH 41/57] matrixsynapse: Convert functional tests to non-BDD python format Signed-off-by: James Valleroy [sunil: Add markers] Signed-off-by: Sunil Mohan Adapa Reviewed-by: Sunil Mohan Adapa --- .../matrixsynapse/tests/matrixsynapse.feature | 21 -------------- .../matrixsynapse/tests/test_functional.py | 28 +++++++++++++++++-- 2 files changed, 26 insertions(+), 23 deletions(-) delete mode 100644 plinth/modules/matrixsynapse/tests/matrixsynapse.feature diff --git a/plinth/modules/matrixsynapse/tests/matrixsynapse.feature b/plinth/modules/matrixsynapse/tests/matrixsynapse.feature deleted file mode 100644 index aefa9fc72..000000000 --- a/plinth/modules/matrixsynapse/tests/matrixsynapse.feature +++ /dev/null @@ -1,21 +0,0 @@ -# SPDX-License-Identifier: AGPL-3.0-or-later - -@apps @matrixsynapse -Feature: Matrix Synapse VoIP and Chat Server - Run Matrix Synapse server - -Background: - Given I'm a logged in user - Given the domain name is set to mydomain.example - Given the matrixsynapse application is installed - Given the domain name for matrixsynapse is set to mydomain.example - -Scenario: Enable matrixsynapse application - Given the matrixsynapse application is disabled - When I enable the matrixsynapse application - Then the matrixsynapse service should be running - -Scenario: Disable matrixsynapse application - Given the matrixsynapse application is enabled - When I disable the matrixsynapse application - Then the matrixsynapse service should not be running diff --git a/plinth/modules/matrixsynapse/tests/test_functional.py b/plinth/modules/matrixsynapse/tests/test_functional.py index 46d251545..3d13bc553 100644 --- a/plinth/modules/matrixsynapse/tests/test_functional.py +++ b/plinth/modules/matrixsynapse/tests/test_functional.py @@ -3,6 +3,30 @@ Functional, browser based tests for matrixsynapse app. """ -from pytest_bdd import scenarios +import pytest +from plinth.tests import functional -scenarios('matrixsynapse.feature') +pytestmark = [pytest.mark.apps, pytest.mark.matrixsynapse] + + +@pytest.fixture(scope='module', autouse=True) +def fixture_background(session_browser): + """Login and install the app.""" + functional.login(session_browser) + functional.set_domain_name(session_browser, 'mydomain.example') + functional.install(session_browser, 'matrixsynapse') + functional.app_select_domain_name(session_browser, 'matrixsynapse', + 'mydomain.example') + yield + functional.app_disable(session_browser, 'matrixsynapse') + + +def test_enable_disable(session_browser): + """Test enabling the app.""" + functional.app_disable(session_browser, 'matrixsynapse') + + functional.app_enable(session_browser, 'matrixsynapse') + assert functional.service_is_running(session_browser, 'matrixsynapse') + + functional.app_disable(session_browser, 'matrixsynapse') + assert functional.service_is_not_running(session_browser, 'matrixsynapse') From e097250d906e6263645e85e44e5f69d6b91fb7a2 Mon Sep 17 00:00:00 2001 From: James Valleroy Date: Tue, 7 Sep 2021 17:20:26 -0400 Subject: [PATCH 42/57] jsxc: Convert functional tests to non-BDD python format Signed-off-by: James Valleroy [sunil: Add markers] Signed-off-by: Sunil Mohan Adapa Reviewed-by: Sunil Mohan Adapa --- plinth/modules/jsxc/tests/jsxc.feature | 19 --------------- plinth/modules/jsxc/tests/test_functional.py | 25 ++++++++++++++++++-- 2 files changed, 23 insertions(+), 21 deletions(-) delete mode 100644 plinth/modules/jsxc/tests/jsxc.feature diff --git a/plinth/modules/jsxc/tests/jsxc.feature b/plinth/modules/jsxc/tests/jsxc.feature deleted file mode 100644 index f39577bd9..000000000 --- a/plinth/modules/jsxc/tests/jsxc.feature +++ /dev/null @@ -1,19 +0,0 @@ -# SPDX-License-Identifier: AGPL-3.0-or-later - -@apps @jsxc -Feature: JSXC XMPP Client - Run the JSXC XMPP client. - -Background: - Given I'm a logged in user - -Scenario: Install jsxc application - Given the jsxc application is installed - Then the jsxc site should be available - -@backups -Scenario: Backup and restore jsxc - Given the jsxc application is installed - When I create a backup of the jsxc app data with name test_jsxc - And I restore the jsxc app data backup with name test_jsxc - Then the jsxc site should be available diff --git a/plinth/modules/jsxc/tests/test_functional.py b/plinth/modules/jsxc/tests/test_functional.py index 47f30123e..f482a82c7 100644 --- a/plinth/modules/jsxc/tests/test_functional.py +++ b/plinth/modules/jsxc/tests/test_functional.py @@ -3,6 +3,27 @@ Functional, browser based tests for jsxc app. """ -from pytest_bdd import scenarios +import pytest +from plinth.tests import functional -scenarios('jsxc.feature') +pytestmark = [pytest.mark.apps, pytest.mark.jsxc] + + +@pytest.fixture(scope='module', autouse=True) +def fixture_background(session_browser): + """Login.""" + functional.login(session_browser) + + +def test_install(session_browser): + """Test installing the app.""" + functional.install(session_browser, 'jsxc') + assert functional.is_available(session_browser, 'jsxc') + + +@pytest.mark.backups +def test_backup(session_browser): + """Test backing up and restoring.""" + functional.backup_create(session_browser, 'jsxc', 'test_jsxc') + functional.backup_restore(session_browser, 'jsxc', 'test_jsxc') + assert functional.is_available(session_browser, 'jsxc') From 7cc34199352229c94ef652ee4bb03a236fba7c8d Mon Sep 17 00:00:00 2001 From: James Valleroy Date: Fri, 10 Sep 2021 14:58:22 -0400 Subject: [PATCH 43/57] backups: Convert functional tests to non-BDD python format Signed-off-by: James Valleroy [sunil: Add markers] Signed-off-by: Sunil Mohan Adapa Reviewed-by: Sunil Mohan Adapa --- plinth/modules/backups/tests/backups.feature | 36 --- .../modules/backups/tests/test_functional.py | 218 ++++++++++-------- 2 files changed, 117 insertions(+), 137 deletions(-) delete mode 100644 plinth/modules/backups/tests/backups.feature diff --git a/plinth/modules/backups/tests/backups.feature b/plinth/modules/backups/tests/backups.feature deleted file mode 100644 index d3695483f..000000000 --- a/plinth/modules/backups/tests/backups.feature +++ /dev/null @@ -1,36 +0,0 @@ -# SPDX-License-Identifier: AGPL-3.0-or-later - -@backups @system -Feature: Backups module - Create and restore backups. - -Background: - Given I'm a logged in user - Given the bind application is installed - -Scenario: Browser waits for redirect after restoring a backup - Given the bind application is enabled - When I create a backup of the bind app data with name test_backups - And I restore the bind app data backup with name test_backups - And I open the main page - And I wait for 5 seconds - Then the main page should be shown - -Scenario: Download, upload and restore a backup - Given the bind application is enabled - When I set bind forwarders to 1.1.1.1 - And I create a backup of the bind app data with name test_backups - And I set bind forwarders to 1.0.0.1 - And I download the app data backup with name test_backups - And I restore the downloaded app data backup - Then bind forwarders should be 1.1.1.1 - -Scenario: Set a schedule for a repository - Given the backup schedule is set to disable for 1 daily, 2 weekly and 3 monthly at 2:00 without app names - When I set the backup schedule to enable for 10 daily, 20 weekly and 30 monthly at 15:00 without app firewall - Then the schedule should be set to enable for 10 daily, 20 weekly and 30 monthly at 15:00 without app firewall - -Scenario: Disable schedule for a repository - Given the backup schedule is set to enable for 10 daily, 20 weekly and 30 monthly at 15:00 without app firewall - When I set the backup schedule to disable for 1 daily, 2 weekly and 3 monthly at 2:00 without app names - Then the schedule should be set to disable for 1 daily, 2 weekly and 3 monthly at 2:00 without app names diff --git a/plinth/modules/backups/tests/test_functional.py b/plinth/modules/backups/tests/test_functional.py index fd79a2c73..8f4d29840 100644 --- a/plinth/modules/backups/tests/test_functional.py +++ b/plinth/modules/backups/tests/test_functional.py @@ -5,42 +5,77 @@ Functional, browser based tests for backups app. import os import tempfile +import time import urllib.parse +import pytest import requests -from pytest import fixture -from pytest_bdd import given, parsers, scenarios, then, when - from plinth.tests import functional -scenarios('backups.feature') +pytestmark = [pytest.mark.system, pytest.mark.backups] -@fixture(scope='session') +@pytest.fixture(scope='module', autouse=True) +def fixture_background(session_browser): + """Login and install the app.""" + functional.login(session_browser) + functional.install(session_browser, 'bind') + functional.app_enable(session_browser, 'bind') + yield + functional.app_disable(session_browser, 'bind') + _backup_schedule_disable(session_browser) + + +@pytest.fixture(scope='session') def downloaded_file_info(): return dict() -@when(parsers.parse('I open the main page')) -def open_main_page(session_browser): +def test_browser_waits_after_restore(session_browser): + """Test that browser waits for redirect after restoring a backup.""" + functional.backup_create(session_browser, 'bind', 'test_backups') + functional.backup_restore(session_browser, 'bind', 'test_backups') + _open_main_page(session_browser) + time.sleep(5) + _assert_main_page_is_shown(session_browser) -@then(parsers.parse('the main page should be shown')) -def main_page_is_shown(session_browser): +def test_download_upload_restore(session_browser, downloaded_file_info): + """Test download, upload, and restore a backup.""" + functional.set_forwarders(session_browser, '1.1.1.1') + functional.backup_create(session_browser, 'bind', 'test_backups') + + functional.set_forwarders(session_browser, '1.0.0.1') + _backup_download(session_browser, downloaded_file_info, 'test_backups') + _backup_restore_from_upload(session_browser, 'bind', downloaded_file_info) + + assert functional.get_forwarders(session_browser) == '1.1.1.1' + + +def test_set_schedule(session_browser): + """Test set a schedule for a repository.""" + _backup_schedule_set(session_browser, enable=False, daily=1, weekly=2, + monthly=3, run_at=2, without_app='names') + + _backup_schedule_set(session_browser, enable=True, daily=10, weekly=20, + monthly=30, run_at=15, without_app='firewall') + + _backup_schedule_assert(session_browser, enable=True, daily=10, weekly=20, + monthly=30, run_at=15, without_app='firewall') + + +def _assert_main_page_is_shown(session_browser): assert (session_browser.url.endswith('/plinth/')) -@when( - parsers.parse('I download the app data backup with name {archive_name:w}')) -def backup_download(session_browser, downloaded_file_info, archive_name): +def _backup_download(session_browser, downloaded_file_info, archive_name): file_path = _download(session_browser, archive_name) downloaded_file_info['path'] = file_path -@when(parsers.parse('I restore the downloaded app data backup')) -def backup_restore_from_upload(session_browser, app_name, - downloaded_file_info): +def _backup_restore_from_upload(session_browser, app_name, + downloaded_file_info): path = downloaded_file_info["path"] try: _upload_and_restore(session_browser, app_name, path) @@ -50,34 +85,10 @@ def backup_restore_from_upload(session_browser, app_name, os.remove(path) -@given( - parsers.parse('the backup schedule is set to {enable:w} for {daily:d} ' - 'daily, {weekly:d} weekly and {monthly:d} monthly at ' - '{run_at:d}:00 without app {without_app:w}')) -def backup_schedule_set(session_browser, enable, daily, weekly, monthly, - run_at, without_app): - _backup_schedule_set(session_browser, enable == 'enable', daily, weekly, - monthly, run_at, without_app) - - -@when( - parsers.parse('I set the backup schedule to {enable:w} for {daily:d} ' - 'daily, {weekly:d} weekly and {monthly:d} monthly at ' - '{run_at:d}:00 without app {without_app:w}')) -def backup_schedule_set2(session_browser, enable, daily, weekly, monthly, - run_at, without_app): - _backup_schedule_set(session_browser, enable == 'enable', daily, weekly, - monthly, run_at, without_app) - - -@then( - parsers.parse('the schedule should be set to {enable:w} for {daily:d} ' - 'daily, {weekly:d} weekly and {monthly:d} monthly at ' - '{run_at:d}:00 without app {without_app:w}')) -def backup_schedule_assert(session_browser, enable, daily, weekly, monthly, - run_at, without_app): +def _backup_schedule_assert(session_browser, enable, daily, weekly, monthly, + run_at, without_app): schedule = _backup_schedule_get(session_browser) - assert schedule['enable'] == (enable == 'enable') + assert schedule['enable'] == enable assert schedule['daily'] == daily assert schedule['weekly'] == weekly assert schedule['monthly'] == monthly @@ -86,65 +97,9 @@ def backup_schedule_assert(session_browser, enable, daily, weekly, monthly, assert schedule['without_apps'][0] == without_app -def _open_main_page(browser): - with functional.wait_for_page_update(browser): - browser.find_link_by_href('/plinth/').first.click() - - -def _download_file_logged_in(browser, url, suffix=''): - """Download a file from Plinth, pretend being logged in via cookies""" - if not url.startswith("http"): - current_url = urllib.parse.urlparse(browser.url) - url = "%s://%s%s" % (current_url.scheme, current_url.netloc, url) - cookies = browser.driver.get_cookies() - cookies = {cookie["name"]: cookie["value"] for cookie in cookies} - response = requests.get(url, verify=False, cookies=cookies) - with tempfile.NamedTemporaryFile(delete=False, suffix=suffix) as temp_file: - for chunk in response.iter_content(chunk_size=128): - temp_file.write(chunk) - return temp_file.name - - -def _download(browser, archive_name=None): - functional.nav_to_module(browser, 'backups') - href = f'/plinth/sys/backups/root/download/{archive_name}/' - url = functional.base_url + href - file_path = _download_file_logged_in(browser, url, suffix='.tar.gz') - return file_path - - -def _upload_and_restore(browser, app_name, downloaded_file_path): - functional.nav_to_module(browser, 'backups') - browser.find_link_by_href('/plinth/sys/backups/upload/').first.click() - fileinput = browser.driver.find_element_by_id('id_backups-file') - fileinput.send_keys(downloaded_file_path) - # submit upload form - functional.submit(browser) - # submit restore form - with functional.wait_for_page_update(browser, - expected_url='/plinth/sys/backups/'): - functional.submit(browser) - - -def _backup_schedule_set(browser, enable, daily, weekly, monthly, run_at, - without_app): - """Set the schedule for root repository.""" - functional.nav_to_module(browser, 'backups') - browser.find_link_by_href( - '/plinth/sys/backups/root/schedule/').first.click() - if enable: - browser.find_by_name('backups_schedule-enabled').check() - else: - browser.find_by_name('backups_schedule-enabled').uncheck() - - browser.fill('backups_schedule-daily_to_keep', daily) - browser.fill('backups_schedule-weekly_to_keep', weekly) - browser.fill('backups_schedule-monthly_to_keep', monthly) - browser.fill('backups_schedule-run_at_hour', run_at) - functional.eventually(browser.find_by_css, args=['.select-all']) - browser.find_by_css('.select-all').first.check() - browser.find_by_css(f'input[value="{without_app}"]').first.uncheck() - functional.submit(browser) +def _backup_schedule_disable(session_browser): + """Disable schedule for the root repository.""" + _backup_schedule_set(session_browser, False, 1, 2, 3, 2, 'names') def _backup_schedule_get(browser): @@ -174,3 +129,64 @@ def _backup_schedule_get(browser): 'without_apps': without_apps } + + +def _backup_schedule_set(browser, enable, daily, weekly, monthly, run_at, + without_app): + """Set the schedule for root repository.""" + functional.nav_to_module(browser, 'backups') + browser.find_link_by_href( + '/plinth/sys/backups/root/schedule/').first.click() + if enable: + browser.find_by_name('backups_schedule-enabled').check() + else: + browser.find_by_name('backups_schedule-enabled').uncheck() + + browser.fill('backups_schedule-daily_to_keep', daily) + browser.fill('backups_schedule-weekly_to_keep', weekly) + browser.fill('backups_schedule-monthly_to_keep', monthly) + browser.fill('backups_schedule-run_at_hour', run_at) + functional.eventually(browser.find_by_css, args=['.select-all']) + browser.find_by_css('.select-all').first.check() + browser.find_by_css(f'input[value="{without_app}"]').first.uncheck() + functional.submit(browser) + + +def _download_file_logged_in(browser, url, suffix=''): + """Download a file from Plinth, pretend being logged in via cookies""" + if not url.startswith("http"): + current_url = urllib.parse.urlparse(browser.url) + url = "%s://%s%s" % (current_url.scheme, current_url.netloc, url) + cookies = browser.driver.get_cookies() + cookies = {cookie["name"]: cookie["value"] for cookie in cookies} + response = requests.get(url, verify=False, cookies=cookies) + with tempfile.NamedTemporaryFile(delete=False, suffix=suffix) as temp_file: + for chunk in response.iter_content(chunk_size=128): + temp_file.write(chunk) + return temp_file.name + + +def _download(browser, archive_name=None): + functional.nav_to_module(browser, 'backups') + href = f'/plinth/sys/backups/root/download/{archive_name}/' + url = functional.base_url + href + file_path = _download_file_logged_in(browser, url, suffix='.tar.gz') + return file_path + + +def _open_main_page(browser): + with functional.wait_for_page_update(browser): + browser.find_link_by_href('/plinth/').first.click() + + +def _upload_and_restore(browser, app_name, downloaded_file_path): + functional.nav_to_module(browser, 'backups') + browser.find_link_by_href('/plinth/sys/backups/upload/').first.click() + fileinput = browser.driver.find_element_by_id('id_backups-file') + fileinput.send_keys(downloaded_file_path) + # submit upload form + functional.submit(browser) + # submit restore form + with functional.wait_for_page_update(browser, + expected_url='/plinth/sys/backups/'): + functional.submit(browser) From 8091e2007263236e616d35a8d7bb5d90b54e0b13 Mon Sep 17 00:00:00 2001 From: Sunil Mohan Adapa Date: Mon, 13 Sep 2021 19:00:39 -0700 Subject: [PATCH 44/57] tests: Add some missing markers Signed-off-by: Sunil Mohan Adapa --- pytest.ini | 2 ++ 1 file changed, 2 insertions(+) diff --git a/pytest.ini b/pytest.ini index 4bb087a37..4f3ac0700 100644 --- a/pytest.ini +++ b/pytest.ini @@ -7,6 +7,7 @@ markers = functional bepasty bind calibre + cockpit config coturn datetime @@ -52,3 +53,4 @@ markers = functional ttrss upgrades users + zoph From 1e124e8b7c04cfd31cad9b05810972aa9d7b4b40 Mon Sep 17 00:00:00 2001 From: 109247019824 Date: Tue, 14 Sep 2021 21:29:51 +0000 Subject: [PATCH 45/57] Translated using Weblate (Bulgarian) Currently translated at 2.0% (31 of 1513 strings) --- plinth/locale/bg/LC_MESSAGES/django.po | 67 +++++++++++++------------- 1 file changed, 33 insertions(+), 34 deletions(-) diff --git a/plinth/locale/bg/LC_MESSAGES/django.po b/plinth/locale/bg/LC_MESSAGES/django.po index 0cbad0666..42459d577 100644 --- a/plinth/locale/bg/LC_MESSAGES/django.po +++ b/plinth/locale/bg/LC_MESSAGES/django.po @@ -8,8 +8,8 @@ msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2021-08-30 19:29-0400\n" -"PO-Revision-Date: 2021-01-18 12:32+0000\n" -"Last-Translator: ikmaak \n" +"PO-Revision-Date: 2021-09-15 21:34+0000\n" +"Last-Translator: 109247019824 \n" "Language-Team: Bulgarian \n" "Language: bg\n" @@ -17,11 +17,11 @@ msgstr "" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=n != 1;\n" -"X-Generator: Weblate 4.5-dev\n" +"X-Generator: Weblate 4.9-dev\n" #: doc/dev/_templates/layout.html:11 msgid "Page source" -msgstr "" +msgstr "Изходен код на страницата" #: plinth/context_processors.py:23 plinth/views.py:80 msgid "FreedomBox" @@ -30,17 +30,17 @@ msgstr "FreedomBox" #: plinth/daemon.py:103 #, python-brace-format msgid "Service {service_name} is running" -msgstr "" +msgstr "Услугата {service_name} работи" #: plinth/daemon.py:130 #, python-brace-format msgid "Listening on {kind} port {listen_address}:{port}" -msgstr "" +msgstr "Слушане на {kind} порт {listen_address}:{port}" #: plinth/daemon.py:134 #, python-brace-format msgid "Listening on {kind} port {port}" -msgstr "" +msgstr "Слушане на {kind} порт {port}" #: plinth/daemon.py:202 #, python-brace-format @@ -53,32 +53,28 @@ msgid "Cannot connect to {host}:{port}" msgstr "Не може да се свърже с {host}:{port}" #: plinth/forms.py:39 -#, fuzzy msgid "Select a domain name to be used with this application" -msgstr "Изберете име на домейн, което да се ползва с това приложение" +msgstr "Изберете име на домейн, което да се използва с това приложение" #: plinth/forms.py:41 -#, fuzzy msgid "" "Warning! The application may not work properly if domain name is changed " "later." msgstr "" -"Внимание! Приложението може да не работи коректно, ако по-късно се промени " -"името на домейна." +"Предупреждение! Приложението може да не работи правилно, ако името на " +"домейна бъде променено по-късно." #: plinth/forms.py:49 msgid "Language" msgstr "Език" #: plinth/forms.py:50 -#, fuzzy msgid "Language to use for presenting this web interface" -msgstr "Език, на който ще се показва този уеб интерфейс" +msgstr "Език, на който да се показва този интерфейс" #: plinth/forms.py:57 -#, fuzzy msgid "Use the language preference set in the browser" -msgstr "Използване езиковите настройки на браузъра" +msgstr "Използване на предпочитания от четеца език" #: plinth/middleware.py:59 plinth/templates/setup.html:18 msgid "Application installed." @@ -95,21 +91,19 @@ msgid "Error installing application: {error}" msgstr "Грешка при инсталиране на приложението: {error}" #: plinth/modules/apache/__init__.py:41 -#, fuzzy -#| msgid "Web Server" msgid "Apache HTTP Server" -msgstr "Уеб Сървър" +msgstr "Сървър на Apache HTTP" #: plinth/modules/apache/__init__.py:44 #: plinth/modules/monkeysphere/templates/monkeysphere.html:49 #: plinth/modules/monkeysphere/templates/monkeysphere_details.html:46 msgid "Web Server" -msgstr "Уеб Сървър" +msgstr "Уеб сървър" #: plinth/modules/apache/__init__.py:50 #, python-brace-format msgid "{box_name} Web Interface (Plinth)" -msgstr "{box_name} Уеб Интерфейс (Plinth)" +msgstr "{box_name} Уеб интерфейс (Plinth)" #: plinth/modules/apache/components.py:122 #, python-brace-format @@ -131,14 +125,20 @@ msgid "" "disabled to improve security especially when connecting to a hostile local " "network." msgstr "" +"Откриването на услуги позволява на други устройства в мрежата да откриват " +"вашия {box_name} и услугите, които се предоставят от него. Също така " +"позволява на {box_name} да открива други устройства и услуги, работещи в " +"локалната мрежа. Откриването на услуги не е от съществено значение и работи " +"само във вътрешни мрежи. То може да бъде спряно, за да се подобри " +"сигурността, особено когато се свързвате към враждебна локална мрежа." #: plinth/modules/avahi/__init__.py:59 msgid "Service Discovery" -msgstr "" +msgstr "Откриване на услуги" #: plinth/modules/avahi/__init__.py:69 msgid "Local Network Domain" -msgstr "" +msgstr "Домейн на местната мрежа" #: plinth/modules/backups/__init__.py:34 msgid "Backups allows creating and managing backup archives." @@ -147,13 +147,16 @@ msgstr "" #: plinth/modules/backups/__init__.py:54 plinth/modules/backups/__init__.py:200 #: plinth/modules/backups/__init__.py:245 msgid "Backups" -msgstr "" +msgstr "Резервни копия" #: plinth/modules/backups/__init__.py:197 msgid "" "Enable an automatic backup schedule for data safety. Prefer an encrypted " "remote backup location or an extra attached disk." msgstr "" +"Направете график за автоматично архивиране за безопасност на данните. " +"Предпочитайте шифровано местоположение за отдалечено архивиране или " +"допълнително свързан диск." #: plinth/modules/backups/__init__.py:203 msgid "Enable a Backup Schedule" @@ -164,7 +167,7 @@ msgstr "" #: plinth/modules/storage/__init__.py:323 #, python-brace-format msgid "Go to {app_name}" -msgstr "" +msgstr "Към {app_name}" #: plinth/modules/backups/__init__.py:242 #, python-brace-format @@ -5245,20 +5248,18 @@ msgid "Share enabled." msgstr "" #: plinth/modules/samba/views.py:106 -#, fuzzy, python-brace-format -#| msgid "Error installing application: {error}" +#, python-brace-format msgid "Error enabling share: {error_message}" -msgstr "Грешка при инсталиране на приложението: {error}" +msgstr "Грешка при разрешаване на споделянето: {error_message}" #: plinth/modules/samba/views.py:111 msgid "Share disabled." msgstr "" #: plinth/modules/samba/views.py:116 -#, fuzzy, python-brace-format -#| msgid "Error installing application: {error}" +#, python-brace-format msgid "Error disabling share: {error_message}" -msgstr "Грешка при инсталиране на приложението: {error}" +msgstr "Грешка при забраняване на споделянето: {error_message}" #: plinth/modules/searx/__init__.py:25 msgid "" @@ -6541,10 +6542,8 @@ msgid "There is a new %(box_name)s version available." msgstr "" #: plinth/modules/upgrades/templates/upgrades_configure.html:35 -#, fuzzy -#| msgid "FreedomBox" msgid "Your Freedombox needs an update!" -msgstr "FreedomBox" +msgstr "Вашият Freedombox се нуждае от обновяване!" #: plinth/modules/upgrades/templates/upgrades_configure.html:47 msgid "" From e9a9c72256b55d887e2f674c7d133c006fe796ce Mon Sep 17 00:00:00 2001 From: Andrij Mizyk Date: Fri, 17 Sep 2021 13:26:39 +0000 Subject: [PATCH 46/57] Translated using Weblate (Ukrainian) Currently translated at 67.8% (1027 of 1513 strings) --- plinth/locale/uk/LC_MESSAGES/django.po | 93 ++++++++++++++++---------- 1 file changed, 56 insertions(+), 37 deletions(-) diff --git a/plinth/locale/uk/LC_MESSAGES/django.po b/plinth/locale/uk/LC_MESSAGES/django.po index b42fa5348..e8745242b 100644 --- a/plinth/locale/uk/LC_MESSAGES/django.po +++ b/plinth/locale/uk/LC_MESSAGES/django.po @@ -8,7 +8,7 @@ msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2021-08-30 19:29-0400\n" -"PO-Revision-Date: 2021-09-01 16:33+0000\n" +"PO-Revision-Date: 2021-09-17 13:30+0000\n" "Last-Translator: Andrij Mizyk \n" "Language-Team: Ukrainian \n" @@ -18,7 +18,7 @@ msgstr "" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=3; plural=n%10==1 && n%100!=11 ? 0 : n%10>=2 && n" "%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2;\n" -"X-Generator: Weblate 4.8.1-dev\n" +"X-Generator: Weblate 4.9-dev\n" #: doc/dev/_templates/layout.html:11 msgid "Page source" @@ -79,7 +79,7 @@ msgstr "Використовувати мовні налаштування ог #: plinth/middleware.py:59 plinth/templates/setup.html:18 msgid "Application installed." -msgstr "Застосунок встановлено." +msgstr "Застосунок установлено." #: plinth/middleware.py:65 #, python-brace-format @@ -397,7 +397,7 @@ msgid "" "Connection refused - make sure you provided correct credentials and the " "server is running." msgstr "" -"У зʼєднані відмовлено - переконайтеся, що ви надали правильні облікові дані " +"У зʼєднані відмовлено – переконайтеся, що ви надали правильні облікові дані " "і чи сервер працює." #: plinth/modules/backups/repository.py:41 @@ -1043,7 +1043,7 @@ msgstr "" #: plinth/modules/calibre/__init__.py:58 msgid "Use calibre e-book libraries" -msgstr "Використовуйте бібліотеки ел. книжок calibre" +msgstr "Використання бібліотеки ел. книжок calibre" #: plinth/modules/calibre/__init__.py:61 plinth/modules/calibre/manifest.py:6 msgid "calibre" @@ -1172,7 +1172,7 @@ msgstr "Cockpit" #: plinth/modules/cockpit/__init__.py:66 msgid "Server Administration" -msgstr "Адміністрація сервера" +msgstr "Адміністрування сервера" #: plinth/modules/cockpit/templates/cockpit.html:11 msgid "Access" @@ -1708,6 +1708,8 @@ msgid "" "If this option is selected, your username and password will be used for HTTP " "basic authentication." msgstr "" +"Якщо вибрано цю опцію, ваше імʼя користувача і пароль використовуватиметься " +"для основної автентифікації HTTP." #: plinth/modules/dynamicdns/forms.py:51 msgid "Leave this field empty if you want to keep your current password." @@ -2381,7 +2383,7 @@ msgstr "Налаштування завершено!" #: plinth/modules/first_boot/templates/firstboot_complete.html:14 #, python-format msgid "Without any apps, your %(box_name)s cannot do very much." -msgstr "Без застосунків ваш %(box_name)s не зможе зробити багато." +msgstr "Без застосунків ваш %(box_name)s не зможе багато зробити." #: plinth/modules/first_boot/templates/firstboot_complete.html:21 msgid "Install Apps" @@ -2825,7 +2827,7 @@ msgstr "I2P" #: plinth/modules/i2p/__init__.py:61 plinth/modules/tor/__init__.py:56 msgid "Anonymity Network" -msgstr "Анонімна мережа" +msgstr "Мережа анонімності" #: plinth/modules/i2p/__init__.py:84 msgid "I2P Proxy" @@ -3005,6 +3007,8 @@ msgid "" "JSXC is a web client for XMPP. Typically it is used with an XMPP server " "running locally." msgstr "" +"JSXC – це вебклієнт для XMPP. Як правило, використовується з запущеним " +"локальним сервером XMPP." #: plinth/modules/jsxc/__init__.py:44 plinth/modules/jsxc/manifest.py:7 msgid "JSXC" @@ -3509,7 +3513,7 @@ msgstr "" #: plinth/modules/mldonkey/__init__.py:54 msgid "Download files using eDonkey applications" -msgstr "Завантаження файлів використовуючи застосунок eDonkey" +msgstr "Завантаження файлів через застосунок eDonkey" #: plinth/modules/mldonkey/__init__.py:57 #: plinth/modules/mldonkey/manifest.py:11 @@ -4097,6 +4101,10 @@ msgid "" "have not configured or are unable to configure the router currently and wish " "to be reminded later. Some of the other configuration steps may fail.

      " msgstr "" +"Маршрутизатор поки що не налаштовано

      Оберіть цей " +"пункт, якщо маршрутизатор наразі не налаштовано або неможливо налаштувати, і " +"нагадати про це пізніше. Деякі наступні кроки налаштування можуть бути " +"невдалими.

      " #: plinth/modules/networks/templates/connection_show.html:24 #, python-format @@ -4446,7 +4454,7 @@ msgid "" msgstr "" "Виберіть пункт, що найкраще описує те, як ваш %(box_name)s підʼєднано до " "мережі. Ця інформація використовується лише для подальших вказівок " -"встановлення. Її можна змінити пізніше." +"установлення. Її можна змінити пізніше." #: plinth/modules/networks/templates/network_topology_main.html:9 #, python-format @@ -5176,7 +5184,7 @@ msgstr "Privoxy" #: plinth/modules/privoxy/__init__.py:57 msgid "Web Proxy" -msgstr "" +msgstr "Веб-проксі" #: plinth/modules/privoxy/__init__.py:115 #, python-brace-format @@ -5252,7 +5260,7 @@ msgstr "Radicale" #: plinth/modules/radicale/__init__.py:56 msgid "Calendar and Addressbook" -msgstr "Календар й Адресна книга" +msgstr "Календар і адресна книга" #: plinth/modules/radicale/forms.py:14 msgid "Only the owner of a calendar/addressbook can view or make changes." @@ -5390,7 +5398,7 @@ msgstr "" #: plinth/modules/samba/__init__.py:59 msgid "Access to the private shares" -msgstr "" +msgstr "Доступ до приватних поширень" #: plinth/modules/samba/__init__.py:62 msgid "Samba" @@ -5509,15 +5517,15 @@ msgid "" "Searx is a privacy-respecting Internet metasearch engine. It aggregrates and " "displays results from multiple search engines." msgstr "" -"Searx — це метапошукова система в Інтернеті, що поважає приватність. Вона " -"збирає та відображає результати з різних пошукових систем." +"Searx — це система збірного пошуку в Інтернеті, яка поважає приватність. " +"Вона збирає і відображає результати з різних пошукових систем." #: plinth/modules/searx/__init__.py:27 msgid "" "Searx can be used to avoid tracking and profiling by search engines. It " "stores no cookies by default." msgstr "" -"Searx можна використовувати для обходу стеження та профілювання пошуковими " +"Searx може використовуватися для обходу стеження та профілювання пошуковими " "системами." #: plinth/modules/searx/__init__.py:45 @@ -5557,7 +5565,7 @@ msgstr "Дозволити публічний доступ" #: plinth/modules/searx/forms.py:19 msgid "Allow this application to be used by anyone who can reach it." msgstr "" -"Дозволити використовувати цей застосунок кожному, хто має до нього доступ." +"Дозволити користуватися цим застосуноком кожному, хто має до нього доступ." #: plinth/modules/security/forms.py:13 msgid "Restrict console logins (recommended)" @@ -5827,7 +5835,7 @@ msgstr "Додати ділянку" #: plinth/modules/sharing/templates/sharing.html:27 msgid "No shares currently configured." -msgstr "Поки що немає налаштованих спільних ділянок." +msgstr "Поки що нема налаштованих спільних ділянок." #: plinth/modules/sharing/templates/sharing.html:34 msgid "Disk Path" @@ -5889,7 +5897,7 @@ msgstr "" #: plinth/modules/snapshot/__init__.py:56 msgid "Storage Snapshots" -msgstr "" +msgstr "Зрізи сховища" #: plinth/modules/snapshot/forms.py:12 msgid "Free Disk Space to Maintain" @@ -6049,7 +6057,7 @@ msgstr "" #: plinth/modules/snapshot/views.py:153 msgid "Storage snapshots configuration updated" -msgstr "" +msgstr "Налаштування зрізів сховища оновлено" #: plinth/modules/snapshot/views.py:157 plinth/modules/tor/views.py:60 #, python-brace-format @@ -6141,6 +6149,9 @@ msgid "" "You can view the storage media currently in use, mount and unmount removable " "media, expand the root partition etc." msgstr "" +"Цей модуль дозволяє вам керувати медія-сховищем, яке привʼязане до {box_name}" +". Ви можете переглядати медія-сховище під час використання, монтувати і " +"відмонтовувати знімні накопичувачі, розширювати розділ root тощо." #: plinth/modules/storage/__init__.py:53 plinth/modules/storage/__init__.py:316 #: plinth/modules/storage/__init__.py:347 @@ -6286,11 +6297,11 @@ msgstr "" #: plinth/modules/storage/templates/storage.html:17 msgid "The following storage devices are in use:" -msgstr "" +msgstr "Використовуються наступні пристрої сховища:" #: plinth/modules/storage/templates/storage.html:24 msgid "Label" -msgstr "" +msgstr "Мітка" #: plinth/modules/storage/templates/storage.html:25 msgid "Mount Point" @@ -6298,7 +6309,7 @@ msgstr "Точка монтування" #: plinth/modules/storage/templates/storage.html:27 msgid "Used" -msgstr "" +msgstr "Використано" #: plinth/modules/storage/templates/storage.html:77 msgid "Partition Expansion" @@ -6323,6 +6334,8 @@ msgid "" "Advanced storage operations such as disk partitioning and RAID management " "are provided by the Cockpit app." msgstr "" +"Розширені операції сховища, як розділення диска чи керування RAID, надаються " +"застосунком Cockpit." #: plinth/modules/storage/templates/storage_expand.html:14 #, python-format @@ -6410,7 +6423,7 @@ msgstr "" #: plinth/modules/tahoe/__init__.py:64 msgid "Distributed File Storage" -msgstr "" +msgstr "Розподілене файлове сховище" #: plinth/modules/tahoe/templates/tahoe-post-setup.html:17 #, python-format @@ -6451,6 +6464,10 @@ msgid "" "the Tor Browser." msgstr "" +"Tor — система анонімної комунікації. Дізнатися більше можете на вебсайті Tor Project. Для кращого захисту " +"під час вебсерфінгу, проєкт Tor радить використовувати Tor Browser." #: plinth/modules/tor/__init__.py:55 msgid "Tor" @@ -6613,7 +6630,7 @@ msgstr "" #: plinth/modules/tor/views.py:137 plinth/views.py:218 msgid "Setting unchanged" -msgstr "Налаштування незмінено" +msgstr "Налаштування не змінено" #: plinth/modules/transmission/__init__.py:29 msgid "Transmission is a BitTorrent client with a web interface." @@ -6772,9 +6789,9 @@ msgid "" "this web interface may be temporarily unavailable and show an error. In that " "case, refresh the page to continue." msgstr "" -"Це може зайняти багато часу. Протягом оновлення " -"вебінтерфейс може бути тимчасово недоступним і показувати помилку. В такому " -"випадку оновіть сторінку і продовжіть." +"Це може зайняти багато часу. Під час оновлення вебінтерфейс " +"може бути тимчасово недоступним і показувати помилку. В такому випадку " +"оновіть сторінку і продовжіть." #: plinth/modules/upgrades/templates/update-firstboot-progress.html:31 #, python-format @@ -7105,7 +7122,7 @@ msgstr "Видалити користувача %(username)s" #: plinth/modules/users/templates/users_update.html:11 #, python-format msgid "Edit User %(username)s" -msgstr "Змінити користувача %(username)s" +msgstr "Зміни користувача %(username)s" #: plinth/modules/users/templates/users_update.html:19 #, python-format @@ -7113,6 +7130,8 @@ msgid "" "Use the change password form to " "change the password." msgstr "" +"Щоб змінити пароль використовуйте форму " +"зміни пароля." #: plinth/modules/users/templates/users_update.html:31 #: plinth/templates/language-selection.html:17 @@ -7131,7 +7150,7 @@ msgstr "Користувача %(username)s оновлено." #: plinth/modules/users/views.py:77 msgid "Edit User" -msgstr "Змінити користувача" +msgstr "Зміни користувача" #: plinth/modules/users/views.py:132 #, python-brace-format @@ -7573,11 +7592,11 @@ msgstr "" #: plinth/package.py:136 msgid "Error during installation" -msgstr "Помилка під час встановлення" +msgstr "Помилка під час установлення" #: plinth/package.py:158 msgid "installing" -msgstr "встановлення" +msgstr "установлення" #: plinth/package.py:160 msgid "downloading" @@ -7632,7 +7651,7 @@ msgstr "" #: plinth/templates/app-header.html:22 msgid "Installation" -msgstr "Встановлення" +msgstr "Установлення" #: plinth/templates/app.html:29 #, python-format @@ -7770,8 +7789,8 @@ msgid "" msgstr "" "%(box_name)s є спеціальною збіркою Debian та особистим вебсервером із на " "100%% вільним програмним забезпеченням для розгортання соціальних " -"застосунків на малих машинах. Він надає засоби для мережевого спілкування, " -"що поважають вашу приватність й особисті дані." +"застосунків на малих машинах. Він пропонує засоби для мережевого " +"спілкування, які поважають вашу приватність й особисті дані." #: plinth/templates/index.html:117 #, python-format @@ -7897,7 +7916,7 @@ msgid "" "moments before trying again." msgstr "" "Інше встановлення або оновлення вже запущено. Будь ласка, зачекайте кілька " -"хвилин і спробуйте знову." +"хвилин і спробуйте ще раз." #: plinth/templates/setup.html:46 msgid "This application is currently not available in your distribution." @@ -7922,7 +7941,7 @@ msgstr "Виконання післяінсталяційних операцій #: plinth/templates/setup.html:83 #, python-format msgid "Installing %(package_names)s: %(status)s" -msgstr "Встановлення %(package_names)s: %(status)s" +msgstr "Установлюється %(package_names)s: %(status)s" #: plinth/templates/setup.html:93 #, python-format From ea6ee0f35fdd309ea7b9a9384d114e8274f88135 Mon Sep 17 00:00:00 2001 From: Andrij Mizyk Date: Fri, 17 Sep 2021 15:06:36 +0000 Subject: [PATCH 47/57] Translated using Weblate (Ukrainian) Currently translated at 70.9% (1074 of 1513 strings) --- plinth/locale/uk/LC_MESSAGES/django.po | 227 +++++++++++-------------- 1 file changed, 104 insertions(+), 123 deletions(-) diff --git a/plinth/locale/uk/LC_MESSAGES/django.po b/plinth/locale/uk/LC_MESSAGES/django.po index e8745242b..d20873832 100644 --- a/plinth/locale/uk/LC_MESSAGES/django.po +++ b/plinth/locale/uk/LC_MESSAGES/django.po @@ -8,7 +8,7 @@ msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2021-08-30 19:29-0400\n" -"PO-Revision-Date: 2021-09-17 13:30+0000\n" +"PO-Revision-Date: 2021-09-17 16:32+0000\n" "Last-Translator: Andrij Mizyk \n" "Language-Team: Ukrainian \n" @@ -55,7 +55,7 @@ msgstr "Неможливо підключитись до {host}:{port}" #: plinth/forms.py:39 msgid "Select a domain name to be used with this application" -msgstr "Оберіть ім’я домену, яке буде використовуватись з цим застосунком" +msgstr "Оберіть назву домена, яке буде використовуватись із цим застосунком" #: plinth/forms.py:41 msgid "" @@ -397,8 +397,8 @@ msgid "" "Connection refused - make sure you provided correct credentials and the " "server is running." msgstr "" -"У зʼєднані відмовлено – переконайтеся, що ви надали правильні облікові дані " -"і чи сервер працює." +"У зʼєднані відмовлено – переконайтеся, що надано правильні облікові дані і " +"чи сервер працює." #: plinth/modules/backups/repository.py:41 msgid "Connection refused" @@ -559,7 +559,7 @@ msgid "" "from the listing on the backup page, you can add it again later on." msgstr "" "Віддалений репозиторій не видалиться. Це лише вилучить репозиторій зі списку " -"на сторінці резервування, ви можете додати його пізніше." +"на сторінці резервування, Ви можете додати його пізніше." #: plinth/modules/backups/templates/backups_repository_remove.html:31 msgid "Remove Location" @@ -760,8 +760,8 @@ msgid "" "permissions." msgstr "" "bepasty не використовує імена користувачів для входу. Використовує лише " -"паролі. Для кожного пароля можна вибирати набір дозволів. Створивши пароль, " -"ви зможете ділитися ним із користувачами, які мають відповідні дозволи." +"паролі. Для кожного пароля можна вибрати набір дозволів. Створивши пароль, " +"Ви зможете ділитися ним із користувачами, які мають відповідні дозволи." #: plinth/modules/bepasty/__init__.py:32 msgid "" @@ -1012,9 +1012,9 @@ msgid "" "store your e-books on your {box_name}, read them online or from any of your " "devices." msgstr "" -"сервер calibre надає мережевий доступ до вашої колекції електронних книжок. " -"Ви можете зберігати електронні книжки на своєму {box_name}, читаючи їх " -"онлайн або з будь-якого вашого пристрою." +"сервер calibre надає мережевий доступ до Вашої колекції електронних книжок. " +"Можете зберігати електронні книжки на своєму {box_name}, читаючи їх у мережі " +"або з будь-якого Вашого пристрою." #: plinth/modules/calibre/__init__.py:34 msgid "" @@ -1187,7 +1187,7 @@ msgid "" "Here you can set some general configuration options like hostname, domain " "name, webserver home page etc." msgstr "" -"Тут ви можете задати деякі загальні параметри налаштування, як назва " +"Тут Ви можете задати деякі загальні параметри налаштувань, як-от назва " "компʼютера, назва домена, домашня сторінка вебсервера тощо." #: plinth/modules/config/__init__.py:58 @@ -1432,7 +1432,7 @@ msgid "" "The default password is 'deluge', but you should log in and change it " "immediately after enabling this service." msgstr "" -"Типовий пароль – 'deluge', але ви можете ввійти і змінити його відразу після " +"Типовий пароль – 'deluge', але Ви можете ввійти і змінити його відразу після " "ввімкнення цього сервісу." #: plinth/modules/deluge/__init__.py:47 @@ -1578,7 +1578,7 @@ msgid "" "diaspora* is a decentralized social network where you can store and control " "your own data." msgstr "" -"diaspora* це децентралізована соціальна мережа, де ви можете зберігати і " +"diaspora* це децентралізована соціальна мережа, де Ви можете зберігати і " "контролювати свої дані." #: plinth/modules/diaspora/__init__.py:68 @@ -1695,7 +1695,7 @@ msgstr "" #, python-brace-format msgid "The public domain name you want to use to reach your {box_name}." msgstr "" -"Публічна назва домену, яку ви бажаєте використовувати для входу на ваш " +"Публічна назва домену, яку Ви бажаєте використовувати для входу на свій " "{box_name}." #: plinth/modules/dynamicdns/forms.py:45 @@ -1879,7 +1879,7 @@ msgid "" "XMPP is an open and standardized communication protocol. Here you can run " "and configure your XMPP server, called ejabberd." msgstr "" -"XMPP – це відкритий і стандартизований протокол спілкування. Тут ви можете " +"XMPP – це відкритий і стандартизований протокол спілкування. Тут Ви можете " "запустити і налаштувати свій XMPP-сервер ejabberd." #: plinth/modules/ejabberd/__init__.py:41 @@ -2001,10 +2001,8 @@ msgid "" msgstr "" #: plinth/modules/email_server/__init__.py:49 -#, fuzzy -#| msgid "Chat Server" msgid "Email Server" -msgstr "Сервер чату" +msgstr "Сервер електронної пошти" #: plinth/modules/email_server/__init__.py:81 msgid "Powered by Postfix, Dovecot & Rspamd" @@ -2033,10 +2031,8 @@ msgstr "Не може бути числом" #: plinth/modules/email_server/audit/home.py:23 #: plinth/modules/email_server/audit/home.py:32 -#, fuzzy -#| msgid "Directory does not exist." msgid "User does not exist" -msgstr "Каталог не існує." +msgstr "Користувача не існує" #: plinth/modules/email_server/audit/ldap.py:66 msgid "Postfix-Dovecot SASL integration" @@ -2071,16 +2067,12 @@ msgid "Postfix uses a TLS certificate" msgstr "" #: plinth/modules/email_server/audit/tls.py:90 -#, fuzzy -#| msgid "No certificate" msgid "Has a TLS certificate" -msgstr "Нема сертифікату" +msgstr "Має сертифікат TLS" #: plinth/modules/email_server/forms.py:7 -#, fuzzy -#| msgid "Domain" msgid "domain" -msgstr "Домен" +msgstr "домен" #: plinth/modules/email_server/forms.py:14 msgid "New alias (without @domain)" @@ -2093,10 +2085,8 @@ msgid "Roundcube" msgstr "Roundcube" #: plinth/modules/email_server/manifest.py:12 -#, fuzzy -#| msgid "Mozilla Thunderbird" msgid "Thunderbird" -msgstr "Mozilla Thunderbird" +msgstr "Thunderbird" #: plinth/modules/email_server/manifest.py:27 msgid "K-9 Mail" @@ -2111,28 +2101,20 @@ msgid "You have no email aliases." msgstr "" #: plinth/modules/email_server/templates/email_alias.html:21 -#, fuzzy -#| msgid "Disabled" msgid "Disable selected" -msgstr "Вимкнені" +msgstr "Вимкнути вибране" #: plinth/modules/email_server/templates/email_alias.html:24 -#, fuzzy -#| msgid "cable is connected" msgid "Enable selected" -msgstr "кабель підʼєднано" +msgstr "Дозволити вибране" #: plinth/modules/email_server/templates/email_alias.html:27 -#, fuzzy -#| msgid "Delete User" msgid "Delete selected" -msgstr "Видалити користувача" +msgstr "Видалити вибране" #: plinth/modules/email_server/templates/email_alias.html:31 -#, fuzzy -#| msgid "Create a new backup" msgid "Create a new email alias" -msgstr "Створити нову резервну копію" +msgstr "Створити новий аліяс електронної пошти" #: plinth/modules/email_server/templates/email_alias.html:38 #: plinth/modules/tahoe/templates/tahoe-post-setup.html:59 @@ -2169,62 +2151,48 @@ msgid "Visit Rspamd administration interface" msgstr "" #: plinth/modules/email_server/templates/email_server.html:18 -#, fuzzy -#| msgid "Service Type" msgid "Service Alert" -msgstr "Тип сервісу" +msgstr "Сповіщення сервісу" #: plinth/modules/email_server/templates/email_server.html:38 msgid "Repair" msgstr "" #: plinth/modules/email_server/templates/my_mail.html:15 -#, fuzzy -#| msgid "Path is not a directory." msgid "You do not have a home directory." -msgstr "Шлях не є каталогом." +msgstr "У вас нема домашнього каталогу." #: plinth/modules/email_server/templates/my_mail.html:17 msgid "Create one to begin receiving emails." msgstr "" #: plinth/modules/email_server/templates/my_mail.html:22 -#, fuzzy -#| msgid "Updated media directory" msgid "Create home directory" -msgstr "Оновлено каталог медія" +msgstr "Створити домашній каталог" #: plinth/modules/email_server/templates/tls_form.html:10 msgid "Keep current settings" msgstr "" #: plinth/modules/email_server/templates/tls_form.html:19 -#, fuzzy -#| msgid "Let's Encrypt" msgid "Use Let's Encrypt" -msgstr "Let's Encrypt" +msgstr "Використовувати Let's Encrypt" #: plinth/modules/email_server/templates/tls_form.html:24 -#, fuzzy -#| msgid "Domain name set" msgid "Common name" -msgstr "Доменну назву задано" +msgstr "Звичайна назва" #: plinth/modules/email_server/templates/tls_form.html:38 msgid "Use custom values" msgstr "" #: plinth/modules/email_server/templates/tls_form.html:43 -#, fuzzy -#| msgid "Certificate Status" msgid "Certificate path" -msgstr "Стан сертифікату" +msgstr "Шлях сертифікату" #: plinth/modules/email_server/templates/tls_form.html:53 -#, fuzzy -#| msgid "Private key of this machine" msgid "Private key path" -msgstr "Приватний ключ машини" +msgstr "Шлях приватного ключа" #: plinth/modules/email_server/templates/tls_form.html:67 msgid "Use system default" @@ -2265,16 +2233,12 @@ msgid "Check syslog for more information" msgstr "" #: plinth/modules/email_server/views.py:181 -#, fuzzy -#| msgid "Enable damage" msgid "Enabled aliases" -msgstr "Дозволити пошкодження" +msgstr "Дозволені аліяси" #: plinth/modules/email_server/views.py:182 -#, fuzzy -#| msgid "Disabled" msgid "Disabled aliases" -msgstr "Вимкнені" +msgstr "Вимкнені аліяси" #: plinth/modules/firewall/__init__.py:33 #, python-brace-format @@ -2363,6 +2327,8 @@ msgid "" "Advanced firewall operations such as opening custom ports are provided by " "the Cockpit app." msgstr "" +"Розширені операції фаєрволу, такі як відкриття власних портів надаються " +"застосунком Cockpit." #: plinth/modules/first_boot/forms.py:14 #, python-brace-format @@ -2395,7 +2361,7 @@ msgid "" "You may want to check the network setup and " "modify it if necessary." msgstr "" -"Можливо, ви захочете перевірити налаштування " +"Можливо, Ви захочете перевірити налаштування " "мережі і змінити їх, якщо необхідно." #: plinth/modules/first_boot/templates/firstboot_welcome.html:29 @@ -2427,7 +2393,7 @@ msgstr "" #: plinth/modules/gitweb/__init__.py:52 msgid "Read-write access to Git repositories" -msgstr "Доступ читання-запису до репозиторіїв Git" +msgstr "Доступ до читання-запису репозиторіїв Git" #: plinth/modules/gitweb/__init__.py:57 plinth/modules/gitweb/manifest.py:10 msgid "Gitweb" @@ -2505,12 +2471,12 @@ msgstr "Створити репозиторій" #: plinth/modules/gitweb/templates/gitweb_configure.html:26 msgid "No repositories available." -msgstr "" +msgstr "Нема доступних репозиторіїв." #: plinth/modules/gitweb/templates/gitweb_configure.html:35 #, python-format msgid "Go to repository %(repo.name)s" -msgstr "" +msgstr "Перейти до репозиторію %(repo.name)s" #: plinth/modules/gitweb/templates/gitweb_configure.html:42 msgid "Cloning…" @@ -2524,7 +2490,7 @@ msgstr "Видалити репозиторій %(repo.name)s" #: plinth/modules/gitweb/templates/gitweb_delete.html:12 #, python-format msgid "Delete Git Repository %(name)s" -msgstr "" +msgstr "Видалити Git-репозиторій %(name)s" #: plinth/modules/gitweb/templates/gitweb_delete.html:18 msgid "Delete this repository permanently?" @@ -2819,7 +2785,7 @@ msgstr "" #: plinth/modules/i2p/__init__.py:57 msgid "Manage I2P application" -msgstr "Керувати застосунком I2P" +msgstr "Керування застосунком I2P" #: plinth/modules/i2p/__init__.py:60 plinth/modules/i2p/manifest.py:13 msgid "I2P" @@ -2831,11 +2797,11 @@ msgstr "Мережа анонімності" #: plinth/modules/i2p/__init__.py:84 msgid "I2P Proxy" -msgstr "" +msgstr "Проксі I2P" #: plinth/modules/i2p/templates/i2p.html:12 msgid "I2P Proxies and Tunnels" -msgstr "" +msgstr "Проксі і тунелі I2P" #: plinth/modules/i2p/templates/i2p.html:21 #: plinth/modules/i2p/templates/i2p.html:34 plinth/templates/clients.html:28 @@ -2892,7 +2858,7 @@ msgstr "Вікі та блоґ" #: plinth/modules/ikiwiki/__init__.py:75 msgid "View and edit wiki applications" -msgstr "" +msgstr "Перегляд і редагування застосунків вікі" #: plinth/modules/ikiwiki/forms.py:17 msgid "Admin Account Name" @@ -2914,12 +2880,12 @@ msgstr "Створити вікі або блоґ" #: plinth/modules/ikiwiki/templates/ikiwiki_configure.html:25 msgid "No wikis or blogs available." -msgstr "" +msgstr "Нема доступних вікі або боґів." #: plinth/modules/ikiwiki/templates/ikiwiki_configure.html:31 #, python-format msgid "Go to site %(site)s" -msgstr "" +msgstr "Перейти до сайту %(site)s" #: plinth/modules/ikiwiki/templates/ikiwiki_configure.html:38 #, python-format @@ -2945,7 +2911,7 @@ msgstr "Створено вікі {name}." #: plinth/modules/ikiwiki/views.py:73 #, python-brace-format msgid "Could not create wiki: {error}" -msgstr "" +msgstr "Не можливо створити вікі: {error}" #: plinth/modules/ikiwiki/views.py:83 #, python-brace-format @@ -2955,7 +2921,7 @@ msgstr "Створено блоґ {name}." #: plinth/modules/ikiwiki/views.py:86 #, python-brace-format msgid "Could not create blog: {error}" -msgstr "" +msgstr "Не можливо створити блоґ: {error}" #: plinth/modules/ikiwiki/views.py:101 #, python-brace-format @@ -2965,7 +2931,7 @@ msgstr "{title} видалено." #: plinth/modules/ikiwiki/views.py:105 #, python-brace-format msgid "Could not delete {title}: {error}" -msgstr "" +msgstr "Не можливо видалити {title}: {error}" #: plinth/modules/infinoted/__init__.py:26 msgid "infinoted is a server for Gobby, a collaborative text editor." @@ -3012,7 +2978,7 @@ msgstr "" #: plinth/modules/jsxc/__init__.py:44 plinth/modules/jsxc/manifest.py:7 msgid "JSXC" -msgstr "" +msgstr "JSXC" #: plinth/modules/jsxc/__init__.py:45 msgid "Chat Client" @@ -3051,7 +3017,7 @@ msgstr "Сертифікати" #: plinth/modules/letsencrypt/__init__.py:96 msgid "Cannot test: No domains are configured." -msgstr "" +msgstr "Тестування не можливе: Нема налаштованих доменів." #: plinth/modules/letsencrypt/templates/letsencrypt.html:24 msgid "Domain" @@ -3115,6 +3081,8 @@ msgid "" "No domains have been configured. Configure " "domains to be able to obtain certificates for them." msgstr "" +"Нема налаштованих доменів. Налаштуйте домени, " +"щоб мати можливість отримати сертифікати для них." #: plinth/modules/letsencrypt/views.py:41 #, python-brace-format @@ -3274,7 +3242,7 @@ msgstr "Вікі" #: plinth/modules/mediawiki/forms.py:52 msgid "Administrator Password" -msgstr "" +msgstr "Пароль адміністратора" #: plinth/modules/mediawiki/forms.py:53 msgid "" @@ -3365,7 +3333,7 @@ msgstr "Minetest" #: plinth/modules/minetest/__init__.py:62 msgid "Block Sandbox" -msgstr "Пісочниця блоків" +msgstr "Блокова пісочниця" #: plinth/modules/minetest/forms.py:13 msgid "Maximum number of players" @@ -4529,13 +4497,13 @@ msgid "" "see options to overcome this limitation, choose 'no public address' option " "in Internet connection type selection." msgstr "" -"Якщо ви не контролюєте свій маршрутизатор, не налаштовуйте його. Щоб " -"побачити варіянти обходу цього обмеження, оберіть параметр 'немає публічної " +"Якщо Ви не контролюєте свій маршрутизатор, не налаштовуйте його. Щоб " +"побачити способи обходу цього обмеження, оберіть параметр 'немає публічної " "адреси' під час вибору типу зʼєднання з Інтернетом." #: plinth/modules/networks/templates/router_configuration_content.html:39 msgid "Choose How You Wish to Configure Your Router" -msgstr "Оберіть, як ви хочете сконфіґурувати свій роутер" +msgstr "Оберіть, як Ви бажаєте налаштувати свій маршрутизатор" #: plinth/modules/networks/templates/router_configuration_content.html:42 msgid "" @@ -4554,13 +4522,12 @@ msgid "" "model number and search online for the router's manual. This will provide " "full instructions on how to perform this task." msgstr "" -"Імʼя користувача й пароль конфіґурується під час першого налаштування " +"Імʼя користувача і пароль налаштовується під час першого налаштування " "маршрутизатора. Для багатьох маршрутизаторів ця інформація роздрукована на " -"задній частині маршрутизатора. Якщо ви не памʼятаєте параметри або IP-адресу " -"свого маршрутизатора, ви можливо захочете скинути налаштування та " -"налаштувати наново. Дізнайтеся номер моделі вашого маршрутизатора й " -"пошукайте в мережі посібник для нього. Це може надати повну інструкцію для " -"виконання цього завдання." +"задній частині маршрутизатора. Якщо Ви не памʼятаєте параметри або IP-адресу " +"свого маршрутизатора, можете скинути налаштування та налаштувати наново. " +"Дізнайтеся номер моделі свого маршрутизатора й пошукайте в мережі посібник " +"для нього. Це надасть повну інструкцію для виконання цього завдання." #: plinth/modules/networks/views.py:27 msgid "disabled" @@ -4818,7 +4785,7 @@ msgstr "" #: plinth/modules/openvpn/__init__.py:58 msgid "Connect to VPN services" -msgstr "Підʼєднатися до сервісів VPN" +msgstr "Підʼєднання до сервісів VPN" #: plinth/modules/openvpn/__init__.py:61 plinth/modules/openvpn/manifest.py:17 msgid "OpenVPN" @@ -4827,7 +4794,7 @@ msgstr "OpenVPN" #: plinth/modules/openvpn/__init__.py:62 #: plinth/modules/wireguard/__init__.py:51 msgid "Virtual Private Network" -msgstr "" +msgstr "Віртуальна приватна мережа" #: plinth/modules/openvpn/__init__.py:73 #, python-brace-format @@ -4912,7 +4879,7 @@ msgstr "" #, python-brace-format msgid "{box_name} is connected to a (wireless) router which you don't control." msgstr "" -"{box_name} підʼєднано до (бездротового) маршрутизатора, яким ви не можете " +"{box_name} підʼєднано до (бездротового) маршрутизатора, яким Ви не можете " "керувати." #: plinth/modules/pagekite/__init__.py:38 @@ -4929,7 +4896,7 @@ msgstr "" #: plinth/modules/pagekite/__init__.py:42 msgid "Your ISP limits incoming connections." -msgstr "" +msgstr "Ваш ISP обмежує вхідні зʼєднання." #: plinth/modules/pagekite/__init__.py:44 #, python-brace-format @@ -5132,6 +5099,8 @@ msgid "" "Are you sure you want to restart? You will not be able to access this web " "interface for a few minutes until the system is restarted." msgstr "" +"Ви справді хочете перезавантажити систему? Ви не матимете доступ до " +"вебінтерфейсу протягом кількох хвилин, поки система не перезавантажиться." #: plinth/modules/power/templates/power_restart.html:34 msgid "" @@ -5149,6 +5118,8 @@ msgid "" "Are you sure you want to shut down? You will not be able to access this web " "interface after shut down." msgstr "" +"Ви справді хочете вимкнути систему? Ви не матимете доступу до вебінтерфейсу " +"після вимкнення." #: plinth/modules/power/templates/power_shutdown.html:33 msgid "" @@ -5338,9 +5309,10 @@ msgid "" "manipulation, message searching and spell checking." msgstr "" "Вебпошта Roundcube — це багатомовний клієнт IMAP на основі вебоглядача з " -"користувацьким інтерфейсом схожим на застосунок. Він надає весь функціонал, " -"який ви очікуєте від клієнта ел. пошти, враховуючи підтримку MIME, адресної " -"книжки, маніпулюванням теками, пошуком повідомлень та перевіркою правопису." +"користувацьким інтерфейсом, що виглядає як застосунок. Він надає весь " +"функціонал, який Ви очікуєте від клієнта ел. пошти, враховуючи підтримку " +"MIME, адресної книжки, маніпулювання теками, пошуку повідомлень та перевірки " +"правопису." #: plinth/modules/roundcube/__init__.py:28 msgid "" @@ -5350,6 +5322,11 @@ msgid "" "(recommended), fill the server field like imaps://imap.example.com." msgstr "" +"Можете використовувати його вказавши імʼя користувача і пароль обліківки " +"електронної пошти, до якої Ви хочете отримати доступ за доменною назвою " +"сервера IMAP від Вашого постачальника електронної пошти, наприклад imap" +".example.com. Для IMAP через SSL (рекомендується), поле сервера " +"виглядає як imaps://imap.example.com." #: plinth/modules/roundcube/__init__.py:33 msgid "" @@ -5360,6 +5337,11 @@ msgid "" "lesssecureapps\">https://www.google.com/settings/security/lesssecureapps)." msgstr "" +"Для Gmail, імʼям користувача буде адреса Gmail, паролем – ваш пароль " +"обліківки Google і сервером – imaps://imap.gmail.com. Зауважте, " +"що потрібно дозволити \"Малозахищені додатки\" в налаштуваннях обліківки " +"Google (https://www.google.com/settings/security/lesssecureapps)." #: plinth/modules/roundcube/__init__.py:56 msgid "Email Client" @@ -5530,7 +5512,7 @@ msgstr "" #: plinth/modules/searx/__init__.py:45 msgid "Search the web" -msgstr "Пошук в інтернеті" +msgstr "Пошук в Інтернеті" #: plinth/modules/searx/__init__.py:48 plinth/modules/searx/manifest.py:6 msgid "Searx" @@ -5800,7 +5782,7 @@ msgstr "Шлях до ділянки" #: plinth/modules/sharing/forms.py:25 msgid "Disk path to a folder on this server that you intend to share." -msgstr "Шлях до теки на сервері, якою ви маєте намір ділитися." +msgstr "Шлях до теки на сервері, якою Ви маєте намір ділитися." #: plinth/modules/sharing/forms.py:28 msgid "Public share" @@ -6149,7 +6131,7 @@ msgid "" "You can view the storage media currently in use, mount and unmount removable " "media, expand the root partition etc." msgstr "" -"Цей модуль дозволяє вам керувати медія-сховищем, яке привʼязане до {box_name}" +"Цей модуль дозволяє Вам керувати медія-сховищем, яке привʼязане до {box_name}" ". Ви можете переглядати медія-сховище під час використання, монтувати і " "відмонтовувати знімні накопичувачі, розширювати розділ root тощо." @@ -6390,7 +6372,7 @@ msgstr "" #: plinth/modules/syncthing/__init__.py:58 msgid "Administer Syncthing application" -msgstr "" +msgstr "Адміністрування програми Syncthing" #: plinth/modules/syncthing/__init__.py:62 #: plinth/modules/syncthing/manifest.py:12 @@ -6618,6 +6600,8 @@ msgid "" "If your %(box_name)s is behind a router or firewall, you should make sure " "the following ports are open, and port-forwarded, if necessary:" msgstr "" +"Якщо Ваш %(box_name)s за маршрутизатором або фаєрволом, Ви повинні " +"переконатися, що наступні порти відкриті і перенаправлені, якщо необхідно:" #: plinth/modules/tor/templates/tor.html:87 msgid "SOCKS" @@ -6958,7 +6942,7 @@ msgstr "Пароль для авторизації" #: plinth/modules/users/forms.py:80 msgid "Enter your current password to authorize account modifications." -msgstr "" +msgstr "Уведіть свій поточний пароль для авторизування змін обліківки." #: plinth/modules/users/forms.py:88 msgid "Invalid password." @@ -6985,7 +6969,7 @@ msgstr "Не вдалося додати нового користувача д #: plinth/modules/users/forms.py:177 msgid "Authorized SSH Keys" -msgstr "Авторизовані ключі SSH" +msgstr "Ключі SSH для авторизації" #: plinth/modules/users/forms.py:179 msgid "" @@ -6993,6 +6977,9 @@ msgid "" "system without using a password. You may enter multiple keys, one on each " "line. Blank lines and lines starting with # will be ignored." msgstr "" +"Налаштування публічного ключа SSH дозволяє користувачеві безпечно заходити в " +"систему без використання пароля. Ви можете вказати декілька ключів, один на " +"кожен рядок. Порожні рядки і рядки, що починаються на # іґноруються." #: plinth/modules/users/forms.py:266 msgid "Renaming LDAP user failed." @@ -7034,7 +7021,7 @@ msgstr "Не вдалося обмежити доступ до консолі: { #: plinth/modules/users/forms.py:437 msgid "User account created, you are now logged in" -msgstr "Обліківку користувача створено, ви ввійшли в систему" +msgstr "Обліківку користувача створено, Ви ввійшли в систему" #: plinth/modules/users/templates/users_change_password.html:11 #, python-format @@ -7340,7 +7327,7 @@ msgstr "Додати клієнта" #: plinth/modules/wireguard/templates/wireguard_delete_client.html:14 msgid "Are you sure that you want to delete this client?" -msgstr "Ви дійсно бажаєте видалити цей клієнт?" +msgstr "Ви дійсно бажаєте видалити даний клієнт?" #: plinth/modules/wireguard/templates/wireguard_delete_server.html:14 msgid "Are you sure that you want to delete this server?" @@ -7508,22 +7495,16 @@ msgstr "" #: plinth/modules/wordpress/__init__.py:69 #: plinth/modules/wordpress/manifest.py:6 -#, fuzzy -#| msgid "Address" msgid "WordPress" -msgstr "Адреса" +msgstr "WordPress" #: plinth/modules/wordpress/__init__.py:70 -#, fuzzy -#| msgid "Wiki and Blog" msgid "Website and Blog" -msgstr "Вікі та блоґ" +msgstr "Вебсайт і блоґ" #: plinth/modules/wordpress/forms.py:14 -#, fuzzy -#| msgid "public access" msgid "Public access" -msgstr "публічний доступ" +msgstr "Публічний доступ" #: plinth/modules/wordpress/forms.py:15 msgid "" @@ -7873,8 +7854,8 @@ msgid "" "are using the DMZ feature to forward all ports. No further router " "configuration is necessary." msgstr "" -"Ваш FreedomBox поза маршрутизатором і ви " -"використовуєте функцію DMZ для перенаправлення усіх портів. Подальша " +"Ваш FreedomBox поза маршрутизатором і Ви " +"використовуєте функцію DMZ для перенаправлення всіх портів. Подальша " "конфіґурація маршрутизатора непотрібна." #: plinth/templates/port-forwarding-info.html:26 @@ -7884,7 +7865,7 @@ msgid "" "are not using the DMZ feature. You will need to set up port forwarding on " "your router. You should forward the following ports for %(service_name)s:" msgstr "" -"Ваш FreedomBox поза маршрутизатором і ви не " +"Ваш FreedomBox поза маршрутизатором і Ви не " "використовуєте функцію DMZ. Вам потрібно налаштувати перенаправлення портів " "на маршрутизаторі. Вам слід перенаправити наступні порти для " "%(service_name)s:" From e2c27a794ccf179bdf5119e941caa6563e9b1a91 Mon Sep 17 00:00:00 2001 From: Andrij Mizyk Date: Fri, 17 Sep 2021 18:19:17 +0000 Subject: [PATCH 48/57] Translated using Weblate (Ukrainian) Currently translated at 73.6% (1114 of 1513 strings) --- plinth/locale/uk/LC_MESSAGES/django.po | 106 +++++++++++++++---------- 1 file changed, 66 insertions(+), 40 deletions(-) diff --git a/plinth/locale/uk/LC_MESSAGES/django.po b/plinth/locale/uk/LC_MESSAGES/django.po index d20873832..cfeca91e5 100644 --- a/plinth/locale/uk/LC_MESSAGES/django.po +++ b/plinth/locale/uk/LC_MESSAGES/django.po @@ -8,7 +8,7 @@ msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2021-08-30 19:29-0400\n" -"PO-Revision-Date: 2021-09-17 16:32+0000\n" +"PO-Revision-Date: 2021-09-17 18:30+0000\n" "Last-Translator: Andrij Mizyk \n" "Language-Team: Ukrainian \n" @@ -1201,18 +1201,18 @@ msgstr "Загальні налаштування" #: plinth/modules/tahoe/templates/tahoe-pre-setup.html:24 #: plinth/templates/index.html:46 msgid "Configure" -msgstr "Налаштувати" +msgstr "Налаштування" #: plinth/modules/config/__init__.py:67 plinth/modules/config/forms.py:68 #: plinth/modules/dynamicdns/forms.py:97 #: plinth/modules/names/templates/names.html:16 msgid "Domain Name" -msgstr "Доменне ім’я" +msgstr "Доменна назва" #: plinth/modules/config/forms.py:30 plinth/modules/config/forms.py:80 #: plinth/modules/dynamicdns/forms.py:100 msgid "Invalid domain name" -msgstr "Неправильне доменне імʼя" +msgstr "Неправильна доменна назва" #: plinth/modules/config/forms.py:40 #, python-brace-format @@ -1392,7 +1392,7 @@ msgstr "" #: plinth/modules/datetime/__init__.py:70 msgid "Date & Time" -msgstr "Дата й час" +msgstr "Дата і час" #: plinth/modules/datetime/__init__.py:116 msgid "Time synchronized to NTP server" @@ -1509,7 +1509,7 @@ msgstr "" #: plinth/modules/diagnostics/__init__.py:221 msgid "You should not install any new apps on this system." -msgstr "Не слід встановлювати нових застосунків на цій системі." +msgstr "Не слід установлювати нових застосунків на цій системі." #: plinth/modules/diagnostics/__init__.py:233 #, no-python-format, python-brace-format @@ -1897,7 +1897,7 @@ msgid "" "ejabberd needs a STUN/TURN server for audio/video calls. Install the Coturn app or configure an external server." msgstr "" -"ejabberd потребує сервера STUN/TURN для звукових/відео дзвінків. Встановіть " +"ejabberd потребує сервера STUN/TURN для звукових/відео дзвінків. Установіть " "застосунок Coturn або налаштуйте зовнішній " "сервер." @@ -2123,7 +2123,7 @@ msgstr "Додати" #: plinth/modules/email_server/templates/email_domains.html:19 msgid "New value" -msgstr "" +msgstr "Нове значення" #: plinth/modules/email_server/templates/email_domains.html:31 #: plinth/modules/email_server/templates/email_security.html:23 @@ -2172,7 +2172,7 @@ msgstr "Створити домашній каталог" #: plinth/modules/email_server/templates/tls_form.html:10 msgid "Keep current settings" -msgstr "" +msgstr "Зберігати поточні налаштування" #: plinth/modules/email_server/templates/tls_form.html:19 msgid "Use Let's Encrypt" @@ -2247,10 +2247,13 @@ msgid "" "network traffic on your {box_name}. Keeping a firewall enabled and properly " "configured reduces risk of security threat from the Internet." msgstr "" +"Фаєрвол — це система безпеки, що контролює віхідний і вихідний мережевий " +"трафік вашого {box_name}. Тримайте фаєрвол увімкненим і належним чином " +"налаштованим, це зменшить ризик загрози безпеці з Інтернету." #: plinth/modules/firewall/__init__.py:66 msgid "Firewall" -msgstr "" +msgstr "Фаєрвол" #: plinth/modules/firewall/components.py:134 #, python-brace-format @@ -2291,7 +2294,7 @@ msgstr "Дозволено" #: plinth/modules/snapshot/forms.py:23 plinth/modules/snapshot/forms.py:29 #: plinth/templates/cards.html:34 msgid "Disabled" -msgstr "Вимкнені" +msgstr "Вимкнено" #: plinth/modules/firewall/templates/firewall.html:72 msgid "Permitted" @@ -4913,7 +4916,7 @@ msgstr "PageKite" #: plinth/modules/pagekite/__init__.py:66 msgid "Public Visibility" -msgstr "Громадська видимість" +msgstr "Публічна видимість" #: plinth/modules/pagekite/__init__.py:76 msgid "PageKite Domain" @@ -4928,6 +4931,8 @@ msgid "" "Select your pagekite server. Set \"pagekite.net\" to use the default " "pagekite.net server." msgstr "" +"Виберіть Ваш сервер для pagekite. Вкажіть \"pagekite.net\", щоб " +"використовувати типовий сервер pagekite.net." #: plinth/modules/pagekite/forms.py:37 plinth/modules/shadowsocks/forms.py:39 msgid "Server port" @@ -5861,6 +5866,9 @@ msgid "" "can be used to roll back the system to a previously known good state in case " "of unwanted changes to the system." msgstr "" +"Зрізи дозволяють створювати і керувати зрізами файлової системи btrfs. Це " +"можна використовувати для відкочування системи до попереднього хорошого " +"стану в разі небажаних змін системи." #: plinth/modules/snapshot/__init__.py:31 #, no-python-format @@ -5869,6 +5877,9 @@ msgid "" "and after a software installation. Older snapshots will be automatically " "cleaned up according to the settings below." msgstr "" +"Зрізи робляться періодично (відомі як зрізи за часом) та після і до " +"встановлення ПЗ. Старі зрізи автоматично видалятимуться відповідно до " +"налаштувань нижче." #: plinth/modules/snapshot/__init__.py:34 msgid "" @@ -5876,6 +5887,9 @@ msgid "" "partition only. Snapshots are not a replacement for backups since they can only be stored on the same partition. " msgstr "" +"Наразі зрізи працюють лише на файлових системах btrfs і розділі root. Зрізи " +"не є заміною резервних копій, поки вони " +"не зберігаються на одному розділі. " #: plinth/modules/snapshot/__init__.py:56 msgid "Storage Snapshots" @@ -5883,7 +5897,7 @@ msgstr "Зрізи сховища" #: plinth/modules/snapshot/forms.py:12 msgid "Free Disk Space to Maintain" -msgstr "" +msgstr "Вільний дисковий простір для утримання" #: plinth/modules/snapshot/forms.py:13 msgid "" @@ -5891,69 +5905,76 @@ msgid "" "below this value, older snapshots are removed until this much free space is " "regained. The default value is 30%." msgstr "" +"Зберігати цей відсоток вільного простору на диску. Якщо вільний простір " +"падає нижче цього значення, старі зрізи вилучаються, поки не відновиться " +"заданий обʼєм вільного простору. Типове значення — 30%." #: plinth/modules/snapshot/forms.py:20 msgid "Timeline Snapshots" -msgstr "" +msgstr "Зрізи за часом" #: plinth/modules/snapshot/forms.py:21 msgid "" "Enable or disable timeline snapshots (hourly, daily, monthly and yearly)." msgstr "" +"Дозволити або заборонити зрізи за часом (щогодини, щоденно, щомісячно і " +"щорічно)." #: plinth/modules/snapshot/forms.py:26 msgid "Software Installation Snapshots" -msgstr "" +msgstr "Зрізи встановлення ПЗ" #: plinth/modules/snapshot/forms.py:27 msgid "Enable or disable snapshots before and after software installation" -msgstr "" +msgstr "Дозволити або заборонити зрізи перед і після встановлення програм" #: plinth/modules/snapshot/forms.py:32 msgid "Hourly Snapshots Limit" -msgstr "" +msgstr "Обмеження щогодинних зрізів" #: plinth/modules/snapshot/forms.py:33 msgid "Keep a maximum of this many hourly snapshots." -msgstr "" +msgstr "Найбільша кількість щогодинних зрізів для зберігання." #: plinth/modules/snapshot/forms.py:36 msgid "Daily Snapshots Limit" -msgstr "" +msgstr "Обмеження щоденних зрізів" #: plinth/modules/snapshot/forms.py:37 msgid "Keep a maximum of this many daily snapshots." -msgstr "" +msgstr "Зберігати не більше стількох щоденних зрізів." #: plinth/modules/snapshot/forms.py:40 msgid "Weekly Snapshots Limit" -msgstr "" +msgstr "Обмеження щотижневих зрізів" #: plinth/modules/snapshot/forms.py:41 msgid "Keep a maximum of this many weekly snapshots." -msgstr "" +msgstr "Зберігати не більше стількох щотижневих зрізів." #: plinth/modules/snapshot/forms.py:44 msgid "Monthly Snapshots Limit" -msgstr "" +msgstr "Обмеження щомісячних зрізів" #: plinth/modules/snapshot/forms.py:45 msgid "Keep a maximum of this many monthly snapshots." -msgstr "" +msgstr "Зберігати не більше стількох щомісячних зрізів." #: plinth/modules/snapshot/forms.py:48 msgid "Yearly Snapshots Limit" -msgstr "" +msgstr "Обмеження щорічних зрізів" #: plinth/modules/snapshot/forms.py:49 msgid "" "Keep a maximum of this many yearly snapshots. The default value is 0 (keep " "no yearly snapshot)." msgstr "" +"Зберігати не більше стількох щорічних зрізів. Типове значення — 0 (не " +"зберігати щорічних зрізів)." #: plinth/modules/snapshot/templates/snapshot_delete_selected.html:12 msgid "Delete the following snapshots permanently?" -msgstr "" +msgstr "Видалити наступні зрізи назавжди?" #: plinth/modules/snapshot/templates/snapshot_delete_selected.html:17 #: plinth/modules/snapshot/templates/snapshot_manage.html:27 @@ -5971,15 +5992,15 @@ msgstr "Дата" #: plinth/modules/snapshot/templates/snapshot_manage.html:20 #: plinth/modules/snapshot/views.py:198 msgid "Delete Snapshots" -msgstr "" +msgstr "Видалити зріз" #: plinth/modules/snapshot/templates/snapshot_manage.html:17 msgid "Create Snapshot" -msgstr "" +msgstr "Створити зріз" #: plinth/modules/snapshot/templates/snapshot_manage.html:30 msgid "Rollback" -msgstr "" +msgstr "Відкат" #: plinth/modules/snapshot/templates/snapshot_manage.html:40 msgid "will be used at next boot" @@ -5992,7 +6013,7 @@ msgstr "використовується" #: plinth/modules/snapshot/templates/snapshot_manage.html:54 #, python-format msgid "Rollback to snapshot #%(number)s" -msgstr "" +msgstr "Відкотити до зрізу #%(number)s" #: plinth/modules/snapshot/templates/snapshot_not_supported.html:11 #, python-format @@ -6003,7 +6024,7 @@ msgstr "" #: plinth/modules/snapshot/templates/snapshot_rollback.html:12 msgid "Roll back the system to this snapshot?" -msgstr "" +msgstr "Відкотити систему до цього зрізу?" #: plinth/modules/snapshot/templates/snapshot_rollback.html:15 msgid "" @@ -6023,7 +6044,7 @@ msgstr "створено вручну" #: plinth/modules/snapshot/views.py:29 msgid "timeline" -msgstr "" +msgstr "за часом" #: plinth/modules/snapshot/views.py:30 msgid "apt" @@ -6031,11 +6052,11 @@ msgstr "apt" #: plinth/modules/snapshot/views.py:41 msgid "Manage Snapshots" -msgstr "" +msgstr "Керування зрізами" #: plinth/modules/snapshot/views.py:90 msgid "Created snapshot." -msgstr "" +msgstr "Створено зріз." #: plinth/modules/snapshot/views.py:153 msgid "Storage snapshots configuration updated" @@ -6044,24 +6065,24 @@ msgstr "Налаштування зрізів сховища оновлено" #: plinth/modules/snapshot/views.py:157 plinth/modules/tor/views.py:60 #, python-brace-format msgid "Action error: {0} [{1}] [{2}]" -msgstr "" +msgstr "Помилка дії: {0} [{1}] [{2}]" #: plinth/modules/snapshot/views.py:185 msgid "Deleted selected snapshots" -msgstr "" +msgstr "Видалити вибрані зрізи" #: plinth/modules/snapshot/views.py:190 msgid "Snapshot is currently in use. Please try again later." -msgstr "" +msgstr "Зріз зараз використовується. Повторіть пізніше." #: plinth/modules/snapshot/views.py:209 #, python-brace-format msgid "Rolled back to snapshot #{number}." -msgstr "" +msgstr "Відкочено до зрізу #{number}." #: plinth/modules/snapshot/views.py:212 msgid "The system must be restarted to complete the rollback." -msgstr "" +msgstr "Систему потрібно перезапустити, щоб завершити відкат." #: plinth/modules/snapshot/views.py:224 msgid "Rollback to Snapshot" @@ -7166,6 +7187,8 @@ msgid "" "It can be used to connect to a VPN provider which supports WireGuard, and to " "route all outgoing traffic from {box_name} through the VPN." msgstr "" +"Може використовуватися для зʼєднання з постачальником VPN, що підтримує " +"WireGuard, і перенаправлення всього вихідного трафіку {box_name} через VPN." #: plinth/modules/wireguard/__init__.py:29 #, python-brace-format @@ -7174,6 +7197,9 @@ msgid "" "travelling. While connected to a public Wi-Fi network, all traffic can be " "securely relayed through {box_name}." msgstr "" +"Інше використання — підʼєднати мобільний пристрій до {box_name} під час " +"подорожі. Коли підʼєднано до публічної мережі Wi-Fi, увесь трафік може " +"безпечно передаватися через {box_name}." #: plinth/modules/wireguard/forms.py:32 msgid "Invalid key." @@ -7909,7 +7935,7 @@ msgstr "Перевірити знову" #: plinth/templates/setup.html:60 msgid "Install" -msgstr "Встановити" +msgstr "Установити" #: plinth/templates/setup.html:72 msgid "Performing pre-install operation" From 00be64036a775e1de1e6791de6ffe8fe5493be62 Mon Sep 17 00:00:00 2001 From: Veiko Aasa Date: Fri, 17 Sep 2021 21:21:53 +0300 Subject: [PATCH 49/57] gitweb: tests: Convert functional tests to non-BDD python format Signed-off-by: Veiko Aasa Reviewed-by: Sunil Mohan Adapa --- plinth/modules/gitweb/tests/gitweb.feature | 113 ----- .../modules/gitweb/tests/test_functional.py | 386 ++++++++---------- 2 files changed, 176 insertions(+), 323 deletions(-) delete mode 100644 plinth/modules/gitweb/tests/gitweb.feature diff --git a/plinth/modules/gitweb/tests/gitweb.feature b/plinth/modules/gitweb/tests/gitweb.feature deleted file mode 100644 index 15685d5d4..000000000 --- a/plinth/modules/gitweb/tests/gitweb.feature +++ /dev/null @@ -1,113 +0,0 @@ -# SPDX-License-Identifier: AGPL-3.0-or-later - -@apps @gitweb @sso -Feature: gitweb Simple Git Hosting - Git web interface. - -Background: - Given I'm a logged in user - And the gitweb application is installed - -Scenario: Enable gitweb application - Given the gitweb application is disabled - When I enable the gitweb application - Then the gitweb site should be available - -Scenario: Create public repository - Given the gitweb application is enabled - And a public repository that doesn't exist - When I create the repository - Then the repository should be listed as a public - And the repository should be listed on gitweb - -Scenario: Create private repository - Given the gitweb application is enabled - And a private repository that doesn't exist - When I create the repository - Then the repository should be listed as a private - And the repository should be listed on gitweb - -@backups -Scenario: Backup and restore gitweb - Given the gitweb application is enabled - And a repository - When I create a backup of the gitweb app data with name test_gitweb - And I delete the repository - And I restore the gitweb app data backup with name test_gitweb - Then the repository should be restored - And the gitweb site should be available - -Scenario: Public gitweb site shows only public repositories - Given the gitweb application is enabled - And both public and private repositories exist - When I log out - Then the public repository should be listed on gitweb - And the private repository should not be listed on gitweb - -Scenario: Gitweb is not public if there are only private repositories - Given the gitweb application is enabled - And at least one repository exists - And all repositories are private - When I log out - And I access gitweb application - Then I should be prompted for login - And gitweb app should not be visible on the front page - -Scenario: Edit repository metadata - Given the gitweb application is enabled - And a public repository that doesn't exist - And a repository metadata: - description: Test Description - owner: Test Owner - access: private - When I create the repository - And I set the metadata of the repository - Then the metadata of the repository should be as set - -Scenario: Edit default branch of the repository - Given the gitweb application is enabled - And a repository with the branch branch1 - When I set branch1 as a default branch - Then the gitweb site should show branch1 as a default repo branch - -Scenario: Access public repository with git client - Given the gitweb application is enabled - And a public repository - When using a git client - Then the repository should be publicly readable - And the repository should not be publicly writable - And the repository should be privately writable - -Scenario: Access private repository with git client - Given the gitweb application is enabled - And a private repository - When using a git client - Then the repository should not be publicly readable - And the repository should not be publicly writable - And the repository should be privately readable - And the repository should be privately writable - -Scenario: User of git-access group can access gitweb site - Given the gitweb application is enabled - And all repositories are private - And the user gituser in group git-access exists - When I'm logged in as the user gituser - Then the gitweb site should be available - -Scenario: User not of git-access group can't access gitweb site - Given the gitweb application is enabled - And all repositories are private - And the user nogroupuser exists - When I'm logged in as the user nogroupuser - Then the gitweb site should not be available - -Scenario: Delete repository - Given the gitweb application is enabled - And a repository - When I delete the repository - Then the repository should not be listed - -Scenario: Disable gitweb application - Given the gitweb application is enabled - When I disable the gitweb application - Then the gitweb site should not be available diff --git a/plinth/modules/gitweb/tests/test_functional.py b/plinth/modules/gitweb/tests/test_functional.py index 80bb1e76d..207f4aa4c 100644 --- a/plinth/modules/gitweb/tests/test_functional.py +++ b/plinth/modules/gitweb/tests/test_functional.py @@ -2,185 +2,136 @@ """ Functional, browser based tests for gitweb app. """ - import contextlib import os import shutil import subprocess import tempfile -from pytest_bdd import given, parsers, scenarios, then, when +import pytest from plinth.tests import functional -scenarios('gitweb.feature') +pytestmark = [pytest.mark.apps, pytest.mark.gitweb] _default_url = functional.config['DEFAULT']['url'] -@given('a public repository') -@given('a repository') -@given('at least one repository exists') -def gitweb_repo(session_browser): - _create_repo(session_browser, 'Test-repo', 'public', True) +@pytest.fixture(scope='module', autouse=True) +def fixture_background(session_browser): + """Login and install the app.""" + functional.login(session_browser) + functional.install(session_browser, 'gitweb') + functional.app_enable(session_browser, 'gitweb') + yield + functional.login(session_browser) + functional.app_disable(session_browser, 'gitweb') -@given('a private repository') -def gitweb_private_repo(session_browser): - _create_repo(session_browser, 'Test-repo', 'private', True) +@pytest.fixture(autouse=True) +def fixture_login(session_browser): + """Login fixture.""" + functional.login(session_browser) + functional.app_enable(session_browser, 'gitweb') + yield -@given(parsers.parse('a repository with the branch {branch:w}')) -def _create_repo_with_branch(session_browser, branch): - _delete_repo(session_browser, 'Test-repo', ignore_missing=True) - _create_repo(session_browser, 'Test-repo', 'public') - _create_branch('Test-repo', branch) - - -@given('both public and private repositories exist') -def gitweb_public_and_private_repo(session_browser): - _create_repo(session_browser, 'Test-repo', 'public', True) - _create_repo(session_browser, 'Test-repo2', 'private', True) - - -@given(parsers.parse("a {access:w} repository that doesn't exist")) -def gitweb_nonexistent_repo(session_browser, access): - _delete_repo(session_browser, 'Test-repo', ignore_missing=True) - return dict(access=access) - - -@given('all repositories are private') -def gitweb_all_repositories_private(session_browser): +def test_all_repos_private(session_browser): + """Test repo accessability when all repos are private.""" + _create_repo(session_browser, 'Test-repo', 'private', ok_if_exists=True) _set_all_repos_private(session_browser) + if not functional.user_exists(session_browser, 'gitweb_user'): + functional.create_user(session_browser, 'gitweb_user', + groups=['git-access']) + if not functional.user_exists(session_browser, 'nogroupuser'): + functional.create_user(session_browser, 'nogroupuser', groups=[]) + + functional.login_with_account(session_browser, functional.base_url, + 'gitweb_user') + assert functional.is_available(session_browser, 'gitweb') + assert len(functional.find_on_front_page(session_browser, 'gitweb')) == 1 + + functional.login_with_account(session_browser, functional.base_url, + 'nogroupuser') + assert not functional.is_available(session_browser, 'gitweb') + assert len(functional.find_on_front_page(session_browser, 'gitweb')) == 0 + + functional.logout(session_browser) + functional.access_url(session_browser, 'gitweb') + assert functional.is_login_prompt(session_browser) + assert len(functional.find_on_front_page(session_browser, 'gitweb')) == 0 -@given(parsers.parse('a repository metadata:\n{metadata}'), - target_fixture='gitweb_repo_metadata') -def gitweb_repo_metadata(session_browser, metadata): - metadata_dict = {} - for item in metadata.split('\n'): - item = item.split(': ') - metadata_dict[item[0]] = item[1] - return metadata_dict - - -@when('I create the repository') -def gitweb_create_repo(session_browser, access): - _create_repo(session_browser, 'Test-repo', access) - - -@when('I delete the repository') -def gitweb_delete_repo(session_browser): +@pytest.mark.backups +def test_backup(session_browser): + """Test backing up and restoring.""" + _create_repo(session_browser, 'Test-repo', ok_if_exists=True) + functional.backup_create(session_browser, 'gitweb', 'test_gitweb') _delete_repo(session_browser, 'Test-repo') + functional.backup_restore(session_browser, 'gitweb', 'test_gitweb') + assert _repo_exists(session_browser, 'Test-repo') + assert functional.is_available(session_browser, 'gitweb') -@when(parsers.parse('I set {branch:w} as a default branch')) -def gitweb_set_default_branch(session_browser, branch): - _set_default_branch(session_browser, 'Test-repo', branch) +@pytest.mark.parametrize('access', ['public', 'private']) +@pytest.mark.parametrize('repo_name', ['Test-repo', 'Test-repo.git']) +def test_create_delete_repo(session_browser, access, repo_name): + """Test creating and deleting a repo and accessing with a git client.""" + _delete_repo(session_browser, repo_name, ignore_missing=True) + _create_repo(session_browser, repo_name, access) + + assert _repo_exists(session_browser, repo_name, access) + assert _site_repo_exists(session_browser, repo_name) + + if access == "public": + assert _repo_is_readable(repo_name) + else: + assert not _repo_is_readable(repo_name) + + assert not _repo_is_writable(repo_name) + assert _repo_is_readable(repo_name, with_auth=True) + assert _repo_is_writable(repo_name, with_auth=True) + + _delete_repo(session_browser, repo_name) + assert not _repo_exists(session_browser, repo_name) -@when('I set the metadata of the repository') -def gitweb_edit_repo_metadata(session_browser, gitweb_repo_metadata): - _edit_repo_metadata(session_browser, 'Test-repo', gitweb_repo_metadata) +def test_both_private_and_public_repo_exist(session_browser): + """Tests when both private and public repo exist.""" + _create_repo(session_browser, 'Test-repo', 'public', True) + _create_repo(session_browser, 'Test-repo-private', 'private', True) - -@when('using a git client') -def gitweb_using_git_client(): - pass - - -@then( - parsers.parse( - 'the gitweb site should show {branch:w} as a default repo branch')) -def gitweb_site_check_default_repo_branch(session_browser, branch): - assert _get_gitweb_site_default_repo_branch(session_browser, - 'Test-repo') == branch - - -@then('the repository should be restored') -@then('the repository should be listed as a public') -def gitweb_repo_should_exists(session_browser): - assert _repo_exists(session_browser, 'Test-repo', access='public') - - -@then('the repository should be listed as a private') -def gitweb_private_repo_should_exists(session_browser): - assert _repo_exists(session_browser, 'Test-repo', 'private') - - -@then('the repository should not be listed') -def gitweb_repo_should_not_exist(session_browser): - assert not _repo_exists(session_browser, 'Test-repo') - - -@then('the public repository should be listed on gitweb') -@then('the repository should be listed on gitweb') -def gitweb_repo_should_exist_on_gitweb(session_browser): + functional.logout(session_browser) assert _site_repo_exists(session_browser, 'Test-repo') + assert not _site_repo_exists(session_browser, 'Test-repo-private') -@then('the private repository should not be listed on gitweb') -def gitweb_private_repo_should_exists_on_gitweb(session_browser): - assert not _site_repo_exists(session_browser, 'Test-repo2') +def test_edit_repo_metadata(session_browser): + """Test edit repo metadata.""" + _create_repo(session_browser, 'Test-repo2', 'public', ok_if_exists=True) + _delete_repo(session_browser, 'Test-repo', ignore_missing=True) + repo_metadata = { + 'name': 'Test-repo', + 'description': 'Test Description', + 'owner': 'Test Owner', + 'access': 'private', + } + _edit_repo_metadata(session_browser, 'Test-repo2', repo_metadata) + assert _get_repo_metadata(session_browser, "Test-repo") == repo_metadata + + _create_branch('Test-repo', 'branch1') + _set_default_branch(session_browser, 'Test-repo', 'branch1') + assert _get_gitweb_site_default_repo_branch(session_browser, + 'Test-repo') == 'branch1' -@then('the metadata of the repository should be as set') -def gitweb_repo_metadata_should_match(session_browser, gitweb_repo_metadata): - actual_metadata = _get_repo_metadata(session_browser, 'Test-repo') - assert all(item in actual_metadata.items() - for item in gitweb_repo_metadata.items()) +def test_enable_disable(session_browser): + """Test enabling and disabling the app.""" + functional.app_disable(session_browser, 'gitweb') + assert not functional.is_available(session_browser, 'gitweb') - -@then('the repository should be publicly readable') -def gitweb_repo_publicly_readable(): - assert _repo_is_readable('Test-repo') - assert _repo_is_readable('Test-repo', url_git_extension=True) - - -@then('the repository should not be publicly readable') -def gitweb_repo_not_publicly_readable(): - assert not _repo_is_readable('Test-repo') - assert not _repo_is_readable('Test-repo', url_git_extension=True) - - -@then('the repository should not be publicly writable') -def gitweb_repo_not_publicly_writable(): - assert not _repo_is_writable('Test-repo') - assert not _repo_is_writable('Test-repo', url_git_extension=True) - - -@then('the repository should be privately readable') -def gitweb_repo_privately_readable(): - assert _repo_is_readable('Test-repo', with_auth=True) - assert _repo_is_readable('Test-repo', with_auth=True, - url_git_extension=True) - - -@then('the repository should be privately writable') -def gitweb_repo_privately_writable(): - assert _repo_is_writable('Test-repo', with_auth=True) - assert _repo_is_writable('Test-repo', with_auth=True, - url_git_extension=True) - - -def _create_branch(repo, branch): - """Create a branch on the remote repository.""" - repo_url = _get_repo_url(repo, with_auth=True) - - with _gitweb_temp_directory() as temp_directory: - repo_path = os.path.join(temp_directory, repo) - - _create_local_repo(repo_path) - - add_branch_commands = [['git', 'checkout', '-q', '-b', branch], - [ - 'git', '-c', 'user.name=Tester', '-c', - 'user.email=tester', 'commit', '-q', - '--allow-empty', '-m', 'test_branch1' - ], - ['git', 'push', '-q', '-f', repo_url, branch]] - for command in add_branch_commands: - subprocess.check_call(command, cwd=repo_path) + functional.app_enable(session_browser, 'gitweb') + assert functional.is_available(session_browser, 'gitweb') def _create_local_repo(path): @@ -200,7 +151,7 @@ def _create_repo(browser, repo, access=None, ok_if_exists=False): """Create repository.""" if not _repo_exists(browser, repo, access): _delete_repo(browser, repo, ignore_missing=True) - browser.find_link_by_href('/plinth/apps/gitweb/create/').first.click() + browser.links.find_by_href('/plinth/apps/gitweb/create/').first.click() browser.find_by_id('id_gitweb-name').fill(repo) if access == 'private': browser.find_by_id('id_gitweb-is_private').check() @@ -211,10 +162,32 @@ def _create_repo(browser, repo, access=None, ok_if_exists=False): assert False, 'Repo already exists.' +def _create_branch(repo, branch): + """Add a branch to the repo.""" + repo_url = _get_repo_url(repo, with_auth=True) + + with _gitweb_temp_directory() as temp_directory: + repo_path = os.path.join(temp_directory, repo) + + _create_local_repo(repo_path) + + add_branch_commands = [['git', 'checkout', '-q', '-b', branch], + [ + 'git', '-c', 'user.name=Tester', '-c', + 'user.email=tester', 'commit', '-q', + '--allow-empty', '-m', 'test_branch1' + ], + ['git', 'push', '-q', '-f', repo_url, branch]] + for command in add_branch_commands: + subprocess.check_call(command, cwd=repo_path) + + def _delete_repo(browser, repo, ignore_missing=False): """Delete repository.""" functional.nav_to_module(browser, 'gitweb') - delete_link = browser.find_link_by_href( + if repo.endswith('.git'): + repo = repo[:-4] + delete_link = browser.links.find_by_href( '/plinth/apps/gitweb/{}/delete/'.format(repo)) if delete_link or not ignore_missing: delete_link.first.click() @@ -224,20 +197,15 @@ def _delete_repo(browser, repo, ignore_missing=False): def _edit_repo_metadata(browser, repo, metadata): """Set repository metadata.""" functional.nav_to_module(browser, 'gitweb') - browser.find_link_by_href( + browser.links.find_by_href( '/plinth/apps/gitweb/{}/edit/'.format(repo)).first.click() - if 'name' in metadata: - browser.find_by_id('id_gitweb-name').fill(metadata['name']) - if 'description' in metadata: - browser.find_by_id('id_gitweb-description').fill( - metadata['description']) - if 'owner' in metadata: - browser.find_by_id('id_gitweb-owner').fill(metadata['owner']) - if 'access' in metadata: - if metadata['access'] == 'private': - browser.find_by_id('id_gitweb-is_private').check() - else: - browser.find_by_id('id_gitweb-is_private').uncheck() + browser.find_by_id('id_gitweb-name').fill(metadata['name']) + browser.find_by_id('id_gitweb-description').fill(metadata['description']) + browser.find_by_id('id_gitweb-owner').fill(metadata['owner']) + if metadata['access'] == 'private': + browser.find_by_id('id_gitweb-is_private').check() + else: + browser.find_by_id('id_gitweb-is_private').uncheck() functional.submit(browser) @@ -251,7 +219,7 @@ def _get_gitweb_site_default_repo_branch(browser, repo): def _get_repo_metadata(browser, repo): """Get repository metadata.""" functional.nav_to_module(browser, 'gitweb') - browser.find_link_by_href( + browser.links.find_by_href( '/plinth/apps/gitweb/{}/edit/'.format(repo)).first.click() metadata = {} for item in ['name', 'description', 'owner']: @@ -278,6 +246,22 @@ def _get_repo_url(repo, with_auth): scheme, functional.config['DEFAULT']['username'], password, url, repo) +def _gitweb_git_command_is_successful(command, cwd): + """Check if a command runs successfully or gives authentication error""" + # Tell OS not to translate command return messages + env = os.environ.copy() + env['LC_ALL'] = 'C' + + process = subprocess.run(command, capture_output=True, cwd=cwd, env=env, + check=False) + if process.returncode != 0: + if 'Authentication failed' in process.stderr.decode(): + return False + print(process.stdout.decode()) + process.check_returncode() # Raise exception + return True + + @contextlib.contextmanager def _gitweb_temp_directory(): """Create temporary directory""" @@ -286,26 +270,12 @@ def _gitweb_temp_directory(): shutil.rmtree(name) -def _gitweb_git_command_is_successful(command, cwd): - """Check if a command runs successfully or gives authentication error""" - # Tell OS not to translate command return messages - env = os.environ.copy() - env['LC_ALL'] = 'C' - - process = subprocess.run(command, capture_output=True, cwd=cwd, env=env) - if process.returncode != 0: - if 'Authentication failed' in process.stderr.decode(): - return False - print(process.stdout.decode()) - # raise exception - process.check_returncode() - return True - - def _repo_exists(browser, repo, access=None): """Check whether the repository exists.""" functional.nav_to_module(browser, 'gitweb') - links_found = browser.find_link_by_href('/gitweb/{}.git'.format(repo)) + if not repo.endswith('.git'): + repo = repo + ".git" + links_found = browser.links.find_by_href('/gitweb/{}'.format(repo)) access_matches = True if links_found and access: parent = links_found.first.find_by_xpath('..').first @@ -317,53 +287,26 @@ def _repo_exists(browser, repo, access=None): return bool(links_found) and access_matches -def _repo_is_readable(repo, with_auth=False, url_git_extension=False): +def _repo_is_readable(repo, with_auth=False): """Check if a git repo is readable with git client.""" url = _get_repo_url(repo, with_auth) - if url_git_extension: - url = url + '.git' git_command = ['git', 'clone', '-c', 'http.sslverify=false', url] with _gitweb_temp_directory() as cwd: return _gitweb_git_command_is_successful(git_command, cwd) -def _repo_is_writable(repo, with_auth=False, url_git_extension=False): +def _repo_is_writable(repo, with_auth=False): """Check if a git repo is writable with git client.""" url = _get_repo_url(repo, with_auth) - if url_git_extension: - url = url + '.git' - with _gitweb_temp_directory() as temp_directory: repo_directory = os.path.join(temp_directory, 'test-project') _create_local_repo(repo_directory) - git_push_command = ['git', 'push', '-qf', url, 'master'] return _gitweb_git_command_is_successful(git_push_command, repo_directory) -def _set_default_branch(browser, repo, branch): - """Set default branch of the repository.""" - functional.nav_to_module(browser, 'gitweb') - browser.find_link_by_href( - '/plinth/apps/gitweb/{}/edit/'.format(repo)).first.click() - browser.find_by_id('id_gitweb-default_branch').select(branch) - functional.submit(browser) - - -def _set_repo_access(browser, repo, access): - """Set repository as public or private.""" - functional.nav_to_module(browser, 'gitweb') - browser.find_link_by_href( - '/plinth/apps/gitweb/{}/edit/'.format(repo)).first.click() - if access == 'private': - browser.find_by_id('id_gitweb-is_private').check() - else: - browser.find_by_id('id_gitweb-is_private').uncheck() - functional.submit(browser) - - def _set_all_repos_private(browser): """Set all repositories private""" functional.nav_to_module(browser, 'gitweb') @@ -376,7 +319,30 @@ def _set_all_repos_private(browser): _set_repo_access(browser, repo, 'private') +def _set_default_branch(browser, repo, branch): + """Set default branch of the repository.""" + functional.nav_to_module(browser, 'gitweb') + browser.links.find_by_href( + '/plinth/apps/gitweb/{}/edit/'.format(repo)).first.click() + browser.find_by_id('id_gitweb-default_branch').select(branch) + functional.submit(browser) + + +def _set_repo_access(browser, repo, access): + """Set repository as public or private.""" + functional.nav_to_module(browser, 'gitweb') + browser.links.find_by_href( + '/plinth/apps/gitweb/{}/edit/'.format(repo)).first.click() + if access == 'private': + browser.find_by_id('id_gitweb-is_private').check() + else: + browser.find_by_id('id_gitweb-is_private').uncheck() + functional.submit(browser) + + def _site_repo_exists(browser, repo): """Check whether the repository exists on Gitweb site.""" browser.visit('{}/gitweb'.format(_default_url)) - return browser.find_by_css('a[href="/gitweb/{0}.git"]'.format(repo)) + if not repo.endswith('.git'): + repo = repo + ".git" + return bool(browser.find_by_css('a[href="/gitweb/{0}"]'.format(repo))) From c5c1f5594d356b6e2b209f9628576d68c0b4ff17 Mon Sep 17 00:00:00 2001 From: Veiko Aasa Date: Fri, 17 Sep 2021 21:42:13 +0300 Subject: [PATCH 50/57] gitweb: tests: functional: Fix test failure if initial default branch is not master Closes #2101. Signed-off-by: Veiko Aasa Reviewed-by: Sunil Mohan Adapa --- plinth/modules/gitweb/tests/test_functional.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/plinth/modules/gitweb/tests/test_functional.py b/plinth/modules/gitweb/tests/test_functional.py index 207f4aa4c..16fdfe43d 100644 --- a/plinth/modules/gitweb/tests/test_functional.py +++ b/plinth/modules/gitweb/tests/test_functional.py @@ -301,8 +301,9 @@ def _repo_is_writable(repo, with_auth=False): with _gitweb_temp_directory() as temp_directory: repo_directory = os.path.join(temp_directory, 'test-project') _create_local_repo(repo_directory) - git_push_command = ['git', 'push', '-qf', url, 'master'] - + git_push_command = [ + 'git', '-c', 'push.default=current', 'push', '-qf', url + ] return _gitweb_git_command_is_successful(git_push_command, repo_directory) From 28c1d5eda3822c2edad8f0559671042029faa17d Mon Sep 17 00:00:00 2001 From: Sunil Mohan Adapa Date: Fri, 17 Sep 2021 11:17:30 -0700 Subject: [PATCH 51/57] web_framework, tests: Workaround captcha 0.5.6 vs. Django 3.2 Signed-off-by: Sunil Mohan Adapa Reviewed-by: James Valleroy --- plinth/tests/data/django_test_settings.py | 6 ++++++ plinth/web_framework.py | 4 ++++ 2 files changed, 10 insertions(+) diff --git a/plinth/tests/data/django_test_settings.py b/plinth/tests/data/django_test_settings.py index 1d31afca4..1cc2f15a3 100644 --- a/plinth/tests/data/django_test_settings.py +++ b/plinth/tests/data/django_test_settings.py @@ -5,6 +5,12 @@ Django settings for test modules. import os +# Workaround for django-simple-captcha 0.5.6 not being compatible with +# Django 3.2. 0.5.14 is almost there in Debian. Workaround only until then. +import django.utils.encoding + +django.utils.encoding.python_2_unicode_compatible = lambda x: x + TEST_DATA_DIR = os.path.dirname(os.path.abspath(__file__)) AXES_ENABLED = False diff --git a/plinth/web_framework.py b/plinth/web_framework.py index 22689c963..678012688 100644 --- a/plinth/web_framework.py +++ b/plinth/web_framework.py @@ -22,6 +22,10 @@ logger = logging.getLogger(__name__) def init(read_only=False): """Setup Django configuration in the absence of .settings file""" + # Workaround for django-simple-captcha 0.5.6 not being compatible with + # Django 3.2. 0.5.14 is almost there in Debian. Workaround only until then. + django.utils.encoding.python_2_unicode_compatible = lambda x: x + if cfg.secure_proxy_ssl_header: settings.SECURE_PROXY_SSL_HEADER = (cfg.secure_proxy_ssl_header, 'https') From 2815a30f921a5d2c1fb9e7836bea80999d9b246a Mon Sep 17 00:00:00 2001 From: ikmaak Date: Fri, 17 Sep 2021 07:16:34 +0000 Subject: [PATCH 52/57] Translated using Weblate (Dutch) Currently translated at 99.7% (1509 of 1513 strings) --- plinth/locale/nl/LC_MESSAGES/django.po | 33 +++++++++++++------------- 1 file changed, 16 insertions(+), 17 deletions(-) diff --git a/plinth/locale/nl/LC_MESSAGES/django.po b/plinth/locale/nl/LC_MESSAGES/django.po index 1db3aa311..080929910 100644 --- a/plinth/locale/nl/LC_MESSAGES/django.po +++ b/plinth/locale/nl/LC_MESSAGES/django.po @@ -8,7 +8,7 @@ msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2021-08-30 19:29-0400\n" -"PO-Revision-Date: 2021-09-13 10:44+0000\n" +"PO-Revision-Date: 2021-09-18 13:33+0000\n" "Last-Translator: ikmaak \n" "Language-Team: Dutch \n" @@ -2123,11 +2123,11 @@ msgstr "Postfix-Dovecot SASL-integratie" #: plinth/modules/email_server/audit/ldap.py:67 msgid "Postfix alias maps" -msgstr "" +msgstr "Postfix aliasverwijzingen" #: plinth/modules/email_server/audit/ldap.py:68 msgid "Postfix local recipient maps" -msgstr "" +msgstr "Lokale Postfix ontvanger verwijzingen" #: plinth/modules/email_server/audit/rcube.py:31 msgid "RoundCube availability" @@ -2219,7 +2219,7 @@ msgstr "Update" #: plinth/modules/email_server/templates/email_form_base.html:12 msgid "There was a problem with your request. Please try again." -msgstr "" +msgstr "Er was een probleem met dit verzoek. Probeer het opnieuw." #: plinth/modules/email_server/templates/email_security.html:13 msgid "Postfix TLS" @@ -2231,33 +2231,27 @@ msgstr "Dovecot TLS" #: plinth/modules/email_server/templates/email_server.html:14 msgid "Visit Rspamd administration interface" -msgstr "" +msgstr "Ga naar de Rspamd administration interface" #: plinth/modules/email_server/templates/email_server.html:18 -#, fuzzy -#| msgid "Service Type" msgid "Service Alert" -msgstr "Dienst Type" +msgstr "Servicewaarschuwing" #: plinth/modules/email_server/templates/email_server.html:38 msgid "Repair" msgstr "Herstellen" #: plinth/modules/email_server/templates/my_mail.html:15 -#, fuzzy -#| msgid "Path is not a directory." msgid "You do not have a home directory." -msgstr "Pad is geen map." +msgstr "Je hebt geen homedirectory." #: plinth/modules/email_server/templates/my_mail.html:17 msgid "Create one to begin receiving emails." -msgstr "" +msgstr "Aanmaken om e-mails te kunnen ontvangen." #: plinth/modules/email_server/templates/my_mail.html:22 -#, fuzzy -#| msgid "Updated media directory" msgid "Create home directory" -msgstr "Mediamap bijgewerkt" +msgstr "Home directory aanmaken" #: plinth/modules/email_server/templates/tls_form.html:10 msgid "Keep current settings" @@ -2294,7 +2288,7 @@ msgstr "Startpagina" #: plinth/modules/email_server/views.py:25 msgid "My Mail" -msgstr "" +msgstr "Mijn e-mail" #: plinth/modules/email_server/views.py:26 #: plinth/modules/email_server/views.py:33 @@ -2319,7 +2313,7 @@ msgstr "Interne fout in {0}" #: plinth/modules/email_server/views.py:107 msgid "Check syslog for more information" -msgstr "" +msgstr "Controleer syslog voor meer informatie" #: plinth/modules/email_server/views.py:181 msgid "Enabled aliases" @@ -8243,6 +8237,11 @@ msgid "" "Administration interface and produced web pages are suitable for mobile " "devices." msgstr "" +"WordPress is een veelgebruikte methode om websites te maken en beheren. De " +"inhoud kan worden beheerd met een grafische interface. De indeling en " +"functionaliteit kan worden aangepast, en het uiterlijk kan worden veranderd " +"met thema's. De website en het beheer ervan is bruikbaar met een mobiel " +"apparaat." #: plinth/modules/wordpress/__init__.py:42 #, python-brace-format From 584d8a7215902ef99a3a9dec091d0fde9bb3e1f0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=B1=A0=E8=BE=B9=E6=A0=91=E4=B8=8B?= Date: Thu, 16 Sep 2021 01:14:53 +0000 Subject: [PATCH 53/57] Translated using Weblate (Chinese (Simplified)) Currently translated at 48.2% (730 of 1513 strings) --- plinth/locale/zh_Hans/LC_MESSAGES/django.po | 106 ++++++++++---------- 1 file changed, 53 insertions(+), 53 deletions(-) diff --git a/plinth/locale/zh_Hans/LC_MESSAGES/django.po b/plinth/locale/zh_Hans/LC_MESSAGES/django.po index 1df171d2e..7f5e47cd4 100644 --- a/plinth/locale/zh_Hans/LC_MESSAGES/django.po +++ b/plinth/locale/zh_Hans/LC_MESSAGES/django.po @@ -8,7 +8,7 @@ msgstr "" "Project-Id-Version: Plinth\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2021-08-30 19:29-0400\n" -"PO-Revision-Date: 2021-06-12 02:32+0000\n" +"PO-Revision-Date: 2021-09-18 13:33+0000\n" "Last-Translator: 池边树下 \n" "Language-Team: Chinese (Simplified) \n" @@ -17,7 +17,7 @@ msgstr "" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=1; plural=0;\n" -"X-Generator: Weblate 4.7-dev\n" +"X-Generator: Weblate 4.9-dev\n" #: doc/dev/_templates/layout.html:11 msgid "Page source" @@ -544,15 +544,7 @@ msgid "Restore data from" msgstr "从 恢复数据" #: plinth/modules/backups/templates/backups_upload.html:17 -#, fuzzy, python-format -#| msgid "" -#| "\n" -#| " Upload a backup file downloaded from another %(box_name)s to " -#| "restore is\n" -#| " contents. You can choose the apps you wish to restore after " -#| "uploading a\n" -#| " backup file.\n" -#| " " +#, python-format msgid "" "\n" " Upload a backup file downloaded from another %(box_name)s to restore " @@ -563,8 +555,8 @@ msgid "" " " msgstr "" "\n" -" 上传一个从其它 %(box_name)s 下载的备份文件来恢复内容。\n" -" 备份上传完成后,你可以选择你想要恢复的应用。\n" +" 上传从另一个%(box_name)s下载的备份文件,以恢复其内容。\n" +"\t您可以选择您希望在上传备份文件后恢复的应用程序。\n" " " #: plinth/modules/backups/templates/backups_upload.html:27 @@ -716,38 +708,31 @@ msgid "Mounting failed" msgstr "安装失败" #: plinth/modules/bepasty/__init__.py:24 -#, fuzzy msgid "" "bepasty is a web application that allows large files to be uploaded and " "shared. Text and code snippets can also be pasted and shared. Text, image, " "audio, video and PDF documents can be previewed in the browser. Shared files " "can be set to expire after a time period." msgstr "" -"bepasty是一个允许上传和共享大文件的网络应用。文本和代码片段也可以被粘贴和共" -"享。文本、图像、音频、视频和PDF文件可以在浏览器中预览。共享的文件可以设置为在" -"一段时间后过期。" +"bepasty是一个允许上传和共享大文件的网络应用。文本和代码片段也可以被粘贴和共享。文本、图像、音频、视频和PDF文件可以在浏览器中预览。共享的文件可以" +"设置为在一段时间后过期。" #: plinth/modules/bepasty/__init__.py:28 -#, fuzzy msgid "" "bepasty does not use usernames for login. It only uses passwords. For each " "password, a set of permissions can be selected. Once you have created a " "password, you can share it with the users who should have the associated " "permissions." msgstr "" -"bepasty不使用用户名进行登录。它只使用密码。对于每个密码,都可以选择一组权限。" -"一旦你创建了一个密码,你就可以与应该拥有相关权限的用户分享它。" +"bepasty不使用用户名进行登录。它只使用密码。对于每个密码,都可以选择一组权限。一旦你创建了一个密码,你就可以与应该拥有相关权限的用户分享它。" #: plinth/modules/bepasty/__init__.py:32 -#, fuzzy msgid "" "You can also create multiple passwords with the same set of privileges, and " "distribute them to different people or groups. This will allow you to later " "revoke access for a single person or group, by removing their password from " "the list." -msgstr "" -"你也可以创建具有相同权限的多个密码,并将它们分配给不同的人或团体。这将使你以" -"后可以通过从列表中删除某个人或团体的密码来撤销其访问权。" +msgstr "你也可以创建具有相同权限的多个密码,并将它们分配给不同的人或团体。这将使你以后可以通过从列表中删除某个人或团体的密码来撤销其访问权。" #: plinth/modules/bepasty/__init__.py:41 plinth/modules/bepasty/__init__.py:50 msgid "Read a file, if a web link to the file is available" @@ -1316,15 +1301,15 @@ msgstr "" #: plinth/modules/coturn/__init__.py:62 msgid "Coturn" -msgstr "" +msgstr "Coturn" #: plinth/modules/coturn/__init__.py:63 msgid "VoIP Helper" -msgstr "" +msgstr "网络电话助手" #: plinth/modules/coturn/forms.py:22 msgid "Invalid list of STUN/TURN Server URIs" -msgstr "" +msgstr "无效的STUN/TURN服务器URI列表" #: plinth/modules/coturn/forms.py:30 plinth/modules/mumble/forms.py:21 #: plinth/modules/quassel/forms.py:22 @@ -1338,11 +1323,11 @@ msgstr "子域" msgid "" "Select a domain to use TLS with. If the list is empty, please configure at " "least one domain with certificates." -msgstr "" +msgstr "选择一个要使用TLS的域。如果列表中是空的,请至少配置一个带有证书的域。" #: plinth/modules/coturn/templates/coturn.html:15 msgid "Use the following URLs to configure your communication server:" -msgstr "" +msgstr "使用以下URL来配置你的通信服务器。" #: plinth/modules/coturn/templates/coturn.html:24 #, fuzzy @@ -1362,7 +1347,7 @@ msgstr "日期与时间" #: plinth/modules/datetime/__init__.py:116 msgid "Time synchronized to NTP server" -msgstr "" +msgstr "时间同步到NTP服务器" #: plinth/modules/datetime/forms.py:18 msgid "Time Zone" @@ -1408,7 +1393,7 @@ msgstr "" #: plinth/modules/deluge/__init__.py:47 #: plinth/modules/transmission/__init__.py:50 msgid "Download files using BitTorrent applications" -msgstr "" +msgstr "使用BitTorrent应用程序下载文件" #: plinth/modules/deluge/__init__.py:51 plinth/modules/deluge/manifest.py:6 msgid "Deluge" @@ -1427,7 +1412,7 @@ msgstr "下载目录" #: plinth/modules/deluge/manifest.py:7 msgid "Bittorrent client written in Python/PyGTK" -msgstr "" +msgstr "用Python/PyGTK编写的Bittorrent客户端" #: plinth/modules/diagnostics/__init__.py:28 msgid "" @@ -1444,7 +1429,7 @@ msgstr "诊断程序" #: plinth/modules/diagnostics/__init__.py:98 #: plinth/modules/email_server/templates/email_server.html:29 msgid "passed" -msgstr "" +msgstr "通过了" #: plinth/modules/diagnostics/__init__.py:99 #: plinth/modules/email_server/templates/email_server.html:27 @@ -1457,31 +1442,31 @@ msgstr "安装失败。" #: plinth/modules/diagnostics/__init__.py:100 #: plinth/modules/email_server/templates/email_server.html:25 msgid "error" -msgstr "" +msgstr "错误" #: plinth/modules/diagnostics/__init__.py:101 msgid "warning" -msgstr "" +msgstr "警告" #. Translators: This is the unit of computer storage Mebibyte similar to #. Megabyte. #: plinth/modules/diagnostics/__init__.py:204 msgid "MiB" -msgstr "" +msgstr "MiB" #. Translators: This is the unit of computer storage Gibibyte similar to #. Gigabyte. #: plinth/modules/diagnostics/__init__.py:209 msgid "GiB" -msgstr "" +msgstr "GiB" #: plinth/modules/diagnostics/__init__.py:216 msgid "You should disable some apps to reduce memory usage." -msgstr "" +msgstr "你应该禁用一些应用程序以减少内存的使用。" #: plinth/modules/diagnostics/__init__.py:221 msgid "You should not install any new apps on this system." -msgstr "" +msgstr "你不应该在这个系统上安装任何新的应用程序。" #: plinth/modules/diagnostics/__init__.py:233 #, no-python-format, python-brace-format @@ -1489,10 +1474,12 @@ msgid "" "System is low on memory: {percent_used}% used, {memory_available} " "{memory_available_unit} free. {advice_message}" msgstr "" +"系统内存不足。{percent_used}%已用,{memory_available} " +"{memory_available_unit}可用。{advice_message}。" #: plinth/modules/diagnostics/__init__.py:235 msgid "Low Memory" -msgstr "" +msgstr "低内存" #: plinth/modules/diagnostics/templates/diagnostics.html:17 #: plinth/modules/diagnostics/templates/diagnostics_button.html:11 @@ -1514,6 +1501,9 @@ msgid "" " App: %(app_name)s\n" " " msgstr "" +"\n" +" 应用程序。%(app_name)s\n" +" " #: plinth/modules/diagnostics/templates/diagnostics_app.html:10 msgid "Diagnostic Results" @@ -1522,7 +1512,7 @@ msgstr "诊断结果" #: plinth/modules/diagnostics/templates/diagnostics_app.html:12 #, python-format msgid "App: %(app_name)s" -msgstr "" +msgstr "应用程序。%(app_name)s" #: plinth/modules/diagnostics/templates/diagnostics_app.html:21 #, fuzzy @@ -1546,16 +1536,16 @@ msgstr "诊断测试" msgid "" "diaspora* is a decentralized social network where you can store and control " "your own data." -msgstr "" +msgstr "diaspora*是一个去中心化的社交网络,你可以存储和控制自己的数据。" #: plinth/modules/diaspora/__init__.py:68 #: plinth/modules/diaspora/manifest.py:23 msgid "diaspora*" -msgstr "" +msgstr "diaspora*" #: plinth/modules/diaspora/__init__.py:69 msgid "Federated Social Network" -msgstr "" +msgstr "联合社交网络" #: plinth/modules/diaspora/forms.py:13 msgid "Enable new user registrations" @@ -1563,13 +1553,13 @@ msgstr "实现新用户注册" #: plinth/modules/diaspora/manifest.py:11 msgid "dandelion*" -msgstr "" +msgstr "dandelion*" #: plinth/modules/diaspora/manifest.py:13 msgid "" "It is an unofficial webview based client for the community-run, distributed " "social network diaspora*" -msgstr "" +msgstr "它是一个非官方的基于webview的客户端,用于社区运营的分布式社交网络diaspora*。" #: plinth/modules/diaspora/templates/diaspora-post-setup.html:16 #, python-format @@ -1580,6 +1570,10 @@ msgid "" "podname wouldn't be accessible.
      You can access the diaspora* pod at diaspora.%(domain_name)s " msgstr "" +"diaspora*的pod域名被设置为%(domain_name)s。用户ID将看起来像username@diaspora.%(dom" +"ain_name)s
      如果FreedomBox域名被改变,所有用以前podname注册的用户的数据将无法访问。
      你可以在 diaspora.%(domain_name)s " +"访问diaspora*荚。
      " #: plinth/modules/diaspora/templates/diaspora-pre-setup.html:36 #: plinth/modules/matrixsynapse/templates/matrix-synapse-pre-setup.html:15 @@ -1709,7 +1703,7 @@ msgstr "账户创建时使用的用户名" #: plinth/modules/dynamicdns/forms.py:65 msgid "GnuDIP" -msgstr "" +msgstr "GnuDIP" #: plinth/modules/dynamicdns/forms.py:68 #, fuzzy @@ -1760,7 +1754,7 @@ msgstr "查寻公开 IP 的 URL" #: plinth/modules/dynamicdns/forms.py:119 msgid "Use IPv6 instead of IPv4" -msgstr "" +msgstr "使用IPv6而不是IPv4" #: plinth/modules/dynamicdns/forms.py:142 msgid "Please provide an update URL or a GnuDIP server address" @@ -1895,10 +1889,12 @@ msgid "" "ejabberd needs a STUN/TURN server for audio/video calls. Install the Coturn app or configure an external server." msgstr "" +"ejabberd需要一个STUN/TURN服务器用于音频/视频呼叫。安装Coturn应用程序或配置一个外部服务器。" #: plinth/modules/ejabberd/__init__.py:70 msgid "ejabberd" -msgstr "" +msgstr "ejabberd" #: plinth/modules/ejabberd/__init__.py:71 #: plinth/modules/matrixsynapse/__init__.py:77 @@ -1909,7 +1905,7 @@ msgstr "Web 服务器" #: plinth/modules/ejabberd/forms.py:18 msgid "Enable Message Archive Management" -msgstr "" +msgstr "启用消息存档管理" #: plinth/modules/ejabberd/forms.py:20 #, python-brace-format @@ -1919,10 +1915,12 @@ msgid "" "history of a multi-user chat room. It depends on the client settings whether " "the histories are stored as plain text or encrypted." msgstr "" +"如果启用,您的{box_name}将存储聊天信息历史。这允许在多个客户端之间同步对话,并读取多用户聊天室的历史。聊天记录是以纯文本还是加密形式存储,这取决" +"于客户端的设置。" #: plinth/modules/ejabberd/forms.py:27 plinth/modules/matrixsynapse/forms.py:22 msgid "Automatically manage audio/video call setup" -msgstr "" +msgstr "自动管理音频/视频通话设置" #: plinth/modules/ejabberd/forms.py:29 #, python-brace-format @@ -1931,14 +1929,16 @@ msgid "" "server for ejabberd. Disable this if you want to use a different STUN/TURN " "server." msgstr "" +"将本地coturn应用程序配置为ejabberd的STUN/" +"TURN服务器。如果你想使用一个不同的STUN/TURN服务器,请禁用此功能。" #: plinth/modules/ejabberd/forms.py:36 plinth/modules/matrixsynapse/forms.py:31 msgid "STUN/TURN Server URIs" -msgstr "" +msgstr "STUN/TURN服务器URI" #: plinth/modules/ejabberd/forms.py:38 plinth/modules/matrixsynapse/forms.py:33 msgid "List of public URIs of the STUN/TURN server, one on each line." -msgstr "" +msgstr "STUN/TURN服务器的公共URI列表,每行一个。" #: plinth/modules/ejabberd/forms.py:42 plinth/modules/matrixsynapse/forms.py:37 #, fuzzy From ed48bc14671057f720baaccf9e28696e9e7f557e Mon Sep 17 00:00:00 2001 From: Andrij Mizyk Date: Fri, 17 Sep 2021 19:46:12 +0000 Subject: [PATCH 54/57] Translated using Weblate (Ukrainian) Currently translated at 75.4% (1142 of 1513 strings) --- plinth/locale/uk/LC_MESSAGES/django.po | 79 ++++++++++++++++++-------- 1 file changed, 56 insertions(+), 23 deletions(-) diff --git a/plinth/locale/uk/LC_MESSAGES/django.po b/plinth/locale/uk/LC_MESSAGES/django.po index cfeca91e5..374cfc612 100644 --- a/plinth/locale/uk/LC_MESSAGES/django.po +++ b/plinth/locale/uk/LC_MESSAGES/django.po @@ -8,7 +8,7 @@ msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2021-08-30 19:29-0400\n" -"PO-Revision-Date: 2021-09-17 18:30+0000\n" +"PO-Revision-Date: 2021-09-18 13:33+0000\n" "Last-Translator: Andrij Mizyk \n" "Language-Team: Ukrainian \n" @@ -441,11 +441,11 @@ msgstr "Створити резервну копію" #: plinth/modules/backups/templates/backups.html:24 msgid "Upload and restore a backup archive" -msgstr "Відвантажити й відновити архів резервної копії" +msgstr "Відвантажити і відновити архів резервної копії" #: plinth/modules/backups/templates/backups.html:28 msgid "Upload and Restore" -msgstr "Відвантажити й відновити" +msgstr "Відвантажити і відновити" #: plinth/modules/backups/templates/backups.html:31 msgid "Add a backup location" @@ -665,7 +665,7 @@ msgstr "Архів видалено." #: plinth/modules/backups/views.py:159 msgid "Upload and restore a backup" -msgstr "Вивантажити й відновити резервну копію" +msgstr "Вивантажити і відновити резервну копію" #: plinth/modules/backups/views.py:194 msgid "Restored files from backup." @@ -1071,7 +1071,7 @@ msgid "" "Delete this library permanently? All stored e-books and saved data will be " "lost." msgstr "" -"Видалити цю бібліотеку назавжди? Усі збережені ел. книжки й збережені дані " +"Видалити цю бібліотеку назавжди? Усі збережені ел. книжки і збережені дані " "буде втрачено." #: plinth/modules/calibre/templates/calibre-delete-library.html:27 @@ -1829,6 +1829,8 @@ msgid "" "NAT type was not detected yet. If you do not provide an \"IP Check URL\", we " "will not detect a NAT type." msgstr "" +"Тип NAT поки що не виявлено. Якщо ви не надали \"URL перевірки IP\", ми не " +"виявлятимемо тип NAT." #: plinth/modules/dynamicdns/templates/dynamicdns_status.html:19 msgid "Direct connection to the Internet." @@ -2550,14 +2552,14 @@ msgstr "Співпрацювати" #: plinth/modules/upgrades/templates/upgrades_configure.html:26 #, python-format msgid "You are running %(os_release)s and %(box_name)s version %(version)s." -msgstr "" +msgstr "Зараз Ви працюєте на %(os_release)s і %(box_name)s версії %(version)s." #: plinth/modules/help/templates/help_about.html:23 #, python-format msgid "" "There is a new %(box_name)s version available." -msgstr "" +msgstr "Доступна нова версія %(box_name)s." #: plinth/modules/help/templates/help_about.html:28 #: plinth/modules/upgrades/templates/upgrades_configure.html:37 @@ -3721,12 +3723,16 @@ msgid "" "Configure network devices. Connect to the Internet via Ethernet, Wi-Fi or " "PPPoE. Share that connection with other devices on the network." msgstr "" +"Налаштування мережевих пристроїв. Зʼєднання з Інтернетом через Ethernet, Wi-" +"Fi або PPPoE. Ділитися цим зʼєднанням з іншими пристроями в мережі." #: plinth/modules/networks/__init__.py:43 msgid "" "Devices administered through other methods may not be available for " "configuration here." msgstr "" +"Пристрої, що адмініструються іншими способами, можуть бути недоступними для " +"налаштування тут." #: plinth/modules/networks/__init__.py:61 msgid "Networks" @@ -4038,6 +4044,8 @@ msgid "" "I do not know the type of connection my ISP provides

      You will be suggested the most conservative actions.

      " msgstr "" +"Я не знаю типу зʼєднання, яке надає мій постачальни Інтернет-послуг

      Вам буде запропоновано консервативніші дії.

      " #: plinth/modules/networks/forms.py:411 msgid "Preferred router configuration" @@ -4393,28 +4401,32 @@ msgstr "" #: plinth/modules/networks/templates/internet_connectivity_main.html:23 msgid "My ISP provides a public IP address that does not change over time." msgstr "" +"Мій постачальник Інтернет-послуг надає публічну IP-адресу, яка не змінюється " +"протягом часу." #: plinth/modules/networks/templates/internet_connectivity_main.html:27 msgid "My ISP provides a public IP address that may change over time." msgstr "" +"Мій постачальник Інтернет-послуг надає публічну IP-адресу, яка може " +"змінюватися впродовж часу." #: plinth/modules/networks/templates/internet_connectivity_main.html:31 msgid "My ISP does not provide a public IP address." -msgstr "" +msgstr "Мій постачальник Інтернет-послуг не надає публічної IP-адерси." #: plinth/modules/networks/templates/internet_connectivity_main.html:35 msgid "I do not know the type of connection my ISP provides." -msgstr "" +msgstr "Я не знаю типу зʼєднання, яке надає мій постачальник Інтернет-послуг." #: plinth/modules/networks/templates/internet_connectivity_main.html:41 #: plinth/modules/networks/templates/network_topology_main.html:41 msgid "Update..." -msgstr "" +msgstr "Оновити..." #: plinth/modules/networks/templates/network_topology_content.html:10 #, python-format msgid "How is Your %(box_name)s Connected to the Internet?" -msgstr "Як ваш %(box_name)s підʼєднано до Інтернету?" +msgstr "Як Ваш %(box_name)s підʼєднано до Інтернету?" #: plinth/modules/networks/templates/network_topology_content.html:16 #, python-format @@ -4423,7 +4435,7 @@ msgid "" "your network. This information is used to guide you with further setup. It " "can be changed later." msgstr "" -"Виберіть пункт, що найкраще описує те, як ваш %(box_name)s підʼєднано до " +"Виберіть пункт, що найкраще описує те, як Ваш %(box_name)s підʼєднано до " "мережі. Ця інформація використовується лише для подальших вказівок " "установлення. Її можна змінити пізніше." @@ -4460,6 +4472,8 @@ msgid "" "Your Internet connection is directly attached to your %(box_name)s and there " "are no other devices on the network." msgstr "" +"Ваше Інтернет-зʼєднання напряму підʼєднано до Вашого %(box_name)s і в мережі " +"нема інших пристроїв." #: plinth/modules/networks/templates/networks_configuration.html:24 msgid "" @@ -4890,12 +4904,16 @@ msgid "" "Your ISP does not provide you an external IP address and instead provides " "Internet connection through NAT." msgstr "" +"Ваш постачальник Інтернет-послуг не надає Вам зовнішньої IP-адреси, а надає " +"Інтернет-зʼєднання через NAT." #: plinth/modules/pagekite/__init__.py:40 msgid "" "Your ISP does not provide you a static IP address and your IP address " "changes every time you connect to Internet." msgstr "" +"Ваш постачальник Інтернет-послуг не надає Вам статичної IP-адреси і Ваша IP-" +"адреса змінюється кожного разу, коли ви підʼєднуєтеся до Інтернету." #: plinth/modules/pagekite/__init__.py:42 msgid "Your ISP limits incoming connections." @@ -5556,7 +5574,7 @@ msgstr "" #: plinth/modules/security/forms.py:13 msgid "Restrict console logins (recommended)" -msgstr "" +msgstr "Обмежувати вхід до консолі (рекомендовано)" #: plinth/modules/security/forms.py:14 msgid "" @@ -5564,6 +5582,9 @@ msgid "" "to log in to console or via SSH. Console users may be able to access some " "services without further authorization." msgstr "" +"Коли опція ввімкнена — лише користувачі з групи \"admin\" можуть входити до " +"консолі або через SSH. Користувачі консолі мають можливість доступу до " +"деяких сервісів без додаткової авторизації." #: plinth/modules/security/forms.py:19 msgid "Fail2Ban (recommended)" @@ -5575,6 +5596,8 @@ msgid "" "attempts to the SSH server and other enabled password protected internet-" "services." msgstr "" +"Коли опція ввімкнена — Fail2Ban обмежуватиме спроби вторгнення за допомогою " +"грубого перебору до сервера SSH та інших захищених паролем Інтернет-сервісів." #: plinth/modules/security/templates/security.html:12 #: plinth/modules/security/templates/security.html:14 @@ -5683,7 +5706,7 @@ msgstr "Оновлено конфіґурацію безпеки" #: plinth/modules/shaarli/__init__.py:20 msgid "Shaarli allows you to save and share bookmarks." -msgstr "Shaarli дозволяє зберігати й ділитися закладками." +msgstr "Shaarli дозволяє зберігати і ділитися закладками." #: plinth/modules/shaarli/__init__.py:21 msgid "" @@ -6750,7 +6773,7 @@ msgstr "Дозволити автооновлення" #: plinth/modules/upgrades/forms.py:16 msgid "When enabled, FreedomBox automatically updates once a day." -msgstr "Якщо ввімкнено, FreedomBox автоматично оновлятиметься раз у день." +msgstr "Коли ввімкнено — FreedomBox автоматично оновлятиметься раз на день." #: plinth/modules/upgrades/forms.py:19 msgid "Enable auto-update to next stable release" @@ -6761,6 +6784,8 @@ msgid "" "When enabled, FreedomBox will update to the next stable distribution release " "when it is available." msgstr "" +"Коли ввімкнено — FreedomBox оновлятиметься до наступного стабільного випуску " +"дистрибутиву, якщо він доступний." #: plinth/modules/upgrades/forms.py:34 #: plinth/modules/upgrades/templates/upgrades_configure.html:89 @@ -6885,27 +6910,27 @@ msgstr "" #: plinth/modules/upgrades/views.py:71 msgid "Automatic upgrades enabled" -msgstr "" +msgstr "Дозволено автоматичні оновлення" #: plinth/modules/upgrades/views.py:74 msgid "Automatic upgrades disabled" -msgstr "" +msgstr "Вимкнено автоматичні оновлення" #: plinth/modules/upgrades/views.py:82 msgid "Distribution upgrade enabled" -msgstr "" +msgstr "Дозволено оновлення дистрибутиву" #: plinth/modules/upgrades/views.py:85 msgid "Distribution upgrade disabled" -msgstr "" +msgstr "Вимкнено оновлення дистрибутиву" #: plinth/modules/upgrades/views.py:126 msgid "Upgrade process started." -msgstr "" +msgstr "Процес оновлення розпочато." #: plinth/modules/upgrades/views.py:128 msgid "Starting upgrade failed." -msgstr "" +msgstr "Не вдалося розпочати оновлення." #: plinth/modules/upgrades/views.py:138 msgid "Frequent feature updates activated." @@ -6917,6 +6942,10 @@ msgid "" "authentication mechanism for most apps. Some apps further require a user " "account to be part of a group to authorize the user to access the app." msgstr "" +"Створення і керування обліковими записами користувачів. Ці обліківки " +"обробляються як централізований механізм автентифікації для більшості " +"застосунків. Деякі застосунки також вимагають, щоб обліківка була частиною " +"групи, щоб отримати авторизований доступ до застосунку." #: plinth/modules/users/__init__.py:43 #, python-brace-format @@ -6932,7 +6961,7 @@ msgstr "" #: plinth/modules/users/__init__.py:64 msgid "Users and Groups" -msgstr "Користувачі й групи" +msgstr "Користувачі і групи" #: plinth/modules/users/__init__.py:77 msgid "Access to all services and system settings" @@ -7840,10 +7869,12 @@ msgid "" "%(service_name)s is available only on internal networks or when the " "client is connected to %(box_name)s through VPN." msgstr "" +"%(service_name)s доступне лише у внутрішніх мережах або коли клієнт " +"підключено до %(box_name)s через VPN." #: plinth/templates/internal-zone.html:17 msgid "Currently there are no network interfaces configured as internal." -msgstr "" +msgstr "Зараз тут нема мережевих інтерфейсів, що налаштовані як внутрішні." #: plinth/templates/internal-zone.html:19 #, python-format @@ -7851,6 +7882,8 @@ msgid "" "Currently the following network interfaces are configured as internal: " "%(interface_list)s" msgstr "" +"Наразі наступні мережеві інтерфейси налаштовано як внутрішні: " +"%(interface_list)s" #: plinth/templates/messages.html:11 msgid "Close" From 0eed9ec88824ff19f984ca0b8a098c0b827295e3 Mon Sep 17 00:00:00 2001 From: James Valleroy Date: Sat, 18 Sep 2021 09:35:00 -0400 Subject: [PATCH 55/57] locale: Update translation strings Signed-off-by: James Valleroy --- plinth/locale/ar_SA/LC_MESSAGES/django.po | 8 +++- plinth/locale/bg/LC_MESSAGES/django.po | 8 +++- plinth/locale/bn/LC_MESSAGES/django.po | 8 +++- plinth/locale/cs/LC_MESSAGES/django.po | 10 +++- plinth/locale/da/LC_MESSAGES/django.po | 10 +++- plinth/locale/de/LC_MESSAGES/django.po | 10 +++- plinth/locale/django.pot | 8 +++- plinth/locale/el/LC_MESSAGES/django.po | 10 +++- plinth/locale/es/LC_MESSAGES/django.po | 10 +++- plinth/locale/fa/LC_MESSAGES/django.po | 14 ++++-- plinth/locale/fake/LC_MESSAGES/django.po | 10 +++- plinth/locale/fr/LC_MESSAGES/django.po | 10 +++- plinth/locale/gl/LC_MESSAGES/django.po | 8 +++- plinth/locale/gu/LC_MESSAGES/django.po | 10 +++- plinth/locale/hi/LC_MESSAGES/django.po | 10 +++- plinth/locale/hu/LC_MESSAGES/django.po | 10 +++- plinth/locale/id/LC_MESSAGES/django.po | 10 +++- plinth/locale/it/LC_MESSAGES/django.po | 10 +++- plinth/locale/ja/LC_MESSAGES/django.po | 8 +++- plinth/locale/kn/LC_MESSAGES/django.po | 8 +++- plinth/locale/lt/LC_MESSAGES/django.po | 8 +++- plinth/locale/nb/LC_MESSAGES/django.po | 10 +++- plinth/locale/nl/LC_MESSAGES/django.po | 14 ++++-- plinth/locale/pl/LC_MESSAGES/django.po | 10 +++- plinth/locale/pt/LC_MESSAGES/django.po | 10 +++- plinth/locale/ru/LC_MESSAGES/django.po | 10 +++- plinth/locale/si/LC_MESSAGES/django.po | 8 +++- plinth/locale/sl/LC_MESSAGES/django.po | 8 +++- plinth/locale/sq/LC_MESSAGES/django.po | 10 +++- plinth/locale/sr/LC_MESSAGES/django.po | 8 +++- plinth/locale/sv/LC_MESSAGES/django.po | 10 +++- plinth/locale/ta/LC_MESSAGES/django.po | 8 +++- plinth/locale/te/LC_MESSAGES/django.po | 10 +++- plinth/locale/tr/LC_MESSAGES/django.po | 10 +++- plinth/locale/uk/LC_MESSAGES/django.po | 30 +++++++----- plinth/locale/vi/LC_MESSAGES/django.po | 10 +++- plinth/locale/zh_Hans/LC_MESSAGES/django.po | 51 +++++++++++++-------- plinth/locale/zh_Hant/LC_MESSAGES/django.po | 8 +++- 38 files changed, 316 insertions(+), 107 deletions(-) diff --git a/plinth/locale/ar_SA/LC_MESSAGES/django.po b/plinth/locale/ar_SA/LC_MESSAGES/django.po index 17e4cfba7..8c9b43770 100644 --- a/plinth/locale/ar_SA/LC_MESSAGES/django.po +++ b/plinth/locale/ar_SA/LC_MESSAGES/django.po @@ -7,7 +7,7 @@ msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2021-08-30 19:29-0400\n" +"POT-Creation-Date: 2021-09-18 09:34-0400\n" "PO-Revision-Date: 2020-06-10 15:41+0000\n" "Last-Translator: aiman an \n" "Language-Team: Arabic (Saudi Arabia) \n" "Language-Team: Bulgarian \n" "Language-Team: Bengali \n" "Language-Team: Czech \n" "Language-Team: Danish \n" "Language-Team: German \n" "Language-Team: LANGUAGE \n" @@ -1873,6 +1873,10 @@ msgstr "" msgid "Cannot be a number" msgstr "" +#: plinth/modules/email_server/audit/domain.py:35 +msgid "Postfix domain name config" +msgstr "" + #: plinth/modules/email_server/audit/home.py:23 #: plinth/modules/email_server/audit/home.py:32 msgid "User does not exist" @@ -7601,6 +7605,6 @@ msgstr "" msgid "%(percentage)s%% complete" msgstr "" -#: plinth/web_framework.py:113 +#: plinth/web_framework.py:117 msgid "Gujarati" msgstr "" diff --git a/plinth/locale/el/LC_MESSAGES/django.po b/plinth/locale/el/LC_MESSAGES/django.po index 36a1660a1..fd9214178 100644 --- a/plinth/locale/el/LC_MESSAGES/django.po +++ b/plinth/locale/el/LC_MESSAGES/django.po @@ -7,7 +7,7 @@ msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2021-08-30 19:29-0400\n" +"POT-Creation-Date: 2021-09-18 09:34-0400\n" "PO-Revision-Date: 2021-04-14 04:27+0000\n" "Last-Translator: Michalis \n" "Language-Team: Greek \n" "Language-Team: Spanish \n" "Language-Team: Persian %(service_name)s is available only on internal networks or when the " "client is connected to %(box_name)s through VPN." msgstr "" -"%(service_name)s تنها در شبکه‌های داخلی یا هنگامی که کارخواه از " -"طریق وی‌پی‌ان به %(box_name)s متصل شده است در دسترس می‌باشد." +"%(service_name)s تنها در شبکه‌های داخلی یا هنگامی که کارخواه از طریق " +"وی‌پی‌ان به %(box_name)s متصل شده است در دسترس می‌باشد." #: plinth/templates/internal-zone.html:17 msgid "Currently there are no network interfaces configured as internal." @@ -8359,7 +8365,7 @@ msgstr "" msgid "%(percentage)s%% complete" msgstr "" -#: plinth/web_framework.py:113 +#: plinth/web_framework.py:117 msgid "Gujarati" msgstr "" diff --git a/plinth/locale/fake/LC_MESSAGES/django.po b/plinth/locale/fake/LC_MESSAGES/django.po index 95a6c8cbe..94d0f0d41 100644 --- a/plinth/locale/fake/LC_MESSAGES/django.po +++ b/plinth/locale/fake/LC_MESSAGES/django.po @@ -7,7 +7,7 @@ msgid "" msgstr "" "Project-Id-Version: Plinth 0.6\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2021-08-30 19:29-0400\n" +"POT-Creation-Date: 2021-09-18 09:34-0400\n" "PO-Revision-Date: 2016-01-31 22:24+0530\n" "Last-Translator: Sunil Mohan Adapa \n" "Language-Team: Plinth Developers \n" "Language-Team: French \n" "Language-Team: Galician \n" "Language-Team: Gujarati \n" "Language-Team: Hindi \n" "Language-Team: Hungarian \n" "Language-Team: Indonesian \n" "Language-Team: Italian \n" "Language-Team: Japanese \n" "Language-Team: Kannada \n" "Language-Team: Lithuanian \n" "Language-Team: Norwegian Bokmål \n" "Language-Team: Dutch Debian Security Bug Tracker." +"informatie over de kwetsbaarheden is te vinden op de Debian Security Bug Tracker." #: plinth/modules/security/templates/security_report.html:28 msgid "" @@ -8734,7 +8740,7 @@ msgstr "Installeren van %(package_names)s: %(status)s" msgid "%(percentage)s%% complete" msgstr "%(percentage)s%% voltooid" -#: plinth/web_framework.py:113 +#: plinth/web_framework.py:117 msgid "Gujarati" msgstr "Gujarati" diff --git a/plinth/locale/pl/LC_MESSAGES/django.po b/plinth/locale/pl/LC_MESSAGES/django.po index bbd0c6ab7..5b484d17e 100644 --- a/plinth/locale/pl/LC_MESSAGES/django.po +++ b/plinth/locale/pl/LC_MESSAGES/django.po @@ -7,7 +7,7 @@ msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2021-08-30 19:29-0400\n" +"POT-Creation-Date: 2021-09-18 09:34-0400\n" "PO-Revision-Date: 2021-03-03 16:50+0000\n" "Last-Translator: Karol Werner \n" "Language-Team: Polish \n" "Language-Team: Portuguese \n" "Language-Team: Russian \n" "Language-Team: Sinhala \n" "Language-Team: Slovenian \n" "Language-Team: Albanian \n" "Language-Team: Serbian \n" "Language-Team: Swedish \n" "Language-Team: LANGUAGE \n" @@ -1874,6 +1874,10 @@ msgstr "" msgid "Cannot be a number" msgstr "" +#: plinth/modules/email_server/audit/domain.py:35 +msgid "Postfix domain name config" +msgstr "" + #: plinth/modules/email_server/audit/home.py:23 #: plinth/modules/email_server/audit/home.py:32 msgid "User does not exist" @@ -7602,6 +7606,6 @@ msgstr "" msgid "%(percentage)s%% complete" msgstr "" -#: plinth/web_framework.py:113 +#: plinth/web_framework.py:117 msgid "Gujarati" msgstr "" diff --git a/plinth/locale/te/LC_MESSAGES/django.po b/plinth/locale/te/LC_MESSAGES/django.po index 82d5bc1ae..b2e739c80 100644 --- a/plinth/locale/te/LC_MESSAGES/django.po +++ b/plinth/locale/te/LC_MESSAGES/django.po @@ -9,7 +9,7 @@ msgid "" msgstr "" "Project-Id-Version: FreedomBox UI\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2021-08-30 19:29-0400\n" +"POT-Creation-Date: 2021-09-18 09:34-0400\n" "PO-Revision-Date: 2021-05-17 18:31+0000\n" "Last-Translator: chilumula vamshi krishna \n" "Language-Team: Telugu \n" "Language-Team: Turkish \n" "Language-Team: Ukrainian You will be suggested the most conservative actions.

      " msgstr "" -"Я не знаю типу зʼєднання, яке надає мій постачальни Інтернет-послуг

      Вам буде запропоновано консервативніші дії.

      " +"Я не знаю типу зʼєднання, яке надає мій постачальни Інтернет-послуг

      Вам буде запропоновано консервативніші дії.

      " #: plinth/modules/networks/forms.py:411 msgid "Preferred router configuration" @@ -5347,9 +5353,9 @@ msgid "" msgstr "" "Можете використовувати його вказавши імʼя користувача і пароль обліківки " "електронної пошти, до якої Ви хочете отримати доступ за доменною назвою " -"сервера IMAP від Вашого постачальника електронної пошти, наприклад imap" -".example.com. Для IMAP через SSL (рекомендується), поле сервера " -"виглядає як imaps://imap.example.com." +"сервера IMAP від Вашого постачальника електронної пошти, наприклад " +"imap.example.com. Для IMAP через SSL (рекомендується), поле " +"сервера виглядає як imaps://imap.example.com." #: plinth/modules/roundcube/__init__.py:33 msgid "" @@ -5363,8 +5369,8 @@ msgstr "" "Для Gmail, імʼям користувача буде адреса Gmail, паролем – ваш пароль " "обліківки Google і сервером – imaps://imap.gmail.com. Зауважте, " "що потрібно дозволити \"Малозахищені додатки\" в налаштуваннях обліківки " -"Google (https://www.google.com/settings/security/lesssecureapps)." +"Google (https://www.google.com/settings/security/lesssecureapps)." #: plinth/modules/roundcube/__init__.py:56 msgid "Email Client" @@ -6175,9 +6181,9 @@ msgid "" "You can view the storage media currently in use, mount and unmount removable " "media, expand the root partition etc." msgstr "" -"Цей модуль дозволяє Вам керувати медія-сховищем, яке привʼязане до {box_name}" -". Ви можете переглядати медія-сховище під час використання, монтувати і " -"відмонтовувати знімні накопичувачі, розширювати розділ root тощо." +"Цей модуль дозволяє Вам керувати медія-сховищем, яке привʼязане до " +"{box_name}. Ви можете переглядати медія-сховище під час використання, " +"монтувати і відмонтовувати знімні накопичувачі, розширювати розділ root тощо." #: plinth/modules/storage/__init__.py:53 plinth/modules/storage/__init__.py:316 #: plinth/modules/storage/__init__.py:347 @@ -7988,7 +7994,7 @@ msgstr "Установлюється %(package_names)s: %(status)s" msgid "%(percentage)s%% complete" msgstr "%(percentage)s%% завершено" -#: plinth/web_framework.py:113 +#: plinth/web_framework.py:117 msgid "Gujarati" msgstr "Gujarati" diff --git a/plinth/locale/vi/LC_MESSAGES/django.po b/plinth/locale/vi/LC_MESSAGES/django.po index 6604035c7..6001bc06c 100644 --- a/plinth/locale/vi/LC_MESSAGES/django.po +++ b/plinth/locale/vi/LC_MESSAGES/django.po @@ -7,7 +7,7 @@ msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2021-08-30 19:29-0400\n" +"POT-Creation-Date: 2021-09-18 09:34-0400\n" "PO-Revision-Date: 2021-07-28 08:34+0000\n" "Last-Translator: bruh \n" "Language-Team: Vietnamese \n" "Language-Team: Chinese (Simplified) You can access the diaspora* pod at diaspora.%(domain_name)s " msgstr "" -"diaspora*的pod域名被设置为%(domain_name)s。用户ID将看起来像username@diaspora.%(dom" -"ain_name)s
      如果FreedomBox域名被改变,所有用以前podname注册的用户的数据将无法访问。
      你可以在 diaspora.%(domain_name)s " -"访问diaspora*荚。
      " +"diaspora*的pod域名被设置为%(domain_name)s。用户ID将看起来像" +"username@diaspora.%(domain_name)s
      如果FreedomBox域名被改变,所有用" +"以前podname注册的用户的数据将无法访问。
      你可以在 diaspora.%(domain_name)s 访问diaspora*荚。
      " #: plinth/modules/diaspora/templates/diaspora-pre-setup.html:36 #: plinth/modules/matrixsynapse/templates/matrix-synapse-pre-setup.html:15 @@ -1889,8 +1895,8 @@ msgid "" "ejabberd needs a STUN/TURN server for audio/video calls. Install the Coturn app or configure an external server." msgstr "" -"ejabberd需要一个STUN/TURN服务器用于音频/视频呼叫。安装Coturn应用程序或配置一个外部服务器。" +"ejabberd需要一个STUN/TURN服务器用于音频/视频呼叫。安装Coturn应用程序或配置一个外部服务器。" #: plinth/modules/ejabberd/__init__.py:70 msgid "ejabberd" @@ -1915,8 +1921,9 @@ msgid "" "history of a multi-user chat room. It depends on the client settings whether " "the histories are stored as plain text or encrypted." msgstr "" -"如果启用,您的{box_name}将存储聊天信息历史。这允许在多个客户端之间同步对话,并读取多用户聊天室的历史。聊天记录是以纯文本还是加密形式存储,这取决" -"于客户端的设置。" +"如果启用,您的{box_name}将存储聊天信息历史。这允许在多个客户端之间同步对话," +"并读取多用户聊天室的历史。聊天记录是以纯文本还是加密形式存储,这取决于客户端" +"的设置。" #: plinth/modules/ejabberd/forms.py:27 plinth/modules/matrixsynapse/forms.py:22 msgid "Automatically manage audio/video call setup" @@ -1929,8 +1936,8 @@ msgid "" "server for ejabberd. Disable this if you want to use a different STUN/TURN " "server." msgstr "" -"将本地coturn应用程序配置为ejabberd的STUN/" -"TURN服务器。如果你想使用一个不同的STUN/TURN服务器,请禁用此功能。" +"将本地coturn应用程序配置为ejabberd的STUN/TURN服" +"务器。如果你想使用一个不同的STUN/TURN服务器,请禁用此功能。" #: plinth/modules/ejabberd/forms.py:36 plinth/modules/matrixsynapse/forms.py:31 msgid "STUN/TURN Server URIs" @@ -2034,6 +2041,12 @@ msgstr "" msgid "Cannot be a number" msgstr "" +#: plinth/modules/email_server/audit/domain.py:35 +#, fuzzy +#| msgid "Error setting domain name: {exception}" +msgid "Postfix domain name config" +msgstr "设置域名错误:{exception}" + #: plinth/modules/email_server/audit/home.py:23 #: plinth/modules/email_server/audit/home.py:32 msgid "User does not exist" @@ -8700,7 +8713,7 @@ msgstr "正在安装 %(package_names)s:%(status)s" msgid "%(percentage)s%% complete" msgstr "已完成 %(percentage)s%%" -#: plinth/web_framework.py:113 +#: plinth/web_framework.py:117 msgid "Gujarati" msgstr "古吉拉特语" diff --git a/plinth/locale/zh_Hant/LC_MESSAGES/django.po b/plinth/locale/zh_Hant/LC_MESSAGES/django.po index 55f1f3ffc..09711c2d4 100644 --- a/plinth/locale/zh_Hant/LC_MESSAGES/django.po +++ b/plinth/locale/zh_Hant/LC_MESSAGES/django.po @@ -7,7 +7,7 @@ msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2021-08-30 19:29-0400\n" +"POT-Creation-Date: 2021-09-18 09:34-0400\n" "PO-Revision-Date: 2021-04-27 13:32+0000\n" "Last-Translator: James Pan \n" "Language-Team: Chinese (Traditional) Date: Sat, 18 Sep 2021 10:07:17 -0400 Subject: [PATCH 56/57] doc: Fetch latest manual Signed-off-by: James Valleroy --- doc/manual/en/A20-OLinuXino-Lime2.raw.wiki | 5 ++++- doc/manual/en/Backups.raw.wiki | 9 +++++++++ doc/manual/en/GettingHelp.raw.wiki | 8 ++++---- doc/manual/en/ReleaseNotes.raw.wiki | 20 ++++++++++++++++++++ doc/manual/es/A20-OLinuXino-Lime2.raw.wiki | 5 ++++- doc/manual/es/Backups.raw.wiki | 9 +++++++++ doc/manual/es/GettingHelp.raw.wiki | 8 ++++---- doc/manual/es/ReleaseNotes.raw.wiki | 20 ++++++++++++++++++++ 8 files changed, 74 insertions(+), 10 deletions(-) diff --git a/doc/manual/en/A20-OLinuXino-Lime2.raw.wiki b/doc/manual/en/A20-OLinuXino-Lime2.raw.wiki index 94a440f71..6974528a6 100644 --- a/doc/manual/en/A20-OLinuXino-Lime2.raw.wiki +++ b/doc/manual/en/A20-OLinuXino-Lime2.raw.wiki @@ -43,6 +43,9 @@ An alternative to downloading these images is to [[InstallingDebianOn/Allwinner| === Known Issues === + * Revision G2 (written on the board): The current stable Debian11/bullseye image (from 2021-08-27) '''will not start an Ethernet connection!''' This means you can not continue with the installation process at first. This can be fixed by following [[https://wiki.debian.org/InstallingDebianOn/Allwinner#Olimex_A20-OLinuXino-LIME2__rev._F_and_newer_and_Debian11.2F_bullseye_kernel | this description]] after you [[https://wiki.debian.org/FreedomBox/Download#Installation | copied the image to your SD card]]. Or you Download and use the [[https://ftp.freedombox.org/pub/freedombox/pioneer/|Pioneer Edition image]] to fix the issue. It contains a slightly [[https://salsa.debian.org/freedombox-team/u-boot/commit/2cb18893ef|modified u-boot]]. + * Revision G2 hardware has also [[DebianBug:927397|poor performance when transmitting Ethernet data in Gigabit mode]] for older FreedomBox releases. The procedure above or for rev. C fixes this. + * Revision C hardware has [[DebianBug:845128|poor performance when receiving Ethernet data in Gigabit mode]]. To workaround the problem, you can switch to 100 Mbps mode instead of Gigabit mode. Login to your !FreedomBox as root (or plugin the SD card into another computer) and create the file /etc/NetworkManager/dispatcher.d/20-fix-ethernet-problem with the following contents: {{{ #!/bin/bash @@ -65,7 +68,7 @@ case ${ACTION} in ;; esac }}} - * Revision G2 hardware has [[DebianBug:927397|poor performance when transmitting Ethernet data in Gigabit mode]]. Download and use the [[https://ftp.freedombox.org/pub/freedombox/pioneer/|Pioneer Edition image]] to fix the issue. It contains a slightly [[https://salsa.debian.org/freedombox-team/u-boot/commit/2cb18893ef|modified u-boot]]. The above workaround to put the Ethernet into 100 Mbps mode also fixes this issue. + * Revision K hardware is [[https://salsa.debian.org/freedombox-team/freedom-maker/issues/148|not working properly]]. ## END_INCLUDE diff --git a/doc/manual/en/Backups.raw.wiki b/doc/manual/en/Backups.raw.wiki index 56a03dfaa..2d845576e 100644 --- a/doc/manual/en/Backups.raw.wiki +++ b/doc/manual/en/Backups.raw.wiki @@ -16,14 +16,19 @@ || '''App/Feature''' || '''Support in Version''' || '''Notes''' || || Avahi || - || no backup needed || || Backups || - || no backup needed || +|| bepasty || 20.14 || || || Bind || 0.41 || || +|| calibre || 20.15 || || || Cockpit || - || no backup needed || +|| Coturn || 20.8 || || || Datetime || 0.41 || || || Deluge || 0.41 || does not include downloaded/seeding files || || Diagnostics || - || no backup needed || || Dynamic DNS || 0.39 || || || ejabberd || 0.39 || includes all data and configuration || || Firewall || - || no backup needed || +|| Gitweb || 19.19 || || +|| I2P || 19.6 || || || ikiwiki || 0.39 || includes all wikis/blogs and their content || || infinoted || 0.39 || includes all data and keys || || JSXC || - || no backup needed || @@ -31,6 +36,7 @@ || Matrix Synapse || 0.39 || includes media and uploads || || !MediaWiki || 0.39 || includes wiki pages and uploaded files || || Minetest || 0.39 || || +|| MiniDLNA || 19.23 || || || MLDonkey || 19.0 || || || Monkeysphere || 0.42 || || || Mumble || 0.40 || || @@ -43,6 +49,7 @@ || Quassel || 0.40 || includes users and logs || || Radicale || 0.39 || includes calendar and cards data for all users || || Roundcube || - || no backup needed || +|| Samba || 19.22 || does not include the data in the shared folders || || SearX || - || no backup needed || || Secure Shell (SSH) Server || 0.41 || includes host keys || || Security || 0.41 || || @@ -57,6 +64,8 @@ || Transmission || 0.40 || does not include downloaded/seeding files || || Upgrades || 0.42 || || || Users || No || No plans currently to implement backup || +|| Wordpress || 21.8 || || +|| Zoph || 21.3 || || === How to install and use Backups === diff --git a/doc/manual/en/GettingHelp.raw.wiki b/doc/manual/en/GettingHelp.raw.wiki index 557dd98a8..aaa61a943 100644 --- a/doc/manual/en/GettingHelp.raw.wiki +++ b/doc/manual/en/GettingHelp.raw.wiki @@ -18,16 +18,16 @@ The easiest way to get support is by using the [[https://discuss.freedombox.org| To post new content, you will need to register for an account with name and email address (but you can provide pseudonym and non-primary email address). By watching topics and categories or by enabling 'mailing list mode' in your account preferences, you can interact with the forum by just sending and receiving emails similar to a mailing list. -== IRC #freedombox == - -Providing you are familiar with [[http://www.irchelp.org/|Internet Relay Chat]] (IRC) and [[http://www.irchelp.org/irchelp/clients/|IRC client]], you can get an instant online help from the community on '''irc.debian.org''', channel '''#freedombox'''. Potentially it takes some time before some member is answering you, be patient, a reaction will come later. - == Matrix == You can join our Matrix room '''#freedombox:matrix.org'''. The room is federated with the IRC channel and remembers the chat history. If you do not yet have a client installed, you can [[https://riot.im/app/#/room/#freedombox:matrix.org|use your web browser to join]]. For more options, see this [[https://matrix.to/#/#freedombox:matrix.org|matrix client overview page]]. +== IRC #freedombox == + +Providing you are familiar with [[http://www.irchelp.org/|Internet Relay Chat]] (IRC) and [[http://www.irchelp.org/irchelp/clients/|IRC client]], you can get an instant online help from the community on '''irc.debian.org''', channel '''#freedombox'''. Potentially it takes some time before some member is answering you, be patient, a reaction will come later. + == Email == !FreedomBox users and contributors can be reached by email via a discussion list. In order to ask a question and get an answer from the community, please register from the [[https://lists.alioth.debian.org/mailman/listinfo/freedombox-discuss|mailing list page]] providing your email adress and creating a password. You can also read [[http://lists.alioth.debian.org/pipermail/freedombox-discuss/|discussions archives]]. This list gathers about 700 readers. diff --git a/doc/manual/en/ReleaseNotes.raw.wiki b/doc/manual/en/ReleaseNotes.raw.wiki index c5c45d25f..e6ecdbe34 100644 --- a/doc/manual/en/ReleaseNotes.raw.wiki +++ b/doc/manual/en/ReleaseNotes.raw.wiki @@ -10,6 +10,26 @@ For more technical details, see the [[https://salsa.debian.org/freedombox-team/f The following are the release notes for each !FreedomBox version. +== FreedomBox 21.9 (2021-09-18) == + +=== Highlights === + + * mediawiki: Backup and restore uploaded files + * mediawiki: Enable a subset of default extensions + +=== Other Changes === + + * apache: Update security settings + * Drop support for GnuTLS + * Drop support for SSLv3, TLSv1 and TLSv1.1 + * Enable and prioritize HTTP/2 protocol + * Setup Mozilla recommended configuration + * locale: Update translations for Bulgarian, Chinese (Simplified), Dutch, Persian, Russian, Swedish, Turkish, Ukrainian + * mediawiki: Handle upgrade for 1.35 + * mediawiki: Switch to !MediaWiki 2020 logo + * plinth: remove diagnose command + * Add workaround for Django 3.2 with captcha 0.5.6 + == FreedomBox 21.8 (2021-08-30) == === Highlights === diff --git a/doc/manual/es/A20-OLinuXino-Lime2.raw.wiki b/doc/manual/es/A20-OLinuXino-Lime2.raw.wiki index 94a440f71..6974528a6 100644 --- a/doc/manual/es/A20-OLinuXino-Lime2.raw.wiki +++ b/doc/manual/es/A20-OLinuXino-Lime2.raw.wiki @@ -43,6 +43,9 @@ An alternative to downloading these images is to [[InstallingDebianOn/Allwinner| === Known Issues === + * Revision G2 (written on the board): The current stable Debian11/bullseye image (from 2021-08-27) '''will not start an Ethernet connection!''' This means you can not continue with the installation process at first. This can be fixed by following [[https://wiki.debian.org/InstallingDebianOn/Allwinner#Olimex_A20-OLinuXino-LIME2__rev._F_and_newer_and_Debian11.2F_bullseye_kernel | this description]] after you [[https://wiki.debian.org/FreedomBox/Download#Installation | copied the image to your SD card]]. Or you Download and use the [[https://ftp.freedombox.org/pub/freedombox/pioneer/|Pioneer Edition image]] to fix the issue. It contains a slightly [[https://salsa.debian.org/freedombox-team/u-boot/commit/2cb18893ef|modified u-boot]]. + * Revision G2 hardware has also [[DebianBug:927397|poor performance when transmitting Ethernet data in Gigabit mode]] for older FreedomBox releases. The procedure above or for rev. C fixes this. + * Revision C hardware has [[DebianBug:845128|poor performance when receiving Ethernet data in Gigabit mode]]. To workaround the problem, you can switch to 100 Mbps mode instead of Gigabit mode. Login to your !FreedomBox as root (or plugin the SD card into another computer) and create the file /etc/NetworkManager/dispatcher.d/20-fix-ethernet-problem with the following contents: {{{ #!/bin/bash @@ -65,7 +68,7 @@ case ${ACTION} in ;; esac }}} - * Revision G2 hardware has [[DebianBug:927397|poor performance when transmitting Ethernet data in Gigabit mode]]. Download and use the [[https://ftp.freedombox.org/pub/freedombox/pioneer/|Pioneer Edition image]] to fix the issue. It contains a slightly [[https://salsa.debian.org/freedombox-team/u-boot/commit/2cb18893ef|modified u-boot]]. The above workaround to put the Ethernet into 100 Mbps mode also fixes this issue. + * Revision K hardware is [[https://salsa.debian.org/freedombox-team/freedom-maker/issues/148|not working properly]]. ## END_INCLUDE diff --git a/doc/manual/es/Backups.raw.wiki b/doc/manual/es/Backups.raw.wiki index c7e1ce791..ea056ee20 100644 --- a/doc/manual/es/Backups.raw.wiki +++ b/doc/manual/es/Backups.raw.wiki @@ -16,14 +16,19 @@ || '''App/Funcionalidad''' || '''Soporte en Versión''' || '''Notas''' || || Avahi || - || no precisa ''backup'' || || Backups || - || no precisa ''backup'' || +|| bepasty || 20.14 || || || Bind || 0.41 || || +|| calibre || 20.15 || || || Cockpit || - || no precisa ''backup'' || +|| Coturn || 20.8 || || || Datetime || 0.41 || || || Deluge || 0.41 || '''no''' incluye archivos descargados ni semillas || || Diagnostics || - || no precisa ''backup'' || || Dynamic DNS || 0.39 || || || ejabberd || 0.39 || incluye todos los datos y configuración || || Firewall || - || no precisa ''backup'' || +|| Gitweb || 19.19 || || +|| I2P || 19.6 || || || ikiwiki || 0.39 || incluye todos los wikis/blogs y sus contenidos || || infinoted || 0.39 || incluye todos los datos y claves || || JSXC || - || no precisa ''backup'' || @@ -31,6 +36,7 @@ || Matrix Synapse || 0.39 || incluye media y cargas || || !MediaWiki || 0.39 || incluye páginas de wiki y archivos adjuntos || || Minetest || 0.39 || || +|| MiniDLNA || 19.23 || || || MLDonkey || 19.0 || || || Monkeysphere || 0.42 || || || Mumble || 0.40 || || @@ -43,6 +49,7 @@ || Quassel || 0.40 || incluye usuarios y registros de ejeución (''logs'') || || Radicale || 0.39 || incluye calendario y datos de tarjetas de todos los usuarios || || Roundcube || - || no precisa ''backup'' || +|| Samba || 19.22 || ''no''' incluye datos de las carpetas compartidas || || SearX || - || no precisa ''backup'' || || Secure Shell (SSH) Server || 0.41 || incluye las claves del servidor || || Security || 0.41 || || @@ -57,6 +64,8 @@ || Transmission || 0.40 || '''no''' incluye archivos descargados ni semillas || || Upgrades || 0.42 || || || Users || No || sin planes para implementar ''backup'', de momento || +|| Wordpress || 21.8 || || +|| Zoph || 21.3 || || === Cómo instalar y usar Backups === diff --git a/doc/manual/es/GettingHelp.raw.wiki b/doc/manual/es/GettingHelp.raw.wiki index 72facada1..9b384501c 100644 --- a/doc/manual/es/GettingHelp.raw.wiki +++ b/doc/manual/es/GettingHelp.raw.wiki @@ -15,16 +15,16 @@ La forma más fácil de obtener soporte es usando el [[https://discuss.freedombo Para publicar contenido nuevo necesitarás registrarte con un nombre y una dirección de correo electrónico (pero puedes usar un pseudonimo y una dirección secundaria). Habilitando el 'modo lista de correo' ('mailing list mode') en las preferencias de tu cuenta, puedes interactuar con el foro simplemente enviando y recibiendo correos electrónicos como en una lista de correo. -== IRC #freedombox == - -Si te manejas con IRC ([[http://www.irchelp.org/|Internet Relay Chat]]) y [[http://www.irchelp.org/irchelp/clients/|sus clientes]] puedes obtener ayuda en línea instantánea de la comunidad en el canal '''#freedombox''' de '''irc.debian.org'''. Quizá pase un tiempo antes de que algún miembro te responda. Sé paciente. Ya llegará alguna respuesta. - == Matrix == Puedes unirte a la sala Matrix '''#freedombox:matrix.org'''. La sala está federada con el canal IRC y recuerda la historia del chat. Si aún no tienes un cliente instalado puedes [[https://riot.im/app/#/room/#freedombox:matrix.org|usar tu navegador web para unirte]]. Para más opciones, visita la [[https://matrix.to/#/#freedombox:matrix.org|página de introducción al cliente matrix]]. +== IRC #freedombox == + +Si te manejas con IRC ([[http://www.irchelp.org/|Internet Relay Chat]]) y [[http://www.irchelp.org/irchelp/clients/|sus clientes]] puedes obtener ayuda en línea instantánea de la comunidad en el canal '''#freedombox''' de '''irc.debian.org'''. Quizá pase un tiempo antes de que algún miembro te responda. Sé paciente. Ya llegará alguna respuesta. + == Correo Electrónico == Se puede acceder a los usuarios y contribuyentes de !FreedomBox mediante e-mail con la lista de correo. Para formular preguntas y recibir respuestas de la comunidad por favor regístrate en la [[https://lists.alioth.debian.org/mailman/listinfo/freedombox-discuss|página de la lista de correo]] dando tu dirección de correo electrónico y creando una contraseña. También puedes leer los [[http://lists.alioth.debian.org/pipermail/freedombox-discuss/|hilos archivados]]. Esta lista reune a cerca de 700 lectores. diff --git a/doc/manual/es/ReleaseNotes.raw.wiki b/doc/manual/es/ReleaseNotes.raw.wiki index c5c45d25f..e6ecdbe34 100644 --- a/doc/manual/es/ReleaseNotes.raw.wiki +++ b/doc/manual/es/ReleaseNotes.raw.wiki @@ -10,6 +10,26 @@ For more technical details, see the [[https://salsa.debian.org/freedombox-team/f The following are the release notes for each !FreedomBox version. +== FreedomBox 21.9 (2021-09-18) == + +=== Highlights === + + * mediawiki: Backup and restore uploaded files + * mediawiki: Enable a subset of default extensions + +=== Other Changes === + + * apache: Update security settings + * Drop support for GnuTLS + * Drop support for SSLv3, TLSv1 and TLSv1.1 + * Enable and prioritize HTTP/2 protocol + * Setup Mozilla recommended configuration + * locale: Update translations for Bulgarian, Chinese (Simplified), Dutch, Persian, Russian, Swedish, Turkish, Ukrainian + * mediawiki: Handle upgrade for 1.35 + * mediawiki: Switch to !MediaWiki 2020 logo + * plinth: remove diagnose command + * Add workaround for Django 3.2 with captcha 0.5.6 + == FreedomBox 21.8 (2021-08-30) == === Highlights === From 536798016347680ef42b61df80d5ef4eaf329850 Mon Sep 17 00:00:00 2001 From: James Valleroy Date: Sat, 18 Sep 2021 10:07:51 -0400 Subject: [PATCH 57/57] Release v21.9 to unstable Signed-off-by: James Valleroy --- debian/changelog | 91 ++++++++++++++++++++++++++++++++++++++++++++++ plinth/__init__.py | 2 +- 2 files changed, 92 insertions(+), 1 deletion(-) diff --git a/debian/changelog b/debian/changelog index 7654066ee..52e0f7dd2 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,3 +1,94 @@ +freedombox (21.9) unstable; urgency=medium + + [ Fioddor Superconcentrado ] + * container: Don't fail if there's no fbx network + * container: freedombox-develop callable from anywhere + * lintian: Overrides for remove-on-upgrade dpkg conffiles flag + + [ James Valleroy ] + * debian: Add gbp tag config + * container: Update stable image for bullseye + * backups: Add functional test to disable schedule backups + * avahi: Convert functional tests to non-BDD python format + * cockpit: Convert functional tests to non-BDD python format + * i2p: Convert functional tests to non-BDD python format + * infinoted: Convert functional tests to non-BDD python format + * minetest: Convert functional tests to non-BDD python format + * minidlna: Convert functional tests to non-BDD python format + * performance: Convert functional tests to non-BDD python format + * matrixsynapse: Convert functional tests to non-BDD python format + * jsxc: Convert functional tests to non-BDD python format + * backups: Convert functional tests to non-BDD python format + * locale: Update translation strings + * doc: Fetch latest manual + + [ Burak Yavuz ] + * Translated using Weblate (Turkish) + + [ Michael Breidenbach ] + * Translated using Weblate (Swedish) + + [ Andrij Mizyk ] + * Translated using Weblate (Ukrainian) + * Translated using Weblate (Ukrainian) + * Translated using Weblate (Ukrainian) + * Translated using Weblate (Ukrainian) + * Translated using Weblate (Ukrainian) + + [ Tiago Zaniquelli ] + * plinth: remove diagnose command + + [ Joseph Nuthalapati ] + * apache: Drop support for SSLv3, TLSv1 and TLSv1.1 + * mediawiki: Backup and restore uploaded files + * mediawiki: Bump version number for 1.35 upgrade + * mediawiki: Enable a subset of default extensions + * mediawiki: Switch to MediaWiki 2020 logo + + [ ikmaak ] + * Translated using Weblate (Dutch) + * Translated using Weblate (Dutch) + * Translated using Weblate (Dutch) + * Translated using Weblate (Dutch) + * Translated using Weblate (Dutch) + + [ Sunil Mohan Adapa ] + * mediawiki: tests: functional: Fix races after flipping flags + * d/lintian-overrides: Drop override for a removed tag + * d/lintian-overrides: Override message for not supporting sysvinit + * d/lintian-overrides: Add override for manual outside .../doc/ + * d/lintian-overrides: Drop workaround for remove-on-upgrade dpkg flag + * apache: Drop support for GnuTLS + * apache: Enable and prioritize HTTP/2 protocol + * apache: Setup Mozilla recommended configuration + * container: Fix the update command for new web server + * tests: Add some missing markers + * web_framework, tests: Workaround captcha 0.5.6 vs. Django 3.2 + + [ fliu ] + * email: Fix self.critical not callable error + * email: postconf.get_many_unsafe: batch query + * email: configure postfix domain names + + [ Seyed mohammad ali Hosseinifard ] + * Translated using Weblate (Persian) + + [ Veiko Aasa ] + * gitweb: tests: Fix test failures if initial default branch is not master + * gitweb: tests: Convert functional tests to non-BDD python format + * gitweb: tests: functional: Fix test failure if initial default branch is not master + + [ Artem ] + * Translated using Weblate (Russian) + + [ 109247019824 ] + * Translated using Weblate (Bulgarian) + + [ 池边树下 ] + * Translated using Weblate (Chinese (Simplified)) + + -- James Valleroy Sat, 18 Sep 2021 09:47:06 -0400 + freedombox (21.8) unstable; urgency=medium [ Andrij Mizyk ] diff --git a/plinth/__init__.py b/plinth/__init__.py index 6a912bf81..939e03b60 100644 --- a/plinth/__init__.py +++ b/plinth/__init__.py @@ -3,4 +3,4 @@ Package init file. """ -__version__ = '21.8' +__version__ = '21.9'