From 9dc7b65a4ec1cda867f878664d1a0e84604383d7 Mon Sep 17 00:00:00 2001 From: Luc Didry Date: Aug 04 2019 18:04:10 +0000 Subject: Merge branch 'development' into 'master' Development Closes #130, #153 et #150 See merge request fiat-tux/hat-softwares/lufi!31 --- diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 576cb25..a3d9dab 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -1,12 +1,10 @@ -image: hatsoftwares/lufi-test-ci:latest +image: hatsoftwares/lufi-test-ci:buster stages: - publish_changelog - - podcheck + - pouet_it - carton - carton_bdd - tests -before_script: - - rm -f *db variables: POSTGRES_DB: lufi_db POSTGRES_USER: lufi @@ -19,95 +17,106 @@ variables: ### Jobs templates ## # +.retry: &retry + retry: 2 + except: + - tags .carton_bdd_template: &carton_bdd_definition + <<: *retry stage: carton_bdd - retry: 2 - artifacts: + cache: + key: "$CI_COMMIT_REF_NAME" paths: - local/ - expire_in: 1 week - dependencies: - - carton - except: - - tags -.sqlite_template: &sqlite_definition + policy: pull + artifacts: + paths: + - local.tar + expire_in: 3 hours + after_script: + - tar cf local.tar local/ + +.tests_template: &tests_template + <<: *retry stage: tests - retry: 2 + coverage: '/Total.* (\d+\.\d+)$/' + before_script: + - tar xf local.tar && rm local.tar + - which mariadb_config && cd $(dirname $(which mariadb_config)) && ln -s mariadb_config mysql_config + - cd $CI_PROJECT_DIR + - pwd + script: + - MOJO_CONFIG="t/${CI_JOB_NAME}.conf" make test + - MOJO_CONFIG="t/${CI_JOB_NAME}.conf" make cover + +.sqlite_template: &sqlite_definition + <<: *tests_template dependencies: - carton_sqlite services: - name: rroemhild/test-openldap alias: rroemhild-test-openldap - coverage: '/Total.* (\d+\.\d+)$/' - except: - - tags .pg_template: &pg_definition - stage: tests - retry: 2 + <<: *tests_template dependencies: - carton_postgresql services: - - name: postgres:9.6 + - name: postgres:11 alias: postgres - name: rroemhild/test-openldap alias: rroemhild-test-openldap - coverage: '/Total .*(\d+\.\d+)$/' - except: - - tags .mysql_template: &mysql_definition - stage: tests - retry: 2 + <<: *tests_template dependencies: - carton_mysql services: - - name: mariadb:10.1 + - name: mariadb:10.3 alias: mariadb - name: rroemhild/test-openldap alias: rroemhild-test-openldap - coverage: '/Total .*(\d+\.\d+)$/' - except: - - tags -### Publish tag changelog +### Publish tag changelog and create a toot ## # -publish_changelog: - stage: publish_changelog - script: - - export PROJECT_API_URL="https://framagit.org/api/v4/projects/${CI_PROJECT_ID}" - - export DESCRIPTION_URL="${PROJECT_API_URL}/repository/tags/${CI_COMMIT_TAG}/release" - - 'export HEADER="Private-Token: ${GITLAB_API_TOKEN}"' - - sed -n '/^'$CI_COMMIT_TAG'[[:space:]]/,/^[^\t]/p' CHANGELOG | sed -e 's/^[^\t].*//' -e 's/\t//g' | sed '/^[[:space:]]*$/d' > /tmp/text - - if [[ ! -z $GITLAB_API_TOKEN ]]; then curl -s --request POST --data-urlencode "description@/tmp/text" --header "${HEADER}" "${DESCRIPTION_URL}"; fi - only: - - tags +include: + - 'https://framagit.org/fiat-tux/gitlabci-snippets/raw/master/publish_changelog.yml' + - 'https://framagit.org/fiat-tux/gitlabci-snippets/raw/master/pouet-it-from-ci.yml' ### Podcheck ## # podcheck: - stage: podcheck + stage: carton script: - make podcheck except: - tags +### Cpanfile.snapshot +## Used to get a cpanfile.snapshot from a fresh server (not like my dev VM) +# +#cpanfile_snapshot: +# stage: carton +# script: +# - rm cpanfile.snapshot +# - which mariadb_config && cd $(dirname $(which mariadb_config)) && ln -s mariadb_config mysql_config +# - carton install +# - cat cpanfile.snapshot +# except: +# - tags + ### Install common dependencies ## # carton: + <<: *retry stage: carton - artifacts: + cache: + key: "$CI_COMMIT_REF_NAME" paths: - local/ - expire_in: 1 week - dependencies: [] script: - carton install --deployment --without=sqlite --without=postgresql --without=mysql - when: always - retry: 2 - except: - - tags ### Install DB related dependencies ## @@ -122,32 +131,18 @@ carton_postgresql: - carton install --deployment --without=sqlite --without=mysql carton_mysql: <<: *carton_bdd_definition + before_script: + - which mariadb_config && cd $(dirname $(which mariadb_config)) && ln -s mariadb_config mysql_config + - cd $CI_PROJECT_DIR script: - carton install --deployment --without=sqlite --without=postgresql -### SQLite tests +### Tests ## # sqlite: <<: *sqlite_definition - script: - - MOJO_CONFIG=t/sqlite.conf make test - - MOJO_CONFIG=t/sqlite.conf make cover - -### PostgreSQL tests -## -# postgresql: <<: *pg_definition - script: - - MOJO_CONFIG=t/postgresql.conf make test - - MOJO_CONFIG=t/postgresql.conf make cover - -### MySQL tests -## -# mysql: <<: *mysql_definition - script: - - MOJO_CONFIG=t/mysql.conf make test - - MOJO_CONFIG=t/mysql.conf make cover diff --git a/AUTHORS.md b/AUTHORS.md index 81fe243..b93db5f 100644 --- a/AUTHORS.md +++ b/AUTHORS.md @@ -1,27 +1,35 @@ # Lufi's authors -## Main developers +## Main developer 🤪 -- Luc Didry, aka Sky (), core developer, @framasky on [Twitter](https://twitter.com/framasky) and on [Diaspora*](https://framasphere.org/public/framasky) +- Luc Didry, aka Sky (), core developer, [@framasky@framapiaf.org](https://framapiaf.org/@framasky/) on Mastodon ## Contributors +### Translations 🌐 + - Nikos Filopoulos (italian translation) - Framartin (fix french translation) - Sébastien Duthil (fix english translation) -- Armando Lüscher, https://noplanman.ch/ (fix css) +- Armando Lüscher, https://noplanman.ch/ (german translation) - Quentin Pagès (occitan translation) -- Yann Le Brech (htpasswd file support) - Jéssica Da Cunha (portuguese translation) -- Ilker Kulgu (fix IE11 compatibility, Dutch translation) -- Stéphane Baron (bugfix) +- Ilker Kulgu (dutch translation) - Butterfly of Fire (arabic translation) - Frju365 (german translation) - pi2 (german translation) + +### Code ⌨️ + +- Armando Lüscher, https://noplanman.ch/ (fix css) +- Yann Le Brech (htpasswd file support) +- Ilker Kulgu (fix IE11 compatibility) +- Stéphane Baron (bugfix) - alexandre.LG, http://inios.fr/ -- Mildis, (bugfix) +- Mildis (bugfix) +- TECH'advantage, https://www.tech-advantage.com/ (paid for LDAP invitations feature) -## Vulnerabilities / bug hunters +## Vulnerabilities / bug hunters 🐛 Lufi participated to a [Hackpéro](https://hackpero.com/) (sort of a bug bounty hackathon), thanks to [Bounty factory](https://hackpero.com/). diff --git a/CHANGELOG b/CHANGELOG index b65cbd0..e32dc26 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,5 +1,12 @@ Revision history for Lufi +0.04.0 2019-??-?? + - Allow to zip the files before upload + - Allow to see what’s in zip file on download page + - Allow to individually download files from zip file (only if zip created by Lufi) + - Allow to invite people to send you files on Lufi when using LDAP auth (#150). + Feature paid for by TECH’advantage (https://www.tech-advantage.com/) + 0.03.7 2019-08-01 - Fix missing default values for some settings (mildis) diff --git a/Makefile b/Makefile index e594fa1..dfc3dc9 100644 --- a/Makefile +++ b/Makefile @@ -32,6 +32,11 @@ clean: dev: clean $(CARTON) morbo $(LUFI) --listen http://0.0.0.0:3000 --watch lib/ --watch script/ --watch themes/ --watch lufi.conf +ldap: + sudo docker run --privileged -d -p 389:389 rroemhild/test-openldap; exit 0 + +ldapdev: ldap dev + devlog: multitail log/development.log diff --git a/README.md b/README.md index 17fd374..5e18ce3 100644 --- a/README.md +++ b/README.md @@ -40,10 +40,10 @@ Because Lufi is quite similar to Luffy, like in "[Monkey D. Luffy](https://en.wi ## Wiki (work in progress) -The official wiki will contain all you need to know about Lufi (installation, configuration, etc.). Go to or clone it: +The official wiki will contain all you need to know about Lufi (installation, configuration, etc.). Go to or clone it: ``` -git clone https://framagit.org/luc/lufi.wiki.git +git clone https://framagit.org/fiat-tux/hat-softwares/lufi.wiki.git ``` ## Encryption @@ -58,7 +58,9 @@ However please note that some metadata are sent unencrypted: ## Client -There is the web interface, but you can use a CLI client too! Have a look at or install it directly with `sudo npm install -g lufi-cli`. +There is the web interface, but you can use a CLI client too! Have a look at [lufi-cli](https://framagit.org/fiat-tux/hat-softwares/lufi-cli) or install it directly with `sudo npm install -g lufi-cli`. + +There is another client in Python too: . ## Internationalization @@ -72,9 +74,9 @@ See [AUTHORS.md](AUTHORS.md) file. ## Contribute! -Please consider contributing, either by [reporting issues](https://framagit.org/luc/lufi/issues) or by helping the internationalization. And of course, code contributions are welcome! +Please consider contributing, either by [reporting issues](https://framagit.org/fiat-tux/hat-softwares/lufi/issues) or by helping the internationalization. And of course, code contributions are welcome! -The details on how to contribute are on the [wiki](https://framagit.org/luc/lufi/wikis/contribute). +The details on how to contribute are on the [wiki](https://framagit.org/fiat-tux/hat-softwares/lufi/wikis/contribute). This software uses [Fiat Tux Code of conduct](https://framagit.org/fiat-tux/code-of-conduct/blob/master/README.md). diff --git a/cpanfile b/cpanfile index 06f8bfd..bf8f1ee 100644 --- a/cpanfile +++ b/cpanfile @@ -1,5 +1,4 @@ requires 'inc::Module::Install'; -requires 'List::Util', '>= 1.50'; requires 'Mojolicious', '>= 8.05'; requires 'Mojolicious::Plugin::DebugDumperHelper'; requires 'Mojolicious::Plugin::I18N'; @@ -8,7 +7,7 @@ requires 'Mojolicious::Plugin::GzipStatic'; requires 'Mojolicious::Plugin::StaticCache'; requires 'Mojolicious::Plugin::CSPHeader', '>= 0.06'; requires 'Mojolicious::Plugin::FiatTux::Helpers', '== 0.08', url => 'https://framagit.org/fiat-tux/mojolicious/mojolicious-plugin-fiattux-helpers/-/archive/0.08/mojolicious-plugin-fiattux-helpers-0.08.tar.gz'; -requires 'Mojolicious::Plugin::FiatTux::GrantAccess', '== 0.05', url => 'https://framagit.org/fiat-tux/mojolicious/mojolicious-plugin-fiattux-grantaccess/-/archive/0.05/mojolicious-plugin-fiattux-grantaccess-0.05.tar.gz'; +requires 'Mojolicious::Plugin::FiatTux::GrantAccess', '== 0.06', url => 'https://framagit.org/fiat-tux/mojolicious/mojolicious-plugin-fiattux-grantaccess/-/archive/0.06/mojolicious-plugin-fiattux-grantaccess-0.06.tar.gz'; requires 'Mojolicious::Plugin::FiatTux::Themes', '== 0.02', url => 'https://framagit.org/fiat-tux/mojolicious/mojolicious-plugin-fiattux-themes/-/archive/0.02/mojolicious-plugin-fiattux-themes-0.02.tar.gz'; requires 'Filesys::DiskUsage'; requires 'Switch'; @@ -33,10 +32,12 @@ feature 'optional_deps' => sub { feature 'test' => sub { requires 'Devel::Cover'; + requires 'B::Debug'; }; feature 'ldap', 'LDAP authentication support' => sub { requires 'Net::LDAP'; requires 'Mojolicious::Plugin::Authentication'; + requires 'Date::Language'; }; feature 'htpasswd', 'Htpasswd authentication support' => sub { requires 'Apache::Htpasswd'; diff --git a/cpanfile.snapshot b/cpanfile.snapshot index 5be263f..bfe4c0a 100644 --- a/cpanfile.snapshot +++ b/cpanfile.snapshot @@ -30,10 +30,19 @@ DISTRIBUTIONS ExtUtils::MakeMaker 6.42 Test::More 0 perl 5.005 - Canary-Stability-2012 - pathname: M/ML/MLEHMANN/Canary-Stability-2012.tar.gz + B-Debug-1.26 + pathname: R/RU/RURBAN/B-Debug-1.26.tar.gz provides: - Canary::Stability 2012 + B::Debug 1.26 + requirements: + B 0 + ExtUtils::MakeMaker 0 + Test::More 0 + deprecate 0.03 + Canary-Stability-2013 + pathname: M/ML/MLEHMANN/Canary-Stability-2013.tar.gz + provides: + Canary::Stability 2013 requirements: ExtUtils::MakeMaker 0 Capture-Tiny-0.48 @@ -92,10 +101,10 @@ DISTRIBUTIONS ExtUtils::MakeMaker 6.30 Math::BigInt 1.997 Test::More 0.90 - Cpanel-JSON-XS-4.06 - pathname: R/RU/RURBAN/Cpanel-JSON-XS-4.06.tar.gz + Cpanel-JSON-XS-4.12 + pathname: R/RU/RURBAN/Cpanel-JSON-XS-4.12.tar.gz provides: - Cpanel::JSON::XS 4.06 + Cpanel::JSON::XS 4.12 Cpanel::JSON::XS::Type undef requirements: ExtUtils::MakeMaker 0 @@ -110,10 +119,10 @@ DISTRIBUTIONS Test::More 0.94 strict 0 warnings 0 - Crypt-Rijndael-1.13 - pathname: L/LE/LEONT/Crypt-Rijndael-1.13.tar.gz + Crypt-Rijndael-1.14 + pathname: L/LE/LEONT/Crypt-Rijndael-1.14.tar.gz provides: - Crypt::Rijndael 1.13 + Crypt::Rijndael 1.14 requirements: ExtUtils::MakeMaker 0 perl 5.006 @@ -126,24 +135,25 @@ DISTRIBUTIONS ExtUtils::MakeMaker 6.30 Test::Fatal 0 Test::More 0 - DBD-Pg-3.7.4 - pathname: T/TU/TURNSTEP/DBD-Pg-3.7.4.tar.gz + DBD-Pg-3.8.1 + pathname: T/TU/TURNSTEP/DBD-Pg-3.8.1.tar.gz provides: - Bundle::DBD::Pg v3.7.4 - DBD::Pg v3.7.4 + Bundle::DBD::Pg v3.8.1 + DBD::Pg v3.8.1 requirements: DBI 1.614 ExtUtils::MakeMaker 6.11 Test::More 0.88 Time::HiRes 0 version 0 - DBD-SQLite-1.58 - pathname: I/IS/ISHIGAKI/DBD-SQLite-1.58.tar.gz + DBD-SQLite-1.62 + pathname: I/IS/ISHIGAKI/DBD-SQLite-1.62.tar.gz provides: - DBD::SQLite 1.58 + DBD::SQLite 1.62 DBD::SQLite::Constants undef - DBD::SQLite::VirtualTable 1.58 - DBD::SQLite::VirtualTable::Cursor 1.58 + DBD::SQLite::GetInfo undef + DBD::SQLite::VirtualTable 1.62 + DBD::SQLite::VirtualTable::Cursor 1.62 DBD::SQLite::VirtualTable::FileContent undef DBD::SQLite::VirtualTable::FileContent::Cursor undef DBD::SQLite::VirtualTable::PerlData undef @@ -356,58 +366,58 @@ DISTRIBUTIONS File::Spec 0 File::Temp 0.16 perl 5.00405 - Devel-Cover-1.31 - pathname: P/PJ/PJCJ/Devel-Cover-1.31.tar.gz - provides: - Devel::Cover 1.31 - Devel::Cover::Annotation::Git 1.31 - Devel::Cover::Annotation::Random 1.31 - Devel::Cover::Annotation::Svk 1.31 - Devel::Cover::Branch 1.31 - Devel::Cover::Collection 1.31 - Devel::Cover::Collection::Template::Provider 1.31 - Devel::Cover::Condition 1.31 - Devel::Cover::Condition_and_2 1.31 - Devel::Cover::Condition_and_3 1.31 - Devel::Cover::Condition_or_2 1.31 - Devel::Cover::Condition_or_3 1.31 - Devel::Cover::Condition_xor_4 1.31 - Devel::Cover::Criterion 1.31 - Devel::Cover::DB 1.31 - Devel::Cover::DB::Criterion 1.31 - Devel::Cover::DB::Digests 1.31 - Devel::Cover::DB::File 1.31 - Devel::Cover::DB::IO 1.31 - Devel::Cover::DB::IO::Base 1.31 - Devel::Cover::DB::IO::JSON 1.31 - Devel::Cover::DB::IO::Sereal 1.31 - Devel::Cover::DB::IO::Storable 1.31 - Devel::Cover::DB::Run 1.31 - Devel::Cover::DB::Structure 1.31 - Devel::Cover::Html_Common 1.31 - Devel::Cover::Op 1.31 - Devel::Cover::Pod 1.31 - Devel::Cover::Report::Compilation 1.31 - Devel::Cover::Report::Html 1.31 - Devel::Cover::Report::Html_basic 1.31 - Devel::Cover::Report::Html_basic::Template::Provider 1.31 - Devel::Cover::Report::Html_minimal 1.31 - Devel::Cover::Report::Html_subtle 1.31 - Devel::Cover::Report::Html_subtle::Template::Provider 1.31 - Devel::Cover::Report::Json 1.31 - Devel::Cover::Report::Sort 1.31 - Devel::Cover::Report::Text 1.31 - Devel::Cover::Report::Text2 1.31 - Devel::Cover::Report::Vim 1.31 - Devel::Cover::Report::Vim::Template::Provider 1.31 - Devel::Cover::Statement 1.31 - Devel::Cover::Subroutine 1.31 - Devel::Cover::Test 1.31 - Devel::Cover::Time 1.31 - Devel::Cover::Truth_Table 1.31 - Devel::Cover::Truth_Table::Row 1.31 - Devel::Cover::Util 1.31 - Devel::Cover::Web 1.31 + Devel-Cover-1.33 + pathname: P/PJ/PJCJ/Devel-Cover-1.33.tar.gz + provides: + Devel::Cover 1.33 + Devel::Cover::Annotation::Git 1.33 + Devel::Cover::Annotation::Random 1.33 + Devel::Cover::Annotation::Svk 1.33 + Devel::Cover::Branch 1.33 + Devel::Cover::Collection 1.33 + Devel::Cover::Collection::Template::Provider 1.33 + Devel::Cover::Condition 1.33 + Devel::Cover::Condition_and_2 1.33 + Devel::Cover::Condition_and_3 1.33 + Devel::Cover::Condition_or_2 1.33 + Devel::Cover::Condition_or_3 1.33 + Devel::Cover::Condition_xor_4 1.33 + Devel::Cover::Criterion 1.33 + Devel::Cover::DB 1.33 + Devel::Cover::DB::Criterion 1.33 + Devel::Cover::DB::Digests 1.33 + Devel::Cover::DB::File 1.33 + Devel::Cover::DB::IO 1.33 + Devel::Cover::DB::IO::Base 1.33 + Devel::Cover::DB::IO::JSON 1.33 + Devel::Cover::DB::IO::Sereal 1.33 + Devel::Cover::DB::IO::Storable 1.33 + Devel::Cover::DB::Run 1.33 + Devel::Cover::DB::Structure 1.33 + Devel::Cover::Html_Common 1.33 + Devel::Cover::Op 1.33 + Devel::Cover::Pod 1.33 + Devel::Cover::Report::Compilation 1.33 + Devel::Cover::Report::Html 1.33 + Devel::Cover::Report::Html_basic 1.33 + Devel::Cover::Report::Html_basic::Template::Provider 1.33 + Devel::Cover::Report::Html_minimal 1.33 + Devel::Cover::Report::Html_subtle 1.33 + Devel::Cover::Report::Html_subtle::Template::Provider 1.33 + Devel::Cover::Report::Json 1.33 + Devel::Cover::Report::Sort 1.33 + Devel::Cover::Report::Text 1.33 + Devel::Cover::Report::Text2 1.33 + Devel::Cover::Report::Vim 1.33 + Devel::Cover::Report::Vim::Template::Provider 1.33 + Devel::Cover::Statement 1.33 + Devel::Cover::Subroutine 1.33 + Devel::Cover::Test 1.33 + Devel::Cover::Time 1.33 + Devel::Cover::Truth_Table 1.33 + Devel::Cover::Truth_Table::Row 1.33 + Devel::Cover::Util 1.33 + Devel::Cover::Web 1.33 requirements: B::Debug 0 Digest::MD5 0 @@ -434,10 +444,10 @@ DISTRIBUTIONS Digest::SHA 1 ExtUtils::MakeMaker 0 perl 5.004 - EV-4.22 - pathname: M/ML/MLEHMANN/EV-4.22.tar.gz + EV-4.27 + pathname: M/ML/MLEHMANN/EV-4.27.tar.gz provides: - EV 4.22 + EV 4.27 EV::MakeMaker undef requirements: Canary::Stability 0 @@ -547,10 +557,10 @@ DISTRIBUTIONS Filesys::DfPortable 0.85 requirements: ExtUtils::MakeMaker 0 - Filesys-DiskUsage-0.12 - pathname: M/MA/MANWAR/Filesys-DiskUsage-0.12.tar.gz + Filesys-DiskUsage-0.13 + pathname: M/MA/MANWAR/Filesys-DiskUsage-0.13.tar.gz provides: - Filesys::DiskUsage 0.12 + Filesys::DiskUsage 0.13 requirements: ExtUtils::MakeMaker 0 File::Basename 0 @@ -597,21 +607,25 @@ DISTRIBUTIONS perl 5.008001 strict 0 vars 0 - HTTP-Daemon-6.01 - pathname: G/GA/GAAS/HTTP-Daemon-6.01.tar.gz + HTTP-Daemon-6.05 + pathname: E/ET/ETHER/HTTP-Daemon-6.05.tar.gz provides: - HTTP::Daemon 6.01 - HTTP::Daemon::ClientConn 6.01 + HTTP::Daemon 6.05 requirements: + Carp 0 ExtUtils::MakeMaker 0 HTTP::Date 6 HTTP::Request 6 HTTP::Response 6 HTTP::Status 6 - IO::Socket 0 + IO::Socket::IP 0 LWP::MediaTypes 6 + Module::Build::Tiny 0.034 + Socket 0 Sys::Hostname 0 - perl 5.008001 + perl 5.006 + strict 0 + warnings 0 HTTP-Date-6.02 pathname: G/GA/GAAS/HTTP-Date-6.02.tar.gz provides: @@ -695,17 +709,17 @@ DISTRIBUTIONS Encode 2.10 Exporter 5.57 ExtUtils::MakeMaker 6.30 - IO-Socket-SSL-2.060 - pathname: S/SU/SULLR/IO-Socket-SSL-2.060.tar.gz + IO-Socket-SSL-2.066 + pathname: S/SU/SULLR/IO-Socket-SSL-2.066.tar.gz provides: - IO::Socket::SSL 2.060 + IO::Socket::SSL 2.066 IO::Socket::SSL::Intercept 2.056 - IO::Socket::SSL::OCSP_Cache 2.060 - IO::Socket::SSL::OCSP_Resolver 2.060 + IO::Socket::SSL::OCSP_Cache 2.066 + IO::Socket::SSL::OCSP_Resolver 2.066 IO::Socket::SSL::PublicSuffix undef - IO::Socket::SSL::SSL_Context 2.060 - IO::Socket::SSL::SSL_HANDLE 2.060 - IO::Socket::SSL::Session_Cache 2.060 + IO::Socket::SSL::SSL_Context 2.066 + IO::Socket::SSL::SSL_HANDLE 2.066 + IO::Socket::SSL::Session_Cache 2.066 IO::Socket::SSL::Utils 2.014 requirements: ExtUtils::MakeMaker 0 @@ -726,28 +740,33 @@ DISTRIBUTIONS Socket 1.94 Test::More 0.88 constant 1.03 - ISO-639_1-0.02 - pathname: L/LD/LDIDRY/ISO-639_1-0.02.tar.gz + ISO-639_1-0.03 + pathname: L/LD/LDIDRY/ISO-639_1-0.03.tar.gz provides: - ISO::639_1 0.02 + ISO::639_1 0.03 requirements: Module::Build::Tiny 0.035 perl 5.008001 - JSON-2.97001 - pathname: I/IS/ISHIGAKI/JSON-2.97001.tar.gz + JSON-4.02 + pathname: I/IS/ISHIGAKI/JSON-4.02.tar.gz provides: - JSON 2.97001 - JSON::Backend::PP 2.97001 + JSON 4.02 + JSON::Backend::PP 4.02 requirements: ExtUtils::MakeMaker 0 Test::More 0 - LWP-MediaTypes-6.02 - pathname: G/GA/GAAS/LWP-MediaTypes-6.02.tar.gz + LWP-MediaTypes-6.04 + pathname: O/OA/OALDERS/LWP-MediaTypes-6.04.tar.gz provides: - LWP::MediaTypes 6.02 + LWP::MediaTypes 6.04 requirements: + Carp 0 + Exporter 0 ExtUtils::MakeMaker 0 + File::Basename 0 + Scalar::Util 0 perl 5.006002 + strict 0 Locale-Maketext-Lexicon-1.00 pathname: D/DR/DRTECH/Locale-Maketext-Lexicon-1.00.tar.gz provides: @@ -831,31 +850,31 @@ DISTRIBUTIONS requirements: ExtUtils::MakeMaker 0 perl 5.006 - MailTools-2.20 - pathname: M/MA/MARKOV/MailTools-2.20.tar.gz - provides: - Mail::Address 2.20 - Mail::Cap 2.20 - Mail::Field 2.20 - Mail::Field::AddrList 2.20 - Mail::Field::Date 2.20 - Mail::Field::Generic 2.20 - Mail::Filter 2.20 - Mail::Header 2.20 - Mail::Internet 2.20 - Mail::Mailer 2.20 - Mail::Mailer::qmail 2.20 - Mail::Mailer::rfc822 2.20 - Mail::Mailer::sendmail 2.20 - Mail::Mailer::smtp 2.20 - Mail::Mailer::smtp::pipe 2.20 - Mail::Mailer::smtps 2.20 - Mail::Mailer::smtps::pipe 2.20 - Mail::Mailer::testfile 2.20 - Mail::Mailer::testfile::pipe 2.20 - Mail::Send 2.20 - Mail::Util 2.20 - MailTools 2.20 + MailTools-2.21 + pathname: M/MA/MARKOV/MailTools-2.21.tar.gz + provides: + Mail::Address 2.21 + Mail::Cap 2.21 + Mail::Field 2.21 + Mail::Field::AddrList 2.21 + Mail::Field::Date 2.21 + Mail::Field::Generic 2.21 + Mail::Filter 2.21 + Mail::Header 2.21 + Mail::Internet 2.21 + Mail::Mailer 2.21 + Mail::Mailer::qmail 2.21 + Mail::Mailer::rfc822 2.21 + Mail::Mailer::sendmail 2.21 + Mail::Mailer::smtp 2.21 + Mail::Mailer::smtp::pipe 2.21 + Mail::Mailer::smtps 2.21 + Mail::Mailer::smtps::pipe 2.21 + Mail::Mailer::testfile 2.21 + Mail::Mailer::testfile::pipe 2.21 + Mail::Send 2.21 + Mail::Util 2.21 + MailTools 2.21 requirements: Date::Format 0 Date::Parse 0 @@ -864,31 +883,30 @@ DISTRIBUTIONS Net::Domain 1.05 Net::SMTP 1.03 Test::More 0 - Module-Build-0.4224 - pathname: L/LE/LEONT/Module-Build-0.4224.tar.gz - provides: - Module::Build 0.4224 - Module::Build::Base 0.4224 - Module::Build::Compat 0.4224 - Module::Build::Config 0.4224 - Module::Build::Cookbook 0.4224 - Module::Build::Dumper 0.4224 - Module::Build::Notes 0.4224 - Module::Build::PPMMaker 0.4224 - Module::Build::Platform::Default 0.4224 - Module::Build::Platform::MacOS 0.4224 - Module::Build::Platform::Unix 0.4224 - Module::Build::Platform::VMS 0.4224 - Module::Build::Platform::VOS 0.4224 - Module::Build::Platform::Windows 0.4224 - Module::Build::Platform::aix 0.4224 - Module::Build::Platform::cygwin 0.4224 - Module::Build::Platform::darwin 0.4224 - Module::Build::Platform::os2 0.4224 - Module::Build::PodParser 0.4224 + Module-Build-0.4229 + pathname: L/LE/LEONT/Module-Build-0.4229.tar.gz + provides: + Module::Build 0.4229 + Module::Build::Base 0.4229 + Module::Build::Compat 0.4229 + Module::Build::Config 0.4229 + Module::Build::Cookbook 0.4229 + Module::Build::Dumper 0.4229 + Module::Build::Notes 0.4229 + Module::Build::PPMMaker 0.4229 + Module::Build::Platform::Default 0.4229 + Module::Build::Platform::MacOS 0.4229 + Module::Build::Platform::Unix 0.4229 + Module::Build::Platform::VMS 0.4229 + Module::Build::Platform::VOS 0.4229 + Module::Build::Platform::Windows 0.4229 + Module::Build::Platform::aix 0.4229 + Module::Build::Platform::cygwin 0.4229 + Module::Build::Platform::darwin 0.4229 + Module::Build::Platform::os2 0.4229 + Module::Build::PodParser 0.4229 requirements: CPAN::Meta 2.142060 - CPAN::Meta::YAML 0.003 Cwd 0 Data::Dumper 0 ExtUtils::CBuilder 0.27 @@ -902,14 +920,11 @@ DISTRIBUTIONS File::Find 0 File::Path 0 File::Spec 0.82 - File::Temp 0.15 Getopt::Long 0 Module::Metadata 1.000002 - Parse::CPAN::Meta 1.4401 Perl::OSType 1 Pod::Man 2.17 TAP::Harness 3.29 - Test::More 0.49 Text::Abbrev 0 Text::ParseWords 0 perl 5.006001 @@ -1005,10 +1020,10 @@ DISTRIBUTIONS perl 5.006 strict 0 warnings 0 - Module-ScanDeps-1.25 - pathname: R/RS/RSCHUPP/Module-ScanDeps-1.25.tar.gz + Module-ScanDeps-1.27 + pathname: R/RS/RSCHUPP/Module-ScanDeps-1.27.tar.gz provides: - Module::ScanDeps 1.25 + Module::ScanDeps 1.27 requirements: ExtUtils::MakeMaker 0 File::Spec 0 @@ -1018,10 +1033,10 @@ DISTRIBUTIONS Text::ParseWords 0 perl 5.008001 version 0 - Mojo-Pg-4.11 - pathname: S/SR/SRI/Mojo-Pg-4.11.tar.gz + Mojo-Pg-4.15 + pathname: S/SR/SRI/Mojo-Pg-4.15.tar.gz provides: - Mojo::Pg 4.11 + Mojo::Pg 4.15 Mojo::Pg::Database undef Mojo::Pg::Migrations undef Mojo::Pg::PubSub undef @@ -1034,15 +1049,15 @@ DISTRIBUTIONS Mojolicious 8.03 SQL::Abstract 1.86 perl 5.010001 - Mojo-SQLite-3.001 - pathname: D/DB/DBOOK/Mojo-SQLite-3.001.tar.gz + Mojo-SQLite-3.002 + pathname: D/DB/DBOOK/Mojo-SQLite-3.002.tar.gz provides: - Mojo::SQLite 3.001 - Mojo::SQLite::Database 3.001 - Mojo::SQLite::Migrations 3.001 - Mojo::SQLite::PubSub 3.001 - Mojo::SQLite::Results 3.001 - Mojo::SQLite::Transaction 3.001 + Mojo::SQLite 3.002 + Mojo::SQLite::Database 3.002 + Mojo::SQLite::Migrations 3.002 + Mojo::SQLite::PubSub 3.002 + Mojo::SQLite::Results 3.002 + Mojo::SQLite::Transaction 3.002 requirements: Carp 0 DBD::SQLite 1.54 @@ -1057,25 +1072,27 @@ DISTRIBUTIONS URI::db 0.15 URI::file 4.21 perl 5.010001 - Mojo-mysql-1.07 - pathname: J/JH/JHTHORSEN/Mojo-mysql-1.07.tar.gz + Mojo-mysql-1.16 + pathname: T/TE/TEKKI/Mojo-mysql-1.16.tar.gz provides: Blog undef Blog::Controller::Posts undef Blog::Model::Posts undef - Mojo::mysql 1.07 + Mojo::mysql 1.16 Mojo::mysql::Database undef Mojo::mysql::Migrations undef Mojo::mysql::PubSub undef Mojo::mysql::Results undef Mojo::mysql::Transaction undef + SQL::Abstract::mysql undef requirements: DBD::mysql 4.042 + DBI 1.627 ExtUtils::MakeMaker 0 - Mojolicious 7.55 - SQL::Abstract 1.81 - Mojolicious-8.05 - pathname: S/SR/SRI/Mojolicious-8.05.tar.gz + Mojolicious 8.03 + SQL::Abstract 1.86 + Mojolicious-8.22 + pathname: S/SR/SRI/Mojolicious-8.22.tar.gz provides: Mojo undef Mojo::Asset undef @@ -1130,7 +1147,6 @@ DISTRIBUTIONS Mojo::Server::Morbo::Backend undef Mojo::Server::Morbo::Backend::Poll undef Mojo::Server::PSGI undef - Mojo::Server::PSGI::_IO undef Mojo::Server::Prefork undef Mojo::Template undef Mojo::Transaction undef @@ -1145,7 +1161,7 @@ DISTRIBUTIONS Mojo::UserAgent::Transactor undef Mojo::Util undef Mojo::WebSocket undef - Mojolicious 8.05 + Mojolicious 8.22 Mojolicious::Command undef Mojolicious::Command::Author::cpanify undef Mojolicious::Command::Author::generate undef @@ -1173,7 +1189,6 @@ DISTRIBUTIONS Mojolicious::Plugin::HeaderCondition undef Mojolicious::Plugin::JSONConfig undef Mojolicious::Plugin::Mount undef - Mojolicious::Plugin::PODRenderer undef Mojolicious::Plugin::TagHelpers undef Mojolicious::Plugins undef Mojolicious::Renderer undef @@ -1300,11 +1315,11 @@ DISTRIBUTIONS ExtUtils::MakeMaker 0 Test 0 perl 5.006 - Net-DNS-1.18 - pathname: N/NL/NLNETLABS/Net-DNS-1.18.tar.gz + Net-DNS-1.20 + pathname: N/NL/NLNETLABS/Net-DNS-1.20.tar.gz provides: - Net::DNS 1.18 - Net::DNS::Domain 1698 + Net::DNS 1.20 + Net::DNS::Domain 1726 Net::DNS::DomainName 1605 Net::DNS::DomainName1035 1605 Net::DNS::DomainName2535 1605 @@ -1314,9 +1329,9 @@ DISTRIBUTIONS Net::DNS::Mailbox2535 1605 Net::DNS::Nameserver 1692 Net::DNS::Packet 1714 - Net::DNS::Parameters 1714 - Net::DNS::Question 1714 - Net::DNS::RR 1714 + Net::DNS::Parameters 1729 + Net::DNS::Question 1726 + Net::DNS::RR 1726 Net::DNS::RR::A 1597 Net::DNS::RR::AAAA 1597 Net::DNS::RR::AFSDB 1597 @@ -1325,20 +1340,20 @@ DISTRIBUTIONS Net::DNS::RR::CAA 1597 Net::DNS::RR::CDNSKEY 1586 Net::DNS::RR::CDS 1586 - Net::DNS::RR::CERT 1597 + Net::DNS::RR::CERT 1729 Net::DNS::RR::CNAME 1597 Net::DNS::RR::CSYNC 1597 Net::DNS::RR::DHCID 1597 Net::DNS::RR::DLV 1528 Net::DNS::RR::DNAME 1597 - Net::DNS::RR::DNSKEY 1597 - Net::DNS::RR::DS 1597 + Net::DNS::RR::DNSKEY 1729 + Net::DNS::RR::DS 1729 Net::DNS::RR::EUI48 1597 Net::DNS::RR::EUI64 1597 Net::DNS::RR::GPOS 1528 Net::DNS::RR::HINFO 1597 Net::DNS::RR::HIP 1597 - Net::DNS::RR::IPSECKEY 1597 + Net::DNS::RR::IPSECKEY 1718 Net::DNS::RR::ISDN 1597 Net::DNS::RR::KEY 1528 Net::DNS::RR::KX 1597 @@ -1355,27 +1370,27 @@ DISTRIBUTIONS Net::DNS::RR::NID 1597 Net::DNS::RR::NS 1597 Net::DNS::RR::NSEC 1696 - Net::DNS::RR::NSEC3 1694 + Net::DNS::RR::NSEC3 1726 Net::DNS::RR::NSEC3PARAM 1597 Net::DNS::RR::NULL 1528 Net::DNS::RR::OPENPGPKEY 1597 - Net::DNS::RR::OPT 1605 - Net::DNS::RR::OPT::CHAIN 1605 - Net::DNS::RR::OPT::CLIENT_SUBNET 1605 - Net::DNS::RR::OPT::COOKIE 1605 - Net::DNS::RR::OPT::DAU 1605 - Net::DNS::RR::OPT::DHU 1605 - Net::DNS::RR::OPT::EXPIRE 1605 - Net::DNS::RR::OPT::KEY_TAG 1605 - Net::DNS::RR::OPT::N3U 1605 - Net::DNS::RR::OPT::PADDING 1605 - Net::DNS::RR::OPT::TCP_KEEPALIVE 1605 + Net::DNS::RR::OPT 1717 + Net::DNS::RR::OPT::CHAIN 1717 + Net::DNS::RR::OPT::CLIENT_SUBNET 1717 + Net::DNS::RR::OPT::COOKIE 1717 + Net::DNS::RR::OPT::DAU 1717 + Net::DNS::RR::OPT::DHU 1717 + Net::DNS::RR::OPT::EXPIRE 1717 + Net::DNS::RR::OPT::KEY_TAG 1717 + Net::DNS::RR::OPT::N3U 1717 + Net::DNS::RR::OPT::PADDING 1717 + Net::DNS::RR::OPT::TCP_KEEPALIVE 1717 Net::DNS::RR::PTR 1597 Net::DNS::RR::PX 1597 Net::DNS::RR::RP 1597 - Net::DNS::RR::RRSIG 1709 + Net::DNS::RR::RRSIG 1729 Net::DNS::RR::RT 1597 - Net::DNS::RR::SIG 1709 + Net::DNS::RR::SIG 1729 Net::DNS::RR::SMIMEA 1597 Net::DNS::RR::SOA 1597 Net::DNS::RR::SPF 1593 @@ -1383,21 +1398,21 @@ DISTRIBUTIONS Net::DNS::RR::SSHFP 1597 Net::DNS::RR::TKEY 1528 Net::DNS::RR::TLSA 1597 - Net::DNS::RR::TSIG 1597 + Net::DNS::RR::TSIG 1726 Net::DNS::RR::TXT 1597 Net::DNS::RR::URI 1597 Net::DNS::RR::X25 1597 - Net::DNS::Resolver 1714 - Net::DNS::Resolver::Base 1709 + Net::DNS::Resolver 1726 + Net::DNS::Resolver::Base 1727 Net::DNS::Resolver::MSWin32 1568 - Net::DNS::Resolver::Recurse 1709 + Net::DNS::Resolver::Recurse 1737 Net::DNS::Resolver::UNIX 1573 Net::DNS::Resolver::android 1568 - Net::DNS::Resolver::cygwin 1568 + Net::DNS::Resolver::cygwin 1719 Net::DNS::Resolver::os2 1568 - Net::DNS::Resolver::os390 1579 - Net::DNS::Text 1698 - Net::DNS::Update 1714 + Net::DNS::Resolver::os390 1719 + Net::DNS::Text 1726 + Net::DNS::Update 1726 Net::DNS::ZoneFile 1709 Net::DNS::ZoneFile::Generator 1709 Net::DNS::ZoneFile::Text 1709 @@ -1410,7 +1425,7 @@ DISTRIBUTIONS IO::File 1.08 IO::Select 1.14 IO::Socket::IP 0.38 - MIME::Base64 2.11 + MIME::Base64 2.13 PerlIO 1.05 Scalar::Util 1.25 Test::More 0.52 @@ -1424,13 +1439,13 @@ DISTRIBUTIONS Carp 0 ExtUtils::MakeMaker 0 Storable 0 - Net-HTTP-6.18 - pathname: O/OA/OALDERS/Net-HTTP-6.18.tar.gz + Net-HTTP-6.19 + pathname: O/OA/OALDERS/Net-HTTP-6.19.tar.gz provides: - Net::HTTP 6.18 - Net::HTTP::Methods 6.18 - Net::HTTP::NB 6.18 - Net::HTTPS 6.18 + Net::HTTP 6.19 + Net::HTTP::Methods 6.19 + Net::HTTP::NB 6.19 + Net::HTTPS 6.19 requirements: Carp 0 Compress::Raw::Zlib 0 @@ -1443,16 +1458,15 @@ DISTRIBUTIONS strict 0 vars 0 warnings 0 - Net-SSLeay-1.85 - pathname: M/MI/MIKEM/Net-SSLeay-1.85.tar.gz + Net-SSLeay-1.88 + pathname: C/CH/CHRISN/Net-SSLeay-1.88.tar.gz provides: - Net::SSLeay 1.85 - Net::SSLeay::Handle 0.61 + Net::SSLeay 1.88 + Net::SSLeay::Handle 1.88 requirements: - ExtUtils::MakeMaker 6.36 + ExtUtils::MakeMaker 0 MIME::Base64 0 - Test::More 0.60_01 - perl 5.005 + perl 5.008001 NetAddr-IP-4.079 pathname: M/MI/MIKER/NetAddr-IP-4.079.tar.gz provides: @@ -1519,11 +1533,11 @@ DISTRIBUTIONS Sub::Exporter::Progressive 0.001013 requirements: ExtUtils::MakeMaker 0 - Sub-Quote-2.005001 - pathname: H/HA/HAARG/Sub-Quote-2.005001.tar.gz + Sub-Quote-2.006003 + pathname: H/HA/HAARG/Sub-Quote-2.006003.tar.gz provides: - Sub::Defer 2.005001 - Sub::Quote 2.005001 + Sub::Defer 2.006003 + Sub::Quote 2.006003 requirements: ExtUtils::MakeMaker 0 Scalar::Util 0 @@ -1567,10 +1581,10 @@ DISTRIBUTIONS Test::More 0.80 Test::Warnings 0 perl 5.006 - TermReadKey-2.37 - pathname: J/JS/JSTOWE/TermReadKey-2.37.tar.gz + TermReadKey-2.38 + pathname: J/JS/JSTOWE/TermReadKey-2.38.tar.gz provides: - Term::ReadKey 2.37 + Term::ReadKey 2.38 requirements: ExtUtils::MakeMaker 6.58 Test-Exception-0.43 @@ -1687,52 +1701,52 @@ DISTRIBUTIONS perl 5.006 strict 0 warnings 0 - URI-1.74 - pathname: E/ET/ETHER/URI-1.74.tar.gz + URI-1.76 + pathname: O/OA/OALDERS/URI-1.76.tar.gz provides: - URI 1.74 + URI 1.76 URI::Escape 3.31 URI::Heuristic 4.20 - URI::IRI 1.74 - URI::QueryParam 1.74 - URI::Split 1.74 + URI::IRI 1.76 + URI::QueryParam 1.76 + URI::Split 1.76 URI::URL 5.04 URI::WithBase 2.20 - URI::data 1.74 + URI::data 1.76 URI::file 4.21 - URI::file::Base 1.74 - URI::file::FAT 1.74 - URI::file::Mac 1.74 - URI::file::OS2 1.74 - URI::file::QNX 1.74 - URI::file::Unix 1.74 - URI::file::Win32 1.74 - URI::ftp 1.74 - URI::gopher 1.74 - URI::http 1.74 - URI::https 1.74 - URI::ldap 1.74 - URI::ldapi 1.74 - URI::ldaps 1.74 - URI::mailto 1.74 - URI::mms 1.74 - URI::news 1.74 - URI::nntp 1.74 - URI::pop 1.74 - URI::rlogin 1.74 - URI::rsync 1.74 - URI::rtsp 1.74 - URI::rtspu 1.74 - URI::sftp 1.74 - URI::sip 1.74 - URI::sips 1.74 - URI::snews 1.74 - URI::ssh 1.74 - URI::telnet 1.74 - URI::tn3270 1.74 - URI::urn 1.74 - URI::urn::isbn 1.74 - URI::urn::oid 1.74 + URI::file::Base 1.76 + URI::file::FAT 1.76 + URI::file::Mac 1.76 + URI::file::OS2 1.76 + URI::file::QNX 1.76 + URI::file::Unix 1.76 + URI::file::Win32 1.76 + URI::ftp 1.76 + URI::gopher 1.76 + URI::http 1.76 + URI::https 1.76 + URI::ldap 1.76 + URI::ldapi 1.76 + URI::ldaps 1.76 + URI::mailto 1.76 + URI::mms 1.76 + URI::news 1.76 + URI::nntp 1.76 + URI::pop 1.76 + URI::rlogin 1.76 + URI::rsync 1.76 + URI::rtsp 1.76 + URI::rtspu 1.76 + URI::sftp 1.76 + URI::sip 1.76 + URI::sips 1.76 + URI::snews 1.76 + URI::ssh 1.76 + URI::telnet 1.76 + URI::tn3270 1.76 + URI::urn 1.76 + URI::urn::isbn 1.76 + URI::urn::oid 1.76 requirements: Carp 0 Cwd 0 @@ -1852,32 +1866,32 @@ DISTRIBUTIONS common::sense 3.74 requirements: ExtUtils::MakeMaker 0 - libwww-perl-6.36 - pathname: E/ET/ETHER/libwww-perl-6.36.tar.gz - provides: - LWP 6.36 - LWP::Authen::Basic 6.36 - LWP::Authen::Digest 6.36 - LWP::Authen::Ntlm 6.36 - LWP::ConnCache 6.36 - LWP::Debug 6.36 - LWP::Debug::TraceHTTP 6.36 - LWP::DebugFile 6.36 - LWP::MemberMixin 6.36 - LWP::Protocol 6.36 - LWP::Protocol::cpan 6.36 - LWP::Protocol::data 6.36 - LWP::Protocol::file 6.36 - LWP::Protocol::ftp 6.36 - LWP::Protocol::gopher 6.36 - LWP::Protocol::http 6.36 - LWP::Protocol::loopback 6.36 - LWP::Protocol::mailto 6.36 - LWP::Protocol::nntp 6.36 - LWP::Protocol::nogo 6.36 - LWP::RobotUA 6.36 - LWP::Simple 6.36 - LWP::UserAgent 6.36 + libwww-perl-6.39 + pathname: O/OA/OALDERS/libwww-perl-6.39.tar.gz + provides: + LWP 6.39 + LWP::Authen::Basic 6.39 + LWP::Authen::Digest 6.39 + LWP::Authen::Ntlm 6.39 + LWP::ConnCache 6.39 + LWP::Debug 6.39 + LWP::Debug::TraceHTTP 6.39 + LWP::DebugFile 6.39 + LWP::MemberMixin 6.39 + LWP::Protocol 6.39 + LWP::Protocol::cpan 6.39 + LWP::Protocol::data 6.39 + LWP::Protocol::file 6.39 + LWP::Protocol::ftp 6.39 + LWP::Protocol::gopher 6.39 + LWP::Protocol::http 6.39 + LWP::Protocol::loopback 6.39 + LWP::Protocol::mailto 6.39 + LWP::Protocol::nntp 6.39 + LWP::Protocol::nogo 6.39 + LWP::RobotUA 6.39 + LWP::Simple 6.39 + LWP::UserAgent 6.39 libwww::perl undef requirements: CPAN::Meta::Requirements 2.120620 @@ -1904,7 +1918,7 @@ DISTRIBUTIONS MIME::Base64 2.1 Module::Metadata 0 Net::FTP 2.58 - Net::HTTP 6.07 + Net::HTTP 6.18 Scalar::Util 0 Try::Tiny 0 URI 1.10 @@ -1914,20 +1928,20 @@ DISTRIBUTIONS perl 5.008001 strict 0 warnings 0 - perl-ldap-0.65 - pathname: M/MA/MARSCHAP/perl-ldap-0.65.tar.gz + perl-ldap-0.66 + pathname: M/MA/MARSCHAP/perl-ldap-0.66.tar.gz provides: Bundle::Net::LDAP 0.03 LWP::Protocol::ldap 1.25 LWP::Protocol::ldapi undef LWP::Protocol::ldaps undef - Net::LDAP 0.65 + Net::LDAP 0.66 Net::LDAP::ASN 0.12 Net::LDAP::Bind 1.05 Net::LDAP::Constant 0.23 Net::LDAP::Control 0.18 Net::LDAP::Control::Assertion 0.02 - Net::LDAP::Control::DontUseCopy 0.01 + Net::LDAP::Control::DontUseCopy 0.02 Net::LDAP::Control::EntryChange 0.02 Net::LDAP::Control::ManageDsaIT 0.04 Net::LDAP::Control::MatchedValues 0.02 @@ -1943,19 +1957,19 @@ DISTRIBUTIONS Net::LDAP::Control::SyncDone 0.03 Net::LDAP::Control::SyncRequest 0.03 Net::LDAP::Control::SyncState 0.04 - Net::LDAP::Control::VLV 0.06 + Net::LDAP::Control::VLV 0.07 Net::LDAP::Control::VLVResponse 0.04 - Net::LDAP::DSML 0.16 - Net::LDAP::DSML::output 0.16 - Net::LDAP::DSML::pp 0.16 - Net::LDAP::Entry 0.27 + Net::LDAP::DSML 0.17 + Net::LDAP::DSML::output 0.17 + Net::LDAP::DSML::pp 0.17 + Net::LDAP::Entry 0.28 Net::LDAP::Extension 1.04 Net::LDAP::Extension::Cancel 0.02 - Net::LDAP::Extension::Refresh 0.03 + Net::LDAP::Extension::Refresh 0.04 Net::LDAP::Extension::SetPassword 0.06 Net::LDAP::Extension::WhoAmI 0.02 Net::LDAP::Extra 0.02 - Net::LDAP::Extra::AD 0.04 + Net::LDAP::Extra::AD 0.05 Net::LDAP::Extra::eDirectory 0.03 Net::LDAP::Filter 0.20 Net::LDAP::FilterList 0.02 @@ -1969,14 +1983,14 @@ DISTRIBUTIONS Net::LDAP::RootDSE 0.02 Net::LDAP::Schema 0.9908 Net::LDAP::Search 0.14 - Net::LDAP::Util 0.19 + Net::LDAP::Util 0.20 Net::LDAPI 0.04 Net::LDAPS 0.06 requirements: Authen::SASL 2.00 Convert::ASN1 0.2 Digest::MD5 0 - ExtUtils::MakeMaker 6.42 + ExtUtils::MakeMaker 6.59 File::Basename 0 File::Compare 0 File::Path 0 diff --git a/lib/Date/Language/Occitan.pm b/lib/Date/Language/Occitan.pm new file mode 100644 index 0000000..5fd9453 --- /dev/null +++ b/lib/Date/Language/Occitan.pm @@ -0,0 +1,36 @@ +## +## Occitan tables, contributed by Quentn PAGÈS +## + +package Date::Language::Occitan; + +use Date::Language (); +use vars qw(@ISA @DoW @DoWs @MoY @MoYs @AMPM @Dsuf %MoY %DoW $VERSION); +@ISA = qw(Date::Language); +$VERSION = "1.04"; + +@DoW = qw(dimenge diluns dimars dimècres dijòus divendres dissabte); +@MoY = qw(genièr febrièr mars abrial mai junh + julhet agost octòbre novembre decembre); +@DoWs = map { substr($_,0,3) } @DoW; +@MoYs = map { substr($_,0,3) } @MoY; +$MoYs[6] = 'jul'; +@AMPM = qw(AM PM); + +@Dsuf = ((qw(er e e e e e e e e e)) x 3, 'er'); + +@MoY{@MoY} = (0 .. scalar(@MoY)); +@MoY{@MoYs} = (0 .. scalar(@MoYs)); +@DoW{@DoW} = (0 .. scalar(@DoW)); +@DoW{@DoWs} = (0 .. scalar(@DoWs)); + +# Formatting routines + +sub format_a { $DoWs[$_[0]->[6]] } +sub format_A { $DoW[$_[0]->[6]] } +sub format_b { $MoYs[$_[0]->[4]] } +sub format_B { $MoY[$_[0]->[4]] } +sub format_h { $MoYs[$_[0]->[4]] } +sub format_p { $_[0]->[2] >= 12 ? $AMPM[1] : $AMPM[0] } + +1; diff --git a/lib/Lufi.pm b/lib/Lufi.pm index bdec609..bd0309f 100644 --- a/lib/Lufi.pm +++ b/lib/Lufi.pm @@ -110,6 +110,47 @@ sub startup { $r->post('/logout') ->to('Auth#log_out') ->name('logout'); + + if (defined $self->config('ldap') && defined $self->config('invitations')) { + # Invitation creation page + $r->get('/invite') + ->name('invite') + ->to('Invitation#new_invite'); + + # Send invitation + $r->post('/invite') + ->to('Invitation#send_invite'); + + # Get my invitations + $r->get('/invite/list') + ->name('invite_list') + ->to('Invitation#my_invitations'); + + # Delete invitations + $r->post('/invite/list/delete') + ->name('invite_list_delete') + ->to('Invitation#delete_invitations'); + + # Resend invitation mail + $r->post('/invite/list/resend') + ->name('invite_list_resend') + ->to('Invitation#resend_invitations'); + + # Toggle invitations visibility + $r->post('/invite/list/visibility') + ->name('invite_list_visibility') + ->to('Invitation#toggle_invitations_visibility'); + + # I’m a guest + $r->get('/guest/:token') + ->name('guest') + ->to('Invitation#guest'); + + # I’m a guest and I sent all my files + $r->post('/guest/:token/send_mail') + ->name('guest_send_mail') + ->to('Invitation#send_mail_to_ldap_user'); + } } # About page diff --git a/lib/Lufi/Command/sqliteToOtherDB.pm b/lib/Lufi/Command/sqliteToOtherDB.pm index 20e8561..4c39ff3 100644 --- a/lib/Lufi/Command/sqliteToOtherDB.pm +++ b/lib/Lufi/Command/sqliteToOtherDB.pm @@ -2,6 +2,7 @@ package Lufi::Command::sqliteToOtherDB; use Mojo::Base 'Mojolicious::Command'; use Lufi::DB::File; use Lufi::DB::Slice; +use Lufi::DB::Invitation; use Mojo::SQLite; use FindBin qw($Bin); use Term::ProgressBar; @@ -31,11 +32,12 @@ sub run { exit 1; } - my $sqlite = Mojo::SQLite->new('sqlite:'.$config->{db_path}); - my $files = $sqlite->db->select('files', undef)->hashes; - my $slices = $sqlite->db->select('slices', undef)->hashes; + my $sqlite = Mojo::SQLite->new('sqlite:'.$config->{db_path}); + my $files = $sqlite->db->select('files', undef)->hashes; + my $slices = $sqlite->db->select('slices', undef)->hashes; + my $invitations = $sqlite->db->select('invitations', undef)->hashes; - my $progress = Term::ProgressBar->new({count => $files->size + $slices->size}); + my $progress = Term::ProgressBar->new({count => $files->size + $slices->size + $invitations->size}); $files->each(sub { my ($file, $num) = @_; @@ -72,6 +74,24 @@ sub run { $progress->update(); }); + $invitations->each(sub { + my ($invitation, $num) = @_; + + Lufi::DB::Invitation->new(app => $c->app) + ->token($invitation->{token}) + ->ldap_user($invitation->{ldap_user}) + ->ldap_user_mail($invitation->{ldap_user_mail}) + ->guest_mail($invitation->{guest_mail}) + ->created_at($invitation->{created_at}) + ->expire_at($invitation->{expire_at}) + ->files_sent_at($invitation->{files_sent_at}) + ->expend_expire_at($invitation->{expend_expire_at}) + ->files($invitation->{files}) + ->show_in_list($invitation->{show_in_list}) + ->deleted($invitation->{deleted}) + ->write(); + $progress->update(); + }); } =encoding utf8 diff --git a/lib/Lufi/Controller/Auth.pm b/lib/Lufi/Controller/Auth.pm index 6168309..8f257cb 100644 --- a/lib/Lufi/Controller/Auth.pm +++ b/lib/Lufi/Controller/Auth.pm @@ -4,26 +4,36 @@ use Mojo::Base 'Mojolicious::Controller'; sub login_page { my $c = shift; + my $redirect = $c->param('redirect') // 'index'; if ($c->is_user_authenticated) { $c->redirect_to('index'); } else { - $c->render(template => 'login'); + $c->render( + template => 'login', + redirect => $redirect + ); } } sub login { my $c = shift; - my $login = $c->param('login'); - my $pwd = $c->param('password'); + my $login = $c->param('login'); + my $pwd = $c->param('password'); + my $redirect = $c->param('redirect') // 'index'; if ($c->validation->csrf_protect->has_error('csrf_token')) { $c->stash(msg => $c->l('Bad CSRF token.')); $c->render(template => 'login'); } else { if($c->authenticate($login, $pwd)) { - $c->redirect_to('index'); + if ($redirect eq 'invite') { + return $c->redirect_to('invite'); + } elsif ($redirect eq 'my_invitations') { + return $c->redirect_to('invite_list'); + } + return $c->redirect_to('index'); } else { $c->stash(msg => $c->l('Please, check your credentials or your right to access this service: unable to authenticate.')); $c->render(template => 'login'); diff --git a/lib/Lufi/Controller/Files.pm b/lib/Lufi/Controller/Files.pm index bb72521..71b8c34 100644 --- a/lib/Lufi/Controller/Files.pm +++ b/lib/Lufi/Controller/Files.pm @@ -24,7 +24,10 @@ sub files { sub upload { my $c = shift; - if ((!defined($c->config('ldap')) && !defined($c->config('htpasswd'))) || $c->is_user_authenticated) { + my $invitation; + my $token = $c->session->{guest_token}; + $invitation = Lufi::DB::Invitation->new(app => $c->app)->from_token($token) if $token; + if ((!defined($c->config('ldap')) && !defined($c->config('htpasswd'))) || $c->is_user_authenticated || $invitation) { $c->inactivity_timeout(30000000); $c->app->log->debug('Client connected'); @@ -33,6 +36,8 @@ sub upload { message => sub { my ($ws, $text) = @_; + my $invit = Lufi::DB::Invitation->new(app => $c->app)->from_token($token) if $token; + my $begin = time; my ($json) = split('XXMOJOXX', $text, 2); @@ -42,12 +47,36 @@ sub upload { $c->app->log->debug('Got message'); + if (defined($json->{cancel}) && $json->{cancel}) { + my $f = Lufi::DB::File->new(app => $c->app)->from_short($json->{id}); + if ($f && $f->mod_token && $f->mod_token eq $json->{mod_token}) { + $f = $f->delete(); + return $ws->send(to_json( + { + action => 'cancel', + success => $f->deleted ? true : false, + msg => $f->deleted ? 'Lufi::DB::File->delete() was successfull' : 'Lufi::DB::File->delete() failed', + i => $json->{i} + } + )); + } else { + return $ws->send(to_json( + { + action => 'cancel', + success => false, + msg => 'Lufi::DB::File not found or invalid mod_token', + i => $json->{i} + } + )); + } + } + my $stop = 0; # Check if stop_upload file is present if ($c->stop_upload) { $stop = 1; - $c->send(decode('UTF-8', encode_json( + return $ws->send(decode('UTF-8', encode_json( { success => false, msg => $c->l('Sorry, uploading is disabled.'), @@ -57,10 +86,10 @@ sub upload { ))); } # Check against max_size - elsif (defined $c->config('max_file_size')) { + if (defined $c->config('max_file_size')) { if ($json->{size} > $c->config('max_file_size')) { $stop = 1; - $c->send(decode('UTF-8', encode_json( + return $ws->send(decode('UTF-8', encode_json( { success => false, msg => $c->l('Your file is too big: %1 (maximum size allowed: %2)', format_bytes($json->{size}), format_bytes($c->config('max_file_size'))), @@ -71,9 +100,9 @@ sub upload { } } # Check that we have enough space (multiplying by 2 since it's encrypted, it takes more place that the original file) - elsif ($json->{part} == 0 && ($json->{size} * 2) >= dfportable($c->config('upload_dir'))->{bavail}) { + if ($json->{part} == 0 && ($json->{size} * 2) >= dfportable($c->config('upload_dir'))->{bavail}) { $stop = 1; - $c->send(decode('UTF-8', encode_json( + return $ws->send(decode('UTF-8', encode_json( { success => false, msg => $c->l('No enough space available on the server for this file (size: %1).', format_bytes($json->{size})), @@ -82,6 +111,18 @@ sub upload { } ))); } + # Check that the invitation is still valid, but only if it's the first chunk + # (i.e. a new file, we don't want to stop a current uploading) + if ($json->{part} == 0 && $invit && !$invit->is_valid()) { + $stop = 1; + $c->app->log->info(sprintf('Someone (%s) tried to use an expired or deleted invitation.', $invit->guest_mail)); + $ws->send(decode('UTF-8', encode_json( + { + success => false, + msg => $c->l('Sorry, your invitation has expired or has been deleted. Please contact %1 to have another invitation.', $invit->ldap_user_mail), + } + ))); + } unless ($stop) { my $f; @@ -118,9 +159,15 @@ sub upload { } my $creator = $c->ip; - if (defined($c->config('ldap')) || defined($c->config('htpasswd'))) { - $creator = 'User: '.$c->current_user.', IP: '.$creator; + # Authenticated user logging + if ((defined($c->config('ldap')) || defined($c->config('htpasswd'))) && !$invitation) { + $creator = sprintf('User: %s, IP: %s', $c->current_user->{username}, $creator); + } + # Guest user logging + if ($invitation) { + $creator = sprintf('User: %s, IP: %s', $invitation->guest_mail, $creator); } + my $delete_at_first_view = ($json->{del_at_first_view}) ? 1 : 0; $delete_at_first_view = 1 if $c->app->config('force_burn_after_reading'); $f = Lufi::DB::File->new(app => $c->app)->get_empty() @@ -133,6 +180,7 @@ sub upload { ->nbslices($json->{total}) ->mod_token($c->shortener($c->config('token_length'))) ->passwd($salted_pwd) + ->zipped($json->{zipped}) ->write; } @@ -165,23 +213,22 @@ sub upload { } } - $ws->send(to_json( - { - success => true, - i => $json->{i}, - j => $json->{part}, - parts => $json->{total}, - short => $f->short, - name => $f->filename, - size => $f->filesize, - del_at_first_view => (($f->delete_at_first_view) ? true : false), - created_at => $f->created_at, - delay => $f->delete_at_day, - token => $f->mod_token, - sent_delay => $json->{delay}, - duration => time - $begin - } - )); + my $result = { + success => true, + i => $json->{i}, + j => $json->{part}, + parts => $json->{total}, + short => $f->short, + name => $f->filename, + size => $f->filesize, + del_at_first_view => (($f->delete_at_first_view) ? true : false), + created_at => $f->created_at, + delay => $f->delete_at_day, + token => $f->mod_token, + sent_delay => $json->{delay}, + duration => time - $begin + }; + $ws->send(to_json($result)); } else { $ws->send(decode('UTF-8', encode_json( { diff --git a/lib/Lufi/Controller/Invitation.pm b/lib/Lufi/Controller/Invitation.pm new file mode 100644 index 0000000..29049e4 --- /dev/null +++ b/lib/Lufi/Controller/Invitation.pm @@ -0,0 +1,289 @@ +# vim:set sw=4 ts=4 sts=4 ft=perl expandtab: +package Lufi::Controller::Invitation; +use Mojo::Base 'Mojolicious::Controller'; +use Mojo::Collection 'c'; +use Mojo::File; +use Mojo::JSON qw(true false decode_json encode_json); +use Mojo::URL; +use Email::Valid; +use Lufi::DB::File; +use Lufi::DB::Invitation; +use Date::Format; + +sub new_invite { + my $c = shift; + + # The `if (defined($c->config('ldap')))` is at the router level in lib/Lufi.pm + if ($c->is_user_authenticated) { + my $mail_attr = $c->config('invitations')->{'mail_attr'} // 'mail'; + my $max_expire_at = $c->config('invitations')->{'max_invitation_expiration_delay'} // 30; + my $send_with_user_email = defined $c->config('invitations')->{'send_invitation_with_ldap_user_mail'}; + $c->render( + template => 'invitations/invite', + max_expire_at => $max_expire_at, + send_with_user_email => $send_with_user_email, + user_mail => ($send_with_user_email) ? $c->current_user->{$mail_attr} : '', + fails => [], + success => [] + ); + } else { + $c->redirect_to($c->url_for('login')->query(redirect => 'invite')); + } +} + +sub send_invite { + my $c = shift; + my $guest_mail = $c->param('guest_mail'); + my $expire_at = $c->param('expire_at'); + + my $mail_attr = $c->config('invitations')->{'mail_attr'} // 'mail'; + my $max_expire_at = $c->config('invitations')->{'max_invitation_expiration_delay'} // 30; + my $send_with_user_email = defined $c->config('invitations')->{'send_invitation_with_ldap_user_mail'}; + + # The `if (defined($c->config('ldap')))` is at the router level in lib/Lufi.pm + if ($c->is_user_authenticated) { + my @fails = (); + my @success = (); + unless (Email::Valid->address($guest_mail)) { + push @fails, $c->l('The guest email address (%1) is unvalid.', $guest_mail); + } + unless ($expire_at >= 1 && $expire_at <= $max_expire_at) { + push @fails, $c->l('The expiration delay (%1) is not between 1 and %2 days.', $expire_at, $max_expire_at); + } + + unless (scalar(@fails)) { + my $invitation = Lufi::DB::Invitation->new(app => $c->app); + my $mail_attr = $c->config('invitations')->{'mail_attr'} // 'mail'; + my $expend_expire_at = $c->config('invitations')->{'max_additional_period'} // 10; + + my $token; + do { + $token = $c->create_invitation_token; + } while ($invitation->is_token_used($token)); + + $invitation = $invitation->from_token($token); + $invitation->ldap_user($c->current_user->{username}); + $invitation->ldap_user_mail($c->current_user->{$mail_attr}); + $invitation->created_at(time); + $invitation->guest_mail($guest_mail); + $invitation->expire_at($invitation->created_at + 86400 * $expire_at); + $invitation->expend_expire_at($expend_expire_at); + $invitation->show_in_list(1); + $invitation = $invitation->write; + + my $from = ($c->config('invitations')->{'send_invitation_with_ldap_user_mail'}) ? $invitation->ldap_user_mail : $c->config('mail_sender'); + my $url = $c->url_for('guest', token => $invitation->token)->to_abs; + $c->mail( + from => $from, + to => $invitation->guest_mail, + template => 'invitations/invite', + format => 'mail', + ldap_user => ucfirst($invitation->ldap_user), + url => $url, + invitation => $invitation, + expires => time2str($c->l('%A %d %B %Y at %T'), $invitation->expire_at) + ); + + push @success, $c->l('Invitation sent to %1.
URL: %2', $invitation->guest_mail, $url); + } + + $c->render( + template => 'invitations/invite', + max_expire_at => $max_expire_at, + send_with_user_email => $send_with_user_email, + user_mail => ($send_with_user_email) ? $c->current_user->{$mail_attr} : '', + fails => \@fails, + success => \@success + ); + } else { + $c->redirect_to('login'); + } +} + +sub my_invitations { + my $c = shift; + + # The `if (defined($c->config('ldap')))` is at the router level in lib/Lufi.pm + if ($c->is_user_authenticated) { + my $invitations = Lufi::DB::Invitation->new(app => $c->app) + ->from_user($c->current_user->{username}); + $invitations = c() unless $invitations; + $c->render( + template => 'invitations/my_invitations', + invitations => $invitations + ); + } else { + $c->redirect_to($c->url_for('login')->query(redirect => 'my_invitations')); + } +} + +sub delete_invitations { + my $c = shift; + my @tokens = @{$c->every_param('tokens[]')}; + + my @result = (); + for my $token (@tokens) { + my $i = Lufi::DB::Invitation->new(app => $c->app) + ->from_token($token) + ->deleted(1) + ->write; + push @result, { msg => $c->l('The invitation %1 has been deleted.', $i->token), token => $i->token, deleted => $i->deleted }; + } + + $c->render(json => { + success => true, + tokens => \@result + }); +} + +sub resend_invitations { + my $c = shift; + my @tokens = @{$c->every_param('tokens[]')}; + + my @success; + my @failures; + for my $token (@tokens) { + my $i = Lufi::DB::Invitation->new(app => $c->app) + ->from_token($token); + + if ($i->files_sent_at) { + push @failures, $c->l('The invitation %1 can’t be resent: %2 has already sent files.
Please create a new invitation.', $i->token, $i->guest_mail); + } else { + if ($c->config('invitations')->{'extend_invitation_expiration_on_resend'}) { + $i->expire_at(time + $i->expire_at - $i->created_at) + ->write; + } + + my $from = ($c->config('invitations')->{'send_invitation_with_ldap_user_mail'}) ? $i->ldap_user_mail : $c->config('mail_sender'); + my $url = $c->url_for('guest', token => $i->token)->to_abs; + my $expire = time2str($c->l('%A %d %B %Y at %T'), $i->expire_at); + $c->mail( + from => $from, + to => $i->guest_mail, + template => 'invitations/invite', + format => 'mail', + ldap_user => ucfirst($i->ldap_user), + url => $url, + invitation => $i, + expires => $expire + ); + + push @success, { msg => $c->l('Invitation resent to %1.
URL: %2', $i->guest_mail, $url), expires => $expire, token => $i->token }; + } + } + + $c->render(json => { + success => \@success, + failures => \@failures + }); +} + +sub toggle_invitations_visibility { + my $c = shift; + my @tokens = @{$c->every_param('tokens[]')}; + + my @result = (); + for my $token (@tokens) { + my $i = Lufi::DB::Invitation->new(app => $c->app) + ->from_token($token) + ->toggle_visibility; + push @result, { token => $i->token, show => ($i->show_in_list) ? true : false } + } + + $c->render(json => { + success => true, + tokens => \@result + }); +} + +sub guest { + my $c = shift; + my $token = $c->param('token'); + + my $invitation = Lufi::DB::Invitation->new(app => $c->app)->from_token($token); + if ($invitation) { + if ($invitation->is_valid) { + $c->session->{guest_token} = $token; + $c->session(expires => $invitation->expire_at); + return $c->render( + template => 'index', + invitation => $invitation + ); + } else { + $c->stash('expired_or_deleted_invitation' => 1); + } + } else { + $c->stash('invitation_not_found' => 1); + } + return $c->render(template => 'invitations/exception'); +} + +sub send_mail_to_ldap_user { + my $c = shift; + my $token = $c->param('token'); + my $urls = c(@{$c->every_param('urls[]')}); + + my $invitation = Lufi::DB::Invitation->new(app => $c->app)->from_token($token); + if ($invitation) { + my @files = (); + if ($c->config('invitations')->{'save_files_url_in_db'} && $urls->size) { + my $guest_files = $invitation->files; + my %list_token; + if ($guest_files) { + $guest_files = decode_json($guest_files); + for my $file (@{$guest_files}) { + $list_token{$file->{token}} = 1; + } + } else { + $guest_files = []; + } + push @files, @{$guest_files}; + $urls->each(sub { + my ($e, $num) = @_; + $e = decode_json($e); + if (!defined($list_token{$e->{token}})) { + push @{$guest_files}, $e; + push @files, $e; + } + }); + $invitation->files(encode_json($guest_files)); + $invitation->write; + } else { + $urls->each(sub { + push @files, decode_json(shift); + }); + } + my $already_notified = 1; + unless ($invitation->files_sent_at) { + $invitation->files_sent_at(time); + $invitation->write; + $already_notified = 0; + } + $c->session(expires => $invitation->files_sent_at + 60 * $invitation->expend_expire_at); + $c->mail( + from => $c->config('mail_sender'), + to => $invitation->ldap_user_mail, + template => 'invitations/notification_files_sent', + format => 'mail', + files => c(@files), + invitation => $invitation, + already_notified => $already_notified + ); + + return $c->render( + json => { + success => true, + msg => $c->l('The URLs of your files have been sent by email to %1.', $invitation->ldap_user_mail) + } + ); + } else { + return $c->render( + json => { + success => false, + msg => $c->l('Sorry, the invitation doesn’t exist. Are you sure you are on the right URL?') + } + ); + } +} + +1; diff --git a/lib/Lufi/Controller/Misc.pm b/lib/Lufi/Controller/Misc.pm index 0c196c2..d28791b 100644 --- a/lib/Lufi/Controller/Misc.pm +++ b/lib/Lufi/Controller/Misc.pm @@ -49,14 +49,10 @@ sub fullstats { my $c = shift; my $stats = Lufi::DB::File->new(app => $c->app)->get_stats; + $stats->{timestamp} = time; return $c->render( - json => { - files => $stats->{files}, - deleted => $stats->{deleted}, - empty => $stats->{empty}, - timestamp => time, - } + json => $stats ); } diff --git a/lib/Lufi/DB/File.pm b/lib/Lufi/DB/File.pm index e2b8b32..e0c562c 100644 --- a/lib/Lufi/DB/File.pm +++ b/lib/Lufi/DB/File.pm @@ -26,6 +26,7 @@ has 'slices' => sub { }; has 'passwd'; has 'abuse'; +has 'zipped' => 0; has 'record' => 0; has 'app'; @@ -78,6 +79,8 @@ Have a look at Lufi::DB::File::SQLite's code: it's simple and may be more unders =item B : integer +=item B : boolean + =item B : a Mojolicious object =back @@ -128,7 +131,7 @@ sub new { =over 1 -=item B : C<$c-Edelet> +=item B : C<$c-Edelete> =item B : none @@ -175,9 +178,9 @@ sub write { my $c = shift; if ($c->record) { - $c->app->dbi->db->query('UPDATE files SET short = ?, deleted = ?, mediatype = ?, filename = ?, filesize = ?, counter = ?, delete_at_first_view = ?, delete_at_day = ?, created_at = ?, created_by = ?, last_access_at = ?, mod_token = ?, nbslices = ?, complete = ?, passwd = ?, abuse = ? WHERE short = ?', $c->short, $c->deleted, $c->mediatype, $c->filename, $c->filesize, $c->counter, $c->delete_at_first_view, $c->delete_at_day, $c->created_at, $c->created_by, $c->last_access_at, $c->mod_token, $c->nbslices, $c->complete, $c->passwd, $c->abuse, $c->short); + $c->app->dbi->db->query('UPDATE files SET short = ?, deleted = ?, mediatype = ?, filename = ?, filesize = ?, counter = ?, delete_at_first_view = ?, delete_at_day = ?, created_at = ?, created_by = ?, last_access_at = ?, mod_token = ?, nbslices = ?, complete = ?, passwd = ?, abuse = ?, zipped = ? WHERE short = ?', $c->short, $c->deleted, $c->mediatype, $c->filename, $c->filesize, $c->counter, $c->delete_at_first_view, $c->delete_at_day, $c->created_at, $c->created_by, $c->last_access_at, $c->mod_token, $c->nbslices, $c->complete, $c->passwd, $c->abuse, $c->zipped, $c->short); } else { - $c->app->dbi->db->query('INSERT INTO files (short, deleted, mediatype, filename, filesize, counter, delete_at_first_view, delete_at_day, created_at, created_by, last_access_at, mod_token, nbslices, complete, passwd, abuse) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)', $c->short, $c->deleted, $c->mediatype, $c->filename, $c->filesize, $c->counter, $c->delete_at_first_view, $c->delete_at_day, $c->created_at, $c->created_by, $c->last_access_at, $c->mod_token, $c->nbslices, $c->complete, $c->passwd, $c->abuse); + $c->app->dbi->db->query('INSERT INTO files (short, deleted, mediatype, filename, filesize, counter, delete_at_first_view, delete_at_day, created_at, created_by, last_access_at, mod_token, nbslices, complete, passwd, abuse, zipped) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)', $c->short, $c->deleted, $c->mediatype, $c->filename, $c->filesize, $c->counter, $c->delete_at_first_view, $c->delete_at_day, $c->created_at, $c->created_by, $c->last_access_at, $c->mod_token, $c->nbslices, $c->complete, $c->passwd, $c->abuse, $c->zipped); $c->record(1); } @@ -272,11 +275,12 @@ sub get_empty { sub get_stats { my $c = shift; - my $files = $c->app->dbi->db->query('SELECT count(short) AS count FROM files WHERE created_at IS NOT null AND deleted = ?', 0)->hashes->first->{count}; - my $deleted = $c->app->dbi->db->query('SELECT count(short) AS count FROM files WHERE created_at IS NOT null AND deleted = ?', 1)->hashes->first->{count}; - my $empty = $c->app->dbi->db->query('SELECT count(short) AS count FROM files WHERE created_at IS null')->hashes->first->{count}; + my $files = $c->app->dbi->db->query('SELECT count(short) AS count FROM files WHERE created_at IS NOT null AND deleted = ?', 0)->hashes->first->{count}; + my $deleted = $c->app->dbi->db->query('SELECT count(short) AS count FROM files WHERE created_at IS NOT null AND deleted = ?', 1)->hashes->first->{count}; + my $empty = $c->app->dbi->db->query('SELECT count(short) AS count FROM files WHERE created_at IS null')->hashes->first->{count}; + my $downloads = $c->app->dbi->db->query('SELECT SUM(counter) AS sum FROM files')->hashes->first->{sum}; - return { files => $files, deleted => $deleted, empty => $empty }; + return { files => $files, deleted => $deleted, empty => $empty, downloads => $downloads }; } =head2 from_short @@ -504,6 +508,7 @@ sub _slurp { $c->complete($file->{complete}); $c->passwd($file->{passwd}); $c->abuse($file->{abuse}); + $c->zipped($file->{zipped}); $c->record(1) unless $c->record; } diff --git a/lib/Lufi/DB/Invitation.pm b/lib/Lufi/DB/Invitation.pm new file mode 100644 index 0000000..73450f7 --- /dev/null +++ b/lib/Lufi/DB/Invitation.pm @@ -0,0 +1,426 @@ +# vim:set sw=4 ts=4 sts=4 ft=perl expandtab: +package Lufi::DB::Invitation; +use Mojo::Base -base; +use Mojo::File; +use Mojo::Collection 'c'; + +has 'token'; +has 'ldap_user'; +has 'ldap_user_mail'; +has 'guest_mail'; +has 'created_at'; +has 'expire_at'; +has 'files_sent_at'; +has 'expend_expire_at'; +has 'files'; +has 'show_in_list' => 1; +has 'deleted' => 0; +has 'record' => 0; +has 'app'; + +=head1 NAME + +Lufi::DB::Invitation - DB abstraction layer for Lufi invitations + +=head1 Contributing + +When creating a new database accessor, make sure that it provides the following subroutines. +After that, modify this file and modify the C subroutine to allow to use your accessor. + +Have a look at Lufi::DB::Invitation::SQLite's code: it's simple and may be more understandable that this doc. + +=head1 Attributes + +=over 1 + +=item B : string, invitation token + +=item B : string, the user who created the invitation + +=item B : string, the email of the user who created the invitation + +=item B : string, the email of the guest + +=item B : unix timestamp + +=item B : unix timestamp + +=item B : unix timestamp + +=item B : integer, "error" delay, in minutes + +=item B : string, optional, list of files sent by the guest + +=item B : boolean, if the ldap user want to see the invitation in his/her invitations list + +=item B : boolean + +=item B : a Mojolicious object + +=back + +=head1 Sub routines + +=head2 new + +=over 1 + +=item B : C<$c = Lufi::DB::Invitation-Enew(app =E $self);> + +=item B : any of the attribute above + +=item B : construct a new db accessor object. If the C attribute is provided, it have to load the informations from the database. + +=item B : the db accessor object + +=item B : the app argument is used by Lufi::DB::Invitation to choose which db accessor will be used, you don't need to use it in new(), but you can use it to access helpers or configuration settings in the other subroutines + +=back + +=cut + +sub new { + my $c = shift; + + $c = $c->SUPER::new(@_); + + if (ref($c) eq 'Lufi::DB::Invitation') { + my $dbtype = $c->app->config('dbtype'); + if ($dbtype eq 'sqlite') { + use Lufi::DB::Invitation::SQLite; + $c = Lufi::DB::Invitation::SQLite->new(@_); + } elsif ($dbtype eq 'postgresql') { + use Lufi::DB::Invitation::Pg; + $c = Lufi::DB::Invitation::Pg->new(@_); + } elsif ($dbtype eq 'mysql') { + use Lufi::DB::Invitation::Mysql; + $c = Lufi::DB::Invitation::Mysql->new(@_); + } + } + + return $c; +} + +sub to_hash { + my $c = shift; + + return { + token => $c->token, + ldap_user => $c->ldap_user, + ldap_user_mail => $c->ldap_user_mail, + guest_mail => $c->guest_mail, + created_at => $c->created_at, + expire_at => $c->expire_at, + files_sent_at => $c->files_sent_at, + expend_expire_at => $c->expend_expire_at, + files => $c->files, + show_in_list => $c->show_in_list, + deleted => $c->deleted + }; +} + +=head2 delete + +=over 1 + +=item B : C<$c-Edelete> + +=item B : none + +=item B : set the C flag to true + +=item B : the db accessor object + +=back + +=cut + +sub delete { + my $c = shift; + + $c->deleted(1); + + $c->write; + + return $c; +} + +=head2 hide + +=over 1 + +=item B : C<$c-Ehide> + +=item B : none + +=item B : set the C flag to false + +=item B : the db accessor object + +=back + +=cut + +sub hide { + my $c = shift; + + $c->show_in_list(0); + + $c->write; + + return $c; +} + +=head2 show + +=over 1 + +=item B : C<$c-Eshow> + +=item B : none + +=item B : set the C flag to true + +=item B : the db accessor object + +=back + +=cut + +sub show { + my $c = shift; + + $c->show_in_list(1); + + $c->write; + + return $c; +} + +=head2 toggle_visibility + +=over 1 + +=item B : C<$c-Etoggle_visibility> + +=item B : none + +=item B : toggle the C flag + +=item B : the db accessor object + +=back + +=cut + +sub toggle_visibility { + my $c = shift; + + if ($c->show_in_list) { + return $c->hide; + } else { + return $c->show; + } +} + +=head2 write + +=over 1 + +=item B : C<$c-Ewrite> + +=item B : none + +=item B : create or update a record in the database, with the values of the object's attributes + +=item B : the db accessor object + +=back + +=cut + +sub write { + my $c = shift; + + if ($c->record) { + $c->app->dbi->db->update('invitations', $c->to_hash, { token => $c->token }); + } else { + $c->app->dbi->db->insert('invitations', $c->to_hash); + $c->record(1); + } + + return $c; +} + +=head2 from_token + +=over 1 + +=item B : C<$c-Efrom_token($token)> + +=item B : string + +=item B : find an invitation in the database from its C attribute + +=item B : a db accessor object + +=back + +=cut + +sub from_token { + my $c = shift; + my $token = shift; + + my $r = $c->app->dbi->db->select('invitations', undef, { token => $token })->hashes; + + if ($r->size) { + return $c->_slurp($r->first)->record(1); + } else { + return undef; + } +} + +=head2 from_user + +=over 1 + +=item B : C<$c-Efrom_user($mail)> + +=item B : string + +=item B : find invitations in the database from their C attribute + +=item B : a Mojo::Collection of Lufi::DB::Invitation objects, sorted by creation date + +=back + +=cut + +sub from_user { + my $c = shift; + my $user = shift; + + my $r = $c->app->dbi->db + ->select('invitations', undef, { ldap_user => $user }) + ->hashes; + + if ($r->size) { + my @invitations; + $r->each(sub { + my ($e, $num) = @_; + $e->{app} = $c->app; + $e->{record} = 1; + push @invitations, Lufi::DB::Invitation->new($e); + }); + return c(@invitations); + } else { + return undef; + } +} + +=head2 is_token_used + +=over 1 + +=item B : C<$c-Eis_token_used($token)> + +=item B : string + +=item B : tells if a token is already used. If not, insert it in database to reserve it + +=item B : a boolean + +=back + +=cut + +sub is_token_used { + my $c = shift; + my $token = shift; + + my $r = $c->app->dbi->db->select('invitations', ['token'], { token => $token })->hashes; + + if ($r->size) { + return 1; + } else { + $c->app->dbi->db->insert('invitations', { token => $token }); + return 0; + } +} + +=head2 is_valid + +=over 1 + +=item B : C<$c-Eis_valid()> + +=item B : none + +=item B : tells if an invitation is still valid + +=item B : a boolean + +=back + +=cut + +sub is_valid { + my $c = shift; + + my $time = time; + # Active After creation date Before expiration date Before files send date plus extension delay + return (!$c->deleted && $time >= $c->created_at && $time < $c->expire_at && (!defined($c->files_sent_at) || $time < ($c->files_sent_at + $c->expend_expire_at * 60))); +} + +=head2 _slurp + +=over 1 + +=item B : C<$c-E_slurp> + +=item B : none + +=item B : put a database record's columns into the Lufi::DB::Invitation object's attributes + +=item B : the Lufi::DB::Invitation object + +=back + +=cut + +sub _slurp { + my $c = shift; + my $r = shift; + + my $invitation; + if (defined $r) { + $invitation = $r; + } else { + my $invitations = $c->app->dbi->db->select('invitations', undef, { token => $c->token })->hashes; + + if ($invitations->size) { + $invitation = $invitations->first; + } + } + + if ($invitation) { + $c->token( $invitation->{token} ); + $c->ldap_user( $invitation->{ldap_user} ); + $c->ldap_user_mail( $invitation->{ldap_user_mail} ); + $c->guest_mail( $invitation->{guest_mail} ); + $c->created_at( $invitation->{created_at} ); + $c->expire_at( $invitation->{expire_at} ); + $c->files_sent_at( $invitation->{files_sent_at} ); + $c->expend_expire_at($invitation->{expend_expire_at}); + $c->files( $invitation->{files} ); + $c->show_in_list( $invitation->{show_in_list} ); + $c->deleted( $invitation->{deleted} ); + + $c->record(1) unless $c->record; + } + + return $c; +} + +1; diff --git a/lib/Lufi/DB/Invitation/Mysql.pm b/lib/Lufi/DB/Invitation/Mysql.pm new file mode 100644 index 0000000..e265d2e --- /dev/null +++ b/lib/Lufi/DB/Invitation/Mysql.pm @@ -0,0 +1,13 @@ +# vim:set sw=4 ts=4 sts=4 ft=perl expandtab: +package Lufi::DB::Invitation::Mysql; +use Mojo::Base 'Lufi::DB::Invitation'; + +sub new { + my $c = shift; + + $c = $c->SUPER::new(@_); + + return $c; +} + +1; diff --git a/lib/Lufi/DB/Invitation/Pg.pm b/lib/Lufi/DB/Invitation/Pg.pm new file mode 100644 index 0000000..d41ce77 --- /dev/null +++ b/lib/Lufi/DB/Invitation/Pg.pm @@ -0,0 +1,13 @@ +# vim:set sw=4 ts=4 sts=4 ft=perl expandtab: +package Lufi::DB::Invitation::Pg; +use Mojo::Base 'Lufi::DB::Invitation'; + +sub new { + my $c = shift; + + $c = $c->SUPER::new(@_); + + return $c; +} + +1; diff --git a/lib/Lufi/DB/Invitation/SQLite.pm b/lib/Lufi/DB/Invitation/SQLite.pm new file mode 100644 index 0000000..e406363 --- /dev/null +++ b/lib/Lufi/DB/Invitation/SQLite.pm @@ -0,0 +1,14 @@ +# vim:set sw=4 ts=4 sts=4 ft=perl expandtab: +package Lufi::DB::Invitation::SQLite; +use Mojo::Base 'Lufi::DB::Invitation'; + +sub new { + my $c = shift; + + $c = $c->SUPER::new(@_); + $c = $c->_slurp if defined $c->record; + + return $c; +} + +1; diff --git a/lib/Lufi/Plugin/Helpers.pm b/lib/Lufi/Plugin/Helpers.pm index 1b7794f..b06fda0 100644 --- a/lib/Lufi/Plugin/Helpers.pm +++ b/lib/Lufi/Plugin/Helpers.pm @@ -2,6 +2,8 @@ package Lufi::Plugin::Helpers; use Mojo::Base 'Mojolicious::Plugin'; use Lufi::DB::File; +use Lufi::DB::Invitation; +use Date::Language; sub register { my ($self, $app) = @_; @@ -11,7 +13,6 @@ sub register { $app->plugin('PgURLHelper'); } - if ($app->config('dbtype') eq 'postgresql') { require Mojo::Pg; $app->helper(dbi => \&_pg); @@ -19,9 +20,9 @@ sub register { # Database migration my $migrations = Mojo::Pg::Migrations->new(pg => $app->dbi); if ($app->mode eq 'development' && $ENV{LUFI_DEV}) { - $migrations->from_file('utilities/migrations/pg.sql')->migrate(0)->migrate(2); + $migrations->from_file('utilities/migrations/pg.sql')->migrate(0)->migrate($migrations->latest); } else { - $migrations->from_file('utilities/migrations/pg.sql')->migrate(2); + $migrations->from_file('utilities/migrations/pg.sql')->migrate($migrations->latest); } } elsif ($app->config('dbtype') eq 'mysql') { require Mojo::mysql; @@ -30,9 +31,9 @@ sub register { # Database migration my $migrations = Mojo::mysql::Migrations->new(mysql => $app->dbi); if ($app->mode eq 'development' && $ENV{LUFI_DEV}) { - $migrations->from_file('utilities/migrations/mysql.sql')->migrate(0)->migrate(1); + $migrations->from_file('utilities/migrations/mysql.sql')->migrate(0)->migrate($migrations->latest); } else { - $migrations->from_file('utilities/migrations/mysql.sql')->migrate(1); + $migrations->from_file('utilities/migrations/mysql.sql')->migrate($migrations->latest); } } elsif ($app->config('dbtype') eq 'sqlite') { require Mojo::SQLite; @@ -43,9 +44,9 @@ sub register { my $sql = $app->dbi; my $migrations = $sql->migrations; if ($app->mode eq 'development' && $ENV{LUFI_DEV}) { - $migrations->from_file('utilities/migrations/sqlite.sql')->migrate(0)->migrate(2); + $migrations->from_file('utilities/migrations/sqlite.sql')->migrate(0)->migrate($migrations->latest); } else { - $migrations->from_file('utilities/migrations/sqlite.sql')->migrate(2); + $migrations->from_file('utilities/migrations/sqlite.sql')->migrate($migrations->latest); } # Check if passwd column is missing @@ -58,13 +59,16 @@ sub register { $app->dbi->db->query('ALTER TABLE files ADD COLUMN passwd TEXT') unless $pwd_col; } - $app->helper(provisioning => \&_provisioning); - $app->helper(get_empty => \&_get_empty); - $app->helper(ip => \&_ip); - $app->helper(default_delay => \&_default_delay); - $app->helper(max_delay => \&_max_delay); - $app->helper(is_selected => \&_is_selected); - $app->helper(stop_upload => \&_stop_upload); + $app->helper(provisioning => \&_provisioning); + $app->helper(get_empty => \&_get_empty); + $app->helper(ip => \&_ip); + $app->helper(default_delay => \&_default_delay); + $app->helper(max_delay => \&_max_delay); + $app->helper(is_selected => \&_is_selected); + $app->helper(stop_upload => \&_stop_upload); + $app->helper(create_invitation_token => \&_create_invitation_token); + $app->helper(is_guest => \&_is_guest); + $app->helper(get_date_lang => \&_get_date_lang); } sub _pg { @@ -170,4 +174,61 @@ sub _stop_upload { return 0; } +sub _create_invitation_token { + my $c = shift; + + return $c->shortener(32); +} + +sub _is_guest { + my $c = shift; + my $token = shift; + + my $invitation = Lufi::DB::Invitation->new(app => $c->app)->from_token($token); + return $invitation if ($invitation && $invitation->is_valid); + return 0; +} + +my %date_langs = ( + aa => 'Afar', + am => 'Amharic', + pt => 'Brazilian', + bg => 'Bulgarian', + zh => 'Chinese', + cs => 'Czech', + da => 'Danish', + nl => 'Dutch', + fi => 'Finnish', + en => 'English', + fr => 'French', + de => 'German', + el => 'Greek', + hu => 'Hungarian', + is => 'Icelandic', + it => 'Italian', + nn => 'Norwegian', + om => 'Oromo', + oc => 'Occitan', + ro => 'Romanian', + ru => 'Russian', + so => 'Somali', + es => 'Spanish', + sv => 'Swedish', + ti => 'Tigrinya', + tk => 'Turkish', +); + +sub _get_date_lang { + my $c = shift; + + my $l = $c->languages(); + + return Date::Language->new($date_langs{$l}) if $date_langs{$l}; + + $l =~ s/^(..).*/$1/; + return Date::Language->new($date_langs{$l}) if $date_langs{$l}; + + return Date::Language->new('English'); +} + 1; diff --git a/lufi.conf.template b/lufi.conf.template index 9db3777..647d944 100644 --- a/lufi.conf.template +++ b/lufi.conf.template @@ -18,67 +18,67 @@ clients => 1, }, - # put a way to contact you here and uncomment it - # you can put some HTML in it + # Put a way to contact you here and uncomment it + # You can put some HTML in it # MANDATORY #contact => 'Contact page', - # put an URL or an email address to receive file reports and uncomment it - # it's for make reporting illegal files easy for users + # Put an URL or an email address to receive file reports and uncomment it + # It's for make reporting illegal files easy for users # MANDATORY #report => 'report@example.com', - # array of random strings used to encrypt cookies + # Array of random strings used to encrypt cookies # optional, default is ['fdjsofjoihrei'], PLEASE, CHANGE IT #secrets => ['fdjsofjoihrei'], - # choose a theme. See the available themes in `themes` directory - # optional, default is 'default' + # Choose a theme. See the available themes in `themes` directory + # Optional, default is 'default' #theme => 'default', - # length of the random URL + # Length of the random URL # optional, default is 8 #length => 8, - # how many URLs will be provisioned in a batch ? + # How many URLs will be provisioned in a batch ? # optional, default is 5 #provis_step => 5, - # max number of URLs to be provisioned + # Max number of URLs to be provisioned # optional, default is 100 #provisioning => 100, - # length of the modify/delete token + # Length of the modify/delete token # optional, default is 32 #token_length => 32, - # max file size, in octets - # you can write it 100*1024*1024 + # Max file size, in octets + # You can write it 100*1024*1024 # optional, no default #max_file_size => 104857600, - # if you want to have piwik statistics, provide a piwik image tracker - # only the image tracker is allowed, no javascript + # If you want to have piwik statistics, provide a piwik image tracker + # Only the image tracker is allowed, no javascript # optional, no default #piwik_img => 'https://piwik.example.org/piwik.php?idsite=1&rec=1', - # broadcast_message which will displayed on the index page + # Broadcast_message which will displayed on the index page # optional, no default #broadcast_message => 'Maintenance', - # default time limit for files - # valid values are 0, 1, 7, 30 and 365 + # Default time limit for files + # Valid values are 0, 1, 7, 30 and 365 # optional, default is 0 (no limit) #default_delay => 0, - # number of days after which the images will be deleted, even if they were uploaded with "no delay" (or value superior to max_delay) - # a warning message will be displayed on homepage + # Number of days after which the files will be deleted, even if they were uploaded with "no delay" (or value superior to max_delay) + # A warning message will be displayed on homepage # optional, default is 0 (no limit) #max_delay => 0, - # size thresholds: if you want to define max delays for different sizes of file - # the keys are size in Bytes, you can't have 10*1000*10000 as key - # if a file is smaller than the smallest configured size, it will have a expiration delay of max_delay (see above) + # Size thresholds: if you want to define max delays for different sizes of file + # The keys are size in Bytes, you can't have 10*1000*10000 as key + # If a file is smaller than the smallest configured size, it will have a expiration delay of max_delay (see above) # optional, default is using max_delay (see above) for all sizes #delay_for_size => { # 10000000 => 90, # between 10MB and 50MB => max is 90 days, less than 10MB => max is max_delay (see above) @@ -92,18 +92,50 @@ # optional, defaut is / #prefix => '/', - # array of authorized domains for API calls. - # if you want to authorize everyone to use the API: ['*'] + # Array of authorized domains for API calls. + # If you want to authorize everyone to use the API: ['*'] # optional, no domains allowed by default #allowed_domains => ['http://1.example.com', 'http://2.example.com'], - # if set, the shortened URLs will use this domain + # If set, the shortened URLs will use this domain # optional #fixed_domain => 'example.org', + # Define a path to the upload directory, where the uploaded files will be stored + # You can define it relative to lufi directory or set an absolute path + # Remember that it has to be in a directory writable by Lufi user + # DO NOT CHANGE THIS IF FILES HAVE BEEN ALREADY UPLOADED: THEY WILL NOT BE DOWNLOADABLE ANYMORE + # optional, default is 'files' + #upload_dir => 'files', + + # Allow to add a password on files, asked before allowing to download files + # optional, default is 0 + #allow_pwd_on_files => 0, + + # Force all files to be in "Burn after reading mode" + # optional, default is 0 + #force_burn_after_reading => 0, + + # If set, the files' URLs will always use this domain + # optional, no default + #fixed_domain => 'example.org', + + # Abuse reasons + # Set an integer in the abuse field of a file in the database and it will not be downloadable anymore + # The reason will be displayed to the downloader, according to the reasons you will configure here. + # optional, no default + #abuse => { + # 0 => 'Copyright infringment', + # 1 => 'Illegal content', + #}, + + ############### + # Mail settings + ############### + # Mail configuration # See https://metacpan.org/pod/Mojolicious::Plugin::Mail#EXAMPLES - # Optional, default to sendmail method with no arguments + # optional, default to sendmail method with no arguments #mail => { # # Valid values are 'sendmail' and 'smtp' # how => 'smtp', @@ -111,23 +143,27 @@ #}, # Email sender address - # Optional, default to no-reply@lufi.io + # optional, default to no-reply@lufi.io #mail_sender => 'no-reply@lufi.io', - # choose what database you want to use - # valid choices are sqlite, postgresql and mysql (all lowercase) + ############# + # DB settings + ############# + + # Choose what database you want to use + # Valid choices are sqlite, postgresql and mysql (all lowercase) # optional, default is sqlite #dbtype => 'sqlite', # SQLite ONLY - only used if dbtype is set to sqlite - # define a path to the SQLite database - # you can define it relative to lufi directory or set an absolute path - # remember that it has to be in a directory writable by Lufi user + # Define a path to the SQLite database + # You can define it relative to lufi directory or set an absolute path + # Remember that it has to be in a directory writable by Lufi user # optional, default is lufi.db #db_path => 'lufi.db', # PostgreSQL ONLY - only used if dbtype is set to postgresql - # these are the credentials to access the PostgreSQL database + # These are the credentials to access the PostgreSQL database # mandatory if you choosed postgresql as dbtype #pgdb => { # database => 'lufi', @@ -142,7 +178,7 @@ #}, # MySQL ONLY - only used if dbtype is set to mysql - # these are the credentials to access the MySQL database + # These are the credentials to access the MySQL database # mandatory if you choosed mysql as dbtype #mysqldb => { # database => 'lufi', @@ -156,15 +192,12 @@ # #max_connections => 5, #}, - # define a path to the upload directory, where the uploaded files will be stored - # you can define it relative to lufi directory or set an absolute path - # remember that it has to be in a directory writable by Lufi user - # DO NOT CHANGE THIS IF FILES HAVE BEEN ALREADY UPLOADED: THEY WILL NOT BE DOWNLOADABLE ANYMORE - # optional, default is 'files' - #upload_dir => 'files', + ############################################# + # LDAP settings (authentication and features) + ############################################# - # set `ldap` if you want that only authenticated users can upload files - # please note that everybody can still download files + # Set `ldap` if you want that only authenticated users can upload files + # Please note that everybody can still download files # optional, no default #ldap => { # uri => 'ldaps://ldap.example.org', # server URI @@ -181,36 +214,71 @@ # } #}, - # set `htpasswd` if you want to use an htpasswd file instead of ldap - # see 'man htpasswd' to know how to create such file - #htpasswd => 'lufi.passwd', - - # if you've set ldap above, the session will last `session_duration` seconds before + # If you've set ldap above, the session will last `session_duration` seconds before # the user needs to reauthenticate # optional, default is 3600 #session_duration => 3600, - # allow to add a password on files, asked before allowing to download files - # optional, default is 0 - #allow_pwd_on_files => 0, - - # force all files to be in "Burn after reading mode" - # optional, default is 0 - #force_burn_after_reading => 0, - - # if set, the files' URLs will always use this domain + # If you use `ldap` for authentication, you can map some attributes from LDAP to be able to access them in Lufi + # Those attributes will be accessible with: + # $c->current_user->{lufi_attribute_name} in Lufi backend files (all that is in `lib` directory) + # <%= $self->current_user->{lufi_attribute_name} %> in templates files (in `themes` directory) + # + # Define the attributes like this: `lufi_attribute_name => 'LDAP_attribute_name'` + # Note that you can’t use `username` as a Lufi attribute name: this name is reserved and will contain the login of the user # optional, no default - #fixed_domain => 'example.org', + #ldap_map_attr => { + # displayname => 'cn', + # mail => 'mail' + #}, - # abuse reasons - # set an integer in the abuse field of a file in the database and it will not be downloadable anymore - # the reason will be displayed to the downloader, according to the reasons you will configure here. + # When using LDAP authentication, LDAP users can invite people (by mail) to use Lufi to send them files without + # being authenticated. + # This is where you configure the behavior of the invitations. + # You may need to fetch some attributes from LDAP to use some invitations settings. See `ldap_map_attr` above. # optional, no default - #abuse => { - # 0 => 'Copyright infringment', - # 1 => 'Illegal content', + #invitations => { + # # The name of the key set in `ldap_map_attr` (above) that corresponds to the mail of the LDAP user + # # optional, default is `mail` + # mail_attr => 'mail', + # # The `From` header of invitation mail can be the mail of the LDAP user + # # Be sure to have a mail system that will correctly send the mail from your users! (DKIM, SPF…) + # # To enable this feature, set it to 1 + # # optional, disabled by default + # send_invitation_with_ldap_user_mail => 1, + # # The user is able to set an expiration delay for the invitation. + # # This expiration delay can’t be more than this setting (in days). + # # optional, default is 30 days + # max_invitation_expiration_delay => 30, + # # Once the guest has submitted his files, he has an additional period of time to submit forgotten files. + # # You can set that additional period of time in minutes here. + # # To disable that feature, set it to 0 or less + # # optional, default is 10 minutes + # max_additional_period => 10, + # # Lufi follows privacy-by-design, so, by default, no files URLs (with the decode secret) are stored in database. + # # However, the concern is different for this case. Storing files URLs makes users able to retrieve the guests’ sent files + # # from their `invitations` page. + # # Set to 1 to store guests’ files URLs in database + # # optional, default is 0 (disabled) + # save_files_url_in_db => 0, + # # Users can resend the invitation to their guest. This does not extend the invitation’s expiration delay unless you + # # set this option to 1. + # # optional, default is 0 (disabled) + # extend_invitation_expiration_on_resend => 0, #}, + ######################### + # Htpasswd authentication + ######################### + + # Set `htpasswd` if you want to use an htpasswd file instead of ldap + # See 'man htpasswd' to know how to create such file + #htpasswd => 'lufi.passwd', + + ####################### + # HTTP Headers settings + ####################### + # Content-Security-Policy header that will be sent by Lufi # Set to '' to disable CSP header # https://content-security-policy.com/ provides a good documentation about CSP. @@ -243,24 +311,24 @@ # Lufi cron jobs settings ######################### - # number of days senders' IP addresses are kept in database - # after that delay, they will be deleted from database (used with script/lufi cron cleanbdd) + # Number of days senders' IP addresses are kept in database + # After that delay, they will be deleted from database (used with script/lufi cron cleanbdd) # optional, default is 365 #keep_ip_during => 365, - # max size of the files directory, in octets - # used by script/lufi cron watch to trigger an action + # Max size of the files directory, in octets + # Used by script/lufi cron watch to trigger an action # optional, no default #max_total_size => 10*1024*1024*1024, - # default action when files directory is over max_total_size (used with script/lufi cron watch) - # valid values are 'warn', 'stop-upload' and 'delete' - # please, see readme + # Default action when files directory is over max_total_size (used with script/lufi cron watch) + # Valid values are 'warn', 'stop-upload' and 'delete' + # Please, see README.md # optional, default is 'warn' #policy_when_full => 'warn', - # images which are not viewed since delete_no_longer_viewed_files days will be deleted by the cron cleanfiles task - # if delete_no_longer_viewed_files is not set, the no longer viewed files will NOT be deleted + # Files which are not viewed since delete_no_longer_viewed_files days will be deleted by the cron cleanfiles task + # If delete_no_longer_viewed_files is not set, the no longer viewed files will NOT be deleted # optional, no default #delete_no_longer_viewed_files => 90, }; diff --git a/t/mysql.conf b/t/mysql.conf index 4b6197c..68f9bdb 100644 --- a/t/mysql.conf +++ b/t/mysql.conf @@ -6,6 +6,7 @@ # see http://mojolicio.us/perldoc/Mojo/Server/Hypnotoad for a full list of settings hypnotoad => { # array of IP addresses and ports you want to listen to + # you can specify a unix socket too, like 'http+unix://%2Ftmp%2Flufi.sock' listen => ['http://127.0.0.1:8081'], # if you use Lufi behind a reverse proxy like Nginx, you want to set proxy to 1 # if you use Lufi directly, let it commented @@ -17,67 +18,67 @@ clients => 1, }, - # put a way to contact you here and uncomment it - # you can put some HTML in it + # Put a way to contact you here and uncomment it + # You can put some HTML in it # MANDATORY contact => 'Contact page', - # put an URL or an email address to receive file reports and uncomment it - # it's for make reporting illegal files easy for users + # Put an URL or an email address to receive file reports and uncomment it + # It's for make reporting illegal files easy for users # MANDATORY report => 'report@example.com', - # array of random strings used to encrypt cookies + # Array of random strings used to encrypt cookies # optional, default is ['fdjsofjoihrei'], PLEASE, CHANGE IT #secrets => ['fdjsofjoihrei'], - # choose a theme. See the available themes in `themes` directory - # optional, default is 'default' + # Choose a theme. See the available themes in `themes` directory + # Optional, default is 'default' #theme => 'default', - # length of the random URL + # Length of the random URL # optional, default is 8 #length => 8, - # how many URLs will be provisioned in a batch ? + # How many URLs will be provisioned in a batch ? # optional, default is 5 #provis_step => 5, - # max number of URLs to be provisioned + # Max number of URLs to be provisioned # optional, default is 100 #provisioning => 100, - # length of the modify/delete token + # Length of the modify/delete token # optional, default is 32 #token_length => 32, - # max file size, in octets - # you can write it 100*1024*1024 + # Max file size, in octets + # You can write it 100*1024*1024 # optional, no default #max_file_size => 104857600, - # if you want to have piwik statistics, provide a piwik image tracker - # only the image tracker is allowed, no javascript + # If you want to have piwik statistics, provide a piwik image tracker + # Only the image tracker is allowed, no javascript # optional, no default #piwik_img => 'https://piwik.example.org/piwik.php?idsite=1&rec=1', - # broadcast_message which will displayed on the index page + # Broadcast_message which will displayed on the index page # optional, no default #broadcast_message => 'Maintenance', - # default time limit for files - # valid values are 0, 1, 7, 30 and 365 + # Default time limit for files + # Valid values are 0, 1, 7, 30 and 365 # optional, default is 0 (no limit) #default_delay => 0, - # number of days after which the images will be deleted, even if they were uploaded with "no delay" (or value superior to max_delay) - # a warning message will be displayed on homepage + # Number of days after which the images will be deleted, even if they were uploaded with "no delay" (or value superior to max_delay) + # A warning message will be displayed on homepage # optional, default is 0 (no limit) #max_delay => 0, - # size thresholds: if you want to define max delays for different sizes of file - # the keys are size in Bytes, you can't have 10*1000*10000 as key - # if a file is smaller than the smallest configured size, it will have a expiration delay of max_delay (see above) + # Size thresholds: if you want to define max delays for different sizes of file + # The keys are size in Bytes, you can't have 10*1000*10000 as key + # If a file is smaller than the smallest configured size, it will have a expiration delay of max_delay (see above) # optional, default is using max_delay (see above) for all sizes #delay_for_size => { # 10000000 => 90, # between 10MB and 50MB => max is 90 days, less than 10MB => max is max_delay (see above) @@ -91,18 +92,54 @@ # optional, defaut is / #prefix => '/', - # array of authorized domains for API calls. - # if you want to authorize everyone to use the API: ['*'] + # Array of authorized domains for API calls. + # If you want to authorize everyone to use the API: ['*'] # optional, no domains allowed by default #allowed_domains => ['http://1.example.com', 'http://2.example.com'], - # if set, the shortened URLs will use this domain + # If set, the shortened URLs will use this domain # optional #fixed_domain => 'example.org', + # Define a path to the upload directory, where the uploaded files will be stored + # You can define it relative to lufi directory or set an absolute path + # Remember that it has to be in a directory writable by Lufi user + # DO NOT CHANGE THIS IF FILES HAVE BEEN ALREADY UPLOADED: THEY WILL NOT BE DOWNLOADABLE ANYMORE + # optional, default is 'files' + #upload_dir => 'files', + + # Allow to add a password on files, asked before allowing to download files + # optional, default is 0 + allow_pwd_on_files => 1, + + # Force all files to be in "Burn after reading mode" + # optional, default is 0 + #force_burn_after_reading => 0, + + # If set, the files' URLs will always use this domain + # optional, no default + #fixed_domain => 'example.org', + + # Abuse reasons + # Set an integer in the abuse field of a file in the database and it will not be downloadable anymore + # The reason will be displayed to the downloader, according to the reasons you will configure here. + # optional, no default + #abuse => { + # 0 => 'Copyright infringment', + # 1 => 'Illegal content', + #}, + abuse => { + 0 => 'Copyright infringment', + 1 => 'Illegal content', + }, + + ############### + # Mail settings + ############### + # Mail configuration # See https://metacpan.org/pod/Mojolicious::Plugin::Mail#EXAMPLES - # Optional, default to sendmail method with no arguments + # optional, default to sendmail method with no arguments #mail => { # # Valid values are 'sendmail' and 'smtp' # how => 'smtp', @@ -110,23 +147,28 @@ #}, # Email sender address - # Optional, default to no-reply@lufi.io + # optional, default to no-reply@lufi.io #mail_sender => 'no-reply@lufi.io', - # choose what database you want to use - # valid choices are sqlite, postgresql and mysql (all lowercase) + ############# + # DB settings + ############# + + # Choose what database you want to use + # Valid choices are sqlite, postgresql and mysql (all lowercase) # optional, default is sqlite + #dbtype => 'sqlite', dbtype => 'mysql', # SQLite ONLY - only used if dbtype is set to sqlite - # define a path to the SQLite database - # you can define it relative to lufi directory or set an absolute path - # remember that it has to be in a directory writable by Lufi user + # Define a path to the SQLite database + # You can define it relative to lufi directory or set an absolute path + # Remember that it has to be in a directory writable by Lufi user # optional, default is lufi.db #db_path => 'lufi.db', # PostgreSQL ONLY - only used if dbtype is set to postgresql - # these are the credentials to access the PostgreSQL database + # These are the credentials to access the PostgreSQL database # mandatory if you choosed postgresql as dbtype #pgdb => { # database => 'lufi', @@ -135,13 +177,25 @@ # #port => 5432, # user => 'DBUSER', # pwd => 'DBPASSWORD', + # # https://mojolicious.org/perldoc/Mojo/Pg#max_connections # # optional, default is 1 # #max_connections => 1, #}, # MySQL ONLY - only used if dbtype is set to mysql - # these are the credentials to access the MySQL database + # These are the credentials to access the MySQL database # mandatory if you choosed mysql as dbtype + #mysqldb => { + # database => 'lufi', + # host => 'localhost', + # # optional, default is 3306 + # #port => 3306, + # user => 'DBUSER', + # pwd => 'DBPASSWORD', + # # https://metacpan.org/pod/Mojo::mysql#max_connections + # # optional, default is 5 (set to 0 to disable persistent connections) + # #max_connections => 5, + #}, mysqldb => { database => 'lufi_db', host => 'mariadb', @@ -153,55 +207,50 @@ # #max_connections => 5, }, - # define a path to the upload directory, where the uploaded files will be stored - # you can define it relative to lufi directory or set an absolute path - # remember that it has to be in a directory writable by Lufi user - # DO NOT CHANGE THIS IF FILES HAVE BEEN ALREADY UPLOADED: THEY WILL NOT BE DOWNLOADABLE ANYMORE - # optional, default is 'files' - #upload_dir => 'files', + ############################################# + # LDAP settings (authentication and features) + ############################################# - # set `ldap` if you want that only authenticated users can upload files - # please note that everybody can still download files + # Set `ldap` if you want that only authenticated users can upload files + # Please note that everybody can still download files # optional, no default #ldap => { uri => 'ldap://rroemhild-test-openldap', user_tree => 'ou=people,dc=planetexpress,dc=com', bind_dn => 'cn=admin,dc=planetexpress,dc=com', bind_pwd => 'GoodNewsEveryone', user_attr => 'uid', user_filter => '' }, - # set `htpasswd` if you want to use an htpasswd file instead of ldap - # see 'man htpasswd' to know how to create such file - #htpasswd => 't/lstu.passwd', - - # if you've set ldap above, the session will last `session_duration` seconds before + # If you've set ldap above, the session will last `session_duration` seconds before # the user needs to reauthenticate # optional, default is 3600 #session_duration => 3600, - # allow to add a password on files, asked before allowing to download files - # optional, default is 0 - allow_pwd_on_files => 1, + # If you use `ldap` for authentication, you can map some attributes from LDAP to be able to access them in Lufi + # Those attributes will be accessible with: + # $c->current_user->{lufi_attribute_name} in Lufi backend files (all that is in `lib` directory) + # <%= $self->current_user->{lufi_attribute_name} %> in templates files (in `themes` directory) + # + # Define the attributes like this: `lufi_attribute_name => 'LDAP_attribute_name'` + # Note that you can’t use `username` as a Lufi attribute name: this name is reserved and will contain the login of the user + #ldap_map_attr => { + # displayname => 'cn', + # mail => 'mail' + #}, - # force all files to be in "Burn after reading mode" - # optional, default is 0 - #force_burn_after_reading => 0, + ######################### + # Htpasswd authentication + ######################### - # if set, the files' URLs will always use this domain - # optional, no default - #fixed_domain => 'example.org', + # Set `htpasswd` if you want to use an htpasswd file instead of ldap + # See 'man htpasswd' to know how to create such file + #htpasswd => 't/lstu.passwd', - # abuse reasons - # set an integer in the abuse field of a file in the database and it will not be downloadable anymore - # the reason will be displayed to the downloader, according to the reasons you will configure here. - # optional, no default - abuse => { - 0 => 'Copyright infringment', - 1 => 'Illegal content', - }, + ####################### + # HTTP Headers settings + ####################### # Content-Security-Policy header that will be sent by Lufi # Set to '' to disable CSP header # https://content-security-policy.com/ provides a good documentation about CSP. # https://report-uri.com/home/generate provides a tool to generate a CSP header. - # optional, default is "base-uri 'self'; connect-src 'self'; default-src 'none'; font-src 'self'; form-action 'self'; frame-ancestors 'none'; img-src 'self' blob:; media-src blob:; script-src 'self' 'unsafe-inline' 'unsafe-eval'; style-src 'self' 'unsafe-inline'" - # the default value is good for `default` and `milligram` themes - #csp => "base-uri 'self'; connect-src 'self'; default-src 'none'; font-src 'self'; form-action 'self'; frame-ancestors 'none'; img-src 'self' blob:; media-src blob:; script-src 'self' 'unsafe-inline' 'unsafe-eval'; style-src 'self' 'unsafe-inline'" + # optional, default is "base-uri 'self'; connect-src 'self' ws://YOUR_HOST; default-src 'none'; font-src 'self'; form-action 'self'; frame-ancestors 'none'; img-src 'self' blob:; media-src blob:; script-src 'self' 'unsafe-inline' 'unsafe-eval'; style-src 'self' 'unsafe-inline'" + #csp => "", # X-Frame-Options header that will be sent by Lufi # Valid values are: 'DENY', 'SAMEORIGIN', 'ALLOW-FROM https://example.com/' @@ -228,24 +277,24 @@ # Lufi cron jobs settings ######################### - # number of days senders' IP addresses are kept in database - # after that delay, they will be deleted from database (used with script/lufi cron cleanbdd) + # Number of days senders' IP addresses are kept in database + # After that delay, they will be deleted from database (used with script/lufi cron cleanbdd) # optional, default is 365 #keep_ip_during => 365, - # max size of the files directory, in octets - # used by script/lufi cron watch to trigger an action + # Max size of the files directory, in octets + # Used by script/lufi cron watch to trigger an action # optional, no default #max_total_size => 10*1024*1024*1024, - # default action when files directory is over max_total_size (used with script/lufi cron watch) - # valid values are 'warn', 'stop-upload' and 'delete' - # please, see readme + # Default action when files directory is over max_total_size (used with script/lufi cron watch) + # Valid values are 'warn', 'stop-upload' and 'delete' + # Please, see README.md # optional, default is 'warn' #policy_when_full => 'warn', - # images which are not viewed since delete_no_longer_viewed_files days will be deleted by the cron cleanfiles task - # if delete_no_longer_viewed_files is not set, the no longer viewed files will NOT be deleted + # Files which are not viewed since delete_no_longer_viewed_files days will be deleted by the cron cleanfiles task + # If delete_no_longer_viewed_files is not set, the no longer viewed files will NOT be deleted # optional, no default #delete_no_longer_viewed_files => 90, }; diff --git a/t/postgresql.conf b/t/postgresql.conf index 5306c8d..702da4a 100644 --- a/t/postgresql.conf +++ b/t/postgresql.conf @@ -6,6 +6,7 @@ # see http://mojolicio.us/perldoc/Mojo/Server/Hypnotoad for a full list of settings hypnotoad => { # array of IP addresses and ports you want to listen to + # you can specify a unix socket too, like 'http+unix://%2Ftmp%2Flufi.sock' listen => ['http://127.0.0.1:8081'], # if you use Lufi behind a reverse proxy like Nginx, you want to set proxy to 1 # if you use Lufi directly, let it commented @@ -17,67 +18,67 @@ clients => 1, }, - # put a way to contact you here and uncomment it - # you can put some HTML in it + # Put a way to contact you here and uncomment it + # You can put some HTML in it # MANDATORY contact => 'Contact page', - # put an URL or an email address to receive file reports and uncomment it - # it's for make reporting illegal files easy for users + # Put an URL or an email address to receive file reports and uncomment it + # It's for make reporting illegal files easy for users # MANDATORY report => 'report@example.com', - # array of random strings used to encrypt cookies + # Array of random strings used to encrypt cookies # optional, default is ['fdjsofjoihrei'], PLEASE, CHANGE IT #secrets => ['fdjsofjoihrei'], - # choose a theme. See the available themes in `themes` directory - # optional, default is 'default' + # Choose a theme. See the available themes in `themes` directory + # Optional, default is 'default' #theme => 'default', - # length of the random URL + # Length of the random URL # optional, default is 8 #length => 8, - # how many URLs will be provisioned in a batch ? + # How many URLs will be provisioned in a batch ? # optional, default is 5 #provis_step => 5, - # max number of URLs to be provisioned + # Max number of URLs to be provisioned # optional, default is 100 #provisioning => 100, - # length of the modify/delete token + # Length of the modify/delete token # optional, default is 32 #token_length => 32, - # max file size, in octets - # you can write it 100*1024*1024 + # Max file size, in octets + # You can write it 100*1024*1024 # optional, no default #max_file_size => 104857600, - # if you want to have piwik statistics, provide a piwik image tracker - # only the image tracker is allowed, no javascript + # If you want to have piwik statistics, provide a piwik image tracker + # Only the image tracker is allowed, no javascript # optional, no default #piwik_img => 'https://piwik.example.org/piwik.php?idsite=1&rec=1', - # broadcast_message which will displayed on the index page + # Broadcast_message which will displayed on the index page # optional, no default #broadcast_message => 'Maintenance', - # default time limit for files - # valid values are 0, 1, 7, 30 and 365 + # Default time limit for files + # Valid values are 0, 1, 7, 30 and 365 # optional, default is 0 (no limit) #default_delay => 0, - # number of days after which the images will be deleted, even if they were uploaded with "no delay" (or value superior to max_delay) - # a warning message will be displayed on homepage + # Number of days after which the images will be deleted, even if they were uploaded with "no delay" (or value superior to max_delay) + # A warning message will be displayed on homepage # optional, default is 0 (no limit) #max_delay => 0, - # size thresholds: if you want to define max delays for different sizes of file - # the keys are size in Bytes, you can't have 10*1000*10000 as key - # if a file is smaller than the smallest configured size, it will have a expiration delay of max_delay (see above) + # Size thresholds: if you want to define max delays for different sizes of file + # The keys are size in Bytes, you can't have 10*1000*10000 as key + # If a file is smaller than the smallest configured size, it will have a expiration delay of max_delay (see above) # optional, default is using max_delay (see above) for all sizes #delay_for_size => { # 10000000 => 90, # between 10MB and 50MB => max is 90 days, less than 10MB => max is max_delay (see above) @@ -91,18 +92,50 @@ # optional, defaut is / #prefix => '/', - # array of authorized domains for API calls. - # if you want to authorize everyone to use the API: ['*'] + # Array of authorized domains for API calls. + # If you want to authorize everyone to use the API: ['*'] # optional, no domains allowed by default #allowed_domains => ['http://1.example.com', 'http://2.example.com'], - # if set, the shortened URLs will use this domain + # If set, the shortened URLs will use this domain # optional #fixed_domain => 'example.org', + # Define a path to the upload directory, where the uploaded files will be stored + # You can define it relative to lufi directory or set an absolute path + # Remember that it has to be in a directory writable by Lufi user + # DO NOT CHANGE THIS IF FILES HAVE BEEN ALREADY UPLOADED: THEY WILL NOT BE DOWNLOADABLE ANYMORE + # optional, default is 'files' + #upload_dir => 'files', + + # Allow to add a password on files, asked before allowing to download files + # optional, default is 0 + allow_pwd_on_files => 1, + + # Force all files to be in "Burn after reading mode" + # optional, default is 0 + #force_burn_after_reading => 0, + + # If set, the files' URLs will always use this domain + # optional, no default + #fixed_domain => 'example.org', + + # Abuse reasons + # Set an integer in the abuse field of a file in the database and it will not be downloadable anymore + # The reason will be displayed to the downloader, according to the reasons you will configure here. + # optional, no default + abuse => { + 0 => 'Copyright infringment', + 1 => 'Illegal content', + }, + + ############### + # Mail settings + ############### + # Mail configuration # See https://metacpan.org/pod/Mojolicious::Plugin::Mail#EXAMPLES - # Optional, default to sendmail method with no arguments + # optional, default to sendmail method with no arguments #mail => { # # Valid values are 'sendmail' and 'smtp' # how => 'smtp', @@ -110,23 +143,27 @@ #}, # Email sender address - # Optional, default to no-reply@lufi.io + # optional, default to no-reply@lufi.io #mail_sender => 'no-reply@lufi.io', - # choose what database you want to use - # valid choices are sqlite, postgresql and mysql (all lowercase) + ############# + # DB settings + ############# + + # Choose what database you want to use + # Valid choices are sqlite, postgresql and mysql (all lowercase) # optional, default is sqlite dbtype => 'postgresql', # SQLite ONLY - only used if dbtype is set to sqlite - # define a path to the SQLite database - # you can define it relative to lufi directory or set an absolute path - # remember that it has to be in a directory writable by Lufi user + # Define a path to the SQLite database + # You can define it relative to lufi directory or set an absolute path + # Remember that it has to be in a directory writable by Lufi user # optional, default is lufi.db #db_path => 'lufi.db', # PostgreSQL ONLY - only used if dbtype is set to postgresql - # these are the credentials to access the PostgreSQL database + # These are the credentials to access the PostgreSQL database # mandatory if you choosed postgresql as dbtype pgdb => { database => 'lufi_db', @@ -140,7 +177,7 @@ }, # MySQL ONLY - only used if dbtype is set to mysql - # these are the credentials to access the MySQL database + # These are the credentials to access the MySQL database # mandatory if you choosed mysql as dbtype #mysqldb => { # database => 'lufi', @@ -149,59 +186,55 @@ # #port => 3306, # user => 'DBUSER', # pwd => 'DBPASSWORD', + # # https://metacpan.org/pod/Mojo::mysql#max_connections # # optional, default is 5 (set to 0 to disable persistent connections) # #max_connections => 5, #}, - # define a path to the upload directory, where the uploaded files will be stored - # you can define it relative to lufi directory or set an absolute path - # remember that it has to be in a directory writable by Lufi user - # DO NOT CHANGE THIS IF FILES HAVE BEEN ALREADY UPLOADED: THEY WILL NOT BE DOWNLOADABLE ANYMORE - # optional, default is 'files' - #upload_dir => 'files', + ############################################# + # LDAP settings (authentication and features) + ############################################# - # set `ldap` if you want that only authenticated users can upload files - # please note that everybody can still download files + # Set `ldap` if you want that only authenticated users can upload files + # Please note that everybody can still download files # optional, no default #ldap => { uri => 'ldap://rroemhild-test-openldap', user_tree => 'ou=people,dc=planetexpress,dc=com', bind_dn => 'cn=admin,dc=planetexpress,dc=com', bind_pwd => 'GoodNewsEveryone', user_attr => 'uid', user_filter => '' }, - # set `htpasswd` if you want to use an htpasswd file instead of ldap - # see 'man htpasswd' to know how to create such file - #htpasswd => 't/lstu.passwd', - - # if you've set ldap above, the session will last `session_duration` seconds before + # If you've set ldap above, the session will last `session_duration` seconds before # the user needs to reauthenticate # optional, default is 3600 #session_duration => 3600, - # allow to add a password on files, asked before allowing to download files - # optional, default is 0 - allow_pwd_on_files => 1, + # If you use `ldap` for authentication, you can map some attributes from LDAP to be able to access them in Lufi + # Those attributes will be accessible with: + # $c->current_user->{lufi_attribute_name} in Lufi backend files (all that is in `lib` directory) + # <%= $self->current_user->{lufi_attribute_name} %> in templates files (in `themes` directory) + # + # Define the attributes like this: `lufi_attribute_name => 'LDAP_attribute_name'` + # Note that you can’t use `username` as a Lufi attribute name: this name is reserved and will contain the login of the user + #ldap_map_attr => { + # displayname => 'cn', + # mail => 'mail' + #}, - # force all files to be in "Burn after reading mode" - # optional, default is 0 - #force_burn_after_reading => 0, + ######################### + # Htpasswd authentication + ######################### - # if set, the files' URLs will always use this domain - # optional, no default - #fixed_domain => 'example.org', + # Set `htpasswd` if you want to use an htpasswd file instead of ldap + # See 'man htpasswd' to know how to create such file + #htpasswd => 'lufi.passwd', - # abuse reasons - # set an integer in the abuse field of a file in the database and it will not be downloadable anymore - # the reason will be displayed to the downloader, according to the reasons you will configure here. - # optional, no default - abuse => { - 0 => 'Copyright infringment', - 1 => 'Illegal content', - }, + ####################### + # HTTP Headers settings + ####################### # Content-Security-Policy header that will be sent by Lufi # Set to '' to disable CSP header # https://content-security-policy.com/ provides a good documentation about CSP. # https://report-uri.com/home/generate provides a tool to generate a CSP header. - # optional, default is "base-uri 'self'; connect-src 'self'; default-src 'none'; font-src 'self'; form-action 'self'; frame-ancestors 'none'; img-src 'self' blob:; media-src blob:; script-src 'self' 'unsafe-inline' 'unsafe-eval'; style-src 'self' 'unsafe-inline'" - # the default value is good for `default` and `milligram` themes - #csp => "base-uri 'self'; connect-src 'self'; default-src 'none'; font-src 'self'; form-action 'self'; frame-ancestors 'none'; img-src 'self' blob:; media-src blob:; script-src 'self' 'unsafe-inline' 'unsafe-eval'; style-src 'self' 'unsafe-inline'" + # optional, default is "base-uri 'self'; connect-src 'self' ws://YOUR_HOST; default-src 'none'; font-src 'self'; form-action 'self'; frame-ancestors 'none'; img-src 'self' blob:; media-src blob:; script-src 'self' 'unsafe-inline' 'unsafe-eval'; style-src 'self' 'unsafe-inline'" + #csp => "", # X-Frame-Options header that will be sent by Lufi # Valid values are: 'DENY', 'SAMEORIGIN', 'ALLOW-FROM https://example.com/' @@ -228,24 +261,24 @@ # Lufi cron jobs settings ######################### - # number of days senders' IP addresses are kept in database - # after that delay, they will be deleted from database (used with script/lufi cron cleanbdd) + # Number of days senders' IP addresses are kept in database + # After that delay, they will be deleted from database (used with script/lufi cron cleanbdd) # optional, default is 365 #keep_ip_during => 365, - # max size of the files directory, in octets - # used by script/lufi cron watch to trigger an action + # Max size of the files directory, in octets + # Used by script/lufi cron watch to trigger an action # optional, no default #max_total_size => 10*1024*1024*1024, - # default action when files directory is over max_total_size (used with script/lufi cron watch) - # valid values are 'warn', 'stop-upload' and 'delete' - # please, see readme + # Default action when files directory is over max_total_size (used with script/lufi cron watch) + # Valid values are 'warn', 'stop-upload' and 'delete' + # Please, see README.md # optional, default is 'warn' #policy_when_full => 'warn', - # images which are not viewed since delete_no_longer_viewed_files days will be deleted by the cron cleanfiles task - # if delete_no_longer_viewed_files is not set, the no longer viewed files will NOT be deleted + # Files which are not viewed since delete_no_longer_viewed_files days will be deleted by the cron cleanfiles task + # If delete_no_longer_viewed_files is not set, the no longer viewed files will NOT be deleted # optional, no default #delete_no_longer_viewed_files => 90, }; diff --git a/t/sqlite.conf b/t/sqlite.conf index 03ee083..fb1b55e 100644 --- a/t/sqlite.conf +++ b/t/sqlite.conf @@ -6,6 +6,7 @@ # see http://mojolicio.us/perldoc/Mojo/Server/Hypnotoad for a full list of settings hypnotoad => { # array of IP addresses and ports you want to listen to + # you can specify a unix socket too, like 'http+unix://%2Ftmp%2Flufi.sock' listen => ['http://127.0.0.1:8081'], # if you use Lufi behind a reverse proxy like Nginx, you want to set proxy to 1 # if you use Lufi directly, let it commented @@ -17,67 +18,67 @@ clients => 1, }, - # put a way to contact you here and uncomment it - # you can put some HTML in it + # Put a way to contact you here and uncomment it + # You can put some HTML in it # MANDATORY contact => 'Contact page', - # put an URL or an email address to receive file reports and uncomment it - # it's for make reporting illegal files easy for users + # Put an URL or an email address to receive file reports and uncomment it + # It's for make reporting illegal files easy for users # MANDATORY report => 'report@example.com', - # array of random strings used to encrypt cookies + # Array of random strings used to encrypt cookies # optional, default is ['fdjsofjoihrei'], PLEASE, CHANGE IT #secrets => ['fdjsofjoihrei'], - # choose a theme. See the available themes in `themes` directory - # optional, default is 'default' + # Choose a theme. See the available themes in `themes` directory + # Optional, default is 'default' #theme => 'default', - # length of the random URL + # Length of the random URL # optional, default is 8 #length => 8, - # how many URLs will be provisioned in a batch ? + # How many URLs will be provisioned in a batch ? # optional, default is 5 #provis_step => 5, - # max number of URLs to be provisioned + # Max number of URLs to be provisioned # optional, default is 100 #provisioning => 100, - # length of the modify/delete token + # Length of the modify/delete token # optional, default is 32 #token_length => 32, - # max file size, in octets - # you can write it 100*1024*1024 + # Max file size, in octets + # You can write it 100*1024*1024 # optional, no default #max_file_size => 104857600, - # if you want to have piwik statistics, provide a piwik image tracker - # only the image tracker is allowed, no javascript + # If you want to have piwik statistics, provide a piwik image tracker + # Only the image tracker is allowed, no javascript # optional, no default #piwik_img => 'https://piwik.example.org/piwik.php?idsite=1&rec=1', - # broadcast_message which will displayed on the index page + # Broadcast_message which will displayed on the index page # optional, no default #broadcast_message => 'Maintenance', - # default time limit for files - # valid values are 0, 1, 7, 30 and 365 + # Default time limit for files + # Valid values are 0, 1, 7, 30 and 365 # optional, default is 0 (no limit) #default_delay => 0, - # number of days after which the images will be deleted, even if they were uploaded with "no delay" (or value superior to max_delay) - # a warning message will be displayed on homepage + # Number of days after which the images will be deleted, even if they were uploaded with "no delay" (or value superior to max_delay) + # A warning message will be displayed on homepage # optional, default is 0 (no limit) #max_delay => 0, - # size thresholds: if you want to define max delays for different sizes of file - # the keys are size in Bytes, you can't have 10*1000*10000 as key - # if a file is smaller than the smallest configured size, it will have a expiration delay of max_delay (see above) + # Size thresholds: if you want to define max delays for different sizes of file + # The keys are size in Bytes, you can't have 10*1000*10000 as key + # If a file is smaller than the smallest configured size, it will have a expiration delay of max_delay (see above) # optional, default is using max_delay (see above) for all sizes #delay_for_size => { # 10000000 => 90, # between 10MB and 50MB => max is 90 days, less than 10MB => max is max_delay (see above) @@ -91,18 +92,54 @@ # optional, defaut is / #prefix => '/', - # array of authorized domains for API calls. - # if you want to authorize everyone to use the API: ['*'] + # Array of authorized domains for API calls. + # If you want to authorize everyone to use the API: ['*'] # optional, no domains allowed by default #allowed_domains => ['http://1.example.com', 'http://2.example.com'], - # if set, the shortened URLs will use this domain + # If set, the shortened URLs will use this domain # optional #fixed_domain => 'example.org', + # Define a path to the upload directory, where the uploaded files will be stored + # You can define it relative to lufi directory or set an absolute path + # Remember that it has to be in a directory writable by Lufi user + # DO NOT CHANGE THIS IF FILES HAVE BEEN ALREADY UPLOADED: THEY WILL NOT BE DOWNLOADABLE ANYMORE + # optional, default is 'files' + #upload_dir => 'files', + + # Allow to add a password on files, asked before allowing to download files + # optional, default is 0 + allow_pwd_on_files => 1, + + # Force all files to be in "Burn after reading mode" + # optional, default is 0 + #force_burn_after_reading => 0, + + # If set, the files' URLs will always use this domain + # optional, no default + #fixed_domain => 'example.org', + + # Abuse reasons + # Set an integer in the abuse field of a file in the database and it will not be downloadable anymore + # The reason will be displayed to the downloader, according to the reasons you will configure here. + # optional, no default + #abuse => { + # 0 => 'Copyright infringment', + # 1 => 'Illegal content', + #}, + abuse => { + 0 => 'Copyright infringment', + 1 => 'Illegal content', + }, + + ############### + # Mail settings + ############### + # Mail configuration # See https://metacpan.org/pod/Mojolicious::Plugin::Mail#EXAMPLES - # Optional, default to sendmail method with no arguments + # optional, default to sendmail method with no arguments #mail => { # # Valid values are 'sendmail' and 'smtp' # how => 'smtp', @@ -110,23 +147,27 @@ #}, # Email sender address - # Optional, default to no-reply@lufi.io + # optional, default to no-reply@lufi.io #mail_sender => 'no-reply@lufi.io', - # choose what database you want to use - # valid choices are sqlite, postgresql and mysql (all lowercase) + ############# + # DB settings + ############# + + # Choose what database you want to use + # Valid choices are sqlite, postgresql and mysql (all lowercase) # optional, default is sqlite #dbtype => 'sqlite', # SQLite ONLY - only used if dbtype is set to sqlite - # define a path to the SQLite database - # you can define it relative to lufi directory or set an absolute path - # remember that it has to be in a directory writable by Lufi user + # Define a path to the SQLite database + # You can define it relative to lufi directory or set an absolute path + # Remember that it has to be in a directory writable by Lufi user # optional, default is lufi.db db_path => 'sqlite.db', # PostgreSQL ONLY - only used if dbtype is set to postgresql - # these are the credentials to access the PostgreSQL database + # These are the credentials to access the PostgreSQL database # mandatory if you choosed postgresql as dbtype #pgdb => { # database => 'lufi', @@ -141,7 +182,7 @@ #}, # MySQL ONLY - only used if dbtype is set to mysql - # these are the credentials to access the MySQL database + # These are the credentials to access the MySQL database # mandatory if you choosed mysql as dbtype #mysqldb => { # database => 'lufi', @@ -155,55 +196,50 @@ # #max_connections => 5, #}, - # define a path to the upload directory, where the uploaded files will be stored - # you can define it relative to lufi directory or set an absolute path - # remember that it has to be in a directory writable by Lufi user - # DO NOT CHANGE THIS IF FILES HAVE BEEN ALREADY UPLOADED: THEY WILL NOT BE DOWNLOADABLE ANYMORE - # optional, default is 'files' - #upload_dir => 'files', + ############################################# + # LDAP settings (authentication and features) + ############################################# - # set `ldap` if you want that only authenticated users can upload files - # please note that everybody can still download files + # Set `ldap` if you want that only authenticated users can upload files + # Please note that everybody can still download files # optional, no default #ldap => { uri => 'ldap://rroemhild-test-openldap', user_tree => 'ou=people,dc=planetexpress,dc=com', bind_dn => 'cn=admin,dc=planetexpress,dc=com', bind_pwd => 'GoodNewsEveryone', user_attr => 'uid', user_filter => '' }, - # set `htpasswd` if you want to use an htpasswd file instead of ldap - # see 'man htpasswd' to know how to create such file - #htpasswd => 't/lstu.passwd', - - # if you've set ldap above, the session will last `session_duration` seconds before + # If you've set ldap above, the session will last `session_duration` seconds before # the user needs to reauthenticate # optional, default is 3600 #session_duration => 3600, - # allow to add a password on files, asked before allowing to download files - # optional, default is 0 - allow_pwd_on_files => 1, + # If you use `ldap` for authentication, you can map some attributes from LDAP to be able to access them in Lufi + # Those attributes will be accessible with: + # $c->current_user->{lufi_attribute_name} in Lufi backend files (all that is in `lib` directory) + # <%= $self->current_user->{lufi_attribute_name} %> in templates files (in `themes` directory) + # + # Define the attributes like this: `lufi_attribute_name => 'LDAP_attribute_name'` + # Note that you can’t use `username` as a Lufi attribute name: this name is reserved and will contain the login of the user + #ldap_map_attr => { + # displayname => 'cn', + # mail => 'mail' + #}, - # force all files to be in "Burn after reading mode" - # optional, default is 0 - #force_burn_after_reading => 0, + ######################### + # Htpasswd authentication + ######################### - # if set, the files' URLs will always use this domain - # optional, no default - #fixed_domain => 'example.org', + # Set `htpasswd` if you want to use an htpasswd file instead of ldap + # See 'man htpasswd' to know how to create such file + #htpasswd => 't/lstu.passwd', - # abuse reasons - # set an integer in the abuse field of a file in the database and it will not be downloadable anymore - # the reason will be displayed to the downloader, according to the reasons you will configure here. - # optional, no default - abuse => { - 0 => 'Copyright infringment', - 1 => 'Illegal content', - }, + ####################### + # HTTP Headers settings + ####################### # Content-Security-Policy header that will be sent by Lufi # Set to '' to disable CSP header # https://content-security-policy.com/ provides a good documentation about CSP. # https://report-uri.com/home/generate provides a tool to generate a CSP header. - # optional, default is "base-uri 'self'; connect-src 'self'; default-src 'none'; font-src 'self'; form-action 'self'; frame-ancestors 'none'; img-src 'self' blob:; media-src blob:; script-src 'self' 'unsafe-inline' 'unsafe-eval'; style-src 'self' 'unsafe-inline'" - # the default value is good for `default` and `milligram` themes - #csp => "base-uri 'self'; connect-src 'self'; default-src 'none'; font-src 'self'; form-action 'self'; frame-ancestors 'none'; img-src 'self' blob:; media-src blob:; script-src 'self' 'unsafe-inline' 'unsafe-eval'; style-src 'self' 'unsafe-inline'" + # optional, default is "base-uri 'self'; connect-src 'self' ws://YOUR_HOST; default-src 'none'; font-src 'self'; form-action 'self'; frame-ancestors 'none'; img-src 'self' blob:; media-src blob:; script-src 'self' 'unsafe-inline' 'unsafe-eval'; style-src 'self' 'unsafe-inline'" + #csp => "", # X-Frame-Options header that will be sent by Lufi # Valid values are: 'DENY', 'SAMEORIGIN', 'ALLOW-FROM https://example.com/' @@ -230,24 +266,24 @@ # Lufi cron jobs settings ######################### - # number of days senders' IP addresses are kept in database - # after that delay, they will be deleted from database (used with script/lufi cron cleanbdd) + # Number of days senders' IP addresses are kept in database + # After that delay, they will be deleted from database (used with script/lufi cron cleanbdd) # optional, default is 365 #keep_ip_during => 365, - # max size of the files directory, in octets - # used by script/lufi cron watch to trigger an action + # Max size of the files directory, in octets + # Used by script/lufi cron watch to trigger an action # optional, no default #max_total_size => 10*1024*1024*1024, - # default action when files directory is over max_total_size (used with script/lufi cron watch) - # valid values are 'warn', 'stop-upload' and 'delete' - # please, see readme + # Default action when files directory is over max_total_size (used with script/lufi cron watch) + # Valid values are 'warn', 'stop-upload' and 'delete' + # Please, see README.md # optional, default is 'warn' #policy_when_full => 'warn', - # images which are not viewed since delete_no_longer_viewed_files days will be deleted by the cron cleanfiles task - # if delete_no_longer_viewed_files is not set, the no longer viewed files will NOT be deleted + # Files which are not viewed since delete_no_longer_viewed_files days will be deleted by the cron cleanfiles task + # If delete_no_longer_viewed_files is not set, the no longer viewed files will NOT be deleted # optional, no default #delete_no_longer_viewed_files => 90, }; diff --git a/t/test.t b/t/test.t index c512a48..4b4dedb 100644 --- a/t/test.t +++ b/t/test.t @@ -22,6 +22,7 @@ my $msg = to_json { "delay" => "0", "del_at_first_view" => 1, "id" => undef, + "zipped" => 0, "i" => 0 }; my $encrypted = '"{\\"iv\\":\\"2RGAviAeYybBqcLCmnqlgA==\\",\\"v\\":1,\\"iter\\":10000,\\"ks\\":128,\\"ts\\":64,\\"mode\\":\\"ccm\\",\\"adata\\":\\"\\",\\"cipher\\":\\"aes\\",\\"salt\\":\\"1dvKtbZ8hxA=\\",\\"ct\\":\\"w9wDZCwNSyH/yL7q1GW5fPSdi+w=\\"}"'; @@ -33,7 +34,7 @@ $encrypted_rgx =~ s@(\{|\})@\\$1@g; BEGIN { use lib 'lib'; $m = Mojolicious->new; - $cfile = Mojo::File->new($Bin, '..', 'lutim.conf'); + $cfile = Mojo::File->new($Bin, '..', 'lufi.conf'); if (defined $ENV{MOJO_CONFIG}) { $cfile = Mojo::File->new($ENV{MOJO_CONFIG}); unless (-e $cfile->to_abs) { diff --git a/themes/default/lib/Lufi/I18N/ar.po b/themes/default/lib/Lufi/I18N/ar.po index 4c9d218..4d2bd74 100644 --- a/themes/default/lib/Lufi/I18N/ar.po +++ b/themes/default/lib/Lufi/I18N/ar.po @@ -1,4 +1,6 @@ # ButterflyOfFire , 2018. #zanata +# ButterflyOfFire , 2019. #zanata +# Luc Didry , 2019. #zanata msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" @@ -6,8 +8,8 @@ msgstr "" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"PO-Revision-Date: 2018-10-31 09:02+0000\n" -"Last-Translator: ButterflyOfFire \n" +"PO-Revision-Date: 2019-08-04 05:33+0000\n" +"Last-Translator: Luc Didry \n" "Language: ar\n" "Plural-Forms: nplurals=6; plural=n==0 ? 0 : n==1 ? 1 : n==2 ? 2 : n%100>=3 " "&& n%100<=10 ? 3 : n%100>=11 ? 4 : 5;\n" @@ -16,11 +18,35 @@ msgstr "" #. ($delay) #. (max_delay) -#: themes/default/templates/index.html.ep:47 themes/default/templates/index.html.ep:56 themes/default/templates/index.html.ep:57 +#: themes/default/templates/index.html.ep:56 themes/default/templates/index.html.ep:65 themes/default/templates/index.html.ep:66 msgid "%1 days" msgstr "%1 أيام" -#: themes/default/templates/partial/index.js.ep:26 +#. (stash('ldap_user') +#: themes/default/templates/invitations/invite.mail.ep:2 +msgid "%1 invites you to send him/her files" +msgstr "" + +#. (stash('ldap_user') +#: themes/default/templates/invitations/invite.mail.ep:6 +msgid "%1 invites you to send him/her files through Lufi." +msgstr "" + +#. (stash('invitation') +#: themes/default/templates/invitations/notification_files_sent.mail.ep:4 +msgid "%1 sent you files" +msgstr "أرسل إليك %1 ملفات" + +#. (stash('invitation') +#: themes/default/templates/invitations/notification_files_sent.mail.ep:8 +msgid "%1 used your invitation to send you files:" +msgstr "لقد استخدم %1 دعوتك ليرسل لك ملفات:" + +#: lib/Lufi/Controller/Invitation.pm:159 lib/Lufi/Controller/Invitation.pm:84 themes/default/templates/invitations/my_invitations.html.ep:51 themes/default/templates/invitations/my_invitations.html.ep:52 themes/default/templates/invitations/my_invitations.html.ep:53 themes/default/templates/invitations/notification_files_sent.mail.ep:12 +msgid "%A %d %B %Y at %T" +msgstr "%A %d %B %Y على %T" + +#: themes/default/templates/partial/index.js.ep:27 msgid "(max size: XXX)" msgstr "(الحجم الأقصى: XXX)" @@ -28,7 +54,7 @@ msgstr "(الحجم الأقصى: XXX)" msgid "1 year" msgstr "سنة واحدة" -#: themes/default/templates/index.html.ep:4 themes/default/templates/index.html.ep:56 +#: themes/default/templates/index.html.ep:4 themes/default/templates/index.html.ep:65 msgid "24 hours" msgstr "24 ساعة" @@ -36,23 +62,15 @@ msgstr "24 ساعة" msgid ":" msgstr ":" -#: themes/default/templates/about.html.ep:16 -msgid "" -"A thank you with a photo of kitten on Diaspora* or " -"Mastodon " -"is cool too ;-)" -msgstr "" - #: themes/default/templates/render.html.ep:42 msgid "Abort" msgstr "إلغاء" -#: themes/default/templates/layouts/default.html.ep:48 themes/default/templates/layouts/default.html.ep:76 +#: themes/default/templates/layouts/default.html.ep:53 themes/default/templates/layouts/default.html.ep:86 msgid "About" msgstr "عن البرنامج" -#: themes/default/templates/index.html.ep:88 +#: themes/default/templates/index.html.ep:107 msgid "Add a password to file(s)" msgstr "إضافة كلمة سرية إلى الملف" @@ -62,7 +80,17 @@ msgid "" "prohibited." msgstr "" -#: themes/default/templates/about.html.ep:18 +#: themes/default/templates/partial/invitations.js.ep:3 +msgid "Are you sure you want to delete the selected invitations?" +msgstr "متأكد أنك تريد حذف كافة الدعوات المختارة؟" + +#: themes/default/templates/partial/invitations.js.ep:4 +msgid "" +"Are you sure you want to resend the invitation mail for the selected " +"invitations?" +msgstr "" + +#: themes/default/templates/about.html.ep:17 msgid "" "As Lufi is a free software licensed under of the terms of the AGPLv3, you can " @@ -75,7 +103,7 @@ msgstr "" msgid "Asking for file part XX1 of %1" msgstr "" -#: themes/default/templates/about.html.ep:21 +#: themes/default/templates/about.html.ep:20 msgid "Back to homepage" msgstr "العودة إلى الصفحة الرئيسة" @@ -83,7 +111,7 @@ msgstr "العودة إلى الصفحة الرئيسة" msgid "Bad CSRF token!" msgstr "رمز CSRF خاطئ!" -#: lib/Lufi/Controller/Auth.pm:22 lib/Lufi/Controller/Auth.pm:39 +#: lib/Lufi/Controller/Auth.pm:27 lib/Lufi/Controller/Auth.pm:49 msgid "Bad CSRF token." msgstr "رمز CSRF خاطئ." @@ -91,11 +119,15 @@ msgstr "رمز CSRF خاطئ." msgid "Click here to refresh the page and restart the download." msgstr "اضغط لتحديث الصفحة و إعادة التنزيل." -#: themes/default/templates/index.html.ep:98 +#: themes/default/templates/invitations/invite.mail.ep:8 +msgid "Click on the following URL to upload files on Lufi:" +msgstr "اضغط على الربط التالي قصد تحميل ملفات على لوفي :" + +#: themes/default/templates/index.html.ep:126 msgid "Click to open the file browser" msgstr "اضغط لفتح متصفح الملفات" -#: themes/default/templates/delays.html.ep:38 +#: themes/default/templates/delays.html.ep:38 themes/default/templates/invitations/my_invitations.html.ep:80 msgid "Close" msgstr "إغلاق" @@ -103,31 +135,47 @@ msgstr "إغلاق" msgid "Comma-separated email addresses" msgstr "عناوين البريد الإلكترونية مُقسّمة بفاصلة" -#: themes/default/templates/partial/index.js.ep:14 +#: themes/default/templates/index.html.ep:140 +msgid "Compressing zip file…" +msgstr "جارٍ ضغط ملف zip..." + +#: themes/default/templates/partial/index.js.ep:15 msgid "Copy all links to clipboard" msgstr "نسخ كافة الروابط إلى الحافظة" -#: themes/default/templates/partial/index.js.ep:17 +#: themes/default/templates/partial/index.js.ep:18 msgid "Copy to clipboard" msgstr "انسخ إلى الحافظة" -#: lib/Lufi/Controller/Files.pm:460 +#: lib/Lufi/Controller/Files.pm:507 msgid "Could not delete the file. You are not authenticated." msgstr "لا يمكن حذف الملف. إنك لم تقم بتسجيل الدخول." -#: lib/Lufi/Controller/Files.pm:442 +#: lib/Lufi/Controller/Files.pm:489 msgid "Could not find the file. Are you sure of the URL and the token?" msgstr "" -#: lib/Lufi/Controller/Files.pm:353 +#: lib/Lufi/Controller/Files.pm:400 msgid "Could not find the file. Are you sure of the URL?" -msgstr "" +msgstr "لا يمكن العثور على الملفات. متأكد مٍن الرابط؟" #: themes/default/templates/files.html.ep:28 msgid "Counter" msgstr "العداد" -#: themes/default/templates/files.html.ep:29 themes/default/templates/index.html.ep:81 +#: themes/default/templates/index.html.ep:100 +msgid "Create a zip archive with the files before uploading?" +msgstr "" + +#: themes/default/templates/invitations/my_invitations.html.ep:26 +msgid "Created at" +msgstr "أنشئ في" + +#: themes/default/templates/invitations/my_invitations.html.ep:14 +msgid "Delete" +msgstr "حذف" + +#: themes/default/templates/files.html.ep:29 themes/default/templates/index.html.ep:90 msgid "Delete at first download?" msgstr "هل تريد أن يُحذَف الملف عند أول تنزيل؟" @@ -135,7 +183,7 @@ msgstr "هل تريد أن يُحذَف الملف عند أول تنزيل؟" msgid "Delete selected files" msgstr "حذف الملفات التي تم اختيارها" -#: themes/default/templates/files.html.ep:32 themes/default/templates/partial/index.js.ep:18 +#: themes/default/templates/files.html.ep:32 themes/default/templates/partial/index.js.ep:19 msgid "Deletion link" msgstr "رابط الحذف" @@ -145,7 +193,7 @@ msgid "" "the download ends after the expiration, he will be able to get the file." msgstr "" -#: themes/default/templates/partial/index.js.ep:20 themes/default/templates/render.html.ep:28 +#: themes/default/templates/partial/index.js.ep:21 themes/default/templates/render.html.ep:28 msgid "Download" msgstr "تنزيل" @@ -153,7 +201,7 @@ msgstr "تنزيل" msgid "Download aborted." msgstr "لقد ألغِيَ التنزيل." -#: themes/default/templates/files.html.ep:27 themes/default/templates/partial/index.js.ep:19 +#: themes/default/templates/files.html.ep:27 themes/default/templates/partial/index.js.ep:20 msgid "Download link" msgstr "رابط التنزيل" @@ -166,10 +214,14 @@ msgid "" "delete the file whenever you want." msgstr "" -#: themes/default/templates/index.html.ep:94 +#: themes/default/templates/index.html.ep:122 msgid "Drop files here" msgstr "قم بوضع الملفات هنا" +#: themes/default/templates/invitations/invite.html.ep:40 +msgid "Email address of your guest" +msgstr "عنوان البريد الإلكتروني لضيفك" + #: themes/default/templates/mail.html.ep:39 msgid "Email body" msgstr "مضمون الرسالة الإلكترونية" @@ -182,35 +234,39 @@ msgstr "موضوع الرسالة الإلكترونية" msgid "Emails" msgstr "عناوين البريد الإلكترونية" -#: themes/default/templates/partial/index.js.ep:21 +#: themes/default/templates/partial/index.js.ep:22 msgid "Encrypting part XX1 of XX2" msgstr "تعمية الجزء XX1 مِن XX2" -#: lib/Lufi/Controller/Files.pm:242 +#: lib/Lufi/Controller/Files.pm:289 msgid "Error: the file existed but was deleted." msgstr "خطأ: لقد كان الملف موجودا بالفعل و لكن تم حذفه." -#: lib/Lufi/Controller/Files.pm:322 +#: lib/Lufi/Controller/Files.pm:369 msgid "Error: the file has not been sent entirely." msgstr "خطأ: لم يتم إرسال الملف كاملا." -#: lib/Lufi/Controller/Files.pm:332 +#: lib/Lufi/Controller/Files.pm:379 msgid "Error: unable to find the file. Are you sure of your URL?" msgstr "خطأ: غير قادر على العثور على الملف. هل أنت متأكد من العنوان؟" -#: themes/default/templates/partial/index.js.ep:22 +#: themes/default/templates/partial/index.js.ep:23 msgid "Expiration:" msgstr "تاريخ نهاية الصلاحية:" +#: themes/default/templates/invitations/my_invitations.html.ep:27 +msgid "Expire at" +msgstr "تنتهي صلاحيتها في" + #: themes/default/templates/files.html.ep:31 msgid "Expires at" msgstr "تنتهي صلاحيته في" #: themes/default/templates/files.html.ep:12 msgid "Export localStorage data" -msgstr "تصدير بيانات مساحة التخزين المحلية" +msgstr "" -#: lib/Lufi/Controller/Files.pm:424 +#: lib/Lufi/Controller/Files.pm:471 msgid "File deleted" msgstr "تم حذف الملف" @@ -218,23 +274,45 @@ msgstr "تم حذف الملف" msgid "File name" msgstr "اسم الملف" -#: themes/default/templates/index.html.ep:71 +#: themes/default/templates/invitations/my_invitations.html.ep:61 +msgid "Files" +msgstr "الملفات" + +#: themes/default/templates/index.html.ep:80 msgid "Files deleted at first download" msgstr "تم حذف الملفات عند أول تنزيل" +#: themes/default/templates/invitations/my_invitations.html.ep:28 +msgid "Files sent at" +msgstr "تم إرسال الملف في" + +#: themes/default/templates/partial/invitations.js.ep:8 +msgid "Files sent in invitation XX1 by XX2" +msgstr "" + #: themes/default/templates/partial/render.js.ep:8 msgid "Get the file" msgstr "تحصّل على الملف" -#: themes/default/templates/about.html.ep:19 +#: themes/default/templates/about.html.ep:18 msgid "" "Get the source code on the official repository or on its Github mirror" msgstr "" -"تحصّل على الشيفرة المصدرية من المستودع الرسمي أو على المستودع المرآة على غيت هب" + +#: themes/default/templates/invitations/my_invitations.html.ep:24 +msgid "Guest mail" +msgstr "البريد الإلكتروني للضيف" + +#. (ucfirst(stash('invitation') +#: themes/default/templates/invitations/notification_files_sent.mail.ep:6 +msgid "Hello %1," +msgstr "سلام %1," + +#: themes/default/templates/invitations/invite.mail.ep:4 +msgid "Hello," +msgstr "سلام،" #: themes/default/templates/partial/mail.js.ep:35 msgid "Hello,\\n\\nHere's some files I want to share with you:\\n" @@ -244,11 +322,15 @@ msgstr "السلام،\\n\\nها هي بعض الملفات التي أحببت msgid "Here's some files" msgstr "ها هي بعض الملفات" -#: themes/default/templates/partial/index.js.ep:24 +#: themes/default/templates/partial/invitations.js.ep:7 +msgid "Hide hidden invitations" +msgstr "اخف الدعوات المخفية" + +#: themes/default/templates/partial/index.js.ep:25 msgid "Hit Enter, then Ctrl+C to copy all the download links" msgstr "اضغط على Enter، ثم Ctrl+C لنسخ جميع روابط التحميل" -#: themes/default/templates/partial/index.js.ep:23 +#: themes/default/templates/partial/index.js.ep:24 msgid "Hit Enter, then Ctrl+C to copy the download link" msgstr "اضغط على Enter، ثم Ctrl+C لنسخ رابط التحميل" @@ -256,7 +338,11 @@ msgstr "اضغط على Enter، ثم Ctrl+C لنسخ رابط التحميل" msgid "How does it work?" msgstr "كيف يعمل؟" -#: themes/default/templates/about.html.ep:17 +#: themes/default/templates/invitations/invite.html.ep:46 +msgid "How many days would you like the invitation to be valid?" +msgstr "" + +#: themes/default/templates/about.html.ep:16 msgid "How to install the software on my server?" msgstr "كيف يمكنني تثبيت البرنامج على خادومي؟" @@ -278,9 +364,9 @@ msgstr "" #: themes/default/templates/files.html.ep:14 msgid "Import localStorage data" -msgstr "استيراد بيانات مساحة التخزين المحلية" +msgstr "" -#: themes/default/templates/index.html.ep:44 +#: themes/default/templates/index.html.ep:53 msgid "Important: more information on delays" msgstr "هام: مزيد من المعلومات عن التأخير" @@ -288,6 +374,24 @@ msgstr "هام: مزيد من المعلومات عن التأخير" msgid "Information about delays" msgstr "معلومات حول التأخير" +#: themes/default/templates/invitations/my_invitations.html.ep:12 +msgid "Invert selection" +msgstr "" + +#. ($i->guest_mail, $url) +#: lib/Lufi/Controller/Invitation.pm:171 +msgid "Invitation resent to %1.
URL: %2" +msgstr "" + +#. ($invitation->guest_mail, $url) +#: lib/Lufi/Controller/Invitation.pm:87 +msgid "Invitation sent to %1.
URL: %2" +msgstr "" + +#: themes/default/templates/invitations/invite.html.ep:27 themes/default/templates/layouts/default.html.ep:36 themes/default/templates/layouts/default.html.ep:69 +msgid "Invite a guest" +msgstr "أدع ضيفا" + #: themes/default/templates/partial/render.js.ep:6 msgid "" "It seems that the key in your URL is incorrect. Please, verify your URL." @@ -298,7 +402,7 @@ msgstr "" msgid "Javascript is disabled. You won't be able to use Lufi." msgstr "الجافا سكريبت غير مفعل. ليس بإمكانك استخدام لوفي Lufi." -#: themes/default/templates/layouts/default.html.ep:40 themes/default/templates/layouts/default.html.ep:68 +#: themes/default/templates/layouts/default.html.ep:44 themes/default/templates/layouts/default.html.ep:46 themes/default/templates/layouts/default.html.ep:77 themes/default/templates/layouts/default.html.ep:79 msgid "Language" msgstr "اللغة" @@ -306,7 +410,7 @@ msgstr "اللغة" msgid "Login" msgstr "تسجيل الدخول" -#: themes/default/templates/layouts/default.html.ep:53 themes/default/templates/layouts/default.html.ep:78 +#: themes/default/templates/layouts/default.html.ep:58 themes/default/templates/layouts/default.html.ep:91 msgid "Logout" msgstr "الخروج" @@ -319,16 +423,29 @@ msgstr "" msgid "Mail" msgstr "البريد" -#: themes/default/templates/files.html.ep:3 themes/default/templates/layouts/default.html.ep:34 themes/default/templates/layouts/default.html.ep:62 +#: themes/default/templates/files.html.ep:3 themes/default/templates/layouts/default.html.ep:34 themes/default/templates/layouts/default.html.ep:67 msgid "My files" msgstr "ملفاتي" +#: themes/default/templates/invitations/my_invitations.html.ep:5 themes/default/templates/layouts/default.html.ep:37 themes/default/templates/layouts/default.html.ep:70 +msgid "My invitations" +msgstr "دعواتي" + +#: themes/default/templates/invitations/notification_files_sent.mail.ep:17 +msgid "" +"NB: this list includes the list of files that have already been sent to you." +msgstr "" + +#: themes/default/templates/index.html.ep:115 +msgid "Name of the zip file" +msgstr "اسم الملف المضغوط zip" + #. (format_bytes($json->{size}) -#: lib/Lufi/Controller/Files.pm:79 +#: lib/Lufi/Controller/Files.pm:108 msgid "No enough space available on the server for this file (size: %1)." msgstr "لا توجد مساحة كافية متوفرة على السيرفر لاستضافة الملف (الحجم: %1)." -#: themes/default/templates/partial/files.js.ep:10 themes/default/templates/partial/index.js.ep:27 +#: themes/default/templates/partial/files.js.ep:10 themes/default/templates/partial/index.js.ep:28 msgid "No expiration delay" msgstr "دون تاريخ لنهاية الصلاحية" @@ -339,7 +456,7 @@ msgid "" "this list." msgstr "" -#: themes/default/templates/index.html.ep:87 themes/default/templates/login.html.ep:21 themes/default/templates/render.html.ep:26 +#: themes/default/templates/index.html.ep:106 themes/default/templates/login.html.ep:21 themes/default/templates/render.html.ep:26 msgid "Password" msgstr "الكلمة السرية" @@ -354,7 +471,7 @@ msgid "" "decrypt all parts before you can get it." msgstr "" -#: lib/Lufi/Controller/Auth.pm:28 +#: lib/Lufi/Controller/Auth.pm:38 msgid "" "Please, check your credentials or your right to access this service: unable " "to authenticate." @@ -368,19 +485,39 @@ msgstr "الخصوصية" msgid "Purge expired files from localStorage" msgstr "تطهير الملفات منتهية الصلاحية من مساحة التخزين المحلية" -#: themes/default/templates/layouts/default.html.ep:31 themes/default/templates/layouts/default.html.ep:59 +#: themes/default/templates/invitations/notification_files_sent.mail.ep:20 +msgid "Regards," +msgstr "تحياتي،" + +#: themes/default/templates/invitations/invite.mail.ep:15 +msgid "Regards." +msgstr "تحياتي." + +#: themes/default/templates/layouts/default.html.ep:31 themes/default/templates/layouts/default.html.ep:64 msgid "Report file" msgstr "الإبلاغ عن ملف" +#: themes/default/templates/invitations/my_invitations.html.ep:15 +msgid "Resend invitation mail" +msgstr "أعد إرسال رسالة الدعوة" + +#: themes/default/templates/invitations/my_invitations.html.ep:9 +msgid "Rows in purple mean that the invitations have expired." +msgstr "" + #: themes/default/templates/files.html.ep:9 msgid "" "Rows in red mean that the files have expired and are no longer available." msgstr "تعني الأعمدة باللون الأحمر انتهاء صلاحية الملفات وأنها لم تعد متوفرة." -#: themes/default/templates/partial/index.js.ep:25 +#: themes/default/templates/partial/index.js.ep:26 msgid "Send all links by email" msgstr "إرسال كافة الروابط عبر البريد الإلكتروني" +#: themes/default/templates/invitations/invite.html.ep:50 +msgid "Send the invitation" +msgstr "ابعث الدعوة" + #: themes/default/templates/mail.html.ep:46 msgid "Send with this server" msgstr "إرسال عبر هذا الخادوم" @@ -389,7 +526,7 @@ msgstr "إرسال عبر هذا الخادوم" msgid "Send with your own mail software" msgstr "إرسال باستخدام برنامج البريد الخاص بك" -#: themes/default/templates/partial/index.js.ep:28 +#: themes/default/templates/partial/index.js.ep:29 msgid "" "Sending part XX1 of XX2. Please, be patient, the progress bar can take a " "while to move." @@ -400,18 +537,47 @@ msgstr "" msgid "Share your files in total privacy on %1" msgstr "شارك ملفاتك بخصوصية تامة على %1" -#: themes/default/templates/layouts/default.html.ep:36 themes/default/templates/layouts/default.html.ep:64 themes/default/templates/login.html.ep:27 themes/default/templates/logout.html.ep:17 +#: themes/default/templates/invitations/my_invitations.html.ep:13 themes/default/templates/partial/invitations.js.ep:9 +msgid "Show hidden invitations" +msgstr "أظهر الدعوات المخفية" + +#: themes/default/templates/partial/render.js.ep:11 +msgid "Show zip content" +msgstr "أظهر محتوى الملف المضغوط zip" + +#: themes/default/templates/layouts/default.html.ep:40 themes/default/templates/layouts/default.html.ep:73 themes/default/templates/login.html.ep:28 themes/default/templates/logout.html.ep:17 msgid "Signin" msgstr "تسجيل الدخول" -#: themes/default/templates/index.html.ep:37 +#: lib/Lufi/Controller/Invitation.pm:283 themes/default/templates/invitations/exception.html.ep:16 +msgid "" +"Sorry, the invitation doesn’t exist. Are you sure you are on the right URL?" +msgstr "" + +#: themes/default/templates/index.html.ep:46 msgid "Sorry, the uploading is currently disabled. Please try again later." msgstr "عذراً، التحميل معطل حاليا. يرجى إعادة المحاولة لاحقًا." -#: lib/Lufi/Controller/Files.pm:53 +#: lib/Lufi/Controller/Files.pm:82 msgid "Sorry, uploading is disabled." msgstr "عذرًا ، التحميل معطل." +#: themes/default/templates/invitations/exception.html.ep:7 +msgid "Sorry, your invitation has expired or has been deleted." +msgstr "المعذرة، إنّ دعوتك منتهية الصلاحية أو تم حذفها." + +#. ($invit->ldap_user_mail) +#: lib/Lufi/Controller/Files.pm:122 +msgid "" +"Sorry, your invitation has expired or has been deleted. Please contact %1 to " +"have another invitation." +msgstr "" + +#. ($invitation->ldap_user_mail) +#: lib/Lufi/Controller/Invitation.pm:276 +msgid "The URLs of your files have been sent by email to %1." +msgstr "" + #: themes/default/templates/about.html.ep:7 msgid "" "The administrator can only see the file's name, its size and its mimetype " @@ -436,7 +602,12 @@ msgstr "لا يمكن أن يُترك مضمون الرسالة فارغًا." msgid "The email subject can't be empty." msgstr "لا يمكن أن يُترك موضوع الرسالة فارغًا." -#: lib/Lufi/Controller/Files.pm:421 +#. ($expire_at, $max_expire_at) +#: lib/Lufi/Controller/Invitation.pm:51 +msgid "The expiration delay (%1) is not between 1 and %2 days." +msgstr "" + +#: lib/Lufi/Controller/Files.pm:468 msgid "The file has already been deleted" msgstr "لقد تم حذف الملف مِن قبلُ" @@ -453,10 +624,43 @@ msgstr "" msgid "The following email addresses are not valid: %1" msgstr "عناوين البريد الإلكترونية التالية غير صحيحة: %1" -#: themes/default/templates/partial/index.js.ep:15 +#. ($guest_mail) +#: lib/Lufi/Controller/Invitation.pm:48 +msgid "The guest email address (%1) is unvalid." +msgstr "" + +#. ($i->token, $i->guest_mail) +#: lib/Lufi/Controller/Invitation.pm:150 +msgid "" +"The invitation %1 can’t be resent: %2 has already sent files.
Please " +"create a new invitation." +msgstr "" + +#. ($i->token) +#: lib/Lufi/Controller/Invitation.pm:130 +msgid "The invitation %1 has been deleted." +msgstr "" + +#. (stash('user_mail') +#: themes/default/templates/invitations/invite.html.ep:34 +msgid "The invitation mail will be send from your email address (%1)." +msgstr "" + +#: themes/default/templates/partial/index.js.ep:16 msgid "The link(s) has been copied to your clipboard" msgstr "لقد تم نسخ الرابط أو الروابط إلى الحافظة" +#. (stash('invitation') +#: themes/default/templates/index.html.ep:30 +msgid "" +"The link(s) of your file(s) will automatically be sent by mail to %1 (%2)" +msgstr "" + +#. (stash('ldap_user') +#: themes/default/templates/invitations/invite.mail.ep:11 +msgid "The links of your file(s) will automatically be sent by mail to %1." +msgstr "" + #: lib/Lufi/Controller/Mail.pm:97 msgid "The mail has been sent." msgstr "لقد تم إرسال الرسالة الإلكترونية." @@ -464,23 +668,29 @@ msgstr "لقد تم إرسال الرسالة الإلكترونية." #: themes/default/templates/about.html.ep:15 msgid "" "The original (and only for now) author is Luc Didry. If you want to support him, you can do it " -"via Tipeee " -"or via Liberapay." +"class=\"classic\">Luc Didry." msgstr "" -#: lib/Lufi/Controller/Files.pm:189 +#: lib/Lufi/Controller/Files.pm:236 msgid "" "The server was unable to find the file record to add your file part to. " "Please, contact the administrator." msgstr "" -#: lib/Lufi/Controller/Files.pm:248 +#: lib/Lufi/Controller/Files.pm:295 msgid "" "This file has been deactivated by the admins. Contact them to know why." msgstr "لقد تم تعطيل الملف من طرف المشرفين. الرجاء الإتصال بهم لمعرفة السبب." +#: themes/default/templates/invitations/my_invitations.html.ep:46 themes/default/templates/partial/invitations.js.ep:6 +msgid "This invitation is normally hidden" +msgstr "" + +#. (stash('expires') +#: themes/default/templates/invitations/invite.mail.ep:13 +msgid "This invitation is valid until %1." +msgstr "" + #: themes/default/templates/delays.html.ep:10 msgid "" "This server sets limitations according to the file size. The expiration " @@ -488,40 +698,52 @@ msgid "" "following limitations:" msgstr "" -#: themes/default/templates/partial/index.js.ep:16 +#: themes/default/templates/invitations/my_invitations.html.ep:16 +msgid "Toggle visibility" +msgstr "" + +#: themes/default/templates/invitations/my_invitations.html.ep:25 +msgid "URL" +msgstr "الرابط" + +#: themes/default/templates/partial/index.js.ep:17 msgid "Unable to copy the link(s) to your clipboard" msgstr "تعذر نسخ كافة الروابط إلى الحافظة" #. ($short) -#: lib/Lufi/Controller/Files.pm:392 +#: lib/Lufi/Controller/Files.pm:439 msgid "" "Unable to get counter for %1. The file does not exists. It will be removed " "from your localStorage." msgstr "" #. ($short) -#: lib/Lufi/Controller/Files.pm:382 +#: lib/Lufi/Controller/Files.pm:429 msgid "Unable to get counter for %1. The token is invalid." msgstr "لا يمكن جلب عداد %1. إن الرمز غير صالح." #. ($short) -#: lib/Lufi/Controller/Files.pm:402 +#: lib/Lufi/Controller/Files.pm:449 msgid "Unable to get counter for %1. You are not authenticated." msgstr "لا يمكن جلب عداد %1. إنك لم تقم بتسجيل الدخول." -#: themes/default/templates/layouts/default.html.ep:33 themes/default/templates/layouts/default.html.ep:61 +#: themes/default/templates/layouts/default.html.ep:33 themes/default/templates/layouts/default.html.ep:66 msgid "Upload files" msgstr "إرسال ملفات" +#: themes/default/templates/index.html.ep:145 +msgid "Upload generated zip file" +msgstr "" + #: themes/default/templates/files.html.ep:30 msgid "Uploaded at" msgstr "أرسِل بتاريخ" -#: themes/default/templates/index.html.ep:103 +#: themes/default/templates/index.html.ep:153 msgid "Uploaded files" msgstr "الملفات المُرسَلة" -#: themes/default/templates/partial/index.js.ep:29 +#: themes/default/templates/partial/index.js.ep:30 msgid "Websocket communication error" msgstr "خطأ في اتصال ويب سوكيت" @@ -533,6 +755,16 @@ msgstr "ماذا نعني بـ لوفي Lufi؟" msgid "Who wrote this software?" msgstr "من قام بتطوير هذا البرنامج؟" +#: themes/default/templates/partial/index.js.ep:13 +msgid "XXX file has been added to upload queue." +msgstr "" + +#: themes/default/templates/invitations/invite.html.ep:30 +msgid "" +"You can invite someone to send you files through this Lufi instance even if " +"they don’t have an account on it." +msgstr "" + #: themes/default/templates/about.html.ep:11 msgid "" "You can see the list of your files by clicking on the \"My files\" link at " @@ -543,7 +775,7 @@ msgstr "" #: lib/Lufi/Controller/Mail.pm:42 msgid "You can't add URLs that are not related to this instance." -msgstr "لا يمكنك إضافة روابط غير متعلقة بمثيل الخادوم هذا." +msgstr "" #: themes/default/templates/about.html.ep:8 msgid "" @@ -560,8 +792,6 @@ msgid "" "You don't seem to have a key in your URL. You won't be able to decrypt the " "file. Download canceled." msgstr "" -"يبدو أنّ رابطك لا يحتوي على مفتاح. لذا لن يكون بإمكانك فك تشفير الملف. جارٍ " -"إلغاء التنزيل." #: themes/default/templates/partial/render.js.ep:7 msgid "" @@ -571,7 +801,7 @@ msgstr "" "لقد حاولتَ ترك هذه الصفحة. سيتم إلغاء تنزيل الملف الذي أنت بصدد تنزيله. هل " "أنت متأكد؟" -#: themes/default/templates/partial/index.js.ep:13 +#: themes/default/templates/partial/index.js.ep:14 msgid "" "You have attempted to leave this page. The upload will be canceled. Are you " "sure?" @@ -587,19 +817,19 @@ msgstr "لقد تم تسجيل خروجك بنجاح." msgid "You must give email addresses." msgstr "يجب إدخال عناوين للبريد الإلكتروني." -#: themes/default/templates/index.html.ep:29 -#, fuzzy +#: themes/default/templates/index.html.ep:38 msgid "" -"Your browser has not enough entropy to generate a strong encryption key. " -"Please wait (it's better if you do things on your computer while waiting)." +"Your browser does not have enough entropy to generate a strong encryption " +"key. Please wait (it's better if you do things on your computer while " +"waiting)." msgstr "لا يحتوي متصفحك على قدر كافٍ من الإنتروبيا لإنشاء مفتاح تشفير قوي." #. (format_bytes($json->{size}) -#: lib/Lufi/Controller/Files.pm:66 +#: lib/Lufi/Controller/Files.pm:95 msgid "Your file is too big: %1 (maximum size allowed: %2)" msgstr "الملف كبير جدًا: %1 (الحد الأقصى المسموح به للحجم هو: %2)" -#: lib/Lufi/Controller/Files.pm:304 +#: lib/Lufi/Controller/Files.pm:351 msgid "Your password is not valid. Please refresh the page to retry." msgstr "الكلمة السرية غير صالحة. يرجى تحديث الصفحة لإعادة المحاولة." @@ -617,6 +847,10 @@ msgstr "" msgid "deadline: " msgstr "آخر أجل: " +#: themes/default/templates/partial/invitations.js.ep:5 +msgid "expires on XXX" +msgstr "منتهية صلاحيتها في XXX" + #. (format_bytes($keys[$i]) #: themes/default/templates/delays.html.ep:26 msgid "for %1 and more, the file will be kept %2 day(s)" @@ -631,6 +865,11 @@ msgstr "" msgid "no time limit" msgstr "بلا حدود" -#: themes/default/templates/index.html.ep:96 +#: themes/default/templates/index.html.ep:124 msgid "or" msgstr "أو" + +#. ($e->{name}, format_bytes($e->{size}) +#: themes/default/templates/invitations/notification_files_sent.mail.ep:12 +msgid "— %1 (%2), that will expire on %3" +msgstr "" diff --git a/themes/default/lib/Lufi/I18N/ca.po b/themes/default/lib/Lufi/I18N/ca.po index 4aee29b..0ee9db9 100644 --- a/themes/default/lib/Lufi/I18N/ca.po +++ b/themes/default/lib/Lufi/I18N/ca.po @@ -4,7 +4,8 @@ # FIRST AUTHOR , YEAR. # xd , 2017. # Luc Didry , 2018. #zanata -# Quentí , 2018. #zanata +# Quentí, 2018. #zanata +# Luc Didry , 2019. #zanata msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" @@ -12,8 +13,8 @@ msgstr "" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"PO-Revision-Date: 2018-10-28 06:24+0000\n" -"Last-Translator: Quentí \n" +"PO-Revision-Date: 2019-04-19 04:55+0000\n" +"Last-Translator: Luc Didry \n" "Language-Team: català; valencià <>\n" "Plural-Forms: nplurals=2; plural=(n != 1);\n" "X-Generator: Zanata 4.6.2\n" @@ -21,11 +22,35 @@ msgstr "" #. ($delay) #. (max_delay) -#: themes/default/templates/index.html.ep:47 themes/default/templates/index.html.ep:56 themes/default/templates/index.html.ep:57 +#: themes/default/templates/index.html.ep:56 themes/default/templates/index.html.ep:65 themes/default/templates/index.html.ep:66 msgid "%1 days" msgstr "%1 dies" -#: themes/default/templates/partial/index.js.ep:26 +#. (stash('ldap_user') +#: themes/default/templates/invitations/invite.mail.ep:2 +msgid "%1 invites you to send him/her files" +msgstr "" + +#. (stash('ldap_user') +#: themes/default/templates/invitations/invite.mail.ep:6 +msgid "%1 invites you to send him/her files through Lufi." +msgstr "" + +#. (stash('invitation') +#: themes/default/templates/invitations/notification_files_sent.mail.ep:4 +msgid "%1 sent you files" +msgstr "" + +#. (stash('invitation') +#: themes/default/templates/invitations/notification_files_sent.mail.ep:8 +msgid "%1 used your invitation to send you files:" +msgstr "" + +#: lib/Lufi/Controller/Invitation.pm:159 lib/Lufi/Controller/Invitation.pm:84 themes/default/templates/invitations/my_invitations.html.ep:51 themes/default/templates/invitations/my_invitations.html.ep:52 themes/default/templates/invitations/my_invitations.html.ep:53 themes/default/templates/invitations/notification_files_sent.mail.ep:12 +msgid "%A %d %B %Y at %T" +msgstr "" + +#: themes/default/templates/partial/index.js.ep:27 msgid "(max size: XXX)" msgstr "(mida max: XXX)" @@ -33,7 +58,7 @@ msgstr "(mida max: XXX)" msgid "1 year" msgstr "1 any" -#: themes/default/templates/index.html.ep:4 themes/default/templates/index.html.ep:56 +#: themes/default/templates/index.html.ep:4 themes/default/templates/index.html.ep:65 msgid "24 hours" msgstr "24 hores" @@ -41,27 +66,15 @@ msgstr "24 hores" msgid ":" msgstr ":" -#: themes/default/templates/about.html.ep:16 -msgid "" -"A thank you with a photo of kitten on Diaspora* or " -"Mastodon " -"is cool too ;-)" -msgstr "" -"Un agraïment amb la foto d'un gatet a Diaspora* o " -"a Mastodon " -"també fa goig ;-)" - #: themes/default/templates/render.html.ep:42 msgid "Abort" msgstr "Avorta" -#: themes/default/templates/layouts/default.html.ep:48 themes/default/templates/layouts/default.html.ep:76 +#: themes/default/templates/layouts/default.html.ep:53 themes/default/templates/layouts/default.html.ep:86 msgid "About" msgstr "Quant a" -#: themes/default/templates/index.html.ep:88 +#: themes/default/templates/index.html.ep:107 msgid "Add a password to file(s)" msgstr "Afegir una contrasenya al(s) fitxer(s)" @@ -71,7 +84,17 @@ msgid "" "prohibited." msgstr "" -#: themes/default/templates/about.html.ep:18 +#: themes/default/templates/partial/invitations.js.ep:3 +msgid "Are you sure you want to delete the selected invitations?" +msgstr "" + +#: themes/default/templates/partial/invitations.js.ep:4 +msgid "" +"Are you sure you want to resend the invitation mail for the selected " +"invitations?" +msgstr "" + +#: themes/default/templates/about.html.ep:17 msgid "" "As Lufi is a free software licensed under of the terms of the AGPLv3, you can " @@ -89,7 +112,7 @@ msgstr "" msgid "Asking for file part XX1 of %1" msgstr "Demanem la part XX1 de %1 del fitxer" -#: themes/default/templates/about.html.ep:21 +#: themes/default/templates/about.html.ep:20 msgid "Back to homepage" msgstr "Retorna a la pàgina d'inici" @@ -97,7 +120,7 @@ msgstr "Retorna a la pàgina d'inici" msgid "Bad CSRF token!" msgstr "Mal testimoni CSRF!" -#: lib/Lufi/Controller/Auth.pm:22 lib/Lufi/Controller/Auth.pm:39 +#: lib/Lufi/Controller/Auth.pm:27 lib/Lufi/Controller/Auth.pm:49 msgid "Bad CSRF token." msgstr "Token CSRF incorrect." @@ -105,11 +128,15 @@ msgstr "Token CSRF incorrect." msgid "Click here to refresh the page and restart the download." msgstr "Premeu aquí per tal de refrescar la pàgina i reiniciar la descàrrega" -#: themes/default/templates/index.html.ep:98 +#: themes/default/templates/invitations/invite.mail.ep:8 +msgid "Click on the following URL to upload files on Lufi:" +msgstr "" + +#: themes/default/templates/index.html.ep:126 msgid "Click to open the file browser" msgstr "Premeu per obrir la selecció de fitxer." -#: themes/default/templates/delays.html.ep:38 +#: themes/default/templates/delays.html.ep:38 themes/default/templates/invitations/my_invitations.html.ep:80 msgid "Close" msgstr "Tanca" @@ -117,23 +144,27 @@ msgstr "Tanca" msgid "Comma-separated email addresses" msgstr "Adreces de correu electrònic separades per comes" -#: themes/default/templates/partial/index.js.ep:14 +#: themes/default/templates/index.html.ep:140 +msgid "Compressing zip file…" +msgstr "" + +#: themes/default/templates/partial/index.js.ep:15 msgid "Copy all links to clipboard" msgstr "Copia tots els enllaços al porta-retalls" -#: themes/default/templates/partial/index.js.ep:17 +#: themes/default/templates/partial/index.js.ep:18 msgid "Copy to clipboard" msgstr "Copia al porta-retalls" -#: lib/Lufi/Controller/Files.pm:460 +#: lib/Lufi/Controller/Files.pm:507 msgid "Could not delete the file. You are not authenticated." msgstr "No es pot esborrar el fitxer. No esteu autenticat." -#: lib/Lufi/Controller/Files.pm:442 +#: lib/Lufi/Controller/Files.pm:489 msgid "Could not find the file. Are you sure of the URL and the token?" msgstr "No es troba el fitxer. Esteu segur de la URL i el testimoni?" -#: lib/Lufi/Controller/Files.pm:353 +#: lib/Lufi/Controller/Files.pm:400 msgid "Could not find the file. Are you sure of the URL?" msgstr "No trobo el fitxer. Esteu segurs de la URL?" @@ -141,7 +172,19 @@ msgstr "No trobo el fitxer. Esteu segurs de la URL?" msgid "Counter" msgstr "Comptador" -#: themes/default/templates/files.html.ep:29 themes/default/templates/index.html.ep:81 +#: themes/default/templates/index.html.ep:100 +msgid "Create a zip archive with the files before uploading?" +msgstr "" + +#: themes/default/templates/invitations/my_invitations.html.ep:26 +msgid "Created at" +msgstr "" + +#: themes/default/templates/invitations/my_invitations.html.ep:14 +msgid "Delete" +msgstr "" + +#: themes/default/templates/files.html.ep:29 themes/default/templates/index.html.ep:90 msgid "Delete at first download?" msgstr "Eliminar-lo a la primera descàrrega?" @@ -149,7 +192,7 @@ msgstr "Eliminar-lo a la primera descàrrega?" msgid "Delete selected files" msgstr "Suprimir els fitxers seleccionats" -#: themes/default/templates/files.html.ep:32 themes/default/templates/partial/index.js.ep:18 +#: themes/default/templates/files.html.ep:32 themes/default/templates/partial/index.js.ep:19 msgid "Deletion link" msgstr "Enllaç per a eliminació" @@ -162,7 +205,7 @@ msgstr "" "l'expiració i la descàrrega acaba després de l'expiració, encara podrà " "obtenir el fitxer." -#: themes/default/templates/partial/index.js.ep:20 themes/default/templates/render.html.ep:28 +#: themes/default/templates/partial/index.js.ep:21 themes/default/templates/render.html.ep:28 msgid "Download" msgstr "Descàrrega" @@ -170,7 +213,7 @@ msgstr "Descàrrega" msgid "Download aborted." msgstr "Descàrrega avortada." -#: themes/default/templates/files.html.ep:27 themes/default/templates/partial/index.js.ep:19 +#: themes/default/templates/files.html.ep:27 themes/default/templates/partial/index.js.ep:20 msgid "Download link" msgstr "Enllaç per a descàrrega" @@ -189,10 +232,14 @@ msgstr "" "enllaç per a l'eliminació, que us permet eliminar el fitxer del servidor " "quan vulgueu. " -#: themes/default/templates/index.html.ep:94 +#: themes/default/templates/index.html.ep:122 msgid "Drop files here" msgstr "Deixeu anar aquí fitxers." +#: themes/default/templates/invitations/invite.html.ep:40 +msgid "Email address of your guest" +msgstr "" + #: themes/default/templates/mail.html.ep:39 msgid "Email body" msgstr "Cos del correu electrònic" @@ -205,26 +252,30 @@ msgstr "Assumpte del correu electrònic" msgid "Emails" msgstr "correus electrònics" -#: themes/default/templates/partial/index.js.ep:21 +#: themes/default/templates/partial/index.js.ep:22 msgid "Encrypting part XX1 of XX2" msgstr "S'està xifrant la part XX1 de XX2" -#: lib/Lufi/Controller/Files.pm:242 +#: lib/Lufi/Controller/Files.pm:289 msgid "Error: the file existed but was deleted." msgstr "Error: el fitxer existia però va ser eliminat." -#: lib/Lufi/Controller/Files.pm:322 +#: lib/Lufi/Controller/Files.pm:369 msgid "Error: the file has not been sent entirely." msgstr "Error: el fitxer no s'ha enviat del tot." -#: lib/Lufi/Controller/Files.pm:332 +#: lib/Lufi/Controller/Files.pm:379 msgid "Error: unable to find the file. Are you sure of your URL?" msgstr "Error: no trobo el fitxer. Esteu segur de la URL ?" -#: themes/default/templates/partial/index.js.ep:22 +#: themes/default/templates/partial/index.js.ep:23 msgid "Expiration:" msgstr "Expiració:" +#: themes/default/templates/invitations/my_invitations.html.ep:27 +msgid "Expire at" +msgstr "" + #: themes/default/templates/files.html.ep:31 msgid "Expires at" msgstr "Expira el" @@ -233,7 +284,7 @@ msgstr "Expira el" msgid "Export localStorage data" msgstr "Exporta dades a l'emmagatzematge local" -#: lib/Lufi/Controller/Files.pm:424 +#: lib/Lufi/Controller/Files.pm:471 msgid "File deleted" msgstr "Fitxer eliminat" @@ -241,15 +292,27 @@ msgstr "Fitxer eliminat" msgid "File name" msgstr "Nom de fitxer" -#: themes/default/templates/index.html.ep:71 +#: themes/default/templates/invitations/my_invitations.html.ep:61 +msgid "Files" +msgstr "" + +#: themes/default/templates/index.html.ep:80 msgid "Files deleted at first download" msgstr "Fitxers suprimits a la primera descàrrega" +#: themes/default/templates/invitations/my_invitations.html.ep:28 +msgid "Files sent at" +msgstr "" + +#: themes/default/templates/partial/invitations.js.ep:8 +msgid "Files sent in invitation XX1 by XX2" +msgstr "" + #: themes/default/templates/partial/render.js.ep:8 msgid "Get the file" msgstr "Obté el fitxer" -#: themes/default/templates/about.html.ep:19 +#: themes/default/templates/about.html.ep:18 msgid "" "Get the source code on the official repository or on its repositori oficial o a la seva rèplica a Github" +#: themes/default/templates/invitations/my_invitations.html.ep:24 +msgid "Guest mail" +msgstr "" + +#. (ucfirst(stash('invitation') +#: themes/default/templates/invitations/notification_files_sent.mail.ep:6 +msgid "Hello %1," +msgstr "" + +#: themes/default/templates/invitations/invite.mail.ep:4 +msgid "Hello," +msgstr "" + #: themes/default/templates/partial/mail.js.ep:35 msgid "Hello,\\n\\nHere's some files I want to share with you:\\n" msgstr "Hola,\\n\\nVe't aquí uns fitxers que vull compartir amb tu:" @@ -267,12 +343,16 @@ msgstr "Hola,\\n\\nVe't aquí uns fitxers que vull compartir amb tu:" msgid "Here's some files" msgstr "Ve't aquí uns fitxers" -#: themes/default/templates/partial/index.js.ep:24 +#: themes/default/templates/partial/invitations.js.ep:7 +msgid "Hide hidden invitations" +msgstr "" + +#: themes/default/templates/partial/index.js.ep:25 msgid "Hit Enter, then Ctrl+C to copy all the download links" msgstr "" "Prem Retorn, i després Ctrl+C per copiar tots els enllaços de descàrrega" -#: themes/default/templates/partial/index.js.ep:23 +#: themes/default/templates/partial/index.js.ep:24 msgid "Hit Enter, then Ctrl+C to copy the download link" msgstr "Prem Retorn, i després Ctrl+C per copiar l'enllaç de descàrrega" @@ -280,7 +360,11 @@ msgstr "Prem Retorn, i després Ctrl+C per copiar l'enllaç de descàrrega" msgid "How does it work?" msgstr "Com funciona?" -#: themes/default/templates/about.html.ep:17 +#: themes/default/templates/invitations/invite.html.ep:46 +msgid "How many days would you like the invitation to be valid?" +msgstr "" + +#: themes/default/templates/about.html.ep:16 msgid "How to install the software on my server?" msgstr "Com instal·lo el programari al meu servidor?" @@ -305,7 +389,7 @@ msgstr "" msgid "Import localStorage data" msgstr "Importar dades de l'emmagatzematge local" -#: themes/default/templates/index.html.ep:44 +#: themes/default/templates/index.html.ep:53 msgid "Important: more information on delays" msgstr "Importat: més informació sobre moratòries" @@ -313,6 +397,24 @@ msgstr "Importat: més informació sobre moratòries" msgid "Information about delays" msgstr "Informació sobre moratòries" +#: themes/default/templates/invitations/my_invitations.html.ep:12 +msgid "Invert selection" +msgstr "" + +#. ($i->guest_mail, $url) +#: lib/Lufi/Controller/Invitation.pm:171 +msgid "Invitation resent to %1.
URL: %2" +msgstr "" + +#. ($invitation->guest_mail, $url) +#: lib/Lufi/Controller/Invitation.pm:87 +msgid "Invitation sent to %1.
URL: %2" +msgstr "" + +#: themes/default/templates/invitations/invite.html.ep:27 themes/default/templates/layouts/default.html.ep:36 themes/default/templates/layouts/default.html.ep:69 +msgid "Invite a guest" +msgstr "" + #: themes/default/templates/partial/render.js.ep:6 msgid "" "It seems that the key in your URL is incorrect. Please, verify your URL." @@ -323,7 +425,7 @@ msgstr "" msgid "Javascript is disabled. You won't be able to use Lufi." msgstr "Teniu el javascript deactivat. No podreu usar Lufi." -#: themes/default/templates/layouts/default.html.ep:40 themes/default/templates/layouts/default.html.ep:68 +#: themes/default/templates/layouts/default.html.ep:44 themes/default/templates/layouts/default.html.ep:46 themes/default/templates/layouts/default.html.ep:77 themes/default/templates/layouts/default.html.ep:79 msgid "Language" msgstr "Llengua" @@ -331,7 +433,7 @@ msgstr "Llengua" msgid "Login" msgstr "Entrada" -#: themes/default/templates/layouts/default.html.ep:53 themes/default/templates/layouts/default.html.ep:78 +#: themes/default/templates/layouts/default.html.ep:58 themes/default/templates/layouts/default.html.ep:91 msgid "Logout" msgstr "Sortida" @@ -343,16 +445,29 @@ msgstr "Lufi és programari lliure d'allotjament de fitxers." msgid "Mail" msgstr "Mail" -#: themes/default/templates/files.html.ep:3 themes/default/templates/layouts/default.html.ep:34 themes/default/templates/layouts/default.html.ep:62 +#: themes/default/templates/files.html.ep:3 themes/default/templates/layouts/default.html.ep:34 themes/default/templates/layouts/default.html.ep:67 msgid "My files" msgstr "Els meus fitxers" +#: themes/default/templates/invitations/my_invitations.html.ep:5 themes/default/templates/layouts/default.html.ep:37 themes/default/templates/layouts/default.html.ep:70 +msgid "My invitations" +msgstr "" + +#: themes/default/templates/invitations/notification_files_sent.mail.ep:17 +msgid "" +"NB: this list includes the list of files that have already been sent to you." +msgstr "" + +#: themes/default/templates/index.html.ep:115 +msgid "Name of the zip file" +msgstr "" + #. (format_bytes($json->{size}) -#: lib/Lufi/Controller/Files.pm:79 +#: lib/Lufi/Controller/Files.pm:108 msgid "No enough space available on the server for this file (size: %1)." msgstr "No hi ha prou espai al servidor per a aquest fitxer (mida: %1)" -#: themes/default/templates/partial/files.js.ep:10 themes/default/templates/partial/index.js.ep:27 +#: themes/default/templates/partial/files.js.ep:10 themes/default/templates/partial/index.js.ep:28 msgid "No expiration delay" msgstr "Sense moratòria d'expiració" @@ -366,7 +481,7 @@ msgstr "" "desa en emmagatzematge local: si netegeu l'emmagatzematge local perdreu " "aquesta llista." -#: themes/default/templates/index.html.ep:87 themes/default/templates/login.html.ep:21 themes/default/templates/render.html.ep:26 +#: themes/default/templates/index.html.ep:106 themes/default/templates/login.html.ep:21 themes/default/templates/render.html.ep:26 msgid "Password" msgstr "Contrasenya" @@ -383,7 +498,7 @@ msgstr "" "Si us plau, espereu mentre obtenim el fitxer. Abans que el tingueu " "disponible primer cal descarregar i desxifrar tots els trossos." -#: lib/Lufi/Controller/Auth.pm:28 +#: lib/Lufi/Controller/Auth.pm:38 msgid "" "Please, check your credentials or your right to access this service: unable " "to authenticate." @@ -397,10 +512,26 @@ msgstr "Privacitat" msgid "Purge expired files from localStorage" msgstr "Netegeu els fitxers expirats de l'emmagatzematge local." -#: themes/default/templates/layouts/default.html.ep:31 themes/default/templates/layouts/default.html.ep:59 +#: themes/default/templates/invitations/notification_files_sent.mail.ep:20 +msgid "Regards," +msgstr "" + +#: themes/default/templates/invitations/invite.mail.ep:15 +msgid "Regards." +msgstr "" + +#: themes/default/templates/layouts/default.html.ep:31 themes/default/templates/layouts/default.html.ep:64 msgid "Report file" msgstr "Senyalar un fitxer" +#: themes/default/templates/invitations/my_invitations.html.ep:15 +msgid "Resend invitation mail" +msgstr "" + +#: themes/default/templates/invitations/my_invitations.html.ep:9 +msgid "Rows in purple mean that the invitations have expired." +msgstr "" + #: themes/default/templates/files.html.ep:9 msgid "" "Rows in red mean that the files have expired and are no longer available." @@ -408,10 +539,14 @@ msgstr "" "Les files en vermell indiquen que els fitxers han expirat i ja no són " "disponibles." -#: themes/default/templates/partial/index.js.ep:25 +#: themes/default/templates/partial/index.js.ep:26 msgid "Send all links by email" msgstr "Envia tots els enllaços per correu electrònic" +#: themes/default/templates/invitations/invite.html.ep:50 +msgid "Send the invitation" +msgstr "" + #: themes/default/templates/mail.html.ep:46 msgid "Send with this server" msgstr "Envia amb aquest servidor" @@ -420,7 +555,7 @@ msgstr "Envia amb aquest servidor" msgid "Send with your own mail software" msgstr "Envia amb el vostre propi programa de correu" -#: themes/default/templates/partial/index.js.ep:28 +#: themes/default/templates/partial/index.js.ep:29 msgid "" "Sending part XX1 of XX2. Please, be patient, the progress bar can take a " "while to move." @@ -433,20 +568,49 @@ msgstr "" msgid "Share your files in total privacy on %1" msgstr "Compartiu fitxers amb total privacitat a %1" -#: themes/default/templates/layouts/default.html.ep:36 themes/default/templates/layouts/default.html.ep:64 themes/default/templates/login.html.ep:27 themes/default/templates/logout.html.ep:17 +#: themes/default/templates/invitations/my_invitations.html.ep:13 themes/default/templates/partial/invitations.js.ep:9 +msgid "Show hidden invitations" +msgstr "" + +#: themes/default/templates/partial/render.js.ep:11 +msgid "Show zip content" +msgstr "" + +#: themes/default/templates/layouts/default.html.ep:40 themes/default/templates/layouts/default.html.ep:73 themes/default/templates/login.html.ep:28 themes/default/templates/logout.html.ep:17 msgid "Signin" msgstr "Autenticació" -#: themes/default/templates/index.html.ep:37 +#: lib/Lufi/Controller/Invitation.pm:283 themes/default/templates/invitations/exception.html.ep:16 +msgid "" +"Sorry, the invitation doesn’t exist. Are you sure you are on the right URL?" +msgstr "" + +#: themes/default/templates/index.html.ep:46 msgid "Sorry, the uploading is currently disabled. Please try again later." msgstr "" "Disculpeu, les pujades estan actualment desactivades. Si us plau proveu-ho " "més tard." -#: lib/Lufi/Controller/Files.pm:53 +#: lib/Lufi/Controller/Files.pm:82 msgid "Sorry, uploading is disabled." msgstr "Disculpeu, les pujades estan deshabilitades" +#: themes/default/templates/invitations/exception.html.ep:7 +msgid "Sorry, your invitation has expired or has been deleted." +msgstr "" + +#. ($invit->ldap_user_mail) +#: lib/Lufi/Controller/Files.pm:122 +msgid "" +"Sorry, your invitation has expired or has been deleted. Please contact %1 to " +"have another invitation." +msgstr "" + +#. ($invitation->ldap_user_mail) +#: lib/Lufi/Controller/Invitation.pm:276 +msgid "The URLs of your files have been sent by email to %1." +msgstr "" + #: themes/default/templates/about.html.ep:7 msgid "" "The administrator can only see the file's name, its size and its mimetype " @@ -475,7 +639,12 @@ msgstr "El cos del correu no pot estar buit." msgid "The email subject can't be empty." msgstr "L'assumpte dle correu no pot estar buit." -#: lib/Lufi/Controller/Files.pm:421 +#. ($expire_at, $max_expire_at) +#: lib/Lufi/Controller/Invitation.pm:51 +msgid "The expiration delay (%1) is not between 1 and %2 days." +msgstr "" + +#: lib/Lufi/Controller/Files.pm:468 msgid "The file has already been deleted" msgstr "El fitxer ja ha estat esborrat" @@ -493,10 +662,43 @@ msgstr "" msgid "The following email addresses are not valid: %1" msgstr "Les següents adreces de correu electrònic no són vàlides: %1" -#: themes/default/templates/partial/index.js.ep:15 +#. ($guest_mail) +#: lib/Lufi/Controller/Invitation.pm:48 +msgid "The guest email address (%1) is unvalid." +msgstr "" + +#. ($i->token, $i->guest_mail) +#: lib/Lufi/Controller/Invitation.pm:150 +msgid "" +"The invitation %1 can’t be resent: %2 has already sent files.
Please " +"create a new invitation." +msgstr "" + +#. ($i->token) +#: lib/Lufi/Controller/Invitation.pm:130 +msgid "The invitation %1 has been deleted." +msgstr "" + +#. (stash('user_mail') +#: themes/default/templates/invitations/invite.html.ep:34 +msgid "The invitation mail will be send from your email address (%1)." +msgstr "" + +#: themes/default/templates/partial/index.js.ep:16 msgid "The link(s) has been copied to your clipboard" msgstr "L'enllaç/ els enllaços ja estan copiats al portaretalls" +#. (stash('invitation') +#: themes/default/templates/index.html.ep:30 +msgid "" +"The link(s) of your file(s) will automatically be sent by mail to %1 (%2)" +msgstr "" + +#. (stash('ldap_user') +#: themes/default/templates/invitations/invite.mail.ep:11 +msgid "The links of your file(s) will automatically be sent by mail to %1." +msgstr "" + #: lib/Lufi/Controller/Mail.pm:97 msgid "The mail has been sent." msgstr "El correu ja està enviat." @@ -504,17 +706,12 @@ msgstr "El correu ja està enviat." #: themes/default/templates/about.html.ep:15 msgid "" "The original (and only for now) author is Luc Didry. If you want to support him, you can do it " -"via Tipeee " -"or via Liberapay." +"class=\"classic\">Luc Didry." msgstr "" "L'autor original (i per ara l'únic) és Luc Didry. Si voleu fer una contribució podeu fer-ho via Tipeee o via " -"Liberapay." +"\"classic\">Luc Didry." -#: lib/Lufi/Controller/Files.pm:189 +#: lib/Lufi/Controller/Files.pm:236 msgid "" "The server was unable to find the file record to add your file part to. " "Please, contact the administrator." @@ -522,13 +719,22 @@ msgstr "" "El servidor no ha pogut trobar el registre del fitxer per afegir-hi el tros " "del fitxer. Si us plau, contacteu l'administrador." -#: lib/Lufi/Controller/Files.pm:248 +#: lib/Lufi/Controller/Files.pm:295 msgid "" "This file has been deactivated by the admins. Contact them to know why." msgstr "" "Aquest fitxer es estat desactivat pels administrators. Contacteu-los per " "saber perque." +#: themes/default/templates/invitations/my_invitations.html.ep:46 themes/default/templates/partial/invitations.js.ep:6 +msgid "This invitation is normally hidden" +msgstr "" + +#. (stash('expires') +#: themes/default/templates/invitations/invite.mail.ep:13 +msgid "This invitation is valid until %1." +msgstr "" + #: themes/default/templates/delays.html.ep:10 msgid "" "This server sets limitations according to the file size. The expiration " @@ -539,12 +745,20 @@ msgstr "" "moratòria d'expiració del fitxer serà el mínim de l'escollida i aquestes " "limitacions:" -#: themes/default/templates/partial/index.js.ep:16 +#: themes/default/templates/invitations/my_invitations.html.ep:16 +msgid "Toggle visibility" +msgstr "" + +#: themes/default/templates/invitations/my_invitations.html.ep:25 +msgid "URL" +msgstr "" + +#: themes/default/templates/partial/index.js.ep:17 msgid "Unable to copy the link(s) to your clipboard" msgstr "No s'han pogut copiar l'enllaç o els enllaços al porta-retalls." #. ($short) -#: lib/Lufi/Controller/Files.pm:392 +#: lib/Lufi/Controller/Files.pm:439 msgid "" "Unable to get counter for %1. The file does not exists. It will be removed " "from your localStorage." @@ -553,28 +767,32 @@ msgstr "" "del teu emmagatzematge local." #. ($short) -#: lib/Lufi/Controller/Files.pm:382 +#: lib/Lufi/Controller/Files.pm:429 msgid "Unable to get counter for %1. The token is invalid." msgstr "No he pogut obtenir el comptador de %1. El testimoni no és vàlid." #. ($short) -#: lib/Lufi/Controller/Files.pm:402 +#: lib/Lufi/Controller/Files.pm:449 msgid "Unable to get counter for %1. You are not authenticated." msgstr "No he pogut obtenir el comptador de %1. No esteu autenticat." -#: themes/default/templates/layouts/default.html.ep:33 themes/default/templates/layouts/default.html.ep:61 +#: themes/default/templates/layouts/default.html.ep:33 themes/default/templates/layouts/default.html.ep:66 msgid "Upload files" msgstr "Pujar fitxers" +#: themes/default/templates/index.html.ep:145 +msgid "Upload generated zip file" +msgstr "" + #: themes/default/templates/files.html.ep:30 msgid "Uploaded at" msgstr "Pujat a les" -#: themes/default/templates/index.html.ep:103 +#: themes/default/templates/index.html.ep:153 msgid "Uploaded files" msgstr "Fitxers pujats" -#: themes/default/templates/partial/index.js.ep:29 +#: themes/default/templates/partial/index.js.ep:30 msgid "Websocket communication error" msgstr "Error de comunicacions del websocket" @@ -586,6 +804,16 @@ msgstr "Què és Lufi?" msgid "Who wrote this software?" msgstr "Qui va escriure aquest programa?" +#: themes/default/templates/partial/index.js.ep:13 +msgid "XXX file has been added to upload queue." +msgstr "" + +#: themes/default/templates/invitations/invite.html.ep:30 +msgid "" +"You can invite someone to send you files through this Lufi instance even if " +"they don’t have an account on it." +msgstr "" + #: themes/default/templates/about.html.ep:11 msgid "" "You can see the list of your files by clicking on the \"My files\" link at " @@ -625,7 +853,7 @@ msgstr "" "Heu intentat deixar aquesta pàgina. Es canceŀlarà la descàrrega. N'esteu " "segur?" -#: themes/default/templates/partial/index.js.ep:13 +#: themes/default/templates/partial/index.js.ep:14 msgid "" "You have attempted to leave this page. The upload will be canceled. Are you " "sure?" @@ -640,18 +868,19 @@ msgstr "Heu sortit correctament." msgid "You must give email addresses." msgstr "Heu de donar l'adreça de correu electrònic." -#: themes/default/templates/index.html.ep:29 +#: themes/default/templates/index.html.ep:38 msgid "" -"Your browser has not enough entropy to generate a strong encryption key. " -"Please wait (it's better if you do things on your computer while waiting)." +"Your browser does not have enough entropy to generate a strong encryption " +"key. Please wait (it's better if you do things on your computer while " +"waiting)." msgstr "" #. (format_bytes($json->{size}) -#: lib/Lufi/Controller/Files.pm:66 +#: lib/Lufi/Controller/Files.pm:95 msgid "Your file is too big: %1 (maximum size allowed: %2)" msgstr "El fitxer és massa gran: %1 (mida màxima admesa: %2)" -#: lib/Lufi/Controller/Files.pm:304 +#: lib/Lufi/Controller/Files.pm:351 msgid "Your password is not valid. Please refresh the page to retry." msgstr "" "La vostra contrasenya es incorrecta. Torneu cargar la pàgina per provar de " @@ -671,6 +900,10 @@ msgstr "entre %1 i %2, el fitxer es mantindrà per sempre al servidor." msgid "deadline: " msgstr "termini:" +#: themes/default/templates/partial/invitations.js.ep:5 +msgid "expires on XXX" +msgstr "" + #. (format_bytes($keys[$i]) #: themes/default/templates/delays.html.ep:26 msgid "for %1 and more, the file will be kept %2 day(s)" @@ -685,6 +918,11 @@ msgstr "a partir de %1, el fitxer es mantindrà per sempre al servidor." msgid "no time limit" msgstr "no hi ha límit de temps" -#: themes/default/templates/index.html.ep:96 +#: themes/default/templates/index.html.ep:124 msgid "or" msgstr "o" + +#. ($e->{name}, format_bytes($e->{size}) +#: themes/default/templates/invitations/notification_files_sent.mail.ep:12 +msgid "— %1 (%2), that will expire on %3" +msgstr "" diff --git a/themes/default/lib/Lufi/I18N/de.po b/themes/default/lib/Lufi/I18N/de.po index 9df4b74..9a8ecb1 100644 --- a/themes/default/lib/Lufi/I18N/de.po +++ b/themes/default/lib/Lufi/I18N/de.po @@ -1,4 +1,6 @@ # frju365 , 2018. #zanata +# Armando , 2019. #zanata +# Luc Didry , 2019. #zanata msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" @@ -6,8 +8,8 @@ msgstr "" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"PO-Revision-Date: 2018-10-28 11:20+0000\n" -"Last-Translator: frju365 \n" +"PO-Revision-Date: 2019-08-04 05:32+0000\n" +"Last-Translator: Luc Didry \n" "Language-Team: German\n" "Language: de\n" "X-Generator: Zanata 4.6.2\n" @@ -15,11 +17,35 @@ msgstr "" #. ($delay) #. (max_delay) -#: themes/default/templates/index.html.ep:47 themes/default/templates/index.html.ep:56 themes/default/templates/index.html.ep:57 +#: themes/default/templates/index.html.ep:56 themes/default/templates/index.html.ep:65 themes/default/templates/index.html.ep:66 msgid "%1 days" msgstr "%1 Tage" -#: themes/default/templates/partial/index.js.ep:26 +#. (stash('ldap_user') +#: themes/default/templates/invitations/invite.mail.ep:2 +msgid "%1 invites you to send him/her files" +msgstr "%1 lädt Sie ein, ihm/ihr Dateien zu senden" + +#. (stash('ldap_user') +#: themes/default/templates/invitations/invite.mail.ep:6 +msgid "%1 invites you to send him/her files through Lufi." +msgstr "%1 lädt Sie ein, ihm/ihr Dateien über Lufi zu senden" + +#. (stash('invitation') +#: themes/default/templates/invitations/notification_files_sent.mail.ep:4 +msgid "%1 sent you files" +msgstr "%1 hat Ihnen Dateien zugesandt" + +#. (stash('invitation') +#: themes/default/templates/invitations/notification_files_sent.mail.ep:8 +msgid "%1 used your invitation to send you files:" +msgstr "%1 hat Ihre Einladung genutzt, um Ihnen Dateien zu schicken:" + +#: lib/Lufi/Controller/Invitation.pm:159 lib/Lufi/Controller/Invitation.pm:84 themes/default/templates/invitations/my_invitations.html.ep:51 themes/default/templates/invitations/my_invitations.html.ep:52 themes/default/templates/invitations/my_invitations.html.ep:53 themes/default/templates/invitations/notification_files_sent.mail.ep:12 +msgid "%A %d %B %Y at %T" +msgstr "%A %d %B %Y um %T" + +#: themes/default/templates/partial/index.js.ep:27 msgid "(max size: XXX)" msgstr "(maximale Größe: XXX)" @@ -27,7 +53,7 @@ msgstr "(maximale Größe: XXX)" msgid "1 year" msgstr "1 Jahr" -#: themes/default/templates/index.html.ep:4 themes/default/templates/index.html.ep:56 +#: themes/default/templates/index.html.ep:4 themes/default/templates/index.html.ep:65 msgid "24 hours" msgstr "24 Stunden" @@ -35,27 +61,15 @@ msgstr "24 Stunden" msgid ":" msgstr ":" -#: themes/default/templates/about.html.ep:16 -msgid "" -"A thank you with a photo of kitten on Diaspora* or " -"Mastodon " -"is cool too ;-)" -msgstr "" -"Ein Dankeschön mit einem Foto von Kätzchen an Diaspora* oder an Mastodon ist auch cool ;)" - #: themes/default/templates/render.html.ep:42 msgid "Abort" msgstr "Abbrechen" -#: themes/default/templates/layouts/default.html.ep:48 themes/default/templates/layouts/default.html.ep:76 +#: themes/default/templates/layouts/default.html.ep:53 themes/default/templates/layouts/default.html.ep:86 msgid "About" msgstr "Über" -#: themes/default/templates/index.html.ep:88 +#: themes/default/templates/index.html.ep:107 msgid "Add a password to file(s)" msgstr "Ein Passwort zu den Dateien hinzufügen" @@ -64,10 +78,23 @@ msgid "" "Adding URLs not related to this Lufi instance to the mail body or subject is " "prohibited." msgstr "" -"Das Hinzufügen von URLs, die nicht mit dieser Lufi-Instanz in Verbindung " -"stehen, zum Mailtext oder Betreff ist verboten." +"Das Hinzufügen von URLs in den Mailtext oder Betreff, die nicht mit dieser " +"Lufi-Instanz in Verbindung stehen, ist verboten." -#: themes/default/templates/about.html.ep:18 +#: themes/default/templates/partial/invitations.js.ep:3 +msgid "Are you sure you want to delete the selected invitations?" +msgstr "" +"Sind Sie sicher, dass Sie die ausgewählten Einladungen löschen möchten?" + +#: themes/default/templates/partial/invitations.js.ep:4 +msgid "" +"Are you sure you want to resend the invitation mail for the selected " +"invitations?" +msgstr "" +"Sind Sie sicher, dass Sie die Einladungsmail für die ausgewählten " +"Einladungen erneut versenden möchten?" + +#: themes/default/templates/about.html.ep:17 msgid "" "As Lufi is a free software licensed under of the terms of the AGPLv3, you can " @@ -86,7 +113,7 @@ msgstr "" msgid "Asking for file part XX1 of %1" msgstr "Abfrage des Dateiteils XX1 von %1" -#: themes/default/templates/about.html.ep:21 +#: themes/default/templates/about.html.ep:20 msgid "Back to homepage" msgstr "Zurück zur Hauptseite" @@ -94,7 +121,7 @@ msgstr "Zurück zur Hauptseite" msgid "Bad CSRF token!" msgstr "Schlechtes CSRF-Token!" -#: lib/Lufi/Controller/Auth.pm:22 lib/Lufi/Controller/Auth.pm:39 +#: lib/Lufi/Controller/Auth.pm:27 lib/Lufi/Controller/Auth.pm:49 msgid "Bad CSRF token." msgstr "Schlechtes CSRF-Token." @@ -104,11 +131,15 @@ msgstr "" "Klicken Sie hier, um die Seite zu aktualisieren und den Download neu zu " "starten." -#: themes/default/templates/index.html.ep:98 +#: themes/default/templates/invitations/invite.mail.ep:8 +msgid "Click on the following URL to upload files on Lufi:" +msgstr "Klicken Sie auf die folgende URL, um Dateien auf Lufi hochzuladen:" + +#: themes/default/templates/index.html.ep:126 msgid "Click to open the file browser" msgstr "Klicken um den Dateibrowser zu öffnen" -#: themes/default/templates/delays.html.ep:38 +#: themes/default/templates/delays.html.ep:38 themes/default/templates/invitations/my_invitations.html.ep:80 msgid "Close" msgstr "Schließen" @@ -116,26 +147,30 @@ msgstr "Schließen" msgid "Comma-separated email addresses" msgstr "Kommagetrennte E-Mail-Adressen" -#: themes/default/templates/partial/index.js.ep:14 +#: themes/default/templates/index.html.ep:140 +msgid "Compressing zip file…" +msgstr "Komprimierung der Zip-Datei..." + +#: themes/default/templates/partial/index.js.ep:15 msgid "Copy all links to clipboard" msgstr "Alle Links in die Zwischenablage kopieren" -#: themes/default/templates/partial/index.js.ep:17 +#: themes/default/templates/partial/index.js.ep:18 msgid "Copy to clipboard" msgstr "In die Zwischenablage kopieren" -#: lib/Lufi/Controller/Files.pm:460 +#: lib/Lufi/Controller/Files.pm:507 msgid "Could not delete the file. You are not authenticated." msgstr "" "Die Datei konnte nicht gelöscht werden. Sie sind nicht authentifiziert." -#: lib/Lufi/Controller/Files.pm:442 +#: lib/Lufi/Controller/Files.pm:489 msgid "Could not find the file. Are you sure of the URL and the token?" msgstr "" "Die Datei konnte nicht gefunden werden. Sind Sie sich der URL und des Token " "sicher?" -#: lib/Lufi/Controller/Files.pm:353 +#: lib/Lufi/Controller/Files.pm:400 msgid "Could not find the file. Are you sure of the URL?" msgstr "Die Datei konnte nicht gefunden werden. Sind Sie sich der URL sicher?" @@ -143,15 +178,27 @@ msgstr "Die Datei konnte nicht gefunden werden. Sind Sie sich der URL sicher?" msgid "Counter" msgstr "Zähler" -#: themes/default/templates/files.html.ep:29 themes/default/templates/index.html.ep:81 +#: themes/default/templates/index.html.ep:100 +msgid "Create a zip archive with the files before uploading?" +msgstr "Ein Zip-Archiv mit den Dateien erstellen, vor dem Hochladen?" + +#: themes/default/templates/invitations/my_invitations.html.ep:26 +msgid "Created at" +msgstr "Erstellt am" + +#: themes/default/templates/invitations/my_invitations.html.ep:14 +msgid "Delete" +msgstr "Löschen" + +#: themes/default/templates/files.html.ep:29 themes/default/templates/index.html.ep:90 msgid "Delete at first download?" -msgstr "Nach dem ersten Download löschen?" +msgstr "Beim ersten Download löschen?" #: themes/default/templates/files.html.ep:18 msgid "Delete selected files" -msgstr "Die ausgewählten Dateien löschen" +msgstr "Ausgewählte Dateien löschen" -#: themes/default/templates/files.html.ep:32 themes/default/templates/partial/index.js.ep:18 +#: themes/default/templates/files.html.ep:32 themes/default/templates/partial/index.js.ep:19 msgid "Deletion link" msgstr "Link zum Löschen" @@ -164,15 +211,15 @@ msgstr "" "der Datei beginnt und der Download nach Ablauf der Frist endet, kann er die " "Datei herunterladen." -#: themes/default/templates/partial/index.js.ep:20 themes/default/templates/render.html.ep:28 +#: themes/default/templates/partial/index.js.ep:21 themes/default/templates/render.html.ep:28 msgid "Download" msgstr "Herunterladen" #: themes/default/templates/partial/render.js.ep:4 msgid "Download aborted." -msgstr "Herunterladen abgebrochen" +msgstr "Herunterladen abgebrochen." -#: themes/default/templates/files.html.ep:27 themes/default/templates/partial/index.js.ep:19 +#: themes/default/templates/files.html.ep:27 themes/default/templates/partial/index.js.ep:20 msgid "Download link" msgstr "Link zum Herunterladen" @@ -186,18 +233,22 @@ msgid "" msgstr "" "Ziehen Sie Dateien per Drag-and-Drop in den entsprechenden Bereich oder " "verwenden Sie die traditionelle Methode zum Senden von Dateien und die " -"Dateien werden aufgeteilt, verschlüsselt und an den Server gesendet. Sie " +"Dateien werden zerlegt, verschlüsselt und an den Server gesendet. Sie " "erhalten zwei Links pro Datei: einen Download-Link, den Sie den Personen " "geben, mit denen Sie die Datei teilen möchten, und einen Lösch-Link, mit dem " "Sie die Datei jederzeit löschen können." -#: themes/default/templates/index.html.ep:94 +#: themes/default/templates/index.html.ep:122 msgid "Drop files here" -msgstr "Legen Sie Dateien hier ab" +msgstr "Dateien hier ablegen" + +#: themes/default/templates/invitations/invite.html.ep:40 +msgid "Email address of your guest" +msgstr "E-Mail-Adresse Ihres Gastes" #: themes/default/templates/mail.html.ep:39 msgid "Email body" -msgstr "E-Mail-Inhalt" +msgstr "E-Mail-Text" #: themes/default/templates/mail.html.ep:31 msgid "Email subject" @@ -207,26 +258,31 @@ msgstr "E-Mail-Betreff" msgid "Emails" msgstr "E-Mails" -#: themes/default/templates/partial/index.js.ep:21 +#: themes/default/templates/partial/index.js.ep:22 msgid "Encrypting part XX1 of XX2" msgstr "Verschlüsselung von Teil XX1 von XX2" -#: lib/Lufi/Controller/Files.pm:242 +#: lib/Lufi/Controller/Files.pm:289 msgid "Error: the file existed but was deleted." -msgstr "Error: Die Datei existierte, aber es wurde gelöscht." +msgstr "Fehler: Die Datei existierte, wurde aber gelöscht." -#: lib/Lufi/Controller/Files.pm:322 +#: lib/Lufi/Controller/Files.pm:369 msgid "Error: the file has not been sent entirely." -msgstr "Error: Die Datei wurde nicht vollständig gesendet." +msgstr "Fehler: Die Datei wurde nicht vollständig gesendet." -#: lib/Lufi/Controller/Files.pm:332 +#: lib/Lufi/Controller/Files.pm:379 msgid "Error: unable to find the file. Are you sure of your URL?" -msgstr "Error: Datei nicht gefunden. Sind Sie sich Ihrer URL sicher?" +msgstr "" +"Fehler: Die Datei konnte nicht gefunden werden. Sind Sie Ihrer URL sicher?" -#: themes/default/templates/partial/index.js.ep:22 +#: themes/default/templates/partial/index.js.ep:23 msgid "Expiration:" msgstr "Ablaufdatum:" +#: themes/default/templates/invitations/my_invitations.html.ep:27 +msgid "Expire at" +msgstr "Verfallen am" + #: themes/default/templates/files.html.ep:31 msgid "Expires at" msgstr "Verfällt am" @@ -235,7 +291,7 @@ msgstr "Verfällt am" msgid "Export localStorage data" msgstr "Exportieren von localStorage-Daten" -#: lib/Lufi/Controller/Files.pm:424 +#: lib/Lufi/Controller/Files.pm:471 msgid "File deleted" msgstr "Datei gelöscht" @@ -243,52 +299,85 @@ msgstr "Datei gelöscht" msgid "File name" msgstr "Dateiname" -#: themes/default/templates/index.html.ep:71 +#: themes/default/templates/invitations/my_invitations.html.ep:61 +msgid "Files" +msgstr "Dateien" + +#: themes/default/templates/index.html.ep:80 msgid "Files deleted at first download" -msgstr "Dateien wurde beim ersten Download gelöcht" +msgstr "Dateien, die beim ersten Download gelöscht wurden" + +#: themes/default/templates/invitations/my_invitations.html.ep:28 +msgid "Files sent at" +msgstr "Dateien gesendet am" + +#: themes/default/templates/partial/invitations.js.ep:8 +msgid "Files sent in invitation XX1 by XX2" +msgstr "Dateien, die in der Einladung XX1 von XX2 gesendet wurden." #: themes/default/templates/partial/render.js.ep:8 msgid "Get the file" msgstr "Die Datei herunterladen" -#: themes/default/templates/about.html.ep:19 +#: themes/default/templates/about.html.ep:18 msgid "" "Get the source code on the official repository or on its Github mirror" msgstr "" -"Holen Sie sich den Quellcode von dem offiziellen Repository oder auf dessen das offizielle Repository oder auf dessen Github mirror." +#: themes/default/templates/invitations/my_invitations.html.ep:24 +msgid "Guest mail" +msgstr "Gast-E-Mail-Adresse" + +#. (ucfirst(stash('invitation') +#: themes/default/templates/invitations/notification_files_sent.mail.ep:6 +msgid "Hello %1," +msgstr "Hallo %1," + +#: themes/default/templates/invitations/invite.mail.ep:4 +msgid "Hello," +msgstr "Hallo," + #: themes/default/templates/partial/mail.js.ep:35 msgid "Hello,\\n\\nHere's some files I want to share with you:\\n" -msgstr "Hallo!!\\n\\nHier sind einige Dateien, die ich mit dir teilen möchte:\\n" +msgstr "" +"Hallo,\\n\\nHier sind einige Dateien, die ich mit Ihnen teilen möchte:\\n" #: themes/default/templates/mail.html.ep:35 msgid "Here's some files" msgstr "Hier sind einige Dateien" -#: themes/default/templates/partial/index.js.ep:24 +#: themes/default/templates/partial/invitations.js.ep:7 +msgid "Hide hidden invitations" +msgstr "Versteckte Einladungen ausblenden" + +#: themes/default/templates/partial/index.js.ep:25 msgid "Hit Enter, then Ctrl+C to copy all the download links" -msgstr "" -"Drücken Sie Enter und dann Ctrl+C, um alle Download-Links zu kopieren." +msgstr "Drücken Sie Enter und dann Ctrl+C, um alle Download-Links zu kopieren" -#: themes/default/templates/partial/index.js.ep:23 +#: themes/default/templates/partial/index.js.ep:24 msgid "Hit Enter, then Ctrl+C to copy the download link" -msgstr "Drücken Sie Enter und dann Ctrl+C, um die Download-Links zu kopieren." +msgstr "Drücken Sie Enter und dann Ctrl+C, um den Download-Link zu kopieren" #: themes/default/templates/about.html.ep:9 msgid "How does it work?" msgstr "Wie funktioniert es?" -#: themes/default/templates/about.html.ep:17 +#: themes/default/templates/invitations/invite.html.ep:46 +msgid "How many days would you like the invitation to be valid?" +msgstr "Wie viele Tage soll die Einladung gültig sein?" + +#: themes/default/templates/about.html.ep:16 msgid "How to install the software on my server?" -msgstr "Wie kann ich diese Software auf meinem Server installieren ?" +msgstr "Wie kann ich die Software auf meinem Server installieren?" #: themes/default/templates/about.html.ep:12 msgid "How to report an illegal file?" -msgstr "Wie kann ich eine illegale Datei melden?" +msgstr "Wie kann man eine illegale Datei melden?" #: themes/default/templates/delays.html.ep:7 msgid "If you choose a delay, the file will be deleted after that delay." @@ -301,64 +390,98 @@ msgid "" "If you send the mail from this server, the links will be sent to the server, " "which may lower your privacy protection." msgstr "" -"Wenn Sie die E-Mail von diesem Server schicken, werden die Links an einem " -"Server geschickt, der einen niedrigen Schutz der Privatsphäre haben kann." +"Wenn Sie die E-Mail von diesem Server aus versenden, werden die Links an den " +"Server gesendet, was den Schutz Ihrer Privatsphäre beeinträchtigen kann." #: themes/default/templates/files.html.ep:14 msgid "Import localStorage data" msgstr "Importieren von localStorage-Daten" -#: themes/default/templates/index.html.ep:44 +#: themes/default/templates/index.html.ep:53 msgid "Important: more information on delays" msgstr "Wichtig: weitere Informationen zu den Verzögerungen" #: themes/default/templates/delays.html.ep:5 msgid "Information about delays" -msgstr "Informationen über die Verzögerungen" +msgstr "Informationen über Verzögerungen" + +#: themes/default/templates/invitations/my_invitations.html.ep:12 +msgid "Invert selection" +msgstr "Auswahl umkehren" + +#. ($i->guest_mail, $url) +#: lib/Lufi/Controller/Invitation.pm:171 +msgid "Invitation resent to %1.
URL: %2" +msgstr "Die Einladung wurde erneut an %1 gesendet.
URL: %2" + +#. ($invitation->guest_mail, $url) +#: lib/Lufi/Controller/Invitation.pm:87 +msgid "Invitation sent to %1.
URL: %2" +msgstr "Die Einladung wurde an %1 gesendet.
URL: %2" + +#: themes/default/templates/invitations/invite.html.ep:27 themes/default/templates/layouts/default.html.ep:36 themes/default/templates/layouts/default.html.ep:69 +msgid "Invite a guest" +msgstr "Einen Gast einladen" #: themes/default/templates/partial/render.js.ep:6 msgid "" "It seems that the key in your URL is incorrect. Please, verify your URL." msgstr "" -"Es scheint, dass der Schlüssel in Ihrer URL inkorrekt ist. Bitte überprüfen " -"Sie Ihre URL." +"Es scheint, dass der Schlüssel in Ihrer URL falsch ist. Bitte überprüfen Sie " +"Ihre URL." #: themes/default/templates/index.html.ep:12 msgid "Javascript is disabled. You won't be able to use Lufi." msgstr "Javascript ist deaktiviert. Sie werden Lufi nicht benutzen können." -#: themes/default/templates/layouts/default.html.ep:40 themes/default/templates/layouts/default.html.ep:68 +#: themes/default/templates/layouts/default.html.ep:44 themes/default/templates/layouts/default.html.ep:46 themes/default/templates/layouts/default.html.ep:77 themes/default/templates/layouts/default.html.ep:79 msgid "Language" msgstr "Sprache" #: themes/default/templates/login.html.ep:15 msgid "Login" -msgstr "Nutzername" +msgstr "Login" -#: themes/default/templates/layouts/default.html.ep:53 themes/default/templates/layouts/default.html.ep:78 +#: themes/default/templates/layouts/default.html.ep:58 themes/default/templates/layouts/default.html.ep:91 msgid "Logout" msgstr "Abmelden" #: themes/default/templates/about.html.ep:4 msgid "Lufi is a free (as in free speech) file hosting software." -msgstr "Lufi ist eine freie (wie Redefreiheit) Datei-Hosting-Software." +msgstr "" +"Lufi ist eine kostenlose (wie in der Redefreiheit) Datei-Hosting-Software." #: themes/default/templates/files.html.ep:33 msgid "Mail" msgstr "E-Mail" -#: themes/default/templates/files.html.ep:3 themes/default/templates/layouts/default.html.ep:34 themes/default/templates/layouts/default.html.ep:62 +#: themes/default/templates/files.html.ep:3 themes/default/templates/layouts/default.html.ep:34 themes/default/templates/layouts/default.html.ep:67 msgid "My files" msgstr "Meine Dateien" +#: themes/default/templates/invitations/my_invitations.html.ep:5 themes/default/templates/layouts/default.html.ep:37 themes/default/templates/layouts/default.html.ep:70 +msgid "My invitations" +msgstr "Meine Einladungen" + +#: themes/default/templates/invitations/notification_files_sent.mail.ep:17 +msgid "" +"NB: this list includes the list of files that have already been sent to you." +msgstr "" +"Hinweis: Diese Liste enthält die Liste der Dateien, die Ihnen bereits " +"zugesandt wurden." + +#: themes/default/templates/index.html.ep:115 +msgid "Name of the zip file" +msgstr "Name der Zip-Datei" + #. (format_bytes($json->{size}) -#: lib/Lufi/Controller/Files.pm:79 +#: lib/Lufi/Controller/Files.pm:108 msgid "No enough space available on the server for this file (size: %1)." msgstr "" "Auf dem Server ist nicht genügend Platz für diese Datei vorhanden (Größe: " "%1)." -#: themes/default/templates/partial/files.js.ep:10 themes/default/templates/partial/index.js.ep:27 +#: themes/default/templates/partial/files.js.ep:10 themes/default/templates/partial/index.js.ep:28 msgid "No expiration delay" msgstr "Keine Ablaufverzögerung" @@ -368,18 +491,18 @@ msgid "" "stored in localStorage: if you delete your localStorage data, you'll lose " "this list." msgstr "" -"Nur die mit diesem Browser geschickten Dateien werden hier aufgelistet. " -"Diese Liste wird im localStorage gespeichert: Wenn Sie Ihre localStorage-" -"Daten löschen, verlieren Sie diese Liste. " +"Nur die mit diesem Browser gesendeten Dateien werden hier aufgelistet. Diese " +"Liste wird in localStorage gespeichert: Wenn Sie Ihre localStorage-Daten " +"löschen, verlieren Sie diese Liste." -#: themes/default/templates/index.html.ep:87 themes/default/templates/login.html.ep:21 themes/default/templates/render.html.ep:26 +#: themes/default/templates/index.html.ep:106 themes/default/templates/login.html.ep:21 themes/default/templates/render.html.ep:26 msgid "Password" msgstr "Passwort" #. (config('contact') #: themes/default/templates/about.html.ep:13 msgid "Please contact the administrator: %1" -msgstr "Bitte kontaktieren Sie den Administrator: %1" +msgstr "Bitte kontaktieren sie den Administrator: %1" #: themes/default/templates/render.html.ep:33 msgid "" @@ -387,15 +510,15 @@ msgid "" "decrypt all parts before you can get it." msgstr "" "Bitte warten Sie, bis wir Ihre Datei erhalten. Wir müssen zuerst alle Teile " -"herunterladen und entschlüsseln, bevor Sie sie bekommen können." +"herunterladen und entschlüsseln, bevor Sie sie erhalten können." -#: lib/Lufi/Controller/Auth.pm:28 +#: lib/Lufi/Controller/Auth.pm:38 msgid "" "Please, check your credentials or your right to access this service: unable " "to authenticate." msgstr "" -"Bitte überprüffen Sie Ihre Zugangsdaten oder Ihr Recht, auf diesen Dienst " -"zuzugreifen: keine Authentifizierung möglich." +"Bitte überprüfen Sie Ihre Zugangsdaten oder Ihr Zugangsrecht zu diesem " +"Dienst: keine Authentifizierung möglich." #: themes/default/templates/about.html.ep:5 msgid "Privacy" @@ -405,10 +528,27 @@ msgstr "Privatsphäre" msgid "Purge expired files from localStorage" msgstr "Abgelaufene Dateien aus localStorage bereinigen" -#: themes/default/templates/layouts/default.html.ep:31 themes/default/templates/layouts/default.html.ep:59 +#: themes/default/templates/invitations/notification_files_sent.mail.ep:20 +msgid "Regards," +msgstr "Freundliche Grüße," + +#: themes/default/templates/invitations/invite.mail.ep:15 +msgid "Regards." +msgstr "Freundliche Grüße." + +#: themes/default/templates/layouts/default.html.ep:31 themes/default/templates/layouts/default.html.ep:64 msgid "Report file" msgstr "Datei melden" +#: themes/default/templates/invitations/my_invitations.html.ep:15 +msgid "Resend invitation mail" +msgstr "Einladungsmail erneut senden" + +#: themes/default/templates/invitations/my_invitations.html.ep:9 +msgid "Rows in purple mean that the invitations have expired." +msgstr "" +"Violett markierte Zeilen bedeuten, dass die Einladungen abgelaufen sind." + #: themes/default/templates/files.html.ep:9 msgid "" "Rows in red mean that the files have expired and are no longer available." @@ -416,42 +556,81 @@ msgstr "" "Rot markierte Zeilen bedeuten, dass die Dateien abgelaufen sind und nicht " "mehr verfügbar sind." -#: themes/default/templates/partial/index.js.ep:25 +#: themes/default/templates/partial/index.js.ep:26 msgid "Send all links by email" -msgstr "Alle Links per E-Mail schicken" +msgstr "Alle Links per E-Mail versenden" + +#: themes/default/templates/invitations/invite.html.ep:50 +msgid "Send the invitation" +msgstr "Einladung senden" #: themes/default/templates/mail.html.ep:46 msgid "Send with this server" -msgstr "Mit diesem Server schicken" +msgstr "Mit diesem Server senden" #: themes/default/templates/mail.html.ep:47 msgid "Send with your own mail software" -msgstr "Mit Ihrem eigenen Mail-Programm versenden" +msgstr "Mit Ihrer eigenen Mail-Software senden" -#: themes/default/templates/partial/index.js.ep:28 +#: themes/default/templates/partial/index.js.ep:29 msgid "" "Sending part XX1 of XX2. Please, be patient, the progress bar can take a " "while to move." msgstr "" -"Senden von Teil XX1 von XX2. Bitte haben Sie Geduld, es kann eine Weile " +"Senden von Teil XX1 von XX2. Bitte haben Sie Geduld, es könnte eine Weile " "dauern, bis sich der Fortschrittsbalken bewegt." #. (url_for('/') #: themes/default/templates/partial/mail.js.ep:48 msgid "Share your files in total privacy on %1" -msgstr "Teilen Sie Ihre Dateien in der absoluten Vertraulichkeit auf %1" +msgstr "Teilen Sie Ihre Dateien ganz vertraulich auf %1" + +#: themes/default/templates/invitations/my_invitations.html.ep:13 themes/default/templates/partial/invitations.js.ep:9 +msgid "Show hidden invitations" +msgstr "Versteckte Einladungen anzeigen" + +#: themes/default/templates/partial/render.js.ep:11 +msgid "Show zip content" +msgstr "Zip-Inhalt anzeigen" -#: themes/default/templates/layouts/default.html.ep:36 themes/default/templates/layouts/default.html.ep:64 themes/default/templates/login.html.ep:27 themes/default/templates/logout.html.ep:17 +#: themes/default/templates/layouts/default.html.ep:40 themes/default/templates/layouts/default.html.ep:73 themes/default/templates/login.html.ep:28 themes/default/templates/logout.html.ep:17 msgid "Signin" -msgstr "Anmelden" +msgstr "Einloggen" -#: themes/default/templates/index.html.ep:37 +#: lib/Lufi/Controller/Invitation.pm:283 themes/default/templates/invitations/exception.html.ep:16 +msgid "" +"Sorry, the invitation doesn’t exist. Are you sure you are on the right URL?" +msgstr "" +"Es tut uns leid, diese Einladung existiert nicht. Sind Sie sicher, dass Sie " +"die richtige URL haben?" + +#: themes/default/templates/index.html.ep:46 msgid "Sorry, the uploading is currently disabled. Please try again later." -msgstr "Es tut uns leid, aber das Hochladen ist im Moment deaktiviert" +msgstr "" +"Es tut uns leid, das Hochladen ist derzeit deaktiviert. Bitte versuchen Sie " +"es später noch einmal." -#: lib/Lufi/Controller/Files.pm:53 +#: lib/Lufi/Controller/Files.pm:82 msgid "Sorry, uploading is disabled." -msgstr "Leider ist das Hochladen deaktiviert." +msgstr "Es tut uns leid, das Hochladen ist deaktiviert." + +#: themes/default/templates/invitations/exception.html.ep:7 +msgid "Sorry, your invitation has expired or has been deleted." +msgstr "Es tut uns leid, Ihre Einladung ist abgelaufen oder wurde gelöscht." + +#. ($invit->ldap_user_mail) +#: lib/Lufi/Controller/Files.pm:122 +msgid "" +"Sorry, your invitation has expired or has been deleted. Please contact %1 to " +"have another invitation." +msgstr "" +"Es tut uns leid, Ihre Einladung ist abgelaufen oder wurde gelöscht. Bitte " +"kontaktieren Sie %1, um eine weitere Einladung zu erhalten." + +#. ($invitation->ldap_user_mail) +#: lib/Lufi/Controller/Invitation.pm:276 +msgid "The URLs of your files have been sent by email to %1." +msgstr "Die URLs deiner Dateien wurden per E-Mail an %1 gesendet." #: themes/default/templates/about.html.ep:7 msgid "" @@ -459,15 +638,15 @@ msgid "" "(what kind of file it is: video, text, etc.)." msgstr "" "Der Administrator kann nur den Namen der Datei, ihre Größe und ihren Mimetyp " -"(welche Art von Datei es ist: Video, Text, etc.) sehen." +"(um welchen Dateityp es sich handelt: Video, Text, etc.) sehen." #: lib/Lufi/Controller/Mail.pm:53 msgid "" "The body of the mail must contain at least one URL pointing to a file hosted " "on this instance." msgstr "" -"Der E-Mail-Inhalt muss zu mindestens eine URL enhalten, die auf eine Datei " -"verweist, die auf dieser Instanz gespeichert wird." +"Der Mailtext muss mindestens eine URL enthalten, die auf eine Datei " +"verweist, die auf dieser Instanz gespeichert ist." #: themes/default/templates/partial/files.js.ep:11 msgid "The data has been successfully imported." @@ -475,15 +654,20 @@ msgstr "Die Daten wurden erfolgreich importiert." #: lib/Lufi/Controller/Mail.pm:73 msgid "The email body can't be empty." -msgstr "Der E-Mail-Inhalt darf nicht leer sein." +msgstr "Der Mailtext darf nicht leer sein." #: lib/Lufi/Controller/Mail.pm:72 msgid "The email subject can't be empty." -msgstr "Der E-Mail-Betreff kann nicht leer sein." +msgstr "Der E-Mail-Betreff darf nicht leer sein." + +#. ($expire_at, $max_expire_at) +#: lib/Lufi/Controller/Invitation.pm:51 +msgid "The expiration delay (%1) is not between 1 and %2 days." +msgstr "Die Ablaufverzögerung (%1) liegt nicht zwischen 1 und %2 Tagen." -#: lib/Lufi/Controller/Files.pm:421 +#: lib/Lufi/Controller/Files.pm:468 msgid "The file has already been deleted" -msgstr "Die Datei wurde schon gelöscht." +msgstr "Die Datei wurde bereits gelöscht" #: themes/default/templates/about.html.ep:6 msgid "" @@ -492,49 +676,88 @@ msgid "" msgstr "" "Die auf einer Lufi-Instanz hochgeladenen Dateien werden vor dem Hochladen " "auf den Server verschlüsselt: Der Administrator des Servers kann den Inhalt " -"der Datei nicht sehen." +"der Datei nicht einsehen." #. (join(', ', @bad) #: lib/Lufi/Controller/Mail.pm:68 msgid "The following email addresses are not valid: %1" msgstr "Die folgenden E-Mail-Adressen sind nicht gültig: %1" -#: themes/default/templates/partial/index.js.ep:15 +#. ($guest_mail) +#: lib/Lufi/Controller/Invitation.pm:48 +msgid "The guest email address (%1) is unvalid." +msgstr "Die Gast-E-Mail-Adresse (%1) ist ungültig." + +#. ($i->token, $i->guest_mail) +#: lib/Lufi/Controller/Invitation.pm:150 +msgid "" +"The invitation %1 can’t be resent: %2 has already sent files.
Please " +"create a new invitation." +msgstr "" +"Die Einladung %1 kann nicht erneut gesendet werden: %2 hat bereits Dateien " +"gesendet.
Bitte erstellen Sie eine neue Einladung." + +#. ($i->token) +#: lib/Lufi/Controller/Invitation.pm:130 +msgid "The invitation %1 has been deleted." +msgstr "Die Einladung %1 wurde gelöscht." + +#. (stash('user_mail') +#: themes/default/templates/invitations/invite.html.ep:34 +msgid "The invitation mail will be send from your email address (%1)." +msgstr "Die Einladungsmail wird von Ihrer E-Mail-Adresse (%1) gesendet." + +#: themes/default/templates/partial/index.js.ep:16 msgid "The link(s) has been copied to your clipboard" -msgstr "Die Links wurde(n) in die Zwischenablage kopiert." +msgstr "Die Links wurden in die Zwischenablage kopiert." + +#. (stash('invitation') +#: themes/default/templates/index.html.ep:30 +msgid "" +"The link(s) of your file(s) will automatically be sent by mail to %1 (%2)" +msgstr "" +"Die Links Ihrer Dateien werden automatisch per E-Mail an %1 (%2) gesendet." + +#. (stash('ldap_user') +#: themes/default/templates/invitations/invite.mail.ep:11 +msgid "The links of your file(s) will automatically be sent by mail to %1." +msgstr "Die Links Ihrer Dateien werden automatisch per E-Mail an %1 gesendet." #: lib/Lufi/Controller/Mail.pm:97 msgid "The mail has been sent." -msgstr "Die E-Mail wurde verschickt." +msgstr "Der E-Mail wurde gesendet." #: themes/default/templates/about.html.ep:15 msgid "" "The original (and only for now) author is Luc Didry. If you want to support him, you can do it " -"via Tipeee " -"or via Liberapay." +"class=\"classic\">Luc Didry." msgstr "" -"Der Originalautor (und vorerst einzige) ist Luc Didry. Wenn Sie ihn unterstützen wollen, können " -"Sie dies bei Tipeee oder bei Liberapay tun." +"Der ursprüngliche (und vorerst einzige) Autor ist Luc Didry." -#: lib/Lufi/Controller/Files.pm:189 +#: lib/Lufi/Controller/Files.pm:236 msgid "" "The server was unable to find the file record to add your file part to. " "Please, contact the administrator." msgstr "" -"Der Server konnte den Datensatz nicht finden, zu dem Sie Ihr Dateiteil " -"hinzufügen können. Bitte kontaktieren Sie den Administrator." +"Der Server konnte den Datensatz nicht finden, um Ihr Dateiteil hinzuzufügen. " +"Bitte wenden Sie sich an den Administrator." -#: lib/Lufi/Controller/Files.pm:248 +#: lib/Lufi/Controller/Files.pm:295 msgid "" "This file has been deactivated by the admins. Contact them to know why." msgstr "" -"Diese Datei wurde von Admins deacktiviert. Kontaktieren Sie sie, um die " -"Gründe zu erfahren." +"Diese Datei wurde von den Admins deaktiviert. Kontaktieren Sie sie, um zu " +"erfahren, warum." + +#: themes/default/templates/invitations/my_invitations.html.ep:46 themes/default/templates/partial/invitations.js.ep:6 +msgid "This invitation is normally hidden" +msgstr "Diese Einladung ist normalerweise ausgeblendet" + +#. (stash('expires') +#: themes/default/templates/invitations/invite.mail.ep:13 +msgid "This invitation is valid until %1." +msgstr "Diese Einladung ist bis %1 gültig." #: themes/default/templates/delays.html.ep:10 msgid "" @@ -542,49 +765,61 @@ msgid "" "delay of your file will be the minimum between what you choose and the " "following limitations:" msgstr "" -"Dieser Server setzt Einschränkungen entsprechend der Dateigröße. Die " -"Ablaufverzögerung Ihrer Datei ist das Minimum zwischen dem, was Sie wählen, " -"und den folgenden Einschränkungen:" +"Dieser Server setzt Einschränkungen bezüglich der Dateigröße. Die " +"Ablaufverzögerung Ihrer Datei ist das Minimum zwischen dem, was Sie " +"auswählen, und den folgenden Einschränkungen:" -#: themes/default/templates/partial/index.js.ep:16 +#: themes/default/templates/invitations/my_invitations.html.ep:16 +msgid "Toggle visibility" +msgstr "Sichtbarkeit umschalten" + +#: themes/default/templates/invitations/my_invitations.html.ep:25 +msgid "URL" +msgstr "URL" + +#: themes/default/templates/partial/index.js.ep:17 msgid "Unable to copy the link(s) to your clipboard" -msgstr "Es ist nicht möglich, die Links in die Zwischenablage zu kopieren." +msgstr "Die Links konnten nicht in die Zwischenablage kopiert werden" #. ($short) -#: lib/Lufi/Controller/Files.pm:392 +#: lib/Lufi/Controller/Files.pm:439 msgid "" "Unable to get counter for %1. The file does not exists. It will be removed " "from your localStorage." msgstr "" -"Der Zähler für %1 konnte nicht ermittelt werden. Die Datei existiert nicht. " -"Sie wird aus deinem localStorage gelöscht." +"Der Zähler für %1 konnte nicht gefunden werden. Die Datei existiert nicht. " +"Es wird aus Ihrem localStorage entfernt." #. ($short) -#: lib/Lufi/Controller/Files.pm:382 +#: lib/Lufi/Controller/Files.pm:429 msgid "Unable to get counter for %1. The token is invalid." msgstr "" "Der Zähler für %1 konnte nicht gefunden werden. Das Token ist ungültig." #. ($short) -#: lib/Lufi/Controller/Files.pm:402 +#: lib/Lufi/Controller/Files.pm:449 msgid "Unable to get counter for %1. You are not authenticated." msgstr "" "Der Zähler für %1 konnte nicht gefunden werden. Sie sind nicht " "authentifiziert." -#: themes/default/templates/layouts/default.html.ep:33 themes/default/templates/layouts/default.html.ep:61 +#: themes/default/templates/layouts/default.html.ep:33 themes/default/templates/layouts/default.html.ep:66 msgid "Upload files" msgstr "Dateien hochladen" +#: themes/default/templates/index.html.ep:145 +msgid "Upload generated zip file" +msgstr "Generierte Zip-Datei hochladen" + #: themes/default/templates/files.html.ep:30 msgid "Uploaded at" msgstr "Hochgeladen am" -#: themes/default/templates/index.html.ep:103 +#: themes/default/templates/index.html.ep:153 msgid "Uploaded files" msgstr "Hochgeladene Dateien" -#: themes/default/templates/partial/index.js.ep:29 +#: themes/default/templates/partial/index.js.ep:30 msgid "Websocket communication error" msgstr "Websocket-Kommunikationsfehler" @@ -596,6 +831,18 @@ msgstr "Was ist Lufi?" msgid "Who wrote this software?" msgstr "Wer hat diese Software geschrieben?" +#: themes/default/templates/partial/index.js.ep:13 +msgid "XXX file has been added to upload queue." +msgstr "XXX Datei wurde der Upload-Warteschlange hinzugefügt." + +#: themes/default/templates/invitations/invite.html.ep:30 +msgid "" +"You can invite someone to send you files through this Lufi instance even if " +"they don’t have an account on it." +msgstr "" +"Sie können jemanden einladen, Ihnen Dateien über diese Lufi-Instanz zu " +"senden, auch wenn sie keinen Account haben." + #: themes/default/templates/about.html.ep:11 msgid "" "You can see the list of your files by clicking on the \"My files\" link at " @@ -617,15 +864,15 @@ msgid "" msgstr "" "Sie müssen sich nicht registrieren, um Dateien hochzuladen, aber beachten " "Sie, dass Ihre IP-Adresse aus rechtlichen Gründen beim Senden einer Datei " -"gespeichert wird. Keine Panik, dies ist normalerweise der Fall für alle " -"Internetseiten, zu denen Sie Dateien senden." +"gespeichert wird. Keine Panik, dies ist der Normalfall bei allen Seiten, auf " +"denen Sie Dateien senden." #: themes/default/templates/partial/render.js.ep:10 msgid "" "You don't seem to have a key in your URL. You won't be able to decrypt the " "file. Download canceled." msgstr "" -"Sie scheinen keinen Schlüssel in Ihrer URL zu besitzen. Sie werden die Datei " +"Sie scheinen keinen Schlüssel in Ihrer URL zu haben. Sie werden die Datei " "nicht entschlüsseln können. Der Download wurde abgebrochen." #: themes/default/templates/partial/render.js.ep:7 @@ -636,46 +883,47 @@ msgstr "" "Sie haben versucht, diese Seite zu verlassen. Der Download wird abgebrochen. " "Sind Sie sicher?" -#: themes/default/templates/partial/index.js.ep:13 +#: themes/default/templates/partial/index.js.ep:14 msgid "" "You have attempted to leave this page. The upload will be canceled. Are you " "sure?" msgstr "" -"Sie haben versucht, diese Seite zu verlassen. Das Hochladen wird abgebrochen. " -"Sind Sie sicher?" +"Sie haben versucht, diese Seite zu verlassen. Das Hochladen wird abgebrochen." +" Sind Sie sicher?" #: themes/default/templates/logout.html.ep:14 msgid "You have been successfully logged out." -msgstr "Sie wurden erfolgreich abgemeldet." +msgstr "Sie wurden erfolgreich ausgeloggt." #: lib/Lufi/Controller/Mail.pm:71 msgid "You must give email addresses." -msgstr "Sie müssen E-Mail-Adressen eingeben." +msgstr "Sie müssen E-Mail-Adressen angeben." -#: themes/default/templates/index.html.ep:29 +#: themes/default/templates/index.html.ep:38 msgid "" -"Your browser has not enough entropy to generate a strong encryption key. " -"Please wait (it's better if you do things on your computer while waiting)." +"Your browser does not have enough entropy to generate a strong encryption " +"key. Please wait (it's better if you do things on your computer while " +"waiting)." msgstr "" -"Ihr Browser hat nicht genug Entropie, um einen starken Verschlüsselungscode " -"zu erzeugen. Bitte warten Sie einen Moment (es ist besser, wenn Sie Dinge " -"auf Ihrem Computer tun, während Sie warten)." +"Ihr Browser verfügt nicht über genügend Entropie, um einen starken " +"Verschlüsselungscode zu erzeugen. Bitte warten Sie (es ist besser, wenn Sie " +"während des Wartens Sachen auf Ihrem Computer machen)." #. (format_bytes($json->{size}) -#: lib/Lufi/Controller/Files.pm:66 +#: lib/Lufi/Controller/Files.pm:95 msgid "Your file is too big: %1 (maximum size allowed: %2)" msgstr "Ihre Datei ist zu groß: %1 (maximal erlaubte Größe: %2)" -#: lib/Lufi/Controller/Files.pm:304 +#: lib/Lufi/Controller/Files.pm:351 msgid "Your password is not valid. Please refresh the page to retry." msgstr "" -"Ihr Passwort ist ungültig. Bitte aktualisieren Sie die Seite, um es " -"erneut zu versuchen." +"Ihr Passwort ist ungültig. Bitte aktualisieren Sie die Seite, um es erneut " +"zu versuchen." #. (format_bytes($keys[$i]) #: themes/default/templates/delays.html.ep:20 msgid "between %1 and %2, the file will be kept %3 day(s)." -msgstr "zwischen %1 und %2, wird die Datei %3 Tag(e) behalten." +msgstr "zwischen %1 und %2, wird die Datei %3 Tag(e) aufbewahrt." #. (format_bytes($keys[$i]) #: themes/default/templates/delays.html.ep:22 @@ -684,12 +932,16 @@ msgstr "zwischen %1 und %2, wird die Datei für immer aufbewahrt." #: themes/default/templates/partial/mail.js.ep:42 msgid "deadline: " -msgstr "Abgabetermin:" +msgstr "Frist:" + +#: themes/default/templates/partial/invitations.js.ep:5 +msgid "expires on XXX" +msgstr "Läuft ab am XXX" #. (format_bytes($keys[$i]) #: themes/default/templates/delays.html.ep:26 msgid "for %1 and more, the file will be kept %2 day(s)" -msgstr "für %1 und mehr wird die Datei %2 Tag(e) aufbewahrt" +msgstr "für %1 und mehr, wird die Datei %2 Tag(e) aufbewahrt." #. (format_bytes($keys[$i]) #: themes/default/templates/delays.html.ep:28 @@ -700,6 +952,11 @@ msgstr "für %1 und mehr, wird die Datei für immer aufbewahrt." msgid "no time limit" msgstr "kein Zeitlimit" -#: themes/default/templates/index.html.ep:96 +#: themes/default/templates/index.html.ep:124 msgid "or" msgstr "oder" + +#. ($e->{name}, format_bytes($e->{size}) +#: themes/default/templates/invitations/notification_files_sent.mail.ep:12 +msgid "— %1 (%2), that will expire on %3" +msgstr "— %1 (%2), läuft ab am %3" diff --git a/themes/default/lib/Lufi/I18N/en.po b/themes/default/lib/Lufi/I18N/en.po index e8ddebb..b29bf89 100644 --- a/themes/default/lib/Lufi/I18N/en.po +++ b/themes/default/lib/Lufi/I18N/en.po @@ -1,4 +1,6 @@ # Luc Didry , 2018. #zanata +# Armando , 2019. #zanata +# Luc Didry , 2019. #zanata msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" @@ -6,7 +8,7 @@ msgstr "" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"PO-Revision-Date: 2018-10-28 01:53+0000\n" +"PO-Revision-Date: 2019-08-04 05:31+0000\n" "Last-Translator: Luc Didry \n" "Language-Team: English\n" "Language: en\n" @@ -15,11 +17,35 @@ msgstr "" #. ($delay) #. (max_delay) -#: themes/default/templates/index.html.ep:47 themes/default/templates/index.html.ep:56 themes/default/templates/index.html.ep:57 +#: themes/default/templates/index.html.ep:56 themes/default/templates/index.html.ep:65 themes/default/templates/index.html.ep:66 msgid "%1 days" msgstr "%1 days" -#: themes/default/templates/partial/index.js.ep:26 +#. (stash('ldap_user') +#: themes/default/templates/invitations/invite.mail.ep:2 +msgid "%1 invites you to send him/her files" +msgstr "%1 invites you to send him/her files" + +#. (stash('ldap_user') +#: themes/default/templates/invitations/invite.mail.ep:6 +msgid "%1 invites you to send him/her files through Lufi." +msgstr "%1 invites you to send him/her files through Lufi." + +#. (stash('invitation') +#: themes/default/templates/invitations/notification_files_sent.mail.ep:4 +msgid "%1 sent you files" +msgstr "%1 sent you files" + +#. (stash('invitation') +#: themes/default/templates/invitations/notification_files_sent.mail.ep:8 +msgid "%1 used your invitation to send you files:" +msgstr "%1 used your invitation to send you files:" + +#: lib/Lufi/Controller/Invitation.pm:159 lib/Lufi/Controller/Invitation.pm:84 themes/default/templates/invitations/my_invitations.html.ep:51 themes/default/templates/invitations/my_invitations.html.ep:52 themes/default/templates/invitations/my_invitations.html.ep:53 themes/default/templates/invitations/notification_files_sent.mail.ep:12 +msgid "%A %d %B %Y at %T" +msgstr "%A %d %B %Y at %T" + +#: themes/default/templates/partial/index.js.ep:27 msgid "(max size: XXX)" msgstr "(max size: XXX)" @@ -27,7 +53,7 @@ msgstr "(max size: XXX)" msgid "1 year" msgstr "1 year" -#: themes/default/templates/index.html.ep:4 themes/default/templates/index.html.ep:56 +#: themes/default/templates/index.html.ep:4 themes/default/templates/index.html.ep:65 msgid "24 hours" msgstr "24 hours" @@ -35,27 +61,15 @@ msgstr "24 hours" msgid ":" msgstr ":" -#: themes/default/templates/about.html.ep:16 -msgid "" -"A thank you with a photo of kitten on Diaspora* or " -"Mastodon " -"is cool too ;-)" -msgstr "" -"A thank you with a photo of kitten on Diaspora* or " -"Mastodon " -"is cool too ;-)" - #: themes/default/templates/render.html.ep:42 msgid "Abort" msgstr "Abort" -#: themes/default/templates/layouts/default.html.ep:48 themes/default/templates/layouts/default.html.ep:76 +#: themes/default/templates/layouts/default.html.ep:53 themes/default/templates/layouts/default.html.ep:86 msgid "About" msgstr "About" -#: themes/default/templates/index.html.ep:88 +#: themes/default/templates/index.html.ep:107 msgid "Add a password to file(s)" msgstr "Add a password to file(s)" @@ -67,7 +81,19 @@ msgstr "" "Adding URLs not related to this Lufi instance to the mail body or subject is " "prohibited." -#: themes/default/templates/about.html.ep:18 +#: themes/default/templates/partial/invitations.js.ep:3 +msgid "Are you sure you want to delete the selected invitations?" +msgstr "Are you sure you want to delete the selected invitations?" + +#: themes/default/templates/partial/invitations.js.ep:4 +msgid "" +"Are you sure you want to resend the invitation mail for the selected " +"invitations?" +msgstr "" +"Are you sure you want to resend the invitation mail for the selected " +"invitations?" + +#: themes/default/templates/about.html.ep:17 msgid "" "As Lufi is a free software licensed under of the terms of the AGPLv3, you can " @@ -84,7 +110,7 @@ msgstr "" msgid "Asking for file part XX1 of %1" msgstr "Asking for file part XX1 of %1" -#: themes/default/templates/about.html.ep:21 +#: themes/default/templates/about.html.ep:20 msgid "Back to homepage" msgstr "Back to homepage" @@ -92,7 +118,7 @@ msgstr "Back to homepage" msgid "Bad CSRF token!" msgstr "Bad CSRF token!" -#: lib/Lufi/Controller/Auth.pm:22 lib/Lufi/Controller/Auth.pm:39 +#: lib/Lufi/Controller/Auth.pm:27 lib/Lufi/Controller/Auth.pm:49 msgid "Bad CSRF token." msgstr "Bad CSRF token." @@ -100,11 +126,15 @@ msgstr "Bad CSRF token." msgid "Click here to refresh the page and restart the download." msgstr "Click here to refresh the page and restart the download." -#: themes/default/templates/index.html.ep:98 +#: themes/default/templates/invitations/invite.mail.ep:8 +msgid "Click on the following URL to upload files on Lufi:" +msgstr "Click on the following URL to upload files on Lufi:" + +#: themes/default/templates/index.html.ep:126 msgid "Click to open the file browser" msgstr "Click to open the file browser" -#: themes/default/templates/delays.html.ep:38 +#: themes/default/templates/delays.html.ep:38 themes/default/templates/invitations/my_invitations.html.ep:80 msgid "Close" msgstr "Close" @@ -112,23 +142,27 @@ msgstr "Close" msgid "Comma-separated email addresses" msgstr "Comma-separated email addresses" -#: themes/default/templates/partial/index.js.ep:14 +#: themes/default/templates/index.html.ep:140 +msgid "Compressing zip file…" +msgstr "Compressing zip file…" + +#: themes/default/templates/partial/index.js.ep:15 msgid "Copy all links to clipboard" msgstr "Copy all links to clipboard" -#: themes/default/templates/partial/index.js.ep:17 +#: themes/default/templates/partial/index.js.ep:18 msgid "Copy to clipboard" msgstr "Copy to clipboard" -#: lib/Lufi/Controller/Files.pm:460 +#: lib/Lufi/Controller/Files.pm:507 msgid "Could not delete the file. You are not authenticated." msgstr "Could not delete the file. You are not authenticated." -#: lib/Lufi/Controller/Files.pm:442 +#: lib/Lufi/Controller/Files.pm:489 msgid "Could not find the file. Are you sure of the URL and the token?" msgstr "Could not find the file. Are you sure of the URL and the token?" -#: lib/Lufi/Controller/Files.pm:353 +#: lib/Lufi/Controller/Files.pm:400 msgid "Could not find the file. Are you sure of the URL?" msgstr "Could not find the file. Are you sure of the URL?" @@ -136,7 +170,19 @@ msgstr "Could not find the file. Are you sure of the URL?" msgid "Counter" msgstr "Counter" -#: themes/default/templates/files.html.ep:29 themes/default/templates/index.html.ep:81 +#: themes/default/templates/index.html.ep:100 +msgid "Create a zip archive with the files before uploading?" +msgstr "Create a zip archive with the files before uploading?" + +#: themes/default/templates/invitations/my_invitations.html.ep:26 +msgid "Created at" +msgstr "Created at" + +#: themes/default/templates/invitations/my_invitations.html.ep:14 +msgid "Delete" +msgstr "Delete" + +#: themes/default/templates/files.html.ep:29 themes/default/templates/index.html.ep:90 msgid "Delete at first download?" msgstr "Delete at first download?" @@ -144,7 +190,7 @@ msgstr "Delete at first download?" msgid "Delete selected files" msgstr "Delete selected files" -#: themes/default/templates/files.html.ep:32 themes/default/templates/partial/index.js.ep:18 +#: themes/default/templates/files.html.ep:32 themes/default/templates/partial/index.js.ep:19 msgid "Deletion link" msgstr "Deletion link" @@ -156,7 +202,7 @@ msgstr "" "Don't worry: if a user begins to download the file before the expiration and " "the download ends after the expiration, he will be able to get the file." -#: themes/default/templates/partial/index.js.ep:20 themes/default/templates/render.html.ep:28 +#: themes/default/templates/partial/index.js.ep:21 themes/default/templates/render.html.ep:28 msgid "Download" msgstr "Download" @@ -164,7 +210,7 @@ msgstr "Download" msgid "Download aborted." msgstr "Download aborted." -#: themes/default/templates/files.html.ep:27 themes/default/templates/partial/index.js.ep:19 +#: themes/default/templates/files.html.ep:27 themes/default/templates/partial/index.js.ep:20 msgid "Download link" msgstr "Download link" @@ -182,10 +228,14 @@ msgstr "" "people you want to share the file with and a deletion link, allowing you to " "delete the file whenever you want." -#: themes/default/templates/index.html.ep:94 +#: themes/default/templates/index.html.ep:122 msgid "Drop files here" msgstr "Drop files here" +#: themes/default/templates/invitations/invite.html.ep:40 +msgid "Email address of your guest" +msgstr "Email address of your guest" + #: themes/default/templates/mail.html.ep:39 msgid "Email body" msgstr "Email body" @@ -198,26 +248,30 @@ msgstr "Email subject" msgid "Emails" msgstr "Emails" -#: themes/default/templates/partial/index.js.ep:21 +#: themes/default/templates/partial/index.js.ep:22 msgid "Encrypting part XX1 of XX2" msgstr "Encrypting part XX1 of XX2" -#: lib/Lufi/Controller/Files.pm:242 +#: lib/Lufi/Controller/Files.pm:289 msgid "Error: the file existed but was deleted." msgstr "Error: the file existed but was deleted." -#: lib/Lufi/Controller/Files.pm:322 +#: lib/Lufi/Controller/Files.pm:369 msgid "Error: the file has not been sent entirely." msgstr "Error: the file has not been sent entirely." -#: lib/Lufi/Controller/Files.pm:332 +#: lib/Lufi/Controller/Files.pm:379 msgid "Error: unable to find the file. Are you sure of your URL?" msgstr "Error: unable to find the file. Are you sure of your URL?" -#: themes/default/templates/partial/index.js.ep:22 +#: themes/default/templates/partial/index.js.ep:23 msgid "Expiration:" msgstr "Expiration:" +#: themes/default/templates/invitations/my_invitations.html.ep:27 +msgid "Expire at" +msgstr "Expire at" + #: themes/default/templates/files.html.ep:31 msgid "Expires at" msgstr "Expires at" @@ -226,7 +280,7 @@ msgstr "Expires at" msgid "Export localStorage data" msgstr "Export localStorage data" -#: lib/Lufi/Controller/Files.pm:424 +#: lib/Lufi/Controller/Files.pm:471 msgid "File deleted" msgstr "File deleted" @@ -234,15 +288,27 @@ msgstr "File deleted" msgid "File name" msgstr "File name" -#: themes/default/templates/index.html.ep:71 +#: themes/default/templates/invitations/my_invitations.html.ep:61 +msgid "Files" +msgstr "Files" + +#: themes/default/templates/index.html.ep:80 msgid "Files deleted at first download" msgstr "Files deleted at first download" +#: themes/default/templates/invitations/my_invitations.html.ep:28 +msgid "Files sent at" +msgstr "Files sent at" + +#: themes/default/templates/partial/invitations.js.ep:8 +msgid "Files sent in invitation XX1 by XX2" +msgstr "Files sent in invitation XX1 by XX2" + #: themes/default/templates/partial/render.js.ep:8 msgid "Get the file" msgstr "Get the file" -#: themes/default/templates/about.html.ep:19 +#: themes/default/templates/about.html.ep:18 msgid "" "Get the source code on the official repository or on its the official repository or on its Github mirror" +#: themes/default/templates/invitations/my_invitations.html.ep:24 +msgid "Guest mail" +msgstr "Guest mail" + +#. (ucfirst(stash('invitation') +#: themes/default/templates/invitations/notification_files_sent.mail.ep:6 +msgid "Hello %1," +msgstr "Hello %1," + +#: themes/default/templates/invitations/invite.mail.ep:4 +msgid "Hello," +msgstr "Hello," + #: themes/default/templates/partial/mail.js.ep:35 msgid "Hello,\\n\\nHere's some files I want to share with you:\\n" msgstr "Hello,\\n\\nHere's some files I want to share with you:\\n" @@ -260,11 +339,15 @@ msgstr "Hello,\\n\\nHere's some files I want to share with you:\\n" msgid "Here's some files" msgstr "Here's some files" -#: themes/default/templates/partial/index.js.ep:24 +#: themes/default/templates/partial/invitations.js.ep:7 +msgid "Hide hidden invitations" +msgstr "Hide hidden invitations" + +#: themes/default/templates/partial/index.js.ep:25 msgid "Hit Enter, then Ctrl+C to copy all the download links" msgstr "Hit Enter, then Ctrl+C to copy all the download links" -#: themes/default/templates/partial/index.js.ep:23 +#: themes/default/templates/partial/index.js.ep:24 msgid "Hit Enter, then Ctrl+C to copy the download link" msgstr "Hit Enter, then Ctrl+C to copy the download link" @@ -272,7 +355,11 @@ msgstr "Hit Enter, then Ctrl+C to copy the download link" msgid "How does it work?" msgstr "How does it work?" -#: themes/default/templates/about.html.ep:17 +#: themes/default/templates/invitations/invite.html.ep:46 +msgid "How many days would you like the invitation to be valid?" +msgstr "How many days would you like the invitation to be valid?" + +#: themes/default/templates/about.html.ep:16 msgid "How to install the software on my server?" msgstr "How to install the software on my server?" @@ -296,7 +383,7 @@ msgstr "" msgid "Import localStorage data" msgstr "Import localStorage data" -#: themes/default/templates/index.html.ep:44 +#: themes/default/templates/index.html.ep:53 msgid "Important: more information on delays" msgstr "Important: more information on delays" @@ -304,6 +391,24 @@ msgstr "Important: more information on delays" msgid "Information about delays" msgstr "Information about delays" +#: themes/default/templates/invitations/my_invitations.html.ep:12 +msgid "Invert selection" +msgstr "Invert selection" + +#. ($i->guest_mail, $url) +#: lib/Lufi/Controller/Invitation.pm:171 +msgid "Invitation resent to %1.
URL: %2" +msgstr "Invitation resent to %1.
URL: %2" + +#. ($invitation->guest_mail, $url) +#: lib/Lufi/Controller/Invitation.pm:87 +msgid "Invitation sent to %1.
URL: %2" +msgstr "Invitation sent to %1.
URL: %2" + +#: themes/default/templates/invitations/invite.html.ep:27 themes/default/templates/layouts/default.html.ep:36 themes/default/templates/layouts/default.html.ep:69 +msgid "Invite a guest" +msgstr "Invite a guest" + #: themes/default/templates/partial/render.js.ep:6 msgid "" "It seems that the key in your URL is incorrect. Please, verify your URL." @@ -314,7 +419,7 @@ msgstr "" msgid "Javascript is disabled. You won't be able to use Lufi." msgstr "Javascript is disabled. You won't be able to use Lufi." -#: themes/default/templates/layouts/default.html.ep:40 themes/default/templates/layouts/default.html.ep:68 +#: themes/default/templates/layouts/default.html.ep:44 themes/default/templates/layouts/default.html.ep:46 themes/default/templates/layouts/default.html.ep:77 themes/default/templates/layouts/default.html.ep:79 msgid "Language" msgstr "Language" @@ -322,7 +427,7 @@ msgstr "Language" msgid "Login" msgstr "Login" -#: themes/default/templates/layouts/default.html.ep:53 themes/default/templates/layouts/default.html.ep:78 +#: themes/default/templates/layouts/default.html.ep:58 themes/default/templates/layouts/default.html.ep:91 msgid "Logout" msgstr "Logout" @@ -334,16 +439,30 @@ msgstr "Lufi is a free (as in free speech) file hosting software." msgid "Mail" msgstr "Mail" -#: themes/default/templates/files.html.ep:3 themes/default/templates/layouts/default.html.ep:34 themes/default/templates/layouts/default.html.ep:62 +#: themes/default/templates/files.html.ep:3 themes/default/templates/layouts/default.html.ep:34 themes/default/templates/layouts/default.html.ep:67 msgid "My files" msgstr "My files" +#: themes/default/templates/invitations/my_invitations.html.ep:5 themes/default/templates/layouts/default.html.ep:37 themes/default/templates/layouts/default.html.ep:70 +msgid "My invitations" +msgstr "My invitations" + +#: themes/default/templates/invitations/notification_files_sent.mail.ep:17 +msgid "" +"NB: this list includes the list of files that have already been sent to you." +msgstr "" +"NB: this list includes the list of files that have already been sent to you." + +#: themes/default/templates/index.html.ep:115 +msgid "Name of the zip file" +msgstr "Name of the zip file" + #. (format_bytes($json->{size}) -#: lib/Lufi/Controller/Files.pm:79 +#: lib/Lufi/Controller/Files.pm:108 msgid "No enough space available on the server for this file (size: %1)." msgstr "No enough space available on the server for this file (size: %1)." -#: themes/default/templates/partial/files.js.ep:10 themes/default/templates/partial/index.js.ep:27 +#: themes/default/templates/partial/files.js.ep:10 themes/default/templates/partial/index.js.ep:28 msgid "No expiration delay" msgstr "No expiration delay" @@ -357,7 +476,7 @@ msgstr "" "stored in localStorage: if you delete your localStorage data, you'll lose " "this list." -#: themes/default/templates/index.html.ep:87 themes/default/templates/login.html.ep:21 themes/default/templates/render.html.ep:26 +#: themes/default/templates/index.html.ep:106 themes/default/templates/login.html.ep:21 themes/default/templates/render.html.ep:26 msgid "Password" msgstr "Password" @@ -374,7 +493,7 @@ msgstr "" "Please wait while we are getting your file. We first need to download and " "decrypt all parts before you can get it." -#: lib/Lufi/Controller/Auth.pm:28 +#: lib/Lufi/Controller/Auth.pm:38 msgid "" "Please, check your credentials or your right to access this service: unable " "to authenticate." @@ -390,20 +509,40 @@ msgstr "Privacy" msgid "Purge expired files from localStorage" msgstr "Purge expired files from localStorage" -#: themes/default/templates/layouts/default.html.ep:31 themes/default/templates/layouts/default.html.ep:59 +#: themes/default/templates/invitations/notification_files_sent.mail.ep:20 +msgid "Regards," +msgstr "Regards," + +#: themes/default/templates/invitations/invite.mail.ep:15 +msgid "Regards." +msgstr "Regards." + +#: themes/default/templates/layouts/default.html.ep:31 themes/default/templates/layouts/default.html.ep:64 msgid "Report file" msgstr "Report file" +#: themes/default/templates/invitations/my_invitations.html.ep:15 +msgid "Resend invitation mail" +msgstr "Resend invitation mail" + +#: themes/default/templates/invitations/my_invitations.html.ep:9 +msgid "Rows in purple mean that the invitations have expired." +msgstr "Rows in purple mean that the invitations have expired." + #: themes/default/templates/files.html.ep:9 msgid "" "Rows in red mean that the files have expired and are no longer available." msgstr "" "Rows in red mean that the files have expired and are no longer available." -#: themes/default/templates/partial/index.js.ep:25 +#: themes/default/templates/partial/index.js.ep:26 msgid "Send all links by email" msgstr "Send all links by email" +#: themes/default/templates/invitations/invite.html.ep:50 +msgid "Send the invitation" +msgstr "Send the invitation" + #: themes/default/templates/mail.html.ep:46 msgid "Send with this server" msgstr "Send with this server" @@ -412,7 +551,7 @@ msgstr "Send with this server" msgid "Send with your own mail software" msgstr "Send with your own mail software" -#: themes/default/templates/partial/index.js.ep:28 +#: themes/default/templates/partial/index.js.ep:29 msgid "" "Sending part XX1 of XX2. Please, be patient, the progress bar can take a " "while to move." @@ -425,18 +564,50 @@ msgstr "" msgid "Share your files in total privacy on %1" msgstr "Share your files in total privacy on %1" -#: themes/default/templates/layouts/default.html.ep:36 themes/default/templates/layouts/default.html.ep:64 themes/default/templates/login.html.ep:27 themes/default/templates/logout.html.ep:17 +#: themes/default/templates/invitations/my_invitations.html.ep:13 themes/default/templates/partial/invitations.js.ep:9 +msgid "Show hidden invitations" +msgstr "Show hidden invitations" + +#: themes/default/templates/partial/render.js.ep:11 +msgid "Show zip content" +msgstr "Show zip content" + +#: themes/default/templates/layouts/default.html.ep:40 themes/default/templates/layouts/default.html.ep:73 themes/default/templates/login.html.ep:28 themes/default/templates/logout.html.ep:17 msgid "Signin" msgstr "Signin" -#: themes/default/templates/index.html.ep:37 +#: lib/Lufi/Controller/Invitation.pm:283 themes/default/templates/invitations/exception.html.ep:16 +msgid "" +"Sorry, the invitation doesn’t exist. Are you sure you are on the right URL?" +msgstr "" +"Sorry, the invitation doesn’t exist. Are you sure you are on the right URL?" + +#: themes/default/templates/index.html.ep:46 msgid "Sorry, the uploading is currently disabled. Please try again later." msgstr "Sorry, the uploading is currently disabled. Please try again later." -#: lib/Lufi/Controller/Files.pm:53 +#: lib/Lufi/Controller/Files.pm:82 msgid "Sorry, uploading is disabled." msgstr "Sorry, uploading is disabled." +#: themes/default/templates/invitations/exception.html.ep:7 +msgid "Sorry, your invitation has expired or has been deleted." +msgstr "Sorry, your invitation has expired or has been deleted." + +#. ($invit->ldap_user_mail) +#: lib/Lufi/Controller/Files.pm:122 +msgid "" +"Sorry, your invitation has expired or has been deleted. Please contact %1 to " +"have another invitation." +msgstr "" +"Sorry, your invitation has expired or has been deleted. Please contact %1 to " +"have another invitation." + +#. ($invitation->ldap_user_mail) +#: lib/Lufi/Controller/Invitation.pm:276 +msgid "The URLs of your files have been sent by email to %1." +msgstr "The URLs of your files have been sent by email to %1." + #: themes/default/templates/about.html.ep:7 msgid "" "The administrator can only see the file's name, its size and its mimetype " @@ -465,7 +636,12 @@ msgstr "The email body can't be empty." msgid "The email subject can't be empty." msgstr "The email subject can't be empty." -#: lib/Lufi/Controller/Files.pm:421 +#. ($expire_at, $max_expire_at) +#: lib/Lufi/Controller/Invitation.pm:51 +msgid "The expiration delay (%1) is not between 1 and %2 days." +msgstr "The expiration delay (%1) is not between 1 and %2 days." + +#: lib/Lufi/Controller/Files.pm:468 msgid "The file has already been deleted" msgstr "The file has already been deleted" @@ -482,10 +658,46 @@ msgstr "" msgid "The following email addresses are not valid: %1" msgstr "The following email addresses are not valid: %1" -#: themes/default/templates/partial/index.js.ep:15 +#. ($guest_mail) +#: lib/Lufi/Controller/Invitation.pm:48 +msgid "The guest email address (%1) is unvalid." +msgstr "The guest email address (%1) is unvalid." + +#. ($i->token, $i->guest_mail) +#: lib/Lufi/Controller/Invitation.pm:150 +msgid "" +"The invitation %1 can’t be resent: %2 has already sent files.
Please " +"create a new invitation." +msgstr "" +"The invitation %1 can’t be resent: %2 has already sent files.
Please " +"create a new invitation." + +#. ($i->token) +#: lib/Lufi/Controller/Invitation.pm:130 +msgid "The invitation %1 has been deleted." +msgstr "The invitation %1 has been deleted." + +#. (stash('user_mail') +#: themes/default/templates/invitations/invite.html.ep:34 +msgid "The invitation mail will be send from your email address (%1)." +msgstr "The invitation mail will be sent from your email address (%1)." + +#: themes/default/templates/partial/index.js.ep:16 msgid "The link(s) has been copied to your clipboard" msgstr "The link(s) has been copied to your clipboard" +#. (stash('invitation') +#: themes/default/templates/index.html.ep:30 +msgid "" +"The link(s) of your file(s) will automatically be sent by mail to %1 (%2)" +msgstr "" +"The link(s) of your file(s) will automatically be sent by mail to %1 (%2)" + +#. (stash('ldap_user') +#: themes/default/templates/invitations/invite.mail.ep:11 +msgid "The links of your file(s) will automatically be sent by mail to %1." +msgstr "The link(s) of your file(s) will automatically be sent by mail to %1." + #: lib/Lufi/Controller/Mail.pm:97 msgid "The mail has been sent." msgstr "The mail has been sent." @@ -493,18 +705,12 @@ msgstr "The mail has been sent." #: themes/default/templates/about.html.ep:15 msgid "" "The original (and only for now) author is Luc Didry. If you want to support him, you can do it " -"via Tipeee " -"or via Liberapay." +"class=\"classic\">Luc Didry." msgstr "" "The original (and only for now) author is Luc Didry. If you want to support him, you can do it " -"via Tipeee " -"or via Liberapay." +"class=\"classic\">Luc Didry." -#: lib/Lufi/Controller/Files.pm:189 +#: lib/Lufi/Controller/Files.pm:236 msgid "" "The server was unable to find the file record to add your file part to. " "Please, contact the administrator." @@ -512,12 +718,21 @@ msgstr "" "The server was unable to find the file record to add your file part to. " "Please, contact the administrator." -#: lib/Lufi/Controller/Files.pm:248 +#: lib/Lufi/Controller/Files.pm:295 msgid "" "This file has been deactivated by the admins. Contact them to know why." msgstr "" "This file has been deactivated by the admins. Contact them to know why." +#: themes/default/templates/invitations/my_invitations.html.ep:46 themes/default/templates/partial/invitations.js.ep:6 +msgid "This invitation is normally hidden" +msgstr "This invitation is normally hidden" + +#. (stash('expires') +#: themes/default/templates/invitations/invite.mail.ep:13 +msgid "This invitation is valid until %1." +msgstr "This invitation is valid until %1." + #: themes/default/templates/delays.html.ep:10 msgid "" "This server sets limitations according to the file size. The expiration " @@ -528,12 +743,20 @@ msgstr "" "delay of your file will be the minimum between what you choose and the " "following limitations:" -#: themes/default/templates/partial/index.js.ep:16 +#: themes/default/templates/invitations/my_invitations.html.ep:16 +msgid "Toggle visibility" +msgstr "Toggle visibility" + +#: themes/default/templates/invitations/my_invitations.html.ep:25 +msgid "URL" +msgstr "URL" + +#: themes/default/templates/partial/index.js.ep:17 msgid "Unable to copy the link(s) to your clipboard" msgstr "Unable to copy the link(s) to your clipboard" #. ($short) -#: lib/Lufi/Controller/Files.pm:392 +#: lib/Lufi/Controller/Files.pm:439 msgid "" "Unable to get counter for %1. The file does not exists. It will be removed " "from your localStorage." @@ -542,28 +765,32 @@ msgstr "" "from your localStorage." #. ($short) -#: lib/Lufi/Controller/Files.pm:382 +#: lib/Lufi/Controller/Files.pm:429 msgid "Unable to get counter for %1. The token is invalid." msgstr "Unable to get counter for %1. The token is invalid." #. ($short) -#: lib/Lufi/Controller/Files.pm:402 +#: lib/Lufi/Controller/Files.pm:449 msgid "Unable to get counter for %1. You are not authenticated." msgstr "Unable to get counter for %1. You are not authenticated." -#: themes/default/templates/layouts/default.html.ep:33 themes/default/templates/layouts/default.html.ep:61 +#: themes/default/templates/layouts/default.html.ep:33 themes/default/templates/layouts/default.html.ep:66 msgid "Upload files" msgstr "Upload files" +#: themes/default/templates/index.html.ep:145 +msgid "Upload generated zip file" +msgstr "Upload generated zip file" + #: themes/default/templates/files.html.ep:30 msgid "Uploaded at" msgstr "Uploaded at" -#: themes/default/templates/index.html.ep:103 +#: themes/default/templates/index.html.ep:153 msgid "Uploaded files" msgstr "Uploaded files" -#: themes/default/templates/partial/index.js.ep:29 +#: themes/default/templates/partial/index.js.ep:30 msgid "Websocket communication error" msgstr "Websocket communication error" @@ -575,6 +802,18 @@ msgstr "What is Lufi?" msgid "Who wrote this software?" msgstr "Who wrote this software?" +#: themes/default/templates/partial/index.js.ep:13 +msgid "XXX file has been added to upload queue." +msgstr "XXX file has been added to upload queue." + +#: themes/default/templates/invitations/invite.html.ep:30 +msgid "" +"You can invite someone to send you files through this Lufi instance even if " +"they don’t have an account on it." +msgstr "" +"You can invite someone to send you files through this Lufi instance even if " +"they don’t have an account on it." + #: themes/default/templates/about.html.ep:11 msgid "" "You can see the list of your files by clicking on the \"My files\" link at " @@ -613,7 +852,7 @@ msgstr "" "You have attempted to leave this page. The download will be canceled. Are " "you sure?" -#: themes/default/templates/partial/index.js.ep:13 +#: themes/default/templates/partial/index.js.ep:14 msgid "" "You have attempted to leave this page. The upload will be canceled. Are you " "sure?" @@ -629,20 +868,22 @@ msgstr "You have been successfully logged out." msgid "You must give email addresses." msgstr "You must give email addresses." -#: themes/default/templates/index.html.ep:29 +#: themes/default/templates/index.html.ep:38 msgid "" -"Your browser has not enough entropy to generate a strong encryption key. " -"Please wait (it's better if you do things on your computer while waiting)." +"Your browser does not have enough entropy to generate a strong encryption " +"key. Please wait (it's better if you do things on your computer while " +"waiting)." msgstr "" -"Your browser has not enough entropy to generate a strong encryption key. " -"Please wait (it's better if you do things on your computer while waiting)." +"Your browser does not have enough entropy to generate a strong encryption " +"key. Please wait (it's better if you do things on your computer while " +"waiting)." #. (format_bytes($json->{size}) -#: lib/Lufi/Controller/Files.pm:66 +#: lib/Lufi/Controller/Files.pm:95 msgid "Your file is too big: %1 (maximum size allowed: %2)" msgstr "Your file is too big: %1 (maximum size allowed: %2)" -#: lib/Lufi/Controller/Files.pm:304 +#: lib/Lufi/Controller/Files.pm:351 msgid "Your password is not valid. Please refresh the page to retry." msgstr "Your password is not valid. Please refresh the page to retry." @@ -660,6 +901,10 @@ msgstr "between %1 and %2, the file will be kept forever." msgid "deadline: " msgstr "deadline: " +#: themes/default/templates/partial/invitations.js.ep:5 +msgid "expires on XXX" +msgstr "expires on XXX" + #. (format_bytes($keys[$i]) #: themes/default/templates/delays.html.ep:26 msgid "for %1 and more, the file will be kept %2 day(s)" @@ -674,6 +919,11 @@ msgstr "for %1 and more, the file will be kept forever." msgid "no time limit" msgstr "no time limit" -#: themes/default/templates/index.html.ep:96 +#: themes/default/templates/index.html.ep:124 msgid "or" msgstr "or" + +#. ($e->{name}, format_bytes($e->{size}) +#: themes/default/templates/invitations/notification_files_sent.mail.ep:12 +msgid "— %1 (%2), that will expire on %3" +msgstr "— %1 (%2), that will expire on %3" diff --git a/themes/default/lib/Lufi/I18N/fr.po b/themes/default/lib/Lufi/I18N/fr.po index d9218af..cbda1ec 100644 --- a/themes/default/lib/Lufi/I18N/fr.po +++ b/themes/default/lib/Lufi/I18N/fr.po @@ -3,6 +3,7 @@ # This file is distributed under the same license as the Lufi package. # Luc Didry , 2015. # Luc Didry , 2018. #zanata +# Luc Didry , 2019. #zanata msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" @@ -10,20 +11,44 @@ msgstr "" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"PO-Revision-Date: 2018-10-28 01:59+0000\n" +"PO-Revision-Date: 2019-08-04 05:33+0000\n" "Language-Team: \n" "X-Generator: Zanata 4.6.2\n" -"Last-Translator: Copied by Zanata \n" +"Last-Translator: Luc Didry \n" "Plural-Forms: nplurals=2; plural=(n > 1);\n" "Language: fr\n" #. ($delay) #. (max_delay) -#: themes/default/templates/index.html.ep:47 themes/default/templates/index.html.ep:56 themes/default/templates/index.html.ep:57 +#: themes/default/templates/index.html.ep:56 themes/default/templates/index.html.ep:65 themes/default/templates/index.html.ep:66 msgid "%1 days" msgstr "%1 jours" -#: themes/default/templates/partial/index.js.ep:26 +#. (stash('ldap_user') +#: themes/default/templates/invitations/invite.mail.ep:2 +msgid "%1 invites you to send him/her files" +msgstr "%1 vous invite à lui envoyer des fichiers" + +#. (stash('ldap_user') +#: themes/default/templates/invitations/invite.mail.ep:6 +msgid "%1 invites you to send him/her files through Lufi." +msgstr "%1 vous invite à lui envoyer des fichiers via Lufi." + +#. (stash('invitation') +#: themes/default/templates/invitations/notification_files_sent.mail.ep:4 +msgid "%1 sent you files" +msgstr "%1 vous a envoyé des fichiers" + +#. (stash('invitation') +#: themes/default/templates/invitations/notification_files_sent.mail.ep:8 +msgid "%1 used your invitation to send you files:" +msgstr "%1 a utilisé votre invitation pour vous envoyer des fichiers :" + +#: lib/Lufi/Controller/Invitation.pm:159 lib/Lufi/Controller/Invitation.pm:84 themes/default/templates/invitations/my_invitations.html.ep:51 themes/default/templates/invitations/my_invitations.html.ep:52 themes/default/templates/invitations/my_invitations.html.ep:53 themes/default/templates/invitations/notification_files_sent.mail.ep:12 +msgid "%A %d %B %Y at %T" +msgstr "%A %d %B %Y à %T" + +#: themes/default/templates/partial/index.js.ep:27 msgid "(max size: XXX)" msgstr "(taille max: XXX)" @@ -31,7 +56,7 @@ msgstr "(taille max: XXX)" msgid "1 year" msgstr "1 an" -#: themes/default/templates/index.html.ep:4 themes/default/templates/index.html.ep:56 +#: themes/default/templates/index.html.ep:4 themes/default/templates/index.html.ep:65 msgid "24 hours" msgstr "24 heures" @@ -39,27 +64,15 @@ msgstr "24 heures" msgid ":" msgstr " :" -#: themes/default/templates/about.html.ep:16 -msgid "" -"A thank you with a photo of kitten on Diaspora* or " -"Mastodon " -"is cool too ;-)" -msgstr "" -"Un merci avec une photo de chaton sur Diaspora* ou " -"Mastodon " -"fait aussi l’affaire ;-)" - #: themes/default/templates/render.html.ep:42 msgid "Abort" msgstr "Abandonner" -#: themes/default/templates/layouts/default.html.ep:48 themes/default/templates/layouts/default.html.ep:76 +#: themes/default/templates/layouts/default.html.ep:53 themes/default/templates/layouts/default.html.ep:86 msgid "About" msgstr "À propos" -#: themes/default/templates/index.html.ep:88 +#: themes/default/templates/index.html.ep:107 msgid "Add a password to file(s)" msgstr "Ajouter un mot de passe au(x) fichier(s)" @@ -71,7 +84,19 @@ msgstr "" "L’ajout d’URL non liées à cette instance Lufi au corps ou au sujet du mail " "est interdit." -#: themes/default/templates/about.html.ep:18 +#: themes/default/templates/partial/invitations.js.ep:3 +msgid "Are you sure you want to delete the selected invitations?" +msgstr "Êtes-vous sûr·e de vouloir supprimer les invitations sélectionnées ?" + +#: themes/default/templates/partial/invitations.js.ep:4 +msgid "" +"Are you sure you want to resend the invitation mail for the selected " +"invitations?" +msgstr "" +"Êtes-vous sûr·e de vouloir renvoyer le courriel d’invitation pour les " +"invitations sélectionnées ?" + +#: themes/default/templates/about.html.ep:17 msgid "" "As Lufi is a free software licensed under of the terms of the AGPLv3, you can " @@ -89,7 +114,7 @@ msgstr "" msgid "Asking for file part XX1 of %1" msgstr "Demande de récupération du fragment de fichier XX1 sur %1" -#: themes/default/templates/about.html.ep:21 +#: themes/default/templates/about.html.ep:20 msgid "Back to homepage" msgstr "Retour à la page d’accueil" @@ -97,7 +122,7 @@ msgstr "Retour à la page d’accueil" msgid "Bad CSRF token!" msgstr "Mauvais jeton CSRF !" -#: lib/Lufi/Controller/Auth.pm:22 lib/Lufi/Controller/Auth.pm:39 +#: lib/Lufi/Controller/Auth.pm:27 lib/Lufi/Controller/Auth.pm:49 msgid "Bad CSRF token." msgstr "Jeton CSRF invalide." @@ -105,11 +130,15 @@ msgstr "Jeton CSRF invalide." msgid "Click here to refresh the page and restart the download." msgstr "Cliquez ici pour rafraîchir la page et redémarrer le téléchargement." -#: themes/default/templates/index.html.ep:98 +#: themes/default/templates/invitations/invite.mail.ep:8 +msgid "Click on the following URL to upload files on Lufi:" +msgstr "Cliquez sur l’URL suivante pour envoyer des fichiers sur Lufi :" + +#: themes/default/templates/index.html.ep:126 msgid "Click to open the file browser" msgstr "Cliquez pour ouvrir le navigateur de fichiers" -#: themes/default/templates/delays.html.ep:38 +#: themes/default/templates/delays.html.ep:38 themes/default/templates/invitations/my_invitations.html.ep:80 msgid "Close" msgstr "Fermer" @@ -117,25 +146,29 @@ msgstr "Fermer" msgid "Comma-separated email addresses" msgstr "Adresses mails séparées par des virgules" -#: themes/default/templates/partial/index.js.ep:14 +#: themes/default/templates/index.html.ep:140 +msgid "Compressing zip file…" +msgstr "Compression du fichier zip…" + +#: themes/default/templates/partial/index.js.ep:15 msgid "Copy all links to clipboard" msgstr "Copier tous les liens dans le presse-papier" -#: themes/default/templates/partial/index.js.ep:17 +#: themes/default/templates/partial/index.js.ep:18 msgid "Copy to clipboard" msgstr "Copier dans le presse-papier" -#: lib/Lufi/Controller/Files.pm:460 +#: lib/Lufi/Controller/Files.pm:507 msgid "Could not delete the file. You are not authenticated." msgstr "Impossible de supprimer le fichier. Vous n’êtes pas connecté·e." -#: lib/Lufi/Controller/Files.pm:442 +#: lib/Lufi/Controller/Files.pm:489 msgid "Could not find the file. Are you sure of the URL and the token?" msgstr "" "Impossible de retrouver le fichier. Êtes-vous sûr(e) que l’URL et le jeton " "sont les bons ?" -#: lib/Lufi/Controller/Files.pm:353 +#: lib/Lufi/Controller/Files.pm:400 msgid "Could not find the file. Are you sure of the URL?" msgstr "" "Impossible de retrouver le fichier. Êtes-vous sûr(e) que l’URL est la " @@ -145,7 +178,19 @@ msgstr "" msgid "Counter" msgstr "Compteur" -#: themes/default/templates/files.html.ep:29 themes/default/templates/index.html.ep:81 +#: themes/default/templates/index.html.ep:100 +msgid "Create a zip archive with the files before uploading?" +msgstr "Créer une archive zip avec les fichiers avant l’envoi ?" + +#: themes/default/templates/invitations/my_invitations.html.ep:26 +msgid "Created at" +msgstr "Créée le " + +#: themes/default/templates/invitations/my_invitations.html.ep:14 +msgid "Delete" +msgstr "Supprimer" + +#: themes/default/templates/files.html.ep:29 themes/default/templates/index.html.ep:90 msgid "Delete at first download?" msgstr "Supprimer après le premier téléchargement ?" @@ -153,7 +198,7 @@ msgstr "Supprimer après le premier téléchargement ?" msgid "Delete selected files" msgstr "Supprimer les fichiers sélectionnés" -#: themes/default/templates/files.html.ep:32 themes/default/templates/partial/index.js.ep:18 +#: themes/default/templates/files.html.ep:32 themes/default/templates/partial/index.js.ep:19 msgid "Deletion link" msgstr "Lien de suppression" @@ -166,7 +211,7 @@ msgstr "" "avant son expiration et que le téléchargement se termine après l’expiration, " "l’utilisateur pourra quand même récupérer le fichier." -#: themes/default/templates/partial/index.js.ep:20 themes/default/templates/render.html.ep:28 +#: themes/default/templates/partial/index.js.ep:21 themes/default/templates/render.html.ep:28 msgid "Download" msgstr "Télécharger" @@ -174,7 +219,7 @@ msgstr "Télécharger" msgid "Download aborted." msgstr "Téléchargement abandonné." -#: themes/default/templates/files.html.ep:27 themes/default/templates/partial/index.js.ep:19 +#: themes/default/templates/files.html.ep:27 themes/default/templates/partial/index.js.ep:20 msgid "Download link" msgstr "Lien de téléchargement" @@ -192,10 +237,14 @@ msgstr "" "lien de téléchargement et un lien pour supprimer le fichier quand vous le " "souhaitez." -#: themes/default/templates/index.html.ep:94 +#: themes/default/templates/index.html.ep:122 msgid "Drop files here" msgstr "Glissez vos fichiers ici" +#: themes/default/templates/invitations/invite.html.ep:40 +msgid "Email address of your guest" +msgstr "Adresse de courriel de votre invité·e" + #: themes/default/templates/mail.html.ep:39 msgid "Email body" msgstr "Corps du mail" @@ -208,27 +257,31 @@ msgstr "Sujet du mail" msgid "Emails" msgstr "Mails" -#: themes/default/templates/partial/index.js.ep:21 +#: themes/default/templates/partial/index.js.ep:22 msgid "Encrypting part XX1 of XX2" msgstr "Chiffrement du fragment XX1 sur XX2" -#: lib/Lufi/Controller/Files.pm:242 +#: lib/Lufi/Controller/Files.pm:289 msgid "Error: the file existed but was deleted." msgstr "Erreur : le fichier existait mais a été supprimé" -#: lib/Lufi/Controller/Files.pm:322 +#: lib/Lufi/Controller/Files.pm:369 msgid "Error: the file has not been sent entirely." msgstr "Erreur : le fichier n’a pas été envoyé dans son intégralité" -#: lib/Lufi/Controller/Files.pm:332 +#: lib/Lufi/Controller/Files.pm:379 msgid "Error: unable to find the file. Are you sure of your URL?" msgstr "" "Erreur : impossible de retrouver le fichier. Êtes-vous sûr(e) de l’URL ?" -#: themes/default/templates/partial/index.js.ep:22 +#: themes/default/templates/partial/index.js.ep:23 msgid "Expiration:" msgstr "Expiration :" +#: themes/default/templates/invitations/my_invitations.html.ep:27 +msgid "Expire at" +msgstr "Expire le" + #: themes/default/templates/files.html.ep:31 msgid "Expires at" msgstr "Expire le" @@ -237,7 +290,7 @@ msgstr "Expire le" msgid "Export localStorage data" msgstr "Exporter les données localStorage" -#: lib/Lufi/Controller/Files.pm:424 +#: lib/Lufi/Controller/Files.pm:471 msgid "File deleted" msgstr "Fichier supprimé" @@ -245,15 +298,27 @@ msgstr "Fichier supprimé" msgid "File name" msgstr "Nom du fichier" -#: themes/default/templates/index.html.ep:71 +#: themes/default/templates/invitations/my_invitations.html.ep:61 +msgid "Files" +msgstr "Fichiers" + +#: themes/default/templates/index.html.ep:80 msgid "Files deleted at first download" msgstr "Fichiers supprimés au premier téléchargement" +#: themes/default/templates/invitations/my_invitations.html.ep:28 +msgid "Files sent at" +msgstr "Fichiers envoyés le" + +#: themes/default/templates/partial/invitations.js.ep:8 +msgid "Files sent in invitation XX1 by XX2" +msgstr "Fichiers envoyés via l’invitation XX1 par XX2" + #: themes/default/templates/partial/render.js.ep:8 msgid "Get the file" msgstr "Récupérer le fichier" -#: themes/default/templates/about.html.ep:19 +#: themes/default/templates/about.html.ep:18 msgid "" "Get the source code on the official repository or on its le dépôt officiel ou sur son miroir Github." +#: themes/default/templates/invitations/my_invitations.html.ep:24 +msgid "Guest mail" +msgstr "Courriel de l’invité·e" + +#. (ucfirst(stash('invitation') +#: themes/default/templates/invitations/notification_files_sent.mail.ep:6 +msgid "Hello %1," +msgstr "Bonjour %1," + +#: themes/default/templates/invitations/invite.mail.ep:4 +msgid "Hello," +msgstr "Bonjour," + #: themes/default/templates/partial/mail.js.ep:35 msgid "Hello,\\n\\nHere's some files I want to share with you:\\n" msgstr "" @@ -272,13 +350,17 @@ msgstr "" msgid "Here's some files" msgstr "Voici quelques fichiers" -#: themes/default/templates/partial/index.js.ep:24 +#: themes/default/templates/partial/invitations.js.ep:7 +msgid "Hide hidden invitations" +msgstr "Ne pas afficher les invitations cachées" + +#: themes/default/templates/partial/index.js.ep:25 msgid "Hit Enter, then Ctrl+C to copy all the download links" msgstr "" "Appuyez sur la touche Entrée puis faites Ctrl+C pour copier tous les liens " "de téléchargement" -#: themes/default/templates/partial/index.js.ep:23 +#: themes/default/templates/partial/index.js.ep:24 msgid "Hit Enter, then Ctrl+C to copy the download link" msgstr "" "Appuyez sur la touche Entrée puis faites Ctrl+C pour copier le lien de " @@ -288,7 +370,12 @@ msgstr "" msgid "How does it work?" msgstr "Comment ça marche ?" -#: themes/default/templates/about.html.ep:17 +#: themes/default/templates/invitations/invite.html.ep:46 +msgid "How many days would you like the invitation to be valid?" +msgstr "" +"Pendant combien de jours souhaitez-vous que votre invitation soit valide ?" + +#: themes/default/templates/about.html.ep:16 msgid "How to install the software on my server?" msgstr "Comment installer le logiciel sur mon serveur ?" @@ -312,7 +399,7 @@ msgstr "" msgid "Import localStorage data" msgstr "Importer des données localStorage" -#: themes/default/templates/index.html.ep:44 +#: themes/default/templates/index.html.ep:53 msgid "Important: more information on delays" msgstr "Important : plus d’informations sur les délais" @@ -320,6 +407,24 @@ msgstr "Important : plus d’informations sur les délais" msgid "Information about delays" msgstr "Information sur les délais" +#: themes/default/templates/invitations/my_invitations.html.ep:12 +msgid "Invert selection" +msgstr "Inverser la sélection" + +#. ($i->guest_mail, $url) +#: lib/Lufi/Controller/Invitation.pm:171 +msgid "Invitation resent to %1.
URL: %2" +msgstr "Invitation renvoyÊe à %1 :
URL : %2" + +#. ($invitation->guest_mail, $url) +#: lib/Lufi/Controller/Invitation.pm:87 +msgid "Invitation sent to %1.
URL: %2" +msgstr "Invitation envoyĂŠe Ă  %1 :
URL : %2" + +#: themes/default/templates/invitations/invite.html.ep:27 themes/default/templates/layouts/default.html.ep:36 themes/default/templates/layouts/default.html.ep:69 +msgid "Invite a guest" +msgstr "Inviter quelqu’un" + #: themes/default/templates/partial/render.js.ep:6 msgid "" "It seems that the key in your URL is incorrect. Please, verify your URL." @@ -331,7 +436,7 @@ msgstr "" msgid "Javascript is disabled. You won't be able to use Lufi." msgstr "Javascript est désactivé. Lufi ne fonctionnera pas." -#: themes/default/templates/layouts/default.html.ep:40 themes/default/templates/layouts/default.html.ep:68 +#: themes/default/templates/layouts/default.html.ep:44 themes/default/templates/layouts/default.html.ep:46 themes/default/templates/layouts/default.html.ep:77 themes/default/templates/layouts/default.html.ep:79 msgid "Language" msgstr "Langue" @@ -339,7 +444,7 @@ msgstr "Langue" msgid "Login" msgstr "Identifiant" -#: themes/default/templates/layouts/default.html.ep:53 themes/default/templates/layouts/default.html.ep:78 +#: themes/default/templates/layouts/default.html.ep:58 themes/default/templates/layouts/default.html.ep:91 msgid "Logout" msgstr "Déconnexion" @@ -351,18 +456,32 @@ msgstr "Lufi est un logiciel libre d’hébergement de fichiers." msgid "Mail" msgstr "Mail" -#: themes/default/templates/files.html.ep:3 themes/default/templates/layouts/default.html.ep:34 themes/default/templates/layouts/default.html.ep:62 +#: themes/default/templates/files.html.ep:3 themes/default/templates/layouts/default.html.ep:34 themes/default/templates/layouts/default.html.ep:67 msgid "My files" msgstr "Mes fichiers" +#: themes/default/templates/invitations/my_invitations.html.ep:5 themes/default/templates/layouts/default.html.ep:37 themes/default/templates/layouts/default.html.ep:70 +msgid "My invitations" +msgstr "Mes invitations" + +#: themes/default/templates/invitations/notification_files_sent.mail.ep:17 +msgid "" +"NB: this list includes the list of files that have already been sent to you." +msgstr "" +"NB : cette liste inclue la liste des fichiers qui vous a déjà été envoyée." + +#: themes/default/templates/index.html.ep:115 +msgid "Name of the zip file" +msgstr "Nom du fichier zip" + #. (format_bytes($json->{size}) -#: lib/Lufi/Controller/Files.pm:79 +#: lib/Lufi/Controller/Files.pm:108 msgid "No enough space available on the server for this file (size: %1)." msgstr "" "Espace disque insuffisant sur le serveur pour ce fichier (taille du fichier :" " %1)." -#: themes/default/templates/partial/files.js.ep:10 themes/default/templates/partial/index.js.ep:27 +#: themes/default/templates/partial/files.js.ep:10 themes/default/templates/partial/index.js.ep:28 msgid "No expiration delay" msgstr "Pas de délai d’expiration" @@ -376,7 +495,7 @@ msgstr "" "informations sont stockées en localStorage : si vous supprimez vos données " "localStorage, vous perdrez ces informations." -#: themes/default/templates/index.html.ep:87 themes/default/templates/login.html.ep:21 themes/default/templates/render.html.ep:26 +#: themes/default/templates/index.html.ep:106 themes/default/templates/login.html.ep:21 themes/default/templates/render.html.ep:26 msgid "Password" msgstr "Mot de passe" @@ -394,7 +513,7 @@ msgstr "" "d’abord récupérer et déchiffrer tous les fragments avant que vous puissiez " "le télécharger." -#: lib/Lufi/Controller/Auth.pm:28 +#: lib/Lufi/Controller/Auth.pm:38 msgid "" "Please, check your credentials or your right to access this service: unable " "to authenticate." @@ -410,10 +529,26 @@ msgstr "Confidentialité" msgid "Purge expired files from localStorage" msgstr "Supprimer du localStorage les fichiers expirés" -#: themes/default/templates/layouts/default.html.ep:31 themes/default/templates/layouts/default.html.ep:59 +#: themes/default/templates/invitations/notification_files_sent.mail.ep:20 +msgid "Regards," +msgstr "Cordialement," + +#: themes/default/templates/invitations/invite.mail.ep:15 +msgid "Regards." +msgstr "Cordialement." + +#: themes/default/templates/layouts/default.html.ep:31 themes/default/templates/layouts/default.html.ep:64 msgid "Report file" msgstr "Signaler un fichier" +#: themes/default/templates/invitations/my_invitations.html.ep:15 +msgid "Resend invitation mail" +msgstr "Renvoyer le courriel d’invitation" + +#: themes/default/templates/invitations/my_invitations.html.ep:9 +msgid "Rows in purple mean that the invitations have expired." +msgstr "Les lignes en violet indiquent que les invitations ont expiré." + #: themes/default/templates/files.html.ep:9 msgid "" "Rows in red mean that the files have expired and are no longer available." @@ -421,10 +556,14 @@ msgstr "" "Les lignes en rouge indiquent que le fichier a expiré et n’est plus " "disponible." -#: themes/default/templates/partial/index.js.ep:25 +#: themes/default/templates/partial/index.js.ep:26 msgid "Send all links by email" msgstr "Envoyer tous les liens par mail" +#: themes/default/templates/invitations/invite.html.ep:50 +msgid "Send the invitation" +msgstr "Envoyer l’invitation" + #: themes/default/templates/mail.html.ep:46 msgid "Send with this server" msgstr "Envoyer avec ce serveur" @@ -433,7 +572,7 @@ msgstr "Envoyer avec ce serveur" msgid "Send with your own mail software" msgstr "Envoyer avec votre propre logiciel de mail" -#: themes/default/templates/partial/index.js.ep:28 +#: themes/default/templates/partial/index.js.ep:29 msgid "" "Sending part XX1 of XX2. Please, be patient, the progress bar can take a " "while to move." @@ -446,20 +585,52 @@ msgstr "" msgid "Share your files in total privacy on %1" msgstr "Partagez vos fichiers en toute confidentialité sur %1" -#: themes/default/templates/layouts/default.html.ep:36 themes/default/templates/layouts/default.html.ep:64 themes/default/templates/login.html.ep:27 themes/default/templates/logout.html.ep:17 +#: themes/default/templates/invitations/my_invitations.html.ep:13 themes/default/templates/partial/invitations.js.ep:9 +msgid "Show hidden invitations" +msgstr "Afficher les invitations cachées" + +#: themes/default/templates/partial/render.js.ep:11 +msgid "Show zip content" +msgstr "Afficher le contenu du fichier zip" + +#: themes/default/templates/layouts/default.html.ep:40 themes/default/templates/layouts/default.html.ep:73 themes/default/templates/login.html.ep:28 themes/default/templates/logout.html.ep:17 msgid "Signin" msgstr "Connexion" -#: themes/default/templates/index.html.ep:37 +#: lib/Lufi/Controller/Invitation.pm:283 themes/default/templates/invitations/exception.html.ep:16 +msgid "" +"Sorry, the invitation doesn’t exist. Are you sure you are on the right URL?" +msgstr "" +"Désolé, l’invitation n’existe pas. Êtes-vous sûr·e d’être sur la bonne URL ?" + +#: themes/default/templates/index.html.ep:46 msgid "Sorry, the uploading is currently disabled. Please try again later." msgstr "" "Désolé, l’envoi de fichier est actuellement désactivé. Veuillez réessayer " "plus tard." -#: lib/Lufi/Controller/Files.pm:53 +#: lib/Lufi/Controller/Files.pm:82 msgid "Sorry, uploading is disabled." msgstr "Désolé, l’envoi de fichier est désactivé." +#: themes/default/templates/invitations/exception.html.ep:7 +msgid "Sorry, your invitation has expired or has been deleted." +msgstr "Désolé, votre invitation a expiré ou a été supprimée." + +#. ($invit->ldap_user_mail) +#: lib/Lufi/Controller/Files.pm:122 +msgid "" +"Sorry, your invitation has expired or has been deleted. Please contact %1 to " +"have another invitation." +msgstr "" +"Désolé, votre invitation a expiré ou a été supprimée. Veuillez contacter %1 " +"pour obtenir une nouvelle invitation." + +#. ($invitation->ldap_user_mail) +#: lib/Lufi/Controller/Invitation.pm:276 +msgid "The URLs of your files have been sent by email to %1." +msgstr "Les URL de vos fichiers ont été envoyées par courriel à %1." + #: themes/default/templates/about.html.ep:7 msgid "" "The administrator can only see the file's name, its size and its mimetype " @@ -488,7 +659,12 @@ msgstr "Le corps du mail ne peut être vide." msgid "The email subject can't be empty." msgstr "Le sujet du mail ne peut être vide." -#: lib/Lufi/Controller/Files.pm:421 +#. ($expire_at, $max_expire_at) +#: lib/Lufi/Controller/Invitation.pm:51 +msgid "The expiration delay (%1) is not between 1 and %2 days." +msgstr "Le délai d’expiration (%1) n’est pas compris entre 1 et %2 jours." + +#: lib/Lufi/Controller/Files.pm:468 msgid "The file has already been deleted" msgstr "Le fichier a déjà été supprimé" @@ -506,10 +682,49 @@ msgstr "" msgid "The following email addresses are not valid: %1" msgstr "Les adresses mail suivantes ne sont pas valides : %1" -#: themes/default/templates/partial/index.js.ep:15 +#. ($guest_mail) +#: lib/Lufi/Controller/Invitation.pm:48 +msgid "The guest email address (%1) is unvalid." +msgstr "L’adresse courriel de l’invité·e (%1) est invalide." + +#. ($i->token, $i->guest_mail) +#: lib/Lufi/Controller/Invitation.pm:150 +msgid "" +"The invitation %1 can’t be resent: %2 has already sent files.
Please " +"create a new invitation." +msgstr "" +"L’invitation %1 ne peut pas être renvoyée : %2 a déjà envoyé des fichiers." +"
Veuillez créer une nouvelle invitation." + +#. ($i->token) +#: lib/Lufi/Controller/Invitation.pm:130 +msgid "The invitation %1 has been deleted." +msgstr "L’invitation %1 a été supprimée." + +#. (stash('user_mail') +#: themes/default/templates/invitations/invite.html.ep:34 +msgid "The invitation mail will be send from your email address (%1)." +msgstr "" +"Le courriel d’invitation sera envoyé de votre adresse de courriel (%1)." + +#: themes/default/templates/partial/index.js.ep:16 msgid "The link(s) has been copied to your clipboard" msgstr "Le(s) lien(s) a/ont été copié dans votre presse-papier" +#. (stash('invitation') +#: themes/default/templates/index.html.ep:30 +msgid "" +"The link(s) of your file(s) will automatically be sent by mail to %1 (%2)" +msgstr "" +"Les liens de vos fichiers seront automatiquement envoyés par courriel à %1 " +"(%2)" + +#. (stash('ldap_user') +#: themes/default/templates/invitations/invite.mail.ep:11 +msgid "The links of your file(s) will automatically be sent by mail to %1." +msgstr "" +"Les liens de vos fichiers seront automatiquement envoyés par courriel à %1." + #: lib/Lufi/Controller/Mail.pm:97 msgid "The mail has been sent." msgstr "Le mail a été envoyé." @@ -517,18 +732,12 @@ msgstr "Le mail a été envoyé." #: themes/default/templates/about.html.ep:15 msgid "" "The original (and only for now) author is Luc Didry. If you want to support him, you can do it " -"via Tipeee " -"or via Liberapay." +"class=\"classic\">Luc Didry." msgstr "" "L’auteur originel (et pour l’instant, le seul) est Luc Didry. Si vous avez envie de le " -"supporter, vous pouvez le faire via Tipeee ou via Liberapay." +"tux.fr\" class=\"classic\">Luc Didry." -#: lib/Lufi/Controller/Files.pm:189 +#: lib/Lufi/Controller/Files.pm:236 msgid "" "The server was unable to find the file record to add your file part to. " "Please, contact the administrator." @@ -536,13 +745,22 @@ msgstr "" "Le serveur a été incapable de retrouver l’enregistrement du fichier auquel " "ajouter votre fragment de fichier. Veuillez contacter l’administrateur." -#: lib/Lufi/Controller/Files.pm:248 +#: lib/Lufi/Controller/Files.pm:295 msgid "" "This file has been deactivated by the admins. Contact them to know why." msgstr "" "The fichier a été désactivé par les administrateurs ou administratrices. " "Veuillez les contacter pour savoir pourquoi." +#: themes/default/templates/invitations/my_invitations.html.ep:46 themes/default/templates/partial/invitations.js.ep:6 +msgid "This invitation is normally hidden" +msgstr "Cette invitation est normalement cachée" + +#. (stash('expires') +#: themes/default/templates/invitations/invite.mail.ep:13 +msgid "This invitation is valid until %1." +msgstr "Cette invitation est valide jusqu’au %1." + #: themes/default/templates/delays.html.ep:10 msgid "" "This server sets limitations according to the file size. The expiration " @@ -553,12 +771,20 @@ msgstr "" "d’expiration de votre fichier sera le minimum entre ce que vous avez choisi " "et les limites suivantes :" -#: themes/default/templates/partial/index.js.ep:16 +#: themes/default/templates/invitations/my_invitations.html.ep:16 +msgid "Toggle visibility" +msgstr "Inverser la visibilité" + +#: themes/default/templates/invitations/my_invitations.html.ep:25 +msgid "URL" +msgstr "URL" + +#: themes/default/templates/partial/index.js.ep:17 msgid "Unable to copy the link(s) to your clipboard" msgstr "Impossible de copier le(s) lien(s) dans votre presse-papier" #. ($short) -#: lib/Lufi/Controller/Files.pm:392 +#: lib/Lufi/Controller/Files.pm:439 msgid "" "Unable to get counter for %1. The file does not exists. It will be removed " "from your localStorage." @@ -567,29 +793,33 @@ msgstr "" "être supprimé de votre localStorage." #. ($short) -#: lib/Lufi/Controller/Files.pm:382 +#: lib/Lufi/Controller/Files.pm:429 msgid "Unable to get counter for %1. The token is invalid." msgstr "Impossible de récupérer le compteur pour %1. Le jeton est invalide." #. ($short) -#: lib/Lufi/Controller/Files.pm:402 +#: lib/Lufi/Controller/Files.pm:449 msgid "Unable to get counter for %1. You are not authenticated." msgstr "" "Impossible de récupérer le compteur pour %1. Vous n’êtes pas connecté·e." -#: themes/default/templates/layouts/default.html.ep:33 themes/default/templates/layouts/default.html.ep:61 +#: themes/default/templates/layouts/default.html.ep:33 themes/default/templates/layouts/default.html.ep:66 msgid "Upload files" msgstr "Envoyer des fichiers" +#: themes/default/templates/index.html.ep:145 +msgid "Upload generated zip file" +msgstr "Envoyer le fichier zip généré" + #: themes/default/templates/files.html.ep:30 msgid "Uploaded at" msgstr "Envoyé le" -#: themes/default/templates/index.html.ep:103 +#: themes/default/templates/index.html.ep:153 msgid "Uploaded files" msgstr "Fichiers envoyés" -#: themes/default/templates/partial/index.js.ep:29 +#: themes/default/templates/partial/index.js.ep:30 msgid "Websocket communication error" msgstr "Erreur de communication WebSocket" @@ -601,6 +831,18 @@ msgstr "Qu’est-ce que Lufi ?" msgid "Who wrote this software?" msgstr "Qui a écrit ce logiciel ?" +#: themes/default/templates/partial/index.js.ep:13 +msgid "XXX file has been added to upload queue." +msgstr "Le fichier XXX a été ajouté à la file d’envoi." + +#: themes/default/templates/invitations/invite.html.ep:30 +msgid "" +"You can invite someone to send you files through this Lufi instance even if " +"they don’t have an account on it." +msgstr "" +"Vous pouvez inviter une personne à vous envoyer des fichiers via cette " +"instance Lufi même si cette personne n’y a pas de compte." + #: themes/default/templates/about.html.ep:11 msgid "" "You can see the list of your files by clicking on the \"My files\" link at " @@ -640,7 +882,7 @@ msgstr "" "Vous essayez de quitter la page. Le téléchargement sera annulé. Êtes-vous " "sûr(e) ?" -#: themes/default/templates/partial/index.js.ep:13 +#: themes/default/templates/partial/index.js.ep:14 msgid "" "You have attempted to leave this page. The upload will be canceled. Are you " "sure?" @@ -655,22 +897,23 @@ msgstr "Vous avez été déconnecté·e avec succès." msgid "You must give email addresses." msgstr "Vous devez envoyer des adresses mail." -#: themes/default/templates/index.html.ep:29 +#: themes/default/templates/index.html.ep:38 msgid "" -"Your browser has not enough entropy to generate a strong encryption key. " -"Please wait (it's better if you do things on your computer while waiting)." +"Your browser does not have enough entropy to generate a strong encryption " +"key. Please wait (it's better if you do things on your computer while " +"waiting)." msgstr "" "Votre navigateur n’a pas assez d’entropie pour générer une clé de " "chiffrement forte. Veuillez attendre (il est préférable que vous fassiez " "quelque chose sur votre ordinateur en attendant)." #. (format_bytes($json->{size}) -#: lib/Lufi/Controller/Files.pm:66 +#: lib/Lufi/Controller/Files.pm:95 msgid "Your file is too big: %1 (maximum size allowed: %2)" msgstr "" "Votre fichier est trop volumineux : %1 (la taille maximum autorisée est %2)" -#: lib/Lufi/Controller/Files.pm:304 +#: lib/Lufi/Controller/Files.pm:351 msgid "Your password is not valid. Please refresh the page to retry." msgstr "" "Votre mot de passe est invalide. Veuillez rafraîchir la page pour réessayer." @@ -689,6 +932,10 @@ msgstr "entre %1 et %2, le fichier sera conservé indéfiniment ;" msgid "deadline: " msgstr "dernier délai pour télécharger : " +#: themes/default/templates/partial/invitations.js.ep:5 +msgid "expires on XXX" +msgstr "expire le XXX" + #. (format_bytes($keys[$i]) #: themes/default/templates/delays.html.ep:26 msgid "for %1 and more, the file will be kept %2 day(s)" @@ -703,6 +950,11 @@ msgstr "pour %1 et plus, le fichier sera conservé indéfiniment." msgid "no time limit" msgstr "Pas de délai d’expiration" -#: themes/default/templates/index.html.ep:96 +#: themes/default/templates/index.html.ep:124 msgid "or" msgstr "ou" + +#. ($e->{name}, format_bytes($e->{size}) +#: themes/default/templates/invitations/notification_files_sent.mail.ep:12 +msgid "— %1 (%2), that will expire on %3" +msgstr "— %1 (%2), qui expirera le %3" diff --git a/themes/default/lib/Lufi/I18N/fr_FR.po b/themes/default/lib/Lufi/I18N/fr_FR.po index 256490d..6bf9f89 100644 --- a/themes/default/lib/Lufi/I18N/fr_FR.po +++ b/themes/default/lib/Lufi/I18N/fr_FR.po @@ -3,6 +3,7 @@ # This file is distributed under the same license as the Lufi package. # Luc Didry , 2015. # Luc Didry , 2018. #zanata +# Luc Didry , 2019. #zanata msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" @@ -10,20 +11,44 @@ msgstr "" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"PO-Revision-Date: 2018-10-28 01:58+0000\n" +"PO-Revision-Date: 2019-08-04 05:34+0000\n" "Language-Team: \n" "X-Generator: Zanata 4.6.2\n" -"Last-Translator: Copied by Zanata \n" +"Last-Translator: Luc Didry \n" "Plural-Forms: nplurals=2; plural=(n > 1);\n" "Language: fr_FR\n" #. ($delay) #. (max_delay) -#: themes/default/templates/index.html.ep:47 themes/default/templates/index.html.ep:56 themes/default/templates/index.html.ep:57 +#: themes/default/templates/index.html.ep:56 themes/default/templates/index.html.ep:65 themes/default/templates/index.html.ep:66 msgid "%1 days" msgstr "%1 jours" -#: themes/default/templates/partial/index.js.ep:26 +#. (stash('ldap_user') +#: themes/default/templates/invitations/invite.mail.ep:2 +msgid "%1 invites you to send him/her files" +msgstr "%1 vous invite à lui envoyer des fichiers" + +#. (stash('ldap_user') +#: themes/default/templates/invitations/invite.mail.ep:6 +msgid "%1 invites you to send him/her files through Lufi." +msgstr "%1 vous invite à lui envoyer des fichiers via Lufi." + +#. (stash('invitation') +#: themes/default/templates/invitations/notification_files_sent.mail.ep:4 +msgid "%1 sent you files" +msgstr "%1 vous a envoyé des fichiers" + +#. (stash('invitation') +#: themes/default/templates/invitations/notification_files_sent.mail.ep:8 +msgid "%1 used your invitation to send you files:" +msgstr "%1 a utilisé votre invitation pour vous envoyer des fichiers :" + +#: lib/Lufi/Controller/Invitation.pm:159 lib/Lufi/Controller/Invitation.pm:84 themes/default/templates/invitations/my_invitations.html.ep:51 themes/default/templates/invitations/my_invitations.html.ep:52 themes/default/templates/invitations/my_invitations.html.ep:53 themes/default/templates/invitations/notification_files_sent.mail.ep:12 +msgid "%A %d %B %Y at %T" +msgstr "%A %d %B %Y à %T" + +#: themes/default/templates/partial/index.js.ep:27 msgid "(max size: XXX)" msgstr "(taille max: XXX)" @@ -31,7 +56,7 @@ msgstr "(taille max: XXX)" msgid "1 year" msgstr "1 an" -#: themes/default/templates/index.html.ep:4 themes/default/templates/index.html.ep:56 +#: themes/default/templates/index.html.ep:4 themes/default/templates/index.html.ep:65 msgid "24 hours" msgstr "24 heures" @@ -39,27 +64,15 @@ msgstr "24 heures" msgid ":" msgstr " :" -#: themes/default/templates/about.html.ep:16 -msgid "" -"A thank you with a photo of kitten on Diaspora* or " -"Mastodon " -"is cool too ;-)" -msgstr "" -"Un merci avec une photo de chaton sur Diaspora* ou " -"Mastodon " -"fait aussi l’affaire ;-)" - #: themes/default/templates/render.html.ep:42 msgid "Abort" msgstr "Abandonner" -#: themes/default/templates/layouts/default.html.ep:48 themes/default/templates/layouts/default.html.ep:76 +#: themes/default/templates/layouts/default.html.ep:53 themes/default/templates/layouts/default.html.ep:86 msgid "About" msgstr "À propos" -#: themes/default/templates/index.html.ep:88 +#: themes/default/templates/index.html.ep:107 msgid "Add a password to file(s)" msgstr "Ajouter un mot de passe au(x) fichier(s)" @@ -71,7 +84,19 @@ msgstr "" "L’ajout d’URL non liées à cette instance Lufi au corps ou au sujet du mail " "est interdit." -#: themes/default/templates/about.html.ep:18 +#: themes/default/templates/partial/invitations.js.ep:3 +msgid "Are you sure you want to delete the selected invitations?" +msgstr "Êtes-vous sûr·e de vouloir supprimer les invitations sélectionnées ?" + +#: themes/default/templates/partial/invitations.js.ep:4 +msgid "" +"Are you sure you want to resend the invitation mail for the selected " +"invitations?" +msgstr "" +"Êtes-vous sûr·e de vouloir renvoyer le courriel d’invitation pour les " +"invitations sélectionnées ?" + +#: themes/default/templates/about.html.ep:17 msgid "" "As Lufi is a free software licensed under of the terms of the AGPLv3, you can " @@ -89,7 +114,7 @@ msgstr "" msgid "Asking for file part XX1 of %1" msgstr "Demande de récupération du fragment de fichier XX1 sur %1" -#: themes/default/templates/about.html.ep:21 +#: themes/default/templates/about.html.ep:20 msgid "Back to homepage" msgstr "Retour à la page d’accueil" @@ -97,7 +122,7 @@ msgstr "Retour à la page d’accueil" msgid "Bad CSRF token!" msgstr "Mauvais jeton CSRF !" -#: lib/Lufi/Controller/Auth.pm:22 lib/Lufi/Controller/Auth.pm:39 +#: lib/Lufi/Controller/Auth.pm:27 lib/Lufi/Controller/Auth.pm:49 msgid "Bad CSRF token." msgstr "Jeton CSRF invalide." @@ -105,11 +130,15 @@ msgstr "Jeton CSRF invalide." msgid "Click here to refresh the page and restart the download." msgstr "Cliquez ici pour rafraîchir la page et redémarrer le téléchargement." -#: themes/default/templates/index.html.ep:98 +#: themes/default/templates/invitations/invite.mail.ep:8 +msgid "Click on the following URL to upload files on Lufi:" +msgstr "Cliquez sur l’URL suivante pour envoyer des fichiers sur Lufi :" + +#: themes/default/templates/index.html.ep:126 msgid "Click to open the file browser" msgstr "Cliquez pour ouvrir le navigateur de fichiers" -#: themes/default/templates/delays.html.ep:38 +#: themes/default/templates/delays.html.ep:38 themes/default/templates/invitations/my_invitations.html.ep:80 msgid "Close" msgstr "Fermer" @@ -117,25 +146,29 @@ msgstr "Fermer" msgid "Comma-separated email addresses" msgstr "Adresses mails séparées par des virgules" -#: themes/default/templates/partial/index.js.ep:14 +#: themes/default/templates/index.html.ep:140 +msgid "Compressing zip file…" +msgstr "Compression du fichier zip…" + +#: themes/default/templates/partial/index.js.ep:15 msgid "Copy all links to clipboard" msgstr "Copier tous les liens dans le presse-papier" -#: themes/default/templates/partial/index.js.ep:17 +#: themes/default/templates/partial/index.js.ep:18 msgid "Copy to clipboard" msgstr "Copier dans le presse-papier" -#: lib/Lufi/Controller/Files.pm:460 +#: lib/Lufi/Controller/Files.pm:507 msgid "Could not delete the file. You are not authenticated." msgstr "Impossible de supprimer le fichier. Vous n’êtes pas connecté·e." -#: lib/Lufi/Controller/Files.pm:442 +#: lib/Lufi/Controller/Files.pm:489 msgid "Could not find the file. Are you sure of the URL and the token?" msgstr "" "Impossible de retrouver le fichier. Êtes-vous sûr(e) que l’URL et le jeton " "sont les bons ?" -#: lib/Lufi/Controller/Files.pm:353 +#: lib/Lufi/Controller/Files.pm:400 msgid "Could not find the file. Are you sure of the URL?" msgstr "" "Impossible de retrouver le fichier. Êtes-vous sûr(e) que l’URL est la " @@ -145,7 +178,19 @@ msgstr "" msgid "Counter" msgstr "Compteur" -#: themes/default/templates/files.html.ep:29 themes/default/templates/index.html.ep:81 +#: themes/default/templates/index.html.ep:100 +msgid "Create a zip archive with the files before uploading?" +msgstr "Créer une archive zip avec les fichiers avant l’envoi ?" + +#: themes/default/templates/invitations/my_invitations.html.ep:26 +msgid "Created at" +msgstr "Créée le " + +#: themes/default/templates/invitations/my_invitations.html.ep:14 +msgid "Delete" +msgstr "Supprimer" + +#: themes/default/templates/files.html.ep:29 themes/default/templates/index.html.ep:90 msgid "Delete at first download?" msgstr "Supprimer après le premier téléchargement ?" @@ -153,7 +198,7 @@ msgstr "Supprimer après le premier téléchargement ?" msgid "Delete selected files" msgstr "Supprimer les fichiers sélectionnés" -#: themes/default/templates/files.html.ep:32 themes/default/templates/partial/index.js.ep:18 +#: themes/default/templates/files.html.ep:32 themes/default/templates/partial/index.js.ep:19 msgid "Deletion link" msgstr "Lien de suppression" @@ -166,7 +211,7 @@ msgstr "" "avant son expiration et que le téléchargement se termine après l’expiration, " "l’utilisateur pourra quand même récupérer le fichier." -#: themes/default/templates/partial/index.js.ep:20 themes/default/templates/render.html.ep:28 +#: themes/default/templates/partial/index.js.ep:21 themes/default/templates/render.html.ep:28 msgid "Download" msgstr "Télécharger" @@ -174,7 +219,7 @@ msgstr "Télécharger" msgid "Download aborted." msgstr "Téléchargement abandonné." -#: themes/default/templates/files.html.ep:27 themes/default/templates/partial/index.js.ep:19 +#: themes/default/templates/files.html.ep:27 themes/default/templates/partial/index.js.ep:20 msgid "Download link" msgstr "Lien de téléchargement" @@ -192,10 +237,14 @@ msgstr "" "lien de téléchargement et un lien pour supprimer le fichier quand vous le " "souhaitez." -#: themes/default/templates/index.html.ep:94 +#: themes/default/templates/index.html.ep:122 msgid "Drop files here" msgstr "Glissez vos fichiers ici" +#: themes/default/templates/invitations/invite.html.ep:40 +msgid "Email address of your guest" +msgstr "Adresse de courriel de votre invité·e" + #: themes/default/templates/mail.html.ep:39 msgid "Email body" msgstr "Corps du mail" @@ -208,27 +257,31 @@ msgstr "Sujet du mail" msgid "Emails" msgstr "Mails" -#: themes/default/templates/partial/index.js.ep:21 +#: themes/default/templates/partial/index.js.ep:22 msgid "Encrypting part XX1 of XX2" msgstr "Chiffrement du fragment XX1 sur XX2" -#: lib/Lufi/Controller/Files.pm:242 +#: lib/Lufi/Controller/Files.pm:289 msgid "Error: the file existed but was deleted." msgstr "Erreur : le fichier existait mais a été supprimé" -#: lib/Lufi/Controller/Files.pm:322 +#: lib/Lufi/Controller/Files.pm:369 msgid "Error: the file has not been sent entirely." msgstr "Erreur : le fichier n’a pas été envoyé dans son intégralité" -#: lib/Lufi/Controller/Files.pm:332 +#: lib/Lufi/Controller/Files.pm:379 msgid "Error: unable to find the file. Are you sure of your URL?" msgstr "" "Erreur : impossible de retrouver le fichier. Êtes-vous sûr(e) de l’URL ?" -#: themes/default/templates/partial/index.js.ep:22 +#: themes/default/templates/partial/index.js.ep:23 msgid "Expiration:" msgstr "Expiration :" +#: themes/default/templates/invitations/my_invitations.html.ep:27 +msgid "Expire at" +msgstr "Expire le" + #: themes/default/templates/files.html.ep:31 msgid "Expires at" msgstr "Expire le" @@ -237,7 +290,7 @@ msgstr "Expire le" msgid "Export localStorage data" msgstr "Exporter les données localStorage" -#: lib/Lufi/Controller/Files.pm:424 +#: lib/Lufi/Controller/Files.pm:471 msgid "File deleted" msgstr "Fichier supprimé" @@ -245,15 +298,27 @@ msgstr "Fichier supprimé" msgid "File name" msgstr "Nom du fichier" -#: themes/default/templates/index.html.ep:71 +#: themes/default/templates/invitations/my_invitations.html.ep:61 +msgid "Files" +msgstr "Fichiers" + +#: themes/default/templates/index.html.ep:80 msgid "Files deleted at first download" msgstr "Fichiers supprimés au premier téléchargement" +#: themes/default/templates/invitations/my_invitations.html.ep:28 +msgid "Files sent at" +msgstr "Fichiers envoyés le" + +#: themes/default/templates/partial/invitations.js.ep:8 +msgid "Files sent in invitation XX1 by XX2" +msgstr "Fichiers envoyés via l’invitation XX1 par XX2" + #: themes/default/templates/partial/render.js.ep:8 msgid "Get the file" msgstr "Récupérer le fichier" -#: themes/default/templates/about.html.ep:19 +#: themes/default/templates/about.html.ep:18 msgid "" "Get the source code on the official repository or on its le dépôt officiel ou sur son miroir Github." +#: themes/default/templates/invitations/my_invitations.html.ep:24 +msgid "Guest mail" +msgstr "Courriel de l’invité·e" + +#. (ucfirst(stash('invitation') +#: themes/default/templates/invitations/notification_files_sent.mail.ep:6 +msgid "Hello %1," +msgstr "Bonjour %1," + +#: themes/default/templates/invitations/invite.mail.ep:4 +msgid "Hello," +msgstr "Bonjour," + #: themes/default/templates/partial/mail.js.ep:35 msgid "Hello,\\n\\nHere's some files I want to share with you:\\n" msgstr "" @@ -272,13 +350,17 @@ msgstr "" msgid "Here's some files" msgstr "Voici quelques fichiers" -#: themes/default/templates/partial/index.js.ep:24 +#: themes/default/templates/partial/invitations.js.ep:7 +msgid "Hide hidden invitations" +msgstr "Ne pas afficher les invitations cachées" + +#: themes/default/templates/partial/index.js.ep:25 msgid "Hit Enter, then Ctrl+C to copy all the download links" msgstr "" "Appuyez sur la touche Entrée puis faites Ctrl+C pour copier tous les liens " "de téléchargement" -#: themes/default/templates/partial/index.js.ep:23 +#: themes/default/templates/partial/index.js.ep:24 msgid "Hit Enter, then Ctrl+C to copy the download link" msgstr "" "Appuyez sur la touche Entrée puis faites Ctrl+C pour copier le lien de " @@ -288,7 +370,12 @@ msgstr "" msgid "How does it work?" msgstr "Comment ça marche ?" -#: themes/default/templates/about.html.ep:17 +#: themes/default/templates/invitations/invite.html.ep:46 +msgid "How many days would you like the invitation to be valid?" +msgstr "" +"Pendant combien de jours souhaitez-vous que votre invitation soit valide ?" + +#: themes/default/templates/about.html.ep:16 msgid "How to install the software on my server?" msgstr "Comment installer le logiciel sur mon serveur ?" @@ -312,7 +399,7 @@ msgstr "" msgid "Import localStorage data" msgstr "Importer des données localStorage" -#: themes/default/templates/index.html.ep:44 +#: themes/default/templates/index.html.ep:53 msgid "Important: more information on delays" msgstr "Important : plus d’informations sur les délais" @@ -320,6 +407,24 @@ msgstr "Important : plus d’informations sur les délais" msgid "Information about delays" msgstr "Information sur les délais" +#: themes/default/templates/invitations/my_invitations.html.ep:12 +msgid "Invert selection" +msgstr "Inverser la sélection" + +#. ($i->guest_mail, $url) +#: lib/Lufi/Controller/Invitation.pm:171 +msgid "Invitation resent to %1.
URL: %2" +msgstr "Invitation renvoyÊe à %1 :
URL : %2" + +#. ($invitation->guest_mail, $url) +#: lib/Lufi/Controller/Invitation.pm:87 +msgid "Invitation sent to %1.
URL: %2" +msgstr "Invitation envoyĂŠe Ă  %1 :
URL : %2" + +#: themes/default/templates/invitations/invite.html.ep:27 themes/default/templates/layouts/default.html.ep:36 themes/default/templates/layouts/default.html.ep:69 +msgid "Invite a guest" +msgstr "Inviter quelqu’un" + #: themes/default/templates/partial/render.js.ep:6 msgid "" "It seems that the key in your URL is incorrect. Please, verify your URL." @@ -331,7 +436,7 @@ msgstr "" msgid "Javascript is disabled. You won't be able to use Lufi." msgstr "Javascript est désactivé. Lufi ne fonctionnera pas." -#: themes/default/templates/layouts/default.html.ep:40 themes/default/templates/layouts/default.html.ep:68 +#: themes/default/templates/layouts/default.html.ep:44 themes/default/templates/layouts/default.html.ep:46 themes/default/templates/layouts/default.html.ep:77 themes/default/templates/layouts/default.html.ep:79 msgid "Language" msgstr "Langue" @@ -339,7 +444,7 @@ msgstr "Langue" msgid "Login" msgstr "Identifiant" -#: themes/default/templates/layouts/default.html.ep:53 themes/default/templates/layouts/default.html.ep:78 +#: themes/default/templates/layouts/default.html.ep:58 themes/default/templates/layouts/default.html.ep:91 msgid "Logout" msgstr "Déconnexion" @@ -351,18 +456,32 @@ msgstr "Lufi est un logiciel libre d’hébergement de fichiers." msgid "Mail" msgstr "Mail" -#: themes/default/templates/files.html.ep:3 themes/default/templates/layouts/default.html.ep:34 themes/default/templates/layouts/default.html.ep:62 +#: themes/default/templates/files.html.ep:3 themes/default/templates/layouts/default.html.ep:34 themes/default/templates/layouts/default.html.ep:67 msgid "My files" msgstr "Mes fichiers" +#: themes/default/templates/invitations/my_invitations.html.ep:5 themes/default/templates/layouts/default.html.ep:37 themes/default/templates/layouts/default.html.ep:70 +msgid "My invitations" +msgstr "Mes invitations" + +#: themes/default/templates/invitations/notification_files_sent.mail.ep:17 +msgid "" +"NB: this list includes the list of files that have already been sent to you." +msgstr "" +"NB : cette liste inclue la liste des fichiers qui vous a déjà été envoyée." + +#: themes/default/templates/index.html.ep:115 +msgid "Name of the zip file" +msgstr "Nom du fichier zip" + #. (format_bytes($json->{size}) -#: lib/Lufi/Controller/Files.pm:79 +#: lib/Lufi/Controller/Files.pm:108 msgid "No enough space available on the server for this file (size: %1)." msgstr "" "Espace disque insuffisant sur le serveur pour ce fichier (taille du fichier :" " %1)." -#: themes/default/templates/partial/files.js.ep:10 themes/default/templates/partial/index.js.ep:27 +#: themes/default/templates/partial/files.js.ep:10 themes/default/templates/partial/index.js.ep:28 msgid "No expiration delay" msgstr "Pas de délai d’expiration" @@ -376,7 +495,7 @@ msgstr "" "informations sont stockées en localStorage : si vous supprimez vos données " "localStorage, vous perdrez ces informations." -#: themes/default/templates/index.html.ep:87 themes/default/templates/login.html.ep:21 themes/default/templates/render.html.ep:26 +#: themes/default/templates/index.html.ep:106 themes/default/templates/login.html.ep:21 themes/default/templates/render.html.ep:26 msgid "Password" msgstr "Mot de passe" @@ -394,7 +513,7 @@ msgstr "" "d’abord récupérer et déchiffrer tous les fragments avant que vous puissiez " "le télécharger." -#: lib/Lufi/Controller/Auth.pm:28 +#: lib/Lufi/Controller/Auth.pm:38 msgid "" "Please, check your credentials or your right to access this service: unable " "to authenticate." @@ -410,10 +529,26 @@ msgstr "Confidentialité" msgid "Purge expired files from localStorage" msgstr "Supprimer du localStorage les fichiers expirés" -#: themes/default/templates/layouts/default.html.ep:31 themes/default/templates/layouts/default.html.ep:59 +#: themes/default/templates/invitations/notification_files_sent.mail.ep:20 +msgid "Regards," +msgstr "Cordialement," + +#: themes/default/templates/invitations/invite.mail.ep:15 +msgid "Regards." +msgstr "Cordialement." + +#: themes/default/templates/layouts/default.html.ep:31 themes/default/templates/layouts/default.html.ep:64 msgid "Report file" msgstr "Signaler un fichier" +#: themes/default/templates/invitations/my_invitations.html.ep:15 +msgid "Resend invitation mail" +msgstr "Renvoyer le courriel d’invitation" + +#: themes/default/templates/invitations/my_invitations.html.ep:9 +msgid "Rows in purple mean that the invitations have expired." +msgstr "Les lignes en violet indiquent que les invitations ont expiré." + #: themes/default/templates/files.html.ep:9 msgid "" "Rows in red mean that the files have expired and are no longer available." @@ -421,10 +556,14 @@ msgstr "" "Les lignes en rouge indiquent que le fichier a expiré et n’est plus " "disponible." -#: themes/default/templates/partial/index.js.ep:25 +#: themes/default/templates/partial/index.js.ep:26 msgid "Send all links by email" msgstr "Envoyer tous les liens par mail" +#: themes/default/templates/invitations/invite.html.ep:50 +msgid "Send the invitation" +msgstr "Envoyer l’invitation" + #: themes/default/templates/mail.html.ep:46 msgid "Send with this server" msgstr "Envoyer avec ce serveur" @@ -433,7 +572,7 @@ msgstr "Envoyer avec ce serveur" msgid "Send with your own mail software" msgstr "Envoyer avec votre propre logiciel de mail" -#: themes/default/templates/partial/index.js.ep:28 +#: themes/default/templates/partial/index.js.ep:29 msgid "" "Sending part XX1 of XX2. Please, be patient, the progress bar can take a " "while to move." @@ -446,20 +585,52 @@ msgstr "" msgid "Share your files in total privacy on %1" msgstr "Partagez vos fichiers en toute confidentialité sur %1" -#: themes/default/templates/layouts/default.html.ep:36 themes/default/templates/layouts/default.html.ep:64 themes/default/templates/login.html.ep:27 themes/default/templates/logout.html.ep:17 +#: themes/default/templates/invitations/my_invitations.html.ep:13 themes/default/templates/partial/invitations.js.ep:9 +msgid "Show hidden invitations" +msgstr "Afficher les invitations cachées" + +#: themes/default/templates/partial/render.js.ep:11 +msgid "Show zip content" +msgstr "Afficher le contenu du fichier zip" + +#: themes/default/templates/layouts/default.html.ep:40 themes/default/templates/layouts/default.html.ep:73 themes/default/templates/login.html.ep:28 themes/default/templates/logout.html.ep:17 msgid "Signin" msgstr "Connexion" -#: themes/default/templates/index.html.ep:37 +#: lib/Lufi/Controller/Invitation.pm:283 themes/default/templates/invitations/exception.html.ep:16 +msgid "" +"Sorry, the invitation doesn’t exist. Are you sure you are on the right URL?" +msgstr "" +"Désolé, l’invitation n’existe pas. Êtes-vous sûr·e d’être sur la bonne URL ?" + +#: themes/default/templates/index.html.ep:46 msgid "Sorry, the uploading is currently disabled. Please try again later." msgstr "" "Désolé, l’envoi de fichier est actuellement désactivé. Veuillez réessayer " "plus tard." -#: lib/Lufi/Controller/Files.pm:53 +#: lib/Lufi/Controller/Files.pm:82 msgid "Sorry, uploading is disabled." msgstr "Désolé, l’envoi de fichier est désactivé." +#: themes/default/templates/invitations/exception.html.ep:7 +msgid "Sorry, your invitation has expired or has been deleted." +msgstr "Désolé, votre invitation a expiré ou a été supprimée." + +#. ($invit->ldap_user_mail) +#: lib/Lufi/Controller/Files.pm:122 +msgid "" +"Sorry, your invitation has expired or has been deleted. Please contact %1 to " +"have another invitation." +msgstr "" +"Désolé, votre invitation a expiré ou a été supprimée. Veuillez contacter %1 " +"pour obtenir une nouvelle invitation." + +#. ($invitation->ldap_user_mail) +#: lib/Lufi/Controller/Invitation.pm:276 +msgid "The URLs of your files have been sent by email to %1." +msgstr "Les URL de vos fichiers ont été envoyées par courriel à %1." + #: themes/default/templates/about.html.ep:7 msgid "" "The administrator can only see the file's name, its size and its mimetype " @@ -488,7 +659,12 @@ msgstr "Le corps du mail ne peut être vide." msgid "The email subject can't be empty." msgstr "Le sujet du mail ne peut être vide." -#: lib/Lufi/Controller/Files.pm:421 +#. ($expire_at, $max_expire_at) +#: lib/Lufi/Controller/Invitation.pm:51 +msgid "The expiration delay (%1) is not between 1 and %2 days." +msgstr "Le délai d’expiration (%1) n’est pas compris entre 1 et %2 jours." + +#: lib/Lufi/Controller/Files.pm:468 msgid "The file has already been deleted" msgstr "Le fichier a déjà été supprimé" @@ -506,10 +682,49 @@ msgstr "" msgid "The following email addresses are not valid: %1" msgstr "Les adresses mail suivantes ne sont pas valides : %1" -#: themes/default/templates/partial/index.js.ep:15 +#. ($guest_mail) +#: lib/Lufi/Controller/Invitation.pm:48 +msgid "The guest email address (%1) is unvalid." +msgstr "L’adresse courriel de l’invité·e (%1) est invalide." + +#. ($i->token, $i->guest_mail) +#: lib/Lufi/Controller/Invitation.pm:150 +msgid "" +"The invitation %1 can’t be resent: %2 has already sent files.
Please " +"create a new invitation." +msgstr "" +"L’invitation %1 ne peut pas être renvoyée : %2 a déjà envoyé des fichiers." +"
Veuillez créer une nouvelle invitation." + +#. ($i->token) +#: lib/Lufi/Controller/Invitation.pm:130 +msgid "The invitation %1 has been deleted." +msgstr "L’invitation %1 a été supprimée." + +#. (stash('user_mail') +#: themes/default/templates/invitations/invite.html.ep:34 +msgid "The invitation mail will be send from your email address (%1)." +msgstr "" +"Le courriel d’invitation sera envoyé de votre adresse de courriel (%1)." + +#: themes/default/templates/partial/index.js.ep:16 msgid "The link(s) has been copied to your clipboard" msgstr "Le(s) lien(s) a/ont été copié dans votre presse-papier" +#. (stash('invitation') +#: themes/default/templates/index.html.ep:30 +msgid "" +"The link(s) of your file(s) will automatically be sent by mail to %1 (%2)" +msgstr "" +"Les liens de vos fichiers seront automatiquement envoyés par courriel à %1 " +"(%2)" + +#. (stash('ldap_user') +#: themes/default/templates/invitations/invite.mail.ep:11 +msgid "The links of your file(s) will automatically be sent by mail to %1." +msgstr "" +"Les liens de vos fichiers seront automatiquement envoyés par courriel à %1." + #: lib/Lufi/Controller/Mail.pm:97 msgid "The mail has been sent." msgstr "Le mail a été envoyé." @@ -517,18 +732,12 @@ msgstr "Le mail a été envoyé." #: themes/default/templates/about.html.ep:15 msgid "" "The original (and only for now) author is Luc Didry. If you want to support him, you can do it " -"via Tipeee " -"or via Liberapay." +"class=\"classic\">Luc Didry." msgstr "" "L’auteur originel (et pour l’instant, le seul) est Luc Didry. Si vous avez envie de le " -"supporter, vous pouvez le faire via Tipeee ou via Liberapay." +"tux.fr\" class=\"classic\">Luc Didry." -#: lib/Lufi/Controller/Files.pm:189 +#: lib/Lufi/Controller/Files.pm:236 msgid "" "The server was unable to find the file record to add your file part to. " "Please, contact the administrator." @@ -536,13 +745,22 @@ msgstr "" "Le serveur a été incapable de retrouver l’enregistrement du fichier auquel " "ajouter votre fragment de fichier. Veuillez contacter l’administrateur." -#: lib/Lufi/Controller/Files.pm:248 +#: lib/Lufi/Controller/Files.pm:295 msgid "" "This file has been deactivated by the admins. Contact them to know why." msgstr "" "The fichier a été désactivé par les administrateurs ou administratrices. " "Veuillez les contacter pour savoir pourquoi." +#: themes/default/templates/invitations/my_invitations.html.ep:46 themes/default/templates/partial/invitations.js.ep:6 +msgid "This invitation is normally hidden" +msgstr "Cette invitation est normalement cachée" + +#. (stash('expires') +#: themes/default/templates/invitations/invite.mail.ep:13 +msgid "This invitation is valid until %1." +msgstr "Cette invitation est valide jusqu’au %1." + #: themes/default/templates/delays.html.ep:10 msgid "" "This server sets limitations according to the file size. The expiration " @@ -553,12 +771,20 @@ msgstr "" "d’expiration de votre fichier sera le minimum entre ce que vous avez choisi " "et les limites suivantes :" -#: themes/default/templates/partial/index.js.ep:16 +#: themes/default/templates/invitations/my_invitations.html.ep:16 +msgid "Toggle visibility" +msgstr "Inverser la visibilité" + +#: themes/default/templates/invitations/my_invitations.html.ep:25 +msgid "URL" +msgstr "URL" + +#: themes/default/templates/partial/index.js.ep:17 msgid "Unable to copy the link(s) to your clipboard" msgstr "Impossible de copier le(s) lien(s) dans votre presse-papier" #. ($short) -#: lib/Lufi/Controller/Files.pm:392 +#: lib/Lufi/Controller/Files.pm:439 msgid "" "Unable to get counter for %1. The file does not exists. It will be removed " "from your localStorage." @@ -567,29 +793,33 @@ msgstr "" "être supprimé de votre localStorage." #. ($short) -#: lib/Lufi/Controller/Files.pm:382 +#: lib/Lufi/Controller/Files.pm:429 msgid "Unable to get counter for %1. The token is invalid." msgstr "Impossible de récupérer le compteur pour %1. Le jeton est invalide." #. ($short) -#: lib/Lufi/Controller/Files.pm:402 +#: lib/Lufi/Controller/Files.pm:449 msgid "Unable to get counter for %1. You are not authenticated." msgstr "" "Impossible de récupérer le compteur pour %1. Vous n’êtes pas connecté·e." -#: themes/default/templates/layouts/default.html.ep:33 themes/default/templates/layouts/default.html.ep:61 +#: themes/default/templates/layouts/default.html.ep:33 themes/default/templates/layouts/default.html.ep:66 msgid "Upload files" msgstr "Envoyer des fichiers" +#: themes/default/templates/index.html.ep:145 +msgid "Upload generated zip file" +msgstr "Envoyer le fichier zip généré" + #: themes/default/templates/files.html.ep:30 msgid "Uploaded at" msgstr "Envoyé le" -#: themes/default/templates/index.html.ep:103 +#: themes/default/templates/index.html.ep:153 msgid "Uploaded files" msgstr "Fichiers envoyés" -#: themes/default/templates/partial/index.js.ep:29 +#: themes/default/templates/partial/index.js.ep:30 msgid "Websocket communication error" msgstr "Erreur de communication WebSocket" @@ -601,6 +831,18 @@ msgstr "Qu’est-ce que Lufi ?" msgid "Who wrote this software?" msgstr "Qui a écrit ce logiciel ?" +#: themes/default/templates/partial/index.js.ep:13 +msgid "XXX file has been added to upload queue." +msgstr "Le fichier XXX a été ajouté à la file d’envoi." + +#: themes/default/templates/invitations/invite.html.ep:30 +msgid "" +"You can invite someone to send you files through this Lufi instance even if " +"they don’t have an account on it." +msgstr "" +"Vous pouvez inviter une personne à vous envoyer des fichiers via cette " +"instance Lufi même si cette personne n’y a pas de compte." + #: themes/default/templates/about.html.ep:11 msgid "" "You can see the list of your files by clicking on the \"My files\" link at " @@ -640,7 +882,7 @@ msgstr "" "Vous essayez de quitter la page. Le téléchargement sera annulé. Êtes-vous " "sûr(e) ?" -#: themes/default/templates/partial/index.js.ep:13 +#: themes/default/templates/partial/index.js.ep:14 msgid "" "You have attempted to leave this page. The upload will be canceled. Are you " "sure?" @@ -655,22 +897,23 @@ msgstr "Vous avez été déconnecté·e avec succès." msgid "You must give email addresses." msgstr "Vous devez envoyer des adresses mail." -#: themes/default/templates/index.html.ep:29 +#: themes/default/templates/index.html.ep:38 msgid "" -"Your browser has not enough entropy to generate a strong encryption key. " -"Please wait (it's better if you do things on your computer while waiting)." +"Your browser does not have enough entropy to generate a strong encryption " +"key. Please wait (it's better if you do things on your computer while " +"waiting)." msgstr "" "Votre navigateur n’a pas assez d’entropie pour générer une clé de " "chiffrement forte. Veuillez attendre (il est préférable que vous fassiez " "quelque chose sur votre ordinateur en attendant)." #. (format_bytes($json->{size}) -#: lib/Lufi/Controller/Files.pm:66 +#: lib/Lufi/Controller/Files.pm:95 msgid "Your file is too big: %1 (maximum size allowed: %2)" msgstr "" "Votre fichier est trop volumineux : %1 (la taille maximum autorisée est %2)" -#: lib/Lufi/Controller/Files.pm:304 +#: lib/Lufi/Controller/Files.pm:351 msgid "Your password is not valid. Please refresh the page to retry." msgstr "" "Votre mot de passe est invalide. Veuillez rafraîchir la page pour réessayer." @@ -689,6 +932,10 @@ msgstr "entre %1 et %2, le fichier sera conservé indéfiniment ;" msgid "deadline: " msgstr "dernier délai pour télécharger : " +#: themes/default/templates/partial/invitations.js.ep:5 +msgid "expires on XXX" +msgstr "expire le XXX" + #. (format_bytes($keys[$i]) #: themes/default/templates/delays.html.ep:26 msgid "for %1 and more, the file will be kept %2 day(s)" @@ -703,6 +950,11 @@ msgstr "pour %1 et plus, le fichier sera conservé indéfiniment." msgid "no time limit" msgstr "Pas de délai d’expiration" -#: themes/default/templates/index.html.ep:96 +#: themes/default/templates/index.html.ep:124 msgid "or" msgstr "ou" + +#. ($e->{name}, format_bytes($e->{size}) +#: themes/default/templates/invitations/notification_files_sent.mail.ep:12 +msgid "— %1 (%2), that will expire on %3" +msgstr "— %1 (%2), qui expirera le %3" diff --git a/themes/default/lib/Lufi/I18N/it.po b/themes/default/lib/Lufi/I18N/it.po index e532f21..436c980 100644 --- a/themes/default/lib/Lufi/I18N/it.po +++ b/themes/default/lib/Lufi/I18N/it.po @@ -3,7 +3,7 @@ # This file is distributed under the same license as the Lufi package. # Luc Didry , 2015. # Luc Didry , 2018. #zanata -# Paolo Mauri , 2018. #zanata +# Luc Didry , 2019. #zanata msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" @@ -11,28 +11,52 @@ msgstr "" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"PO-Revision-Date: 2018-12-06 11:40+0000\n" +"PO-Revision-Date: 2019-04-19 04:58+0000\n" "Language-Team: \n" "X-Generator: Zanata 4.6.2\n" -"Last-Translator: Paolo Mauri \n" +"Last-Translator: Luc Didry \n" "Plural-Forms: nplurals=2; plural=(n > 1);\n" "Language: it\n" #. ($delay) #. (max_delay) -#: themes/default/templates/index.html.ep:47 themes/default/templates/index.html.ep:56 themes/default/templates/index.html.ep:57 +#: themes/default/templates/index.html.ep:56 themes/default/templates/index.html.ep:65 themes/default/templates/index.html.ep:66 msgid "%1 days" msgstr "%1 giorni" -#: themes/default/templates/partial/index.js.ep:26 +#. (stash('ldap_user') +#: themes/default/templates/invitations/invite.mail.ep:2 +msgid "%1 invites you to send him/her files" +msgstr "" + +#. (stash('ldap_user') +#: themes/default/templates/invitations/invite.mail.ep:6 +msgid "%1 invites you to send him/her files through Lufi." +msgstr "" + +#. (stash('invitation') +#: themes/default/templates/invitations/notification_files_sent.mail.ep:4 +msgid "%1 sent you files" +msgstr "" + +#. (stash('invitation') +#: themes/default/templates/invitations/notification_files_sent.mail.ep:8 +msgid "%1 used your invitation to send you files:" +msgstr "" + +#: lib/Lufi/Controller/Invitation.pm:159 lib/Lufi/Controller/Invitation.pm:84 themes/default/templates/invitations/my_invitations.html.ep:51 themes/default/templates/invitations/my_invitations.html.ep:52 themes/default/templates/invitations/my_invitations.html.ep:53 themes/default/templates/invitations/notification_files_sent.mail.ep:12 +msgid "%A %d %B %Y at %T" +msgstr "" + +#: themes/default/templates/partial/index.js.ep:27 msgid "(max size: XXX)" -msgstr "(dimensione massima:XXX)" +msgstr "" #: themes/default/templates/index.html.ep:5 msgid "1 year" msgstr "1 anno" -#: themes/default/templates/index.html.ep:4 themes/default/templates/index.html.ep:56 +#: themes/default/templates/index.html.ep:4 themes/default/templates/index.html.ep:65 msgid "24 hours" msgstr "24 ore" @@ -40,39 +64,35 @@ msgstr "24 ore" msgid ":" msgstr "A :" -#: themes/default/templates/about.html.ep:16 -msgid "" -"A thank you with a photo of kitten on Diaspora* or " -"Mastodon " -"is cool too ;-)" -msgstr "" -"Un grazie con una foto di un gattino sur Diaspora* o " -"Mastodon " -"è forte;-)" - #: themes/default/templates/render.html.ep:42 msgid "Abort" msgstr "Annulla" -#: themes/default/templates/layouts/default.html.ep:48 themes/default/templates/layouts/default.html.ep:76 +#: themes/default/templates/layouts/default.html.ep:53 themes/default/templates/layouts/default.html.ep:86 msgid "About" msgstr "A proposito" -#: themes/default/templates/index.html.ep:88 +#: themes/default/templates/index.html.ep:107 msgid "Add a password to file(s)" -msgstr "Aggiungi una password al file" +msgstr "" #: themes/default/templates/mail.html.ep:16 msgid "" "Adding URLs not related to this Lufi instance to the mail body or subject is " "prohibited." msgstr "" -"Non è possibile aggiungere un URL non relativo a questa istanza di Lufi nel " -"testo o nel soggetto della mail." -#: themes/default/templates/about.html.ep:18 +#: themes/default/templates/partial/invitations.js.ep:3 +msgid "Are you sure you want to delete the selected invitations?" +msgstr "" + +#: themes/default/templates/partial/invitations.js.ep:4 +msgid "" +"Are you sure you want to resend the invitation mail for the selected " +"invitations?" +msgstr "" + +#: themes/default/templates/about.html.ep:17 msgid "" "As Lufi is a free software licensed under of the terms of the AGPLv3, you can " @@ -89,7 +109,7 @@ msgstr "" msgid "Asking for file part XX1 of %1" msgstr "Recupero della porzione del file XX1 su %1" -#: themes/default/templates/about.html.ep:21 +#: themes/default/templates/about.html.ep:20 msgid "Back to homepage" msgstr "Ritorna all'homepage" @@ -97,19 +117,23 @@ msgstr "Ritorna all'homepage" msgid "Bad CSRF token!" msgstr "Token CSRF errato!" -#: lib/Lufi/Controller/Auth.pm:22 lib/Lufi/Controller/Auth.pm:39 +#: lib/Lufi/Controller/Auth.pm:27 lib/Lufi/Controller/Auth.pm:49 msgid "Bad CSRF token." -msgstr "Token CSRF errato." +msgstr "" #: themes/default/templates/partial/render.js.ep:5 msgid "Click here to refresh the page and restart the download." msgstr "Click qui per aggiornare la pagina e ricominciare il download." -#: themes/default/templates/index.html.ep:98 +#: themes/default/templates/invitations/invite.mail.ep:8 +msgid "Click on the following URL to upload files on Lufi:" +msgstr "" + +#: themes/default/templates/index.html.ep:126 msgid "Click to open the file browser" msgstr "Click per aprire il file browser" -#: themes/default/templates/delays.html.ep:38 +#: themes/default/templates/delays.html.ep:38 themes/default/templates/invitations/my_invitations.html.ep:80 msgid "Close" msgstr "Chiudi" @@ -117,24 +141,28 @@ msgstr "Chiudi" msgid "Comma-separated email addresses" msgstr "Indirizzi email separati da virgole" -#: themes/default/templates/partial/index.js.ep:14 +#: themes/default/templates/index.html.ep:140 +msgid "Compressing zip file…" +msgstr "" + +#: themes/default/templates/partial/index.js.ep:15 msgid "Copy all links to clipboard" msgstr "Copiare tutti i link negli appunti" -#: themes/default/templates/partial/index.js.ep:17 +#: themes/default/templates/partial/index.js.ep:18 msgid "Copy to clipboard" msgstr "Copiare negli appunti" -#: lib/Lufi/Controller/Files.pm:460 +#: lib/Lufi/Controller/Files.pm:507 msgid "Could not delete the file. You are not authenticated." msgstr "Impossibile cancellare il file. Non siete autenticati." -#: lib/Lufi/Controller/Files.pm:442 +#: lib/Lufi/Controller/Files.pm:489 msgid "Could not find the file. Are you sure of the URL and the token?" msgstr "" "Impossibile trovare il file. Sei sicuro che URL e token siano corretti ?" -#: lib/Lufi/Controller/Files.pm:353 +#: lib/Lufi/Controller/Files.pm:400 msgid "Could not find the file. Are you sure of the URL?" msgstr "Impossibile trovare il file. Sei sicuro che l'URL sia corretto?" @@ -142,15 +170,27 @@ msgstr "Impossibile trovare il file. Sei sicuro che l'URL sia corretto?" msgid "Counter" msgstr "Contatore" -#: themes/default/templates/files.html.ep:29 themes/default/templates/index.html.ep:81 +#: themes/default/templates/index.html.ep:100 +msgid "Create a zip archive with the files before uploading?" +msgstr "" + +#: themes/default/templates/invitations/my_invitations.html.ep:26 +msgid "Created at" +msgstr "" + +#: themes/default/templates/invitations/my_invitations.html.ep:14 +msgid "Delete" +msgstr "" + +#: themes/default/templates/files.html.ep:29 themes/default/templates/index.html.ep:90 msgid "Delete at first download?" msgstr "Cancellare al primo download?" #: themes/default/templates/files.html.ep:18 msgid "Delete selected files" -msgstr "Cancellare i file selezionati" +msgstr "" -#: themes/default/templates/files.html.ep:32 themes/default/templates/partial/index.js.ep:18 +#: themes/default/templates/files.html.ep:32 themes/default/templates/partial/index.js.ep:19 msgid "Deletion link" msgstr "Link per l'eliminazione" @@ -163,7 +203,7 @@ msgstr "" "scadenza ed il download terminasse dopo la scadenza, potrebbe ottenere il " "file." -#: themes/default/templates/partial/index.js.ep:20 themes/default/templates/render.html.ep:28 +#: themes/default/templates/partial/index.js.ep:21 themes/default/templates/render.html.ep:28 msgid "Download" msgstr "Download" @@ -171,7 +211,7 @@ msgstr "Download" msgid "Download aborted." msgstr "Download annullato." -#: themes/default/templates/files.html.ep:27 themes/default/templates/partial/index.js.ep:19 +#: themes/default/templates/files.html.ep:27 themes/default/templates/partial/index.js.ep:20 msgid "Download link" msgstr "Link per il download" @@ -188,10 +228,14 @@ msgstr "" "Otterrete 2 link per ogni file : uno per il download ed uno per eliminare il " "file quando vorrete." -#: themes/default/templates/index.html.ep:94 +#: themes/default/templates/index.html.ep:122 msgid "Drop files here" msgstr "Lasciare i file qui" +#: themes/default/templates/invitations/invite.html.ep:40 +msgid "Email address of your guest" +msgstr "" + #: themes/default/templates/mail.html.ep:39 msgid "Email body" msgstr "Corpo dell'email" @@ -204,26 +248,30 @@ msgstr "Oggetto dell'email" msgid "Emails" msgstr "Email" -#: themes/default/templates/partial/index.js.ep:21 +#: themes/default/templates/partial/index.js.ep:22 msgid "Encrypting part XX1 of XX2" msgstr "Cifratura della parte XX1 di XX2" -#: lib/Lufi/Controller/Files.pm:242 +#: lib/Lufi/Controller/Files.pm:289 msgid "Error: the file existed but was deleted." msgstr "Errore: il file esisteva ma è stato eliminato" -#: lib/Lufi/Controller/Files.pm:322 +#: lib/Lufi/Controller/Files.pm:369 msgid "Error: the file has not been sent entirely." msgstr "Errore: il file non è stato inviato completamente" -#: lib/Lufi/Controller/Files.pm:332 +#: lib/Lufi/Controller/Files.pm:379 msgid "Error: unable to find the file. Are you sure of your URL?" msgstr "Errore: impossibile trovare il file. Sei certo dell'URL ?" -#: themes/default/templates/partial/index.js.ep:22 +#: themes/default/templates/partial/index.js.ep:23 msgid "Expiration:" msgstr "Scadenza:" +#: themes/default/templates/invitations/my_invitations.html.ep:27 +msgid "Expire at" +msgstr "" + #: themes/default/templates/files.html.ep:31 msgid "Expires at" msgstr "Scadrà il" @@ -232,7 +280,7 @@ msgstr "Scadrà il" msgid "Export localStorage data" msgstr "Esportare i dati del localStorage" -#: lib/Lufi/Controller/Files.pm:424 +#: lib/Lufi/Controller/Files.pm:471 msgid "File deleted" msgstr "File cancellato" @@ -240,15 +288,27 @@ msgstr "File cancellato" msgid "File name" msgstr "Nome del file" -#: themes/default/templates/index.html.ep:71 +#: themes/default/templates/invitations/my_invitations.html.ep:61 +msgid "Files" +msgstr "" + +#: themes/default/templates/index.html.ep:80 msgid "Files deleted at first download" -msgstr "File cancellati al primo download" +msgstr "" + +#: themes/default/templates/invitations/my_invitations.html.ep:28 +msgid "Files sent at" +msgstr "" + +#: themes/default/templates/partial/invitations.js.ep:8 +msgid "Files sent in invitation XX1 by XX2" +msgstr "" #: themes/default/templates/partial/render.js.ep:8 msgid "Get the file" msgstr "Ottenere il file" -#: themes/default/templates/about.html.ep:19 +#: themes/default/templates/about.html.ep:18 msgid "" "Get the source code on the official repository or on its le dépôt officiel ou sur son miroir Github." +#: themes/default/templates/invitations/my_invitations.html.ep:24 +msgid "Guest mail" +msgstr "" + +#. (ucfirst(stash('invitation') +#: themes/default/templates/invitations/notification_files_sent.mail.ep:6 +msgid "Hello %1," +msgstr "" + +#: themes/default/templates/invitations/invite.mail.ep:4 +msgid "Hello," +msgstr "" + #: themes/default/templates/partial/mail.js.ep:35 msgid "Hello,\\n\\nHere's some files I want to share with you:\\n" msgstr "Buongiorno,\\n\\necco qualche file che vorrei condividere con te:\\n" @@ -266,11 +339,15 @@ msgstr "Buongiorno,\\n\\necco qualche file che vorrei condividere con te:\\n" msgid "Here's some files" msgstr "Ecco qualche file" -#: themes/default/templates/partial/index.js.ep:24 +#: themes/default/templates/partial/invitations.js.ep:7 +msgid "Hide hidden invitations" +msgstr "" + +#: themes/default/templates/partial/index.js.ep:25 msgid "Hit Enter, then Ctrl+C to copy all the download links" msgstr "Premere Enter, poi Ctrl+C per copiare tutti i link di download" -#: themes/default/templates/partial/index.js.ep:23 +#: themes/default/templates/partial/index.js.ep:24 msgid "Hit Enter, then Ctrl+C to copy the download link" msgstr "Premere Enter, poi Ctrl+C per copiare il link di download" @@ -278,7 +355,11 @@ msgstr "Premere Enter, poi Ctrl+C per copiare il link di download" msgid "How does it work?" msgstr "Come funziona?" -#: themes/default/templates/about.html.ep:17 +#: themes/default/templates/invitations/invite.html.ep:46 +msgid "How many days would you like the invitation to be valid?" +msgstr "" + +#: themes/default/templates/about.html.ep:16 msgid "How to install the software on my server?" msgstr "Come installare il software sul mio server ?" @@ -303,7 +384,7 @@ msgstr "" msgid "Import localStorage data" msgstr "Importare i dati del localStorage" -#: themes/default/templates/index.html.ep:44 +#: themes/default/templates/index.html.ep:53 msgid "Important: more information on delays" msgstr "Importante : più informazioni sulle scadenze" @@ -311,6 +392,24 @@ msgstr "Importante : più informazioni sulle scadenze" msgid "Information about delays" msgstr "Informazione sulle scadenze" +#: themes/default/templates/invitations/my_invitations.html.ep:12 +msgid "Invert selection" +msgstr "" + +#. ($i->guest_mail, $url) +#: lib/Lufi/Controller/Invitation.pm:171 +msgid "Invitation resent to %1.
URL: %2" +msgstr "" + +#. ($invitation->guest_mail, $url) +#: lib/Lufi/Controller/Invitation.pm:87 +msgid "Invitation sent to %1.
URL: %2" +msgstr "" + +#: themes/default/templates/invitations/invite.html.ep:27 themes/default/templates/layouts/default.html.ep:36 themes/default/templates/layouts/default.html.ep:69 +msgid "Invite a guest" +msgstr "" + #: themes/default/templates/partial/render.js.ep:6 msgid "" "It seems that the key in your URL is incorrect. Please, verify your URL." @@ -320,15 +419,15 @@ msgstr "Sembra che la chiave nel tuo URL sia errata. Controllare il tuo URL." msgid "Javascript is disabled. You won't be able to use Lufi." msgstr "Javascript è disattivato. Lufi non può funzionare." -#: themes/default/templates/layouts/default.html.ep:40 themes/default/templates/layouts/default.html.ep:68 +#: themes/default/templates/layouts/default.html.ep:44 themes/default/templates/layouts/default.html.ep:46 themes/default/templates/layouts/default.html.ep:77 themes/default/templates/layouts/default.html.ep:79 msgid "Language" -msgstr "Lingua" +msgstr "" #: themes/default/templates/login.html.ep:15 msgid "Login" msgstr "Login" -#: themes/default/templates/layouts/default.html.ep:53 themes/default/templates/layouts/default.html.ep:78 +#: themes/default/templates/layouts/default.html.ep:58 themes/default/templates/layouts/default.html.ep:91 msgid "Logout" msgstr "Logout" @@ -338,19 +437,32 @@ msgstr "Lufi è un software libero di file hosting." #: themes/default/templates/files.html.ep:33 msgid "Mail" -msgstr "Email" +msgstr "" -#: themes/default/templates/files.html.ep:3 themes/default/templates/layouts/default.html.ep:34 themes/default/templates/layouts/default.html.ep:62 +#: themes/default/templates/files.html.ep:3 themes/default/templates/layouts/default.html.ep:34 themes/default/templates/layouts/default.html.ep:67 msgid "My files" msgstr "I miei file" +#: themes/default/templates/invitations/my_invitations.html.ep:5 themes/default/templates/layouts/default.html.ep:37 themes/default/templates/layouts/default.html.ep:70 +msgid "My invitations" +msgstr "" + +#: themes/default/templates/invitations/notification_files_sent.mail.ep:17 +msgid "" +"NB: this list includes the list of files that have already been sent to you." +msgstr "" + +#: themes/default/templates/index.html.ep:115 +msgid "Name of the zip file" +msgstr "" + #. (format_bytes($json->{size}) -#: lib/Lufi/Controller/Files.pm:79 +#: lib/Lufi/Controller/Files.pm:108 msgid "No enough space available on the server for this file (size: %1)." msgstr "" "Spazio disco insufficiente sul server per questo file (dimensione: %1)." -#: themes/default/templates/partial/files.js.ep:10 themes/default/templates/partial/index.js.ep:27 +#: themes/default/templates/partial/files.js.ep:10 themes/default/templates/partial/index.js.ep:28 msgid "No expiration delay" msgstr "Nessun ritardo per la scadenza" @@ -364,7 +476,7 @@ msgstr "" "sono memorizzate nel localStorage: se cancellaste i dati dal vostro " "localStorage, perdereste questa lista." -#: themes/default/templates/index.html.ep:87 themes/default/templates/login.html.ep:21 themes/default/templates/render.html.ep:26 +#: themes/default/templates/index.html.ep:106 themes/default/templates/login.html.ep:21 themes/default/templates/render.html.ep:26 msgid "Password" msgstr "Password" @@ -381,13 +493,11 @@ msgstr "" "Attendere mentre otteniamo il vostro file. Dobbiamo prima scaricare e " "decifrare tutte le parti prima che possiate averlo." -#: lib/Lufi/Controller/Auth.pm:28 +#: lib/Lufi/Controller/Auth.pm:38 msgid "" "Please, check your credentials or your right to access this service: unable " "to authenticate." msgstr "" -"Controlla le tue credenziali o il corretto accesso a questo servizio: " -"impossibile l'autenticazione." #: themes/default/templates/about.html.ep:5 msgid "Privacy" @@ -397,20 +507,39 @@ msgstr "Riservatezza" msgid "Purge expired files from localStorage" msgstr "Eliminare dal localStorage i file scaduti" -#: themes/default/templates/layouts/default.html.ep:31 themes/default/templates/layouts/default.html.ep:59 +#: themes/default/templates/invitations/notification_files_sent.mail.ep:20 +msgid "Regards," +msgstr "" + +#: themes/default/templates/invitations/invite.mail.ep:15 +msgid "Regards." +msgstr "" + +#: themes/default/templates/layouts/default.html.ep:31 themes/default/templates/layouts/default.html.ep:64 msgid "Report file" -msgstr "Report file" +msgstr "" + +#: themes/default/templates/invitations/my_invitations.html.ep:15 +msgid "Resend invitation mail" +msgstr "" + +#: themes/default/templates/invitations/my_invitations.html.ep:9 +msgid "Rows in purple mean that the invitations have expired." +msgstr "" #: themes/default/templates/files.html.ep:9 msgid "" "Rows in red mean that the files have expired and are no longer available." msgstr "" -"Le righe rosse indicano che i file sono scaduti e non sono più disponibili." -#: themes/default/templates/partial/index.js.ep:25 +#: themes/default/templates/partial/index.js.ep:26 msgid "Send all links by email" msgstr "Inviare tutti i link tramite email" +#: themes/default/templates/invitations/invite.html.ep:50 +msgid "Send the invitation" +msgstr "" + #: themes/default/templates/mail.html.ep:46 msgid "Send with this server" msgstr "Inviare tramite questo server" @@ -419,7 +548,7 @@ msgstr "Inviare tramite questo server" msgid "Send with your own mail software" msgstr "Inviare tramite il vostro programma di posta" -#: themes/default/templates/partial/index.js.ep:28 +#: themes/default/templates/partial/index.js.ep:29 msgid "" "Sending part XX1 of XX2. Please, be patient, the progress bar can take a " "while to move." @@ -432,33 +561,58 @@ msgstr "" msgid "Share your files in total privacy on %1" msgstr "Condividi tutti i file in totale riservatezza su %1" -#: themes/default/templates/layouts/default.html.ep:36 themes/default/templates/layouts/default.html.ep:64 themes/default/templates/login.html.ep:27 themes/default/templates/logout.html.ep:17 +#: themes/default/templates/invitations/my_invitations.html.ep:13 themes/default/templates/partial/invitations.js.ep:9 +msgid "Show hidden invitations" +msgstr "" + +#: themes/default/templates/partial/render.js.ep:11 +msgid "Show zip content" +msgstr "" + +#: themes/default/templates/layouts/default.html.ep:40 themes/default/templates/layouts/default.html.ep:73 themes/default/templates/login.html.ep:28 themes/default/templates/logout.html.ep:17 msgid "Signin" msgstr "Autenticazione" -#: themes/default/templates/index.html.ep:37 +#: lib/Lufi/Controller/Invitation.pm:283 themes/default/templates/invitations/exception.html.ep:16 +msgid "" +"Sorry, the invitation doesn’t exist. Are you sure you are on the right URL?" +msgstr "" + +#: themes/default/templates/index.html.ep:46 msgid "Sorry, the uploading is currently disabled. Please try again later." msgstr "L'invio del file è attualemente disattivato. Riprovare più tardi." -#: lib/Lufi/Controller/Files.pm:53 +#: lib/Lufi/Controller/Files.pm:82 msgid "Sorry, uploading is disabled." msgstr "L'invio del file è attualemente disattivato." +#: themes/default/templates/invitations/exception.html.ep:7 +msgid "Sorry, your invitation has expired or has been deleted." +msgstr "" + +#. ($invit->ldap_user_mail) +#: lib/Lufi/Controller/Files.pm:122 +msgid "" +"Sorry, your invitation has expired or has been deleted. Please contact %1 to " +"have another invitation." +msgstr "" + +#. ($invitation->ldap_user_mail) +#: lib/Lufi/Controller/Invitation.pm:276 +msgid "The URLs of your files have been sent by email to %1." +msgstr "" + #: themes/default/templates/about.html.ep:7 msgid "" "The administrator can only see the file's name, its size and its mimetype " "(what kind of file it is: video, text, etc.)." msgstr "" -"Solo l'amministratore può visualizzare il nome del file, la sua dimensione e " -"i mimetype (che tipo di file è: video, testo, ecc...)." #: lib/Lufi/Controller/Mail.pm:53 msgid "" "The body of the mail must contain at least one URL pointing to a file hosted " "on this instance." msgstr "" -"Il testo della mail deve contenere almeno un URL che reindirizza ad un file " -"ospitato in questa istanza." #: themes/default/templates/partial/files.js.ep:11 msgid "The data has been successfully imported." @@ -472,7 +626,12 @@ msgstr "Il corpo dell'email non può essere vuoto." msgid "The email subject can't be empty." msgstr "Il soggetto dell'email non può essere vuoto." -#: lib/Lufi/Controller/Files.pm:421 +#. ($expire_at, $max_expire_at) +#: lib/Lufi/Controller/Invitation.pm:51 +msgid "The expiration delay (%1) is not between 1 and %2 days." +msgstr "" + +#: lib/Lufi/Controller/Files.pm:468 msgid "The file has already been deleted" msgstr "Il file è già stato cancellato" @@ -489,10 +648,43 @@ msgstr "" msgid "The following email addresses are not valid: %1" msgstr "I seguenti indirizzi email non sono validi: %1" -#: themes/default/templates/partial/index.js.ep:15 +#. ($guest_mail) +#: lib/Lufi/Controller/Invitation.pm:48 +msgid "The guest email address (%1) is unvalid." +msgstr "" + +#. ($i->token, $i->guest_mail) +#: lib/Lufi/Controller/Invitation.pm:150 +msgid "" +"The invitation %1 can’t be resent: %2 has already sent files.
Please " +"create a new invitation." +msgstr "" + +#. ($i->token) +#: lib/Lufi/Controller/Invitation.pm:130 +msgid "The invitation %1 has been deleted." +msgstr "" + +#. (stash('user_mail') +#: themes/default/templates/invitations/invite.html.ep:34 +msgid "The invitation mail will be send from your email address (%1)." +msgstr "" + +#: themes/default/templates/partial/index.js.ep:16 msgid "The link(s) has been copied to your clipboard" msgstr "I link sono stati copiati negli appunti" +#. (stash('invitation') +#: themes/default/templates/index.html.ep:30 +msgid "" +"The link(s) of your file(s) will automatically be sent by mail to %1 (%2)" +msgstr "" + +#. (stash('ldap_user') +#: themes/default/templates/invitations/invite.mail.ep:11 +msgid "The links of your file(s) will automatically be sent by mail to %1." +msgstr "" + #: lib/Lufi/Controller/Mail.pm:97 msgid "The mail has been sent." msgstr "Email inviata." @@ -500,18 +692,12 @@ msgstr "Email inviata." #: themes/default/templates/about.html.ep:15 msgid "" "The original (and only for now) author is Luc Didry. If you want to support him, you can do it " -"via Tipeee " -"or via Liberapay." +"class=\"classic\">Luc Didry." msgstr "" "L'autore ( e per ora l'unico) è Luc Didry. Se aveste voglia di aiutarlo, potreste farlo " -"tramite Tipeee ou via Liberapay." +"\"classic\">Luc Didry." -#: lib/Lufi/Controller/Files.pm:189 +#: lib/Lufi/Controller/Files.pm:236 msgid "" "The server was unable to find the file record to add your file part to. " "Please, contact the administrator." @@ -519,12 +705,19 @@ msgstr "" "Il server non è stato in grado di trovare il file record a cui aggiungere la " "vostra porzione di file. Prego contattare l'amministratore." -#: lib/Lufi/Controller/Files.pm:248 +#: lib/Lufi/Controller/Files.pm:295 msgid "" "This file has been deactivated by the admins. Contact them to know why." msgstr "" -"Questi file sono stati cancellati dall'amministratore. Contattalo per sapere " -"il motivo." + +#: themes/default/templates/invitations/my_invitations.html.ep:46 themes/default/templates/partial/invitations.js.ep:6 +msgid "This invitation is normally hidden" +msgstr "" + +#. (stash('expires') +#: themes/default/templates/invitations/invite.mail.ep:13 +msgid "This invitation is valid until %1." +msgstr "" #: themes/default/templates/delays.html.ep:10 msgid "" @@ -536,12 +729,20 @@ msgstr "" "data di scadenza del tuo file sarà la minore tra quella scelta e queste " "limitazioni:" -#: themes/default/templates/partial/index.js.ep:16 +#: themes/default/templates/invitations/my_invitations.html.ep:16 +msgid "Toggle visibility" +msgstr "" + +#: themes/default/templates/invitations/my_invitations.html.ep:25 +msgid "URL" +msgstr "" + +#: themes/default/templates/partial/index.js.ep:17 msgid "Unable to copy the link(s) to your clipboard" msgstr "Impossibile copiare i link negli appunti" #. ($short) -#: lib/Lufi/Controller/Files.pm:392 +#: lib/Lufi/Controller/Files.pm:439 msgid "" "Unable to get counter for %1. The file does not exists. It will be removed " "from your localStorage." @@ -550,28 +751,32 @@ msgstr "" "eliminato dal tuo localStorage." #. ($short) -#: lib/Lufi/Controller/Files.pm:382 +#: lib/Lufi/Controller/Files.pm:429 msgid "Unable to get counter for %1. The token is invalid." msgstr "Impossibile recuperare il contatore per %1. Il token non è valido." #. ($short) -#: lib/Lufi/Controller/Files.pm:402 +#: lib/Lufi/Controller/Files.pm:449 msgid "Unable to get counter for %1. You are not authenticated." msgstr "Impossibile recuperare il contatore per %1. Non sei autenticato." -#: themes/default/templates/layouts/default.html.ep:33 themes/default/templates/layouts/default.html.ep:61 +#: themes/default/templates/layouts/default.html.ep:33 themes/default/templates/layouts/default.html.ep:66 msgid "Upload files" msgstr "Invio dei file" +#: themes/default/templates/index.html.ep:145 +msgid "Upload generated zip file" +msgstr "" + #: themes/default/templates/files.html.ep:30 msgid "Uploaded at" msgstr "Invio il" -#: themes/default/templates/index.html.ep:103 +#: themes/default/templates/index.html.ep:153 msgid "Uploaded files" msgstr "File inviati" -#: themes/default/templates/partial/index.js.ep:29 +#: themes/default/templates/partial/index.js.ep:30 msgid "Websocket communication error" msgstr "Errore di comunicazione WebSocket" @@ -583,6 +788,16 @@ msgstr "Cos'è Lufi ?" msgid "Who wrote this software?" msgstr "Chi ha scritto questo software?" +#: themes/default/templates/partial/index.js.ep:13 +msgid "XXX file has been added to upload queue." +msgstr "" + +#: themes/default/templates/invitations/invite.html.ep:30 +msgid "" +"You can invite someone to send you files through this Lufi instance even if " +"they don’t have an account on it." +msgstr "" + #: themes/default/templates/about.html.ep:11 msgid "" "You can see the list of your files by clicking on the \"My files\" link at " @@ -594,7 +809,6 @@ msgstr "" #: lib/Lufi/Controller/Mail.pm:42 msgid "You can't add URLs that are not related to this instance." msgstr "" -"Non è possibile aggiungere URL che non sono relativi a questa istanza." #: themes/default/templates/about.html.ep:8 msgid "" @@ -622,7 +836,7 @@ msgstr "" "Hai cercato di uscire da questa pagina. Il download sarà cancellato. Sei " "sicuro?" -#: themes/default/templates/partial/index.js.ep:13 +#: themes/default/templates/partial/index.js.ep:14 msgid "" "You have attempted to leave this page. The upload will be canceled. Are you " "sure?" @@ -637,24 +851,22 @@ msgstr "Logout avvenuto con successo." msgid "You must give email addresses." msgstr "Devi fornire gli indirizzi email." -#: themes/default/templates/index.html.ep:29 +#: themes/default/templates/index.html.ep:38 msgid "" -"Your browser has not enough entropy to generate a strong encryption key. " -"Please wait (it's better if you do things on your computer while waiting)." +"Your browser does not have enough entropy to generate a strong encryption " +"key. Please wait (it's better if you do things on your computer while " +"waiting)." msgstr "" -"Il tuo browser non ha abbastanza entropia per generare una chiave " -"crittografica forte. Attendi (puoi fare altre cose al computer mentre " -"attendi)" #. (format_bytes($json->{size}) -#: lib/Lufi/Controller/Files.pm:66 +#: lib/Lufi/Controller/Files.pm:95 msgid "Your file is too big: %1 (maximum size allowed: %2)" msgstr "" "Il vostro file è troppo grande : %1 (la dimensione massima permessa è %2)" -#: lib/Lufi/Controller/Files.pm:304 +#: lib/Lufi/Controller/Files.pm:351 msgid "Your password is not valid. Please refresh the page to retry." -msgstr "La tua password non è valida. Ricarica la pagina e riprova." +msgstr "" #. (format_bytes($keys[$i]) #: themes/default/templates/delays.html.ep:20 @@ -670,6 +882,10 @@ msgstr "tra %1 e %2, il file sarà conservato per sempre;" msgid "deadline: " msgstr "scadenza: " +#: themes/default/templates/partial/invitations.js.ep:5 +msgid "expires on XXX" +msgstr "" + #. (format_bytes($keys[$i]) #: themes/default/templates/delays.html.ep:26 msgid "for %1 and more, the file will be kept %2 day(s)" @@ -684,6 +900,11 @@ msgstr "per %1 e più, il file sarà conservato per sempre." msgid "no time limit" msgstr "nessuna limitazione temporale" -#: themes/default/templates/index.html.ep:96 +#: themes/default/templates/index.html.ep:124 msgid "or" msgstr "o" + +#. ($e->{name}, format_bytes($e->{size}) +#: themes/default/templates/invitations/notification_files_sent.mail.ep:12 +msgid "— %1 (%2), that will expire on %3" +msgstr "" diff --git a/themes/default/lib/Lufi/I18N/lufi.pot b/themes/default/lib/Lufi/I18N/lufi.pot index 52b27d0..c1c09fb 100644 --- a/themes/default/lib/Lufi/I18N/lufi.pot +++ b/themes/default/lib/Lufi/I18N/lufi.pot @@ -17,11 +17,35 @@ msgstr "" #. ($delay) #. (max_delay) -#: themes/default/templates/index.html.ep:47 themes/default/templates/index.html.ep:56 themes/default/templates/index.html.ep:57 +#: themes/default/templates/index.html.ep:56 themes/default/templates/index.html.ep:65 themes/default/templates/index.html.ep:66 msgid "%1 days" msgstr "" -#: themes/default/templates/partial/index.js.ep:26 +#. (stash('ldap_user') +#: themes/default/templates/invitations/invite.mail.ep:2 +msgid "%1 invites you to send him/her files" +msgstr "" + +#. (stash('ldap_user') +#: themes/default/templates/invitations/invite.mail.ep:6 +msgid "%1 invites you to send him/her files through Lufi." +msgstr "" + +#. (stash('invitation') +#: themes/default/templates/invitations/notification_files_sent.mail.ep:4 +msgid "%1 sent you files" +msgstr "" + +#. (stash('invitation') +#: themes/default/templates/invitations/notification_files_sent.mail.ep:8 +msgid "%1 used your invitation to send you files:" +msgstr "" + +#: lib/Lufi/Controller/Invitation.pm:159 lib/Lufi/Controller/Invitation.pm:84 themes/default/templates/invitations/my_invitations.html.ep:51 themes/default/templates/invitations/my_invitations.html.ep:52 themes/default/templates/invitations/my_invitations.html.ep:53 themes/default/templates/invitations/notification_files_sent.mail.ep:12 +msgid "%A %d %B %Y at %T" +msgstr "" + +#: themes/default/templates/partial/index.js.ep:27 msgid "(max size: XXX)" msgstr "" @@ -29,7 +53,7 @@ msgstr "" msgid "1 year" msgstr "" -#: themes/default/templates/index.html.ep:4 themes/default/templates/index.html.ep:56 +#: themes/default/templates/index.html.ep:4 themes/default/templates/index.html.ep:65 msgid "24 hours" msgstr "" @@ -37,19 +61,15 @@ msgstr "" msgid ":" msgstr "" -#: themes/default/templates/about.html.ep:16 -msgid "A thank you with a photo of kitten on Diaspora* or Mastodon is cool too ;-)" -msgstr "" - #: themes/default/templates/render.html.ep:42 msgid "Abort" msgstr "" -#: themes/default/templates/layouts/default.html.ep:49 themes/default/templates/layouts/default.html.ep:78 +#: themes/default/templates/layouts/default.html.ep:53 themes/default/templates/layouts/default.html.ep:86 msgid "About" msgstr "" -#: themes/default/templates/index.html.ep:88 +#: themes/default/templates/index.html.ep:107 msgid "Add a password to file(s)" msgstr "" @@ -57,7 +77,15 @@ msgstr "" msgid "Adding URLs not related to this Lufi instance to the mail body or subject is prohibited." msgstr "" -#: themes/default/templates/about.html.ep:18 +#: themes/default/templates/partial/invitations.js.ep:3 +msgid "Are you sure you want to delete the selected invitations?" +msgstr "" + +#: themes/default/templates/partial/invitations.js.ep:4 +msgid "Are you sure you want to resend the invitation mail for the selected invitations?" +msgstr "" + +#: themes/default/templates/about.html.ep:17 msgid "As Lufi is a free software licensed under of the terms of the AGPLv3, you can install it on you own server. Have a look on the Wiki for the procedure." msgstr "" @@ -66,7 +94,7 @@ msgstr "" msgid "Asking for file part XX1 of %1" msgstr "" -#: themes/default/templates/about.html.ep:21 +#: themes/default/templates/about.html.ep:20 msgid "Back to homepage" msgstr "" @@ -74,7 +102,7 @@ msgstr "" msgid "Bad CSRF token!" msgstr "" -#: lib/Lufi/Controller/Auth.pm:22 lib/Lufi/Controller/Auth.pm:39 +#: lib/Lufi/Controller/Auth.pm:27 lib/Lufi/Controller/Auth.pm:49 msgid "Bad CSRF token." msgstr "" @@ -82,11 +110,15 @@ msgstr "" msgid "Click here to refresh the page and restart the download." msgstr "" -#: themes/default/templates/index.html.ep:98 +#: themes/default/templates/invitations/invite.mail.ep:8 +msgid "Click on the following URL to upload files on Lufi:" +msgstr "" + +#: themes/default/templates/index.html.ep:126 msgid "Click to open the file browser" msgstr "" -#: themes/default/templates/delays.html.ep:38 +#: themes/default/templates/delays.html.ep:38 themes/default/templates/invitations/my_invitations.html.ep:80 msgid "Close" msgstr "" @@ -94,23 +126,27 @@ msgstr "" msgid "Comma-separated email addresses" msgstr "" -#: themes/default/templates/partial/index.js.ep:14 +#: themes/default/templates/index.html.ep:140 +msgid "Compressing zip file…" +msgstr "" + +#: themes/default/templates/partial/index.js.ep:15 msgid "Copy all links to clipboard" msgstr "" -#: themes/default/templates/partial/index.js.ep:17 +#: themes/default/templates/partial/index.js.ep:18 msgid "Copy to clipboard" msgstr "" -#: lib/Lufi/Controller/Files.pm:460 +#: lib/Lufi/Controller/Files.pm:507 msgid "Could not delete the file. You are not authenticated." msgstr "" -#: lib/Lufi/Controller/Files.pm:442 +#: lib/Lufi/Controller/Files.pm:489 msgid "Could not find the file. Are you sure of the URL and the token?" msgstr "" -#: lib/Lufi/Controller/Files.pm:353 +#: lib/Lufi/Controller/Files.pm:400 msgid "Could not find the file. Are you sure of the URL?" msgstr "" @@ -118,7 +154,19 @@ msgstr "" msgid "Counter" msgstr "" -#: themes/default/templates/files.html.ep:29 themes/default/templates/index.html.ep:81 +#: themes/default/templates/index.html.ep:100 +msgid "Create a zip archive with the files before uploading?" +msgstr "" + +#: themes/default/templates/invitations/my_invitations.html.ep:26 +msgid "Created at" +msgstr "" + +#: themes/default/templates/invitations/my_invitations.html.ep:14 +msgid "Delete" +msgstr "" + +#: themes/default/templates/files.html.ep:29 themes/default/templates/index.html.ep:90 msgid "Delete at first download?" msgstr "" @@ -126,7 +174,7 @@ msgstr "" msgid "Delete selected files" msgstr "" -#: themes/default/templates/files.html.ep:32 themes/default/templates/partial/index.js.ep:18 +#: themes/default/templates/files.html.ep:32 themes/default/templates/partial/index.js.ep:19 msgid "Deletion link" msgstr "" @@ -134,7 +182,7 @@ msgstr "" msgid "Don't worry: if a user begins to download the file before the expiration and the download ends after the expiration, he will be able to get the file." msgstr "" -#: themes/default/templates/partial/index.js.ep:20 themes/default/templates/render.html.ep:28 +#: themes/default/templates/partial/index.js.ep:21 themes/default/templates/render.html.ep:28 msgid "Download" msgstr "" @@ -142,7 +190,7 @@ msgstr "" msgid "Download aborted." msgstr "" -#: themes/default/templates/files.html.ep:27 themes/default/templates/partial/index.js.ep:19 +#: themes/default/templates/files.html.ep:27 themes/default/templates/partial/index.js.ep:20 msgid "Download link" msgstr "" @@ -150,10 +198,14 @@ msgstr "" msgid "Drag and drop files in the appropriate area or use the traditional way to send files and the files will be chunked, encrypted and sent to the server. You will get two links per file: a download link, that you give to the people you want to share the file with and a deletion link, allowing you to delete the file whenever you want." msgstr "" -#: themes/default/templates/index.html.ep:94 +#: themes/default/templates/index.html.ep:122 msgid "Drop files here" msgstr "" +#: themes/default/templates/invitations/invite.html.ep:40 +msgid "Email address of your guest" +msgstr "" + #: themes/default/templates/mail.html.ep:39 msgid "Email body" msgstr "" @@ -166,26 +218,30 @@ msgstr "" msgid "Emails" msgstr "" -#: themes/default/templates/partial/index.js.ep:21 +#: themes/default/templates/partial/index.js.ep:22 msgid "Encrypting part XX1 of XX2" msgstr "" -#: lib/Lufi/Controller/Files.pm:242 +#: lib/Lufi/Controller/Files.pm:289 msgid "Error: the file existed but was deleted." msgstr "" -#: lib/Lufi/Controller/Files.pm:322 +#: lib/Lufi/Controller/Files.pm:369 msgid "Error: the file has not been sent entirely." msgstr "" -#: lib/Lufi/Controller/Files.pm:332 +#: lib/Lufi/Controller/Files.pm:379 msgid "Error: unable to find the file. Are you sure of your URL?" msgstr "" -#: themes/default/templates/partial/index.js.ep:22 +#: themes/default/templates/partial/index.js.ep:23 msgid "Expiration:" msgstr "" +#: themes/default/templates/invitations/my_invitations.html.ep:27 +msgid "Expire at" +msgstr "" + #: themes/default/templates/files.html.ep:31 msgid "Expires at" msgstr "" @@ -194,7 +250,7 @@ msgstr "" msgid "Export localStorage data" msgstr "" -#: lib/Lufi/Controller/Files.pm:424 +#: lib/Lufi/Controller/Files.pm:471 msgid "File deleted" msgstr "" @@ -202,18 +258,43 @@ msgstr "" msgid "File name" msgstr "" -#: themes/default/templates/index.html.ep:71 +#: themes/default/templates/invitations/my_invitations.html.ep:61 +msgid "Files" +msgstr "" + +#: themes/default/templates/index.html.ep:80 msgid "Files deleted at first download" msgstr "" +#: themes/default/templates/invitations/my_invitations.html.ep:28 +msgid "Files sent at" +msgstr "" + +#: themes/default/templates/partial/invitations.js.ep:8 +msgid "Files sent in invitation XX1 by XX2" +msgstr "" + #: themes/default/templates/partial/render.js.ep:8 msgid "Get the file" msgstr "" -#: themes/default/templates/about.html.ep:19 +#: themes/default/templates/about.html.ep:18 msgid "Get the source code on the official repository or on its Github mirror" msgstr "" +#: themes/default/templates/invitations/my_invitations.html.ep:24 +msgid "Guest mail" +msgstr "" + +#. (ucfirst(stash('invitation') +#: themes/default/templates/invitations/notification_files_sent.mail.ep:6 +msgid "Hello %1," +msgstr "" + +#: themes/default/templates/invitations/invite.mail.ep:4 +msgid "Hello," +msgstr "" + #: themes/default/templates/partial/mail.js.ep:35 msgid "Hello,\\n\\nHere's some files I want to share with you:\\n" msgstr "" @@ -222,11 +303,15 @@ msgstr "" msgid "Here's some files" msgstr "" -#: themes/default/templates/partial/index.js.ep:24 +#: themes/default/templates/partial/invitations.js.ep:7 +msgid "Hide hidden invitations" +msgstr "" + +#: themes/default/templates/partial/index.js.ep:25 msgid "Hit Enter, then Ctrl+C to copy all the download links" msgstr "" -#: themes/default/templates/partial/index.js.ep:23 +#: themes/default/templates/partial/index.js.ep:24 msgid "Hit Enter, then Ctrl+C to copy the download link" msgstr "" @@ -234,7 +319,11 @@ msgstr "" msgid "How does it work?" msgstr "" -#: themes/default/templates/about.html.ep:17 +#: themes/default/templates/invitations/invite.html.ep:46 +msgid "How many days would you like the invitation to be valid?" +msgstr "" + +#: themes/default/templates/about.html.ep:16 msgid "How to install the software on my server?" msgstr "" @@ -254,7 +343,7 @@ msgstr "" msgid "Import localStorage data" msgstr "" -#: themes/default/templates/index.html.ep:44 +#: themes/default/templates/index.html.ep:53 msgid "Important: more information on delays" msgstr "" @@ -262,6 +351,24 @@ msgstr "" msgid "Information about delays" msgstr "" +#: themes/default/templates/invitations/my_invitations.html.ep:12 +msgid "Invert selection" +msgstr "" + +#. ($i->guest_mail, $url) +#: lib/Lufi/Controller/Invitation.pm:171 +msgid "Invitation resent to %1.
URL: %2" +msgstr "" + +#. ($invitation->guest_mail, $url) +#: lib/Lufi/Controller/Invitation.pm:87 +msgid "Invitation sent to %1.
URL: %2" +msgstr "" + +#: themes/default/templates/invitations/invite.html.ep:27 themes/default/templates/layouts/default.html.ep:36 themes/default/templates/layouts/default.html.ep:69 +msgid "Invite a guest" +msgstr "" + #: themes/default/templates/partial/render.js.ep:6 msgid "It seems that the key in your URL is incorrect. Please, verify your URL." msgstr "" @@ -270,7 +377,7 @@ msgstr "" msgid "Javascript is disabled. You won't be able to use Lufi." msgstr "" -#: themes/default/templates/layouts/default.html.ep:40 themes/default/templates/layouts/default.html.ep:42 themes/default/templates/layouts/default.html.ep:69 themes/default/templates/layouts/default.html.ep:71 +#: themes/default/templates/layouts/default.html.ep:44 themes/default/templates/layouts/default.html.ep:46 themes/default/templates/layouts/default.html.ep:77 themes/default/templates/layouts/default.html.ep:79 msgid "Language" msgstr "" @@ -278,7 +385,7 @@ msgstr "" msgid "Login" msgstr "" -#: themes/default/templates/layouts/default.html.ep:54 themes/default/templates/layouts/default.html.ep:80 +#: themes/default/templates/layouts/default.html.ep:58 themes/default/templates/layouts/default.html.ep:91 msgid "Logout" msgstr "" @@ -290,16 +397,28 @@ msgstr "" msgid "Mail" msgstr "" -#: themes/default/templates/files.html.ep:3 themes/default/templates/layouts/default.html.ep:34 themes/default/templates/layouts/default.html.ep:63 +#: themes/default/templates/files.html.ep:3 themes/default/templates/layouts/default.html.ep:34 themes/default/templates/layouts/default.html.ep:67 msgid "My files" msgstr "" +#: themes/default/templates/invitations/my_invitations.html.ep:5 themes/default/templates/layouts/default.html.ep:37 themes/default/templates/layouts/default.html.ep:70 +msgid "My invitations" +msgstr "" + +#: themes/default/templates/invitations/notification_files_sent.mail.ep:17 +msgid "NB: this list includes the list of files that have already been sent to you." +msgstr "" + +#: themes/default/templates/index.html.ep:115 +msgid "Name of the zip file" +msgstr "" + #. (format_bytes($json->{size}) -#: lib/Lufi/Controller/Files.pm:79 +#: lib/Lufi/Controller/Files.pm:108 msgid "No enough space available on the server for this file (size: %1)." msgstr "" -#: themes/default/templates/partial/files.js.ep:10 themes/default/templates/partial/index.js.ep:27 +#: themes/default/templates/partial/files.js.ep:10 themes/default/templates/partial/index.js.ep:28 msgid "No expiration delay" msgstr "" @@ -307,7 +426,7 @@ msgstr "" msgid "Only the files sent with this browser will be listed here. This list is stored in localStorage: if you delete your localStorage data, you'll lose this list." msgstr "" -#: themes/default/templates/index.html.ep:87 themes/default/templates/login.html.ep:21 themes/default/templates/render.html.ep:26 +#: themes/default/templates/index.html.ep:106 themes/default/templates/login.html.ep:21 themes/default/templates/render.html.ep:26 msgid "Password" msgstr "" @@ -320,7 +439,7 @@ msgstr "" msgid "Please wait while we are getting your file. We first need to download and decrypt all parts before you can get it." msgstr "" -#: lib/Lufi/Controller/Auth.pm:28 +#: lib/Lufi/Controller/Auth.pm:38 msgid "Please, check your credentials or your right to access this service: unable to authenticate." msgstr "" @@ -332,18 +451,38 @@ msgstr "" msgid "Purge expired files from localStorage" msgstr "" -#: themes/default/templates/layouts/default.html.ep:31 themes/default/templates/layouts/default.html.ep:60 +#: themes/default/templates/invitations/notification_files_sent.mail.ep:20 +msgid "Regards," +msgstr "" + +#: themes/default/templates/invitations/invite.mail.ep:15 +msgid "Regards." +msgstr "" + +#: themes/default/templates/layouts/default.html.ep:31 themes/default/templates/layouts/default.html.ep:64 msgid "Report file" msgstr "" +#: themes/default/templates/invitations/my_invitations.html.ep:15 +msgid "Resend invitation mail" +msgstr "" + +#: themes/default/templates/invitations/my_invitations.html.ep:9 +msgid "Rows in purple mean that the invitations have expired." +msgstr "" + #: themes/default/templates/files.html.ep:9 msgid "Rows in red mean that the files have expired and are no longer available." msgstr "" -#: themes/default/templates/partial/index.js.ep:25 +#: themes/default/templates/partial/index.js.ep:26 msgid "Send all links by email" msgstr "" +#: themes/default/templates/invitations/invite.html.ep:50 +msgid "Send the invitation" +msgstr "" + #: themes/default/templates/mail.html.ep:46 msgid "Send with this server" msgstr "" @@ -352,7 +491,7 @@ msgstr "" msgid "Send with your own mail software" msgstr "" -#: themes/default/templates/partial/index.js.ep:28 +#: themes/default/templates/partial/index.js.ep:29 msgid "Sending part XX1 of XX2. Please, be patient, the progress bar can take a while to move." msgstr "" @@ -361,18 +500,44 @@ msgstr "" msgid "Share your files in total privacy on %1" msgstr "" -#: themes/default/templates/layouts/default.html.ep:36 themes/default/templates/layouts/default.html.ep:65 themes/default/templates/login.html.ep:27 themes/default/templates/logout.html.ep:17 +#: themes/default/templates/invitations/my_invitations.html.ep:13 themes/default/templates/partial/invitations.js.ep:9 +msgid "Show hidden invitations" +msgstr "" + +#: themes/default/templates/partial/render.js.ep:11 +msgid "Show zip content" +msgstr "" + +#: themes/default/templates/layouts/default.html.ep:40 themes/default/templates/layouts/default.html.ep:73 themes/default/templates/login.html.ep:28 themes/default/templates/logout.html.ep:17 msgid "Signin" msgstr "" -#: themes/default/templates/index.html.ep:37 +#: lib/Lufi/Controller/Invitation.pm:283 themes/default/templates/invitations/exception.html.ep:16 +msgid "Sorry, the invitation doesn’t exist. Are you sure you are on the right URL?" +msgstr "" + +#: themes/default/templates/index.html.ep:46 msgid "Sorry, the uploading is currently disabled. Please try again later." msgstr "" -#: lib/Lufi/Controller/Files.pm:53 +#: lib/Lufi/Controller/Files.pm:82 msgid "Sorry, uploading is disabled." msgstr "" +#: themes/default/templates/invitations/exception.html.ep:7 +msgid "Sorry, your invitation has expired or has been deleted." +msgstr "" + +#. ($invit->ldap_user_mail) +#: lib/Lufi/Controller/Files.pm:122 +msgid "Sorry, your invitation has expired or has been deleted. Please contact %1 to have another invitation." +msgstr "" + +#. ($invitation->ldap_user_mail) +#: lib/Lufi/Controller/Invitation.pm:276 +msgid "The URLs of your files have been sent by email to %1." +msgstr "" + #: themes/default/templates/about.html.ep:7 msgid "The administrator can only see the file's name, its size and its mimetype (what kind of file it is: video, text, etc.)." msgstr "" @@ -393,7 +558,12 @@ msgstr "" msgid "The email subject can't be empty." msgstr "" -#: lib/Lufi/Controller/Files.pm:421 +#. ($expire_at, $max_expire_at) +#: lib/Lufi/Controller/Invitation.pm:51 +msgid "The expiration delay (%1) is not between 1 and %2 days." +msgstr "" + +#: lib/Lufi/Controller/Files.pm:468 msgid "The file has already been deleted" msgstr "" @@ -406,62 +576,113 @@ msgstr "" msgid "The following email addresses are not valid: %1" msgstr "" -#: themes/default/templates/partial/index.js.ep:15 +#. ($guest_mail) +#: lib/Lufi/Controller/Invitation.pm:48 +msgid "The guest email address (%1) is unvalid." +msgstr "" + +#. ($i->token, $i->guest_mail) +#: lib/Lufi/Controller/Invitation.pm:150 +msgid "The invitation %1 can’t be resent: %2 has already sent files.
Please create a new invitation." +msgstr "" + +#. ($i->token) +#: lib/Lufi/Controller/Invitation.pm:130 +msgid "The invitation %1 has been deleted." +msgstr "" + +#. (stash('user_mail') +#: themes/default/templates/invitations/invite.html.ep:34 +msgid "The invitation mail will be send from your email address (%1)." +msgstr "" + +#: themes/default/templates/partial/index.js.ep:16 msgid "The link(s) has been copied to your clipboard" msgstr "" +#. (stash('invitation') +#: themes/default/templates/index.html.ep:30 +msgid "The link(s) of your file(s) will automatically be sent by mail to %1 (%2)" +msgstr "" + +#. (stash('ldap_user') +#: themes/default/templates/invitations/invite.mail.ep:11 +msgid "The links of your file(s) will automatically be sent by mail to %1." +msgstr "" + #: lib/Lufi/Controller/Mail.pm:97 msgid "The mail has been sent." msgstr "" #: themes/default/templates/about.html.ep:15 -msgid "The original (and only for now) author is Luc Didry. If you want to support him, you can do it via Tipeee or via Liberapay." +msgid "The original (and only for now) author is Luc Didry." msgstr "" -#: lib/Lufi/Controller/Files.pm:189 +#: lib/Lufi/Controller/Files.pm:236 msgid "The server was unable to find the file record to add your file part to. Please, contact the administrator." msgstr "" -#: lib/Lufi/Controller/Files.pm:248 +#: lib/Lufi/Controller/Files.pm:295 msgid "This file has been deactivated by the admins. Contact them to know why." msgstr "" +#: themes/default/templates/invitations/my_invitations.html.ep:46 themes/default/templates/partial/invitations.js.ep:6 +msgid "This invitation is normally hidden" +msgstr "" + +#. (stash('expires') +#: themes/default/templates/invitations/invite.mail.ep:13 +msgid "This invitation is valid until %1." +msgstr "" + #: themes/default/templates/delays.html.ep:10 msgid "This server sets limitations according to the file size. The expiration delay of your file will be the minimum between what you choose and the following limitations:" msgstr "" -#: themes/default/templates/partial/index.js.ep:16 +#: themes/default/templates/invitations/my_invitations.html.ep:16 +msgid "Toggle visibility" +msgstr "" + +#: themes/default/templates/invitations/my_invitations.html.ep:25 +msgid "URL" +msgstr "" + +#: themes/default/templates/partial/index.js.ep:17 msgid "Unable to copy the link(s) to your clipboard" msgstr "" #. ($short) -#: lib/Lufi/Controller/Files.pm:392 +#: lib/Lufi/Controller/Files.pm:439 msgid "Unable to get counter for %1. The file does not exists. It will be removed from your localStorage." msgstr "" #. ($short) -#: lib/Lufi/Controller/Files.pm:382 +#: lib/Lufi/Controller/Files.pm:429 msgid "Unable to get counter for %1. The token is invalid." msgstr "" #. ($short) -#: lib/Lufi/Controller/Files.pm:402 +#: lib/Lufi/Controller/Files.pm:449 msgid "Unable to get counter for %1. You are not authenticated." msgstr "" -#: themes/default/templates/layouts/default.html.ep:33 themes/default/templates/layouts/default.html.ep:62 +#: themes/default/templates/layouts/default.html.ep:33 themes/default/templates/layouts/default.html.ep:66 msgid "Upload files" msgstr "" +#: themes/default/templates/index.html.ep:145 +msgid "Upload generated zip file" +msgstr "" + #: themes/default/templates/files.html.ep:30 msgid "Uploaded at" msgstr "" -#: themes/default/templates/index.html.ep:103 +#: themes/default/templates/index.html.ep:153 msgid "Uploaded files" msgstr "" -#: themes/default/templates/partial/index.js.ep:29 +#: themes/default/templates/partial/index.js.ep:30 msgid "Websocket communication error" msgstr "" @@ -473,6 +694,14 @@ msgstr "" msgid "Who wrote this software?" msgstr "" +#: themes/default/templates/partial/index.js.ep:13 +msgid "XXX file has been added to upload queue." +msgstr "" + +#: themes/default/templates/invitations/invite.html.ep:30 +msgid "You can invite someone to send you files through this Lufi instance even if they don’t have an account on it." +msgstr "" + #: themes/default/templates/about.html.ep:11 msgid "You can see the list of your files by clicking on the \"My files\" link at the top right of this page." msgstr "" @@ -493,7 +722,7 @@ msgstr "" msgid "You have attempted to leave this page. The download will be canceled. Are you sure?" msgstr "" -#: themes/default/templates/partial/index.js.ep:13 +#: themes/default/templates/partial/index.js.ep:14 msgid "You have attempted to leave this page. The upload will be canceled. Are you sure?" msgstr "" @@ -505,16 +734,16 @@ msgstr "" msgid "You must give email addresses." msgstr "" -#: themes/default/templates/index.html.ep:29 -msgid "Your browser has not enough entropy to generate a strong encryption key. Please wait (it's better if you do things on your computer while waiting)." +#: themes/default/templates/index.html.ep:38 +msgid "Your browser does not have enough entropy to generate a strong encryption key. Please wait (it's better if you do things on your computer while waiting)." msgstr "" #. (format_bytes($json->{size}) -#: lib/Lufi/Controller/Files.pm:66 +#: lib/Lufi/Controller/Files.pm:95 msgid "Your file is too big: %1 (maximum size allowed: %2)" msgstr "" -#: lib/Lufi/Controller/Files.pm:304 +#: lib/Lufi/Controller/Files.pm:351 msgid "Your password is not valid. Please refresh the page to retry." msgstr "" @@ -532,6 +761,10 @@ msgstr "" msgid "deadline: " msgstr "" +#: themes/default/templates/partial/invitations.js.ep:5 +msgid "expires on XXX" +msgstr "" + #. (format_bytes($keys[$i]) #: themes/default/templates/delays.html.ep:26 msgid "for %1 and more, the file will be kept %2 day(s)" @@ -546,6 +779,11 @@ msgstr "" msgid "no time limit" msgstr "" -#: themes/default/templates/index.html.ep:96 +#: themes/default/templates/index.html.ep:124 msgid "or" msgstr "" + +#. ($e->{name}, format_bytes($e->{size}) +#: themes/default/templates/invitations/notification_files_sent.mail.ep:12 +msgid "— %1 (%2), that will expire on %3" +msgstr "" diff --git a/themes/default/lib/Lufi/I18N/nl.po b/themes/default/lib/Lufi/I18N/nl.po index 849745c..8801b88 100644 --- a/themes/default/lib/Lufi/I18N/nl.po +++ b/themes/default/lib/Lufi/I18N/nl.po @@ -1,4 +1,5 @@ # Luc Didry , 2018. #zanata +# Luc Didry , 2019. #zanata msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" @@ -6,8 +7,8 @@ msgstr "" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"PO-Revision-Date: 2018-10-28 01:56+0000\n" -"Last-Translator: Copied by Zanata \n" +"PO-Revision-Date: 2019-04-19 04:57+0000\n" +"Last-Translator: Luc Didry \n" "Language-Team: Dutch\n" "Language: nl\n" "X-Generator: Zanata 4.6.2\n" @@ -15,11 +16,35 @@ msgstr "" #. ($delay) #. (max_delay) -#: themes/default/templates/index.html.ep:47 themes/default/templates/index.html.ep:56 themes/default/templates/index.html.ep:57 +#: themes/default/templates/index.html.ep:56 themes/default/templates/index.html.ep:65 themes/default/templates/index.html.ep:66 msgid "%1 days" msgstr "%1 dagen" -#: themes/default/templates/partial/index.js.ep:26 +#. (stash('ldap_user') +#: themes/default/templates/invitations/invite.mail.ep:2 +msgid "%1 invites you to send him/her files" +msgstr "" + +#. (stash('ldap_user') +#: themes/default/templates/invitations/invite.mail.ep:6 +msgid "%1 invites you to send him/her files through Lufi." +msgstr "" + +#. (stash('invitation') +#: themes/default/templates/invitations/notification_files_sent.mail.ep:4 +msgid "%1 sent you files" +msgstr "" + +#. (stash('invitation') +#: themes/default/templates/invitations/notification_files_sent.mail.ep:8 +msgid "%1 used your invitation to send you files:" +msgstr "" + +#: lib/Lufi/Controller/Invitation.pm:159 lib/Lufi/Controller/Invitation.pm:84 themes/default/templates/invitations/my_invitations.html.ep:51 themes/default/templates/invitations/my_invitations.html.ep:52 themes/default/templates/invitations/my_invitations.html.ep:53 themes/default/templates/invitations/notification_files_sent.mail.ep:12 +msgid "%A %d %B %Y at %T" +msgstr "" + +#: themes/default/templates/partial/index.js.ep:27 msgid "(max size: XXX)" msgstr "" @@ -27,7 +52,7 @@ msgstr "" msgid "1 year" msgstr "1 jaar" -#: themes/default/templates/index.html.ep:4 themes/default/templates/index.html.ep:56 +#: themes/default/templates/index.html.ep:4 themes/default/templates/index.html.ep:65 msgid "24 hours" msgstr "24 uur" @@ -35,27 +60,15 @@ msgstr "24 uur" msgid ":" msgstr ":" -#: themes/default/templates/about.html.ep:16 -msgid "" -"A thank you with a photo of kitten on Diaspora* or " -"Mastodon " -"is cool too ;-)" -msgstr "" -"Een bedankt foto met een katje op Diaspora* of Mastodon is " -"ook goed ;-)" - #: themes/default/templates/render.html.ep:42 msgid "Abort" msgstr "Annuleren" -#: themes/default/templates/layouts/default.html.ep:48 themes/default/templates/layouts/default.html.ep:76 +#: themes/default/templates/layouts/default.html.ep:53 themes/default/templates/layouts/default.html.ep:86 msgid "About" msgstr "Over" -#: themes/default/templates/index.html.ep:88 +#: themes/default/templates/index.html.ep:107 msgid "Add a password to file(s)" msgstr "" @@ -65,7 +78,17 @@ msgid "" "prohibited." msgstr "" -#: themes/default/templates/about.html.ep:18 +#: themes/default/templates/partial/invitations.js.ep:3 +msgid "Are you sure you want to delete the selected invitations?" +msgstr "" + +#: themes/default/templates/partial/invitations.js.ep:4 +msgid "" +"Are you sure you want to resend the invitation mail for the selected " +"invitations?" +msgstr "" + +#: themes/default/templates/about.html.ep:17 msgid "" "As Lufi is a free software licensed under of the terms of the AGPLv3, you can " @@ -83,7 +106,7 @@ msgstr "" msgid "Asking for file part XX1 of %1" msgstr "Deel XX1 van %1 wordt opgehaald" -#: themes/default/templates/about.html.ep:21 +#: themes/default/templates/about.html.ep:20 msgid "Back to homepage" msgstr "Terug naar home" @@ -91,7 +114,7 @@ msgstr "Terug naar home" msgid "Bad CSRF token!" msgstr "Verkeerde CSRF token!" -#: lib/Lufi/Controller/Auth.pm:22 lib/Lufi/Controller/Auth.pm:39 +#: lib/Lufi/Controller/Auth.pm:27 lib/Lufi/Controller/Auth.pm:49 msgid "Bad CSRF token." msgstr "" @@ -99,11 +122,15 @@ msgstr "" msgid "Click here to refresh the page and restart the download." msgstr "Klik hier om de pagina te verversen en opnieuw te downloaden." -#: themes/default/templates/index.html.ep:98 +#: themes/default/templates/invitations/invite.mail.ep:8 +msgid "Click on the following URL to upload files on Lufi:" +msgstr "" + +#: themes/default/templates/index.html.ep:126 msgid "Click to open the file browser" msgstr "Klik voor bestandbrowser" -#: themes/default/templates/delays.html.ep:38 +#: themes/default/templates/delays.html.ep:38 themes/default/templates/invitations/my_invitations.html.ep:80 msgid "Close" msgstr "Sluiten" @@ -111,23 +138,27 @@ msgstr "Sluiten" msgid "Comma-separated email addresses" msgstr "Komma gescheiden email adressen" -#: themes/default/templates/partial/index.js.ep:14 +#: themes/default/templates/index.html.ep:140 +msgid "Compressing zip file…" +msgstr "" + +#: themes/default/templates/partial/index.js.ep:15 msgid "Copy all links to clipboard" msgstr "Kopieer alle links naar klembord" -#: themes/default/templates/partial/index.js.ep:17 +#: themes/default/templates/partial/index.js.ep:18 msgid "Copy to clipboard" msgstr "Kopieer naar klembord" -#: lib/Lufi/Controller/Files.pm:460 +#: lib/Lufi/Controller/Files.pm:507 msgid "Could not delete the file. You are not authenticated." msgstr "Kan het bestand niet verwijderen. Je bent niet geautoriseerd." -#: lib/Lufi/Controller/Files.pm:442 +#: lib/Lufi/Controller/Files.pm:489 msgid "Could not find the file. Are you sure of the URL and the token?" msgstr "Kan het bestand niet vinden. Klopt de URL en token wel?" -#: lib/Lufi/Controller/Files.pm:353 +#: lib/Lufi/Controller/Files.pm:400 msgid "Could not find the file. Are you sure of the URL?" msgstr "Kan het bestand niet vinden. Klopt de URL?" @@ -135,7 +166,19 @@ msgstr "Kan het bestand niet vinden. Klopt de URL?" msgid "Counter" msgstr "Teller" -#: themes/default/templates/files.html.ep:29 themes/default/templates/index.html.ep:81 +#: themes/default/templates/index.html.ep:100 +msgid "Create a zip archive with the files before uploading?" +msgstr "" + +#: themes/default/templates/invitations/my_invitations.html.ep:26 +msgid "Created at" +msgstr "" + +#: themes/default/templates/invitations/my_invitations.html.ep:14 +msgid "Delete" +msgstr "" + +#: themes/default/templates/files.html.ep:29 themes/default/templates/index.html.ep:90 msgid "Delete at first download?" msgstr "Verwijder na eerste download?" @@ -143,7 +186,7 @@ msgstr "Verwijder na eerste download?" msgid "Delete selected files" msgstr "" -#: themes/default/templates/files.html.ep:32 themes/default/templates/partial/index.js.ep:18 +#: themes/default/templates/files.html.ep:32 themes/default/templates/partial/index.js.ep:19 msgid "Deletion link" msgstr "Verwijderingslink" @@ -155,7 +198,7 @@ msgstr "" "Geen zorgen: als een gebruiker de download start voor de vervaldatum, dan " "zal die het bestand kunnen binnenhalen." -#: themes/default/templates/partial/index.js.ep:20 themes/default/templates/render.html.ep:28 +#: themes/default/templates/partial/index.js.ep:21 themes/default/templates/render.html.ep:28 msgid "Download" msgstr "Download" @@ -163,7 +206,7 @@ msgstr "Download" msgid "Download aborted." msgstr "Download geannuleerd." -#: themes/default/templates/files.html.ep:27 themes/default/templates/partial/index.js.ep:19 +#: themes/default/templates/files.html.ep:27 themes/default/templates/partial/index.js.ep:20 msgid "Download link" msgstr "Download link" @@ -181,10 +224,14 @@ msgstr "" "waarmee je het bestand wilt delen en een verwijderings link, waarmee je het " "bestand kan verwijderen wanneer je dat wilt." -#: themes/default/templates/index.html.ep:94 +#: themes/default/templates/index.html.ep:122 msgid "Drop files here" msgstr "Sleep bestand(en) naar dit venster" +#: themes/default/templates/invitations/invite.html.ep:40 +msgid "Email address of your guest" +msgstr "" + #: themes/default/templates/mail.html.ep:39 msgid "Email body" msgstr "Email inhoud" @@ -197,26 +244,30 @@ msgstr "Onderwerp" msgid "Emails" msgstr "Emails" -#: themes/default/templates/partial/index.js.ep:21 +#: themes/default/templates/partial/index.js.ep:22 msgid "Encrypting part XX1 of XX2" msgstr "Encrypten deel XX1 van XX2 " -#: lib/Lufi/Controller/Files.pm:242 +#: lib/Lufi/Controller/Files.pm:289 msgid "Error: the file existed but was deleted." msgstr "Fout: het bestand bestond wel maar is verwijderd." -#: lib/Lufi/Controller/Files.pm:322 +#: lib/Lufi/Controller/Files.pm:369 msgid "Error: the file has not been sent entirely." msgstr "Fout: het bestand is niet volledig opgestuurd." -#: lib/Lufi/Controller/Files.pm:332 +#: lib/Lufi/Controller/Files.pm:379 msgid "Error: unable to find the file. Are you sure of your URL?" msgstr "Fout: kan het bestand niet vinden. Is de URL juist?" -#: themes/default/templates/partial/index.js.ep:22 +#: themes/default/templates/partial/index.js.ep:23 msgid "Expiration:" msgstr "Vervaldatum:" +#: themes/default/templates/invitations/my_invitations.html.ep:27 +msgid "Expire at" +msgstr "" + #: themes/default/templates/files.html.ep:31 msgid "Expires at" msgstr "Vervalt op" @@ -225,7 +276,7 @@ msgstr "Vervalt op" msgid "Export localStorage data" msgstr "Exporteer opgeslagen data" -#: lib/Lufi/Controller/Files.pm:424 +#: lib/Lufi/Controller/Files.pm:471 msgid "File deleted" msgstr "Bestand verwijderd" @@ -233,15 +284,27 @@ msgstr "Bestand verwijderd" msgid "File name" msgstr "Bestandsnaam" -#: themes/default/templates/index.html.ep:71 +#: themes/default/templates/invitations/my_invitations.html.ep:61 +msgid "Files" +msgstr "" + +#: themes/default/templates/index.html.ep:80 msgid "Files deleted at first download" msgstr "" +#: themes/default/templates/invitations/my_invitations.html.ep:28 +msgid "Files sent at" +msgstr "" + +#: themes/default/templates/partial/invitations.js.ep:8 +msgid "Files sent in invitation XX1 by XX2" +msgstr "" + #: themes/default/templates/partial/render.js.ep:8 msgid "Get the file" msgstr "Download bestand" -#: themes/default/templates/about.html.ep:19 +#: themes/default/templates/about.html.ep:18 msgid "" "Get the source code on the official repository or on its de officiële repository of op Github mirror" +#: themes/default/templates/invitations/my_invitations.html.ep:24 +msgid "Guest mail" +msgstr "" + +#. (ucfirst(stash('invitation') +#: themes/default/templates/invitations/notification_files_sent.mail.ep:6 +msgid "Hello %1," +msgstr "" + +#: themes/default/templates/invitations/invite.mail.ep:4 +msgid "Hello," +msgstr "" + #: themes/default/templates/partial/mail.js.ep:35 msgid "Hello,\\n\\nHere's some files I want to share with you:\\n" msgstr "Hallo,\\n\\nHier zijn enkele bestanden die ik met je wil delen:\\n\\n" @@ -259,11 +335,15 @@ msgstr "Hallo,\\n\\nHier zijn enkele bestanden die ik met je wil delen:\\n\\n" msgid "Here's some files" msgstr "Hier zijn enkele bestanden" -#: themes/default/templates/partial/index.js.ep:24 +#: themes/default/templates/partial/invitations.js.ep:7 +msgid "Hide hidden invitations" +msgstr "" + +#: themes/default/templates/partial/index.js.ep:25 msgid "Hit Enter, then Ctrl+C to copy all the download links" msgstr "Druk Enter, vervolgens CTRL+C om alle download links te kopieeren" -#: themes/default/templates/partial/index.js.ep:23 +#: themes/default/templates/partial/index.js.ep:24 msgid "Hit Enter, then Ctrl+C to copy the download link" msgstr "Druk Enter, vervolgens CTRL+C om de download link te kopieeren" @@ -271,7 +351,11 @@ msgstr "Druk Enter, vervolgens CTRL+C om de download link te kopieeren" msgid "How does it work?" msgstr "Hoe werkt het?" -#: themes/default/templates/about.html.ep:17 +#: themes/default/templates/invitations/invite.html.ep:46 +msgid "How many days would you like the invitation to be valid?" +msgstr "" + +#: themes/default/templates/about.html.ep:16 msgid "How to install the software on my server?" msgstr "Hoe installeer ik de software op mijn server?" @@ -297,7 +381,7 @@ msgstr "" msgid "Import localStorage data" msgstr "Importeer opgeslagen data" -#: themes/default/templates/index.html.ep:44 +#: themes/default/templates/index.html.ep:53 msgid "Important: more information on delays" msgstr "Belangrijk: meer informatie over uitstel" @@ -305,6 +389,24 @@ msgstr "Belangrijk: meer informatie over uitstel" msgid "Information about delays" msgstr "Informatie over uitstel" +#: themes/default/templates/invitations/my_invitations.html.ep:12 +msgid "Invert selection" +msgstr "" + +#. ($i->guest_mail, $url) +#: lib/Lufi/Controller/Invitation.pm:171 +msgid "Invitation resent to %1.
URL: %2" +msgstr "" + +#. ($invitation->guest_mail, $url) +#: lib/Lufi/Controller/Invitation.pm:87 +msgid "Invitation sent to %1.
URL: %2" +msgstr "" + +#: themes/default/templates/invitations/invite.html.ep:27 themes/default/templates/layouts/default.html.ep:36 themes/default/templates/layouts/default.html.ep:69 +msgid "Invite a guest" +msgstr "" + #: themes/default/templates/partial/render.js.ep:6 msgid "" "It seems that the key in your URL is incorrect. Please, verify your URL." @@ -315,7 +417,7 @@ msgstr "" msgid "Javascript is disabled. You won't be able to use Lufi." msgstr "Javascript is uitgeschakeld. Je kan geen gebruik maken van Lufi." -#: themes/default/templates/layouts/default.html.ep:40 themes/default/templates/layouts/default.html.ep:68 +#: themes/default/templates/layouts/default.html.ep:44 themes/default/templates/layouts/default.html.ep:46 themes/default/templates/layouts/default.html.ep:77 themes/default/templates/layouts/default.html.ep:79 msgid "Language" msgstr "" @@ -323,7 +425,7 @@ msgstr "" msgid "Login" msgstr "Login" -#: themes/default/templates/layouts/default.html.ep:53 themes/default/templates/layouts/default.html.ep:78 +#: themes/default/templates/layouts/default.html.ep:58 themes/default/templates/layouts/default.html.ep:91 msgid "Logout" msgstr "Logout" @@ -335,16 +437,29 @@ msgstr "Lufi is een gratis bestand hosting software." msgid "Mail" msgstr "" -#: themes/default/templates/files.html.ep:3 themes/default/templates/layouts/default.html.ep:34 themes/default/templates/layouts/default.html.ep:62 +#: themes/default/templates/files.html.ep:3 themes/default/templates/layouts/default.html.ep:34 themes/default/templates/layouts/default.html.ep:67 msgid "My files" msgstr "Mijn bestanden" +#: themes/default/templates/invitations/my_invitations.html.ep:5 themes/default/templates/layouts/default.html.ep:37 themes/default/templates/layouts/default.html.ep:70 +msgid "My invitations" +msgstr "" + +#: themes/default/templates/invitations/notification_files_sent.mail.ep:17 +msgid "" +"NB: this list includes the list of files that have already been sent to you." +msgstr "" + +#: themes/default/templates/index.html.ep:115 +msgid "Name of the zip file" +msgstr "" + #. (format_bytes($json->{size}) -#: lib/Lufi/Controller/Files.pm:79 +#: lib/Lufi/Controller/Files.pm:108 msgid "No enough space available on the server for this file (size: %1)." msgstr "Geen genoeg ruimte op de server voor deze bestand (grootte: %1)." -#: themes/default/templates/partial/files.js.ep:10 themes/default/templates/partial/index.js.ep:27 +#: themes/default/templates/partial/files.js.ep:10 themes/default/templates/partial/index.js.ep:28 msgid "No expiration delay" msgstr "Geen verloop uitstel" @@ -358,7 +473,7 @@ msgstr "" "Deze lijst is opgeslagen: als je opgeslagen data verwijderd, zal je deze " "lijst verlizen." -#: themes/default/templates/index.html.ep:87 themes/default/templates/login.html.ep:21 themes/default/templates/render.html.ep:26 +#: themes/default/templates/index.html.ep:106 themes/default/templates/login.html.ep:21 themes/default/templates/render.html.ep:26 msgid "Password" msgstr "Wachtwoord" @@ -375,7 +490,7 @@ msgstr "" "Een ogenblik geduld, we pakken je bestand er bij. We moeten alle delen " "downloaden en decrypten voordat je het kan downloaden." -#: lib/Lufi/Controller/Auth.pm:28 +#: lib/Lufi/Controller/Auth.pm:38 msgid "" "Please, check your credentials or your right to access this service: unable " "to authenticate." @@ -389,19 +504,39 @@ msgstr "Privacy" msgid "Purge expired files from localStorage" msgstr "Verwijder verlopen data" -#: themes/default/templates/layouts/default.html.ep:31 themes/default/templates/layouts/default.html.ep:59 +#: themes/default/templates/invitations/notification_files_sent.mail.ep:20 +msgid "Regards," +msgstr "" + +#: themes/default/templates/invitations/invite.mail.ep:15 +msgid "Regards." +msgstr "" + +#: themes/default/templates/layouts/default.html.ep:31 themes/default/templates/layouts/default.html.ep:64 msgid "Report file" msgstr "" +#: themes/default/templates/invitations/my_invitations.html.ep:15 +msgid "Resend invitation mail" +msgstr "" + +#: themes/default/templates/invitations/my_invitations.html.ep:9 +msgid "Rows in purple mean that the invitations have expired." +msgstr "" + #: themes/default/templates/files.html.ep:9 msgid "" "Rows in red mean that the files have expired and are no longer available." msgstr "Rode rijen betekenen dat deze bestanden verlopen en verwijderd zijn." -#: themes/default/templates/partial/index.js.ep:25 +#: themes/default/templates/partial/index.js.ep:26 msgid "Send all links by email" msgstr "Verstuur alle links via mail" +#: themes/default/templates/invitations/invite.html.ep:50 +msgid "Send the invitation" +msgstr "" + #: themes/default/templates/mail.html.ep:46 msgid "Send with this server" msgstr "Verstuur via deze server" @@ -410,7 +545,7 @@ msgstr "Verstuur via deze server" msgid "Send with your own mail software" msgstr "Verstuur via eigen mail software" -#: themes/default/templates/partial/index.js.ep:28 +#: themes/default/templates/partial/index.js.ep:29 msgid "" "Sending part XX1 of XX2. Please, be patient, the progress bar can take a " "while to move." @@ -421,19 +556,48 @@ msgstr "Versturen deel XX1 van XX2. Een ogenblik geduld..." msgid "Share your files in total privacy on %1" msgstr "Deel je bestanden met volledige privacy op %1" -#: themes/default/templates/layouts/default.html.ep:36 themes/default/templates/layouts/default.html.ep:64 themes/default/templates/login.html.ep:27 themes/default/templates/logout.html.ep:17 +#: themes/default/templates/invitations/my_invitations.html.ep:13 themes/default/templates/partial/invitations.js.ep:9 +msgid "Show hidden invitations" +msgstr "" + +#: themes/default/templates/partial/render.js.ep:11 +msgid "Show zip content" +msgstr "" + +#: themes/default/templates/layouts/default.html.ep:40 themes/default/templates/layouts/default.html.ep:73 themes/default/templates/login.html.ep:28 themes/default/templates/logout.html.ep:17 msgid "Signin" msgstr "Inloggen" -#: themes/default/templates/index.html.ep:37 +#: lib/Lufi/Controller/Invitation.pm:283 themes/default/templates/invitations/exception.html.ep:16 +msgid "" +"Sorry, the invitation doesn’t exist. Are you sure you are on the right URL?" +msgstr "" + +#: themes/default/templates/index.html.ep:46 msgid "Sorry, the uploading is currently disabled. Please try again later." msgstr "" "Sorry, uploaden is momenteel uitgeschakeld. Probeer het later nogmaals." -#: lib/Lufi/Controller/Files.pm:53 +#: lib/Lufi/Controller/Files.pm:82 msgid "Sorry, uploading is disabled." msgstr "SOrry, uploaden is uitgeschakeld." +#: themes/default/templates/invitations/exception.html.ep:7 +msgid "Sorry, your invitation has expired or has been deleted." +msgstr "" + +#. ($invit->ldap_user_mail) +#: lib/Lufi/Controller/Files.pm:122 +msgid "" +"Sorry, your invitation has expired or has been deleted. Please contact %1 to " +"have another invitation." +msgstr "" + +#. ($invitation->ldap_user_mail) +#: lib/Lufi/Controller/Invitation.pm:276 +msgid "The URLs of your files have been sent by email to %1." +msgstr "" + #: themes/default/templates/about.html.ep:7 msgid "" "The administrator can only see the file's name, its size and its mimetype " @@ -460,7 +624,12 @@ msgstr "Mail inhoud kan niet leeg zijn." msgid "The email subject can't be empty." msgstr "Onderwerp kan niet leeg zijn." -#: lib/Lufi/Controller/Files.pm:421 +#. ($expire_at, $max_expire_at) +#: lib/Lufi/Controller/Invitation.pm:51 +msgid "The expiration delay (%1) is not between 1 and %2 days." +msgstr "" + +#: lib/Lufi/Controller/Files.pm:468 msgid "The file has already been deleted" msgstr "Bestand is reeds verwijderd" @@ -477,10 +646,43 @@ msgstr "" msgid "The following email addresses are not valid: %1" msgstr "Volgende email adressen zijn niet geldig: %1" -#: themes/default/templates/partial/index.js.ep:15 +#. ($guest_mail) +#: lib/Lufi/Controller/Invitation.pm:48 +msgid "The guest email address (%1) is unvalid." +msgstr "" + +#. ($i->token, $i->guest_mail) +#: lib/Lufi/Controller/Invitation.pm:150 +msgid "" +"The invitation %1 can’t be resent: %2 has already sent files.
Please " +"create a new invitation." +msgstr "" + +#. ($i->token) +#: lib/Lufi/Controller/Invitation.pm:130 +msgid "The invitation %1 has been deleted." +msgstr "" + +#. (stash('user_mail') +#: themes/default/templates/invitations/invite.html.ep:34 +msgid "The invitation mail will be send from your email address (%1)." +msgstr "" + +#: themes/default/templates/partial/index.js.ep:16 msgid "The link(s) has been copied to your clipboard" msgstr "De link is gekopieerd naar je klembord" +#. (stash('invitation') +#: themes/default/templates/index.html.ep:30 +msgid "" +"The link(s) of your file(s) will automatically be sent by mail to %1 (%2)" +msgstr "" + +#. (stash('ldap_user') +#: themes/default/templates/invitations/invite.mail.ep:11 +msgid "The links of your file(s) will automatically be sent by mail to %1." +msgstr "" + #: lib/Lufi/Controller/Mail.pm:97 msgid "The mail has been sent." msgstr "Email is verzonden." @@ -488,17 +690,12 @@ msgstr "Email is verzonden." #: themes/default/templates/about.html.ep:15 msgid "" "The original (and only for now) author is Luc Didry. If you want to support him, you can do it " -"via Tipeee " -"or via Liberapay." +"class=\"classic\">Luc Didry." msgstr "" "De oorspronkelijke auteur is Luc Didry. Als je hem wilt ondersteunen, dan kan dat via Tipeee of via " -"Liberapay." +"\"classic\">Luc Didry." -#: lib/Lufi/Controller/Files.pm:189 +#: lib/Lufi/Controller/Files.pm:236 msgid "" "The server was unable to find the file record to add your file part to. " "Please, contact the administrator." @@ -506,11 +703,20 @@ msgstr "" "Server kon een deel van het bestand niet vinden. Neem contact op met " "beheerder." -#: lib/Lufi/Controller/Files.pm:248 +#: lib/Lufi/Controller/Files.pm:295 msgid "" "This file has been deactivated by the admins. Contact them to know why." msgstr "" +#: themes/default/templates/invitations/my_invitations.html.ep:46 themes/default/templates/partial/invitations.js.ep:6 +msgid "This invitation is normally hidden" +msgstr "" + +#. (stash('expires') +#: themes/default/templates/invitations/invite.mail.ep:13 +msgid "This invitation is valid until %1." +msgstr "" + #: themes/default/templates/delays.html.ep:10 msgid "" "This server sets limitations according to the file size. The expiration " @@ -521,12 +727,20 @@ msgstr "" "vervaldatum van uw bestand zal het minimum zijn tussen wat u kiest en de " "volgende beperkingen:" -#: themes/default/templates/partial/index.js.ep:16 +#: themes/default/templates/invitations/my_invitations.html.ep:16 +msgid "Toggle visibility" +msgstr "" + +#: themes/default/templates/invitations/my_invitations.html.ep:25 +msgid "URL" +msgstr "" + +#: themes/default/templates/partial/index.js.ep:17 msgid "Unable to copy the link(s) to your clipboard" msgstr "Kan de link(s) niet naar je klembord kopieeren" #. ($short) -#: lib/Lufi/Controller/Files.pm:392 +#: lib/Lufi/Controller/Files.pm:439 msgid "" "Unable to get counter for %1. The file does not exists. It will be removed " "from your localStorage." @@ -535,28 +749,32 @@ msgstr "" "worden van opgeslagen data." #. ($short) -#: lib/Lufi/Controller/Files.pm:382 +#: lib/Lufi/Controller/Files.pm:429 msgid "Unable to get counter for %1. The token is invalid." msgstr "Kan geen teller verkrijgen voor %1. De token is ongeldig." #. ($short) -#: lib/Lufi/Controller/Files.pm:402 +#: lib/Lufi/Controller/Files.pm:449 msgid "Unable to get counter for %1. You are not authenticated." msgstr "Kan geen teller verkrijgen voor %1. Je bent niet geauthenticeerd." -#: themes/default/templates/layouts/default.html.ep:33 themes/default/templates/layouts/default.html.ep:61 +#: themes/default/templates/layouts/default.html.ep:33 themes/default/templates/layouts/default.html.ep:66 msgid "Upload files" msgstr "Upload bestanden" +#: themes/default/templates/index.html.ep:145 +msgid "Upload generated zip file" +msgstr "" + #: themes/default/templates/files.html.ep:30 msgid "Uploaded at" msgstr "Geupload op" -#: themes/default/templates/index.html.ep:103 +#: themes/default/templates/index.html.ep:153 msgid "Uploaded files" msgstr "Geuploade bestanden" -#: themes/default/templates/partial/index.js.ep:29 +#: themes/default/templates/partial/index.js.ep:30 msgid "Websocket communication error" msgstr "Websocket communicatie fout" @@ -568,6 +786,16 @@ msgstr "Wat is Lufi?" msgid "Who wrote this software?" msgstr "Wie heeft deze software geschreven?" +#: themes/default/templates/partial/index.js.ep:13 +msgid "XXX file has been added to upload queue." +msgstr "" + +#: themes/default/templates/invitations/invite.html.ep:30 +msgid "" +"You can invite someone to send you files through this Lufi instance even if " +"they don’t have an account on it." +msgstr "" + #: themes/default/templates/about.html.ep:11 msgid "" "You can see the list of your files by clicking on the \"My files\" link at " @@ -606,7 +834,7 @@ msgid "" msgstr "" "Je verlaat deze pagina. Download zal geannuleerd worden. Weet je het zeker?" -#: themes/default/templates/partial/index.js.ep:13 +#: themes/default/templates/partial/index.js.ep:14 msgid "" "You have attempted to leave this page. The upload will be canceled. Are you " "sure?" @@ -621,18 +849,19 @@ msgstr "Je bent succesvol uitgelogd." msgid "You must give email addresses." msgstr "Je moet een mail adres opgeven." -#: themes/default/templates/index.html.ep:29 +#: themes/default/templates/index.html.ep:38 msgid "" -"Your browser has not enough entropy to generate a strong encryption key. " -"Please wait (it's better if you do things on your computer while waiting)." +"Your browser does not have enough entropy to generate a strong encryption " +"key. Please wait (it's better if you do things on your computer while " +"waiting)." msgstr "" #. (format_bytes($json->{size}) -#: lib/Lufi/Controller/Files.pm:66 +#: lib/Lufi/Controller/Files.pm:95 msgid "Your file is too big: %1 (maximum size allowed: %2)" msgstr "Je bestand is te groot: %1 (max: %2)" -#: lib/Lufi/Controller/Files.pm:304 +#: lib/Lufi/Controller/Files.pm:351 msgid "Your password is not valid. Please refresh the page to retry." msgstr "" @@ -650,6 +879,10 @@ msgstr "tussen %1 en %2, bestand zal voor altijd bewaard worden." msgid "deadline: " msgstr "deadline: " +#: themes/default/templates/partial/invitations.js.ep:5 +msgid "expires on XXX" +msgstr "" + #. (format_bytes($keys[$i]) #: themes/default/templates/delays.html.ep:26 msgid "for %1 and more, the file will be kept %2 day(s)" @@ -664,6 +897,11 @@ msgstr "voor %1 en meer, bestand zal voor altijd bewaard worden." msgid "no time limit" msgstr "geen tijdslimiet" -#: themes/default/templates/index.html.ep:96 +#: themes/default/templates/index.html.ep:124 msgid "or" msgstr "of" + +#. ($e->{name}, format_bytes($e->{size}) +#: themes/default/templates/invitations/notification_files_sent.mail.ep:12 +msgid "— %1 (%2), that will expire on %3" +msgstr "" diff --git a/themes/default/lib/Lufi/I18N/oc.po b/themes/default/lib/Lufi/I18N/oc.po index bf2f132..fda28df 100644 --- a/themes/default/lib/Lufi/I18N/oc.po +++ b/themes/default/lib/Lufi/I18N/oc.po @@ -5,6 +5,8 @@ # Cédric VALMARY , 2016. # Luc Didry , 2018. #zanata # Quentí, 2018. #zanata +# Luc Didry , 2019. #zanata +# Quentí, 2019. #zanata msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" @@ -12,8 +14,8 @@ msgstr "" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"PO-Revision-Date: 2018-10-28 06:34+0000\n" -"Last-Translator: Quentí \n" +"PO-Revision-Date: 2019-08-04 05:34+0000\n" +"Last-Translator: Luc Didry \n" "Language-Team: Tot en òc (totenoc.eu)\n" "Language: oc\n" "Plural-Forms: nplurals=2; plural=(n > 1);\n" @@ -21,11 +23,35 @@ msgstr "" #. ($delay) #. (max_delay) -#: themes/default/templates/index.html.ep:47 themes/default/templates/index.html.ep:56 themes/default/templates/index.html.ep:57 +#: themes/default/templates/index.html.ep:56 themes/default/templates/index.html.ep:65 themes/default/templates/index.html.ep:66 msgid "%1 days" msgstr "%1 jorns" -#: themes/default/templates/partial/index.js.ep:26 +#. (stash('ldap_user') +#: themes/default/templates/invitations/invite.mail.ep:2 +msgid "%1 invites you to send him/her files" +msgstr "%1 vos convida a li mandar de fichièrs" + +#. (stash('ldap_user') +#: themes/default/templates/invitations/invite.mail.ep:6 +msgid "%1 invites you to send him/her files through Lufi." +msgstr "%1 vos convida a li mandar de fichièrs per Lufi" + +#. (stash('invitation') +#: themes/default/templates/invitations/notification_files_sent.mail.ep:4 +msgid "%1 sent you files" +msgstr "%1 vos mandèt de fichièrs" + +#. (stash('invitation') +#: themes/default/templates/invitations/notification_files_sent.mail.ep:8 +msgid "%1 used your invitation to send you files:" +msgstr "%1 utilizèt vòstra invitacion per vos mandar de fichièrs :" + +#: lib/Lufi/Controller/Invitation.pm:159 lib/Lufi/Controller/Invitation.pm:84 themes/default/templates/invitations/my_invitations.html.ep:51 themes/default/templates/invitations/my_invitations.html.ep:52 themes/default/templates/invitations/my_invitations.html.ep:53 themes/default/templates/invitations/notification_files_sent.mail.ep:12 +msgid "%A %d %B %Y at %T" +msgstr "%A %d %B %Y a %T" + +#: themes/default/templates/partial/index.js.ep:27 msgid "(max size: XXX)" msgstr "(talha max : XXX)" @@ -33,7 +59,7 @@ msgstr "(talha max : XXX)" msgid "1 year" msgstr "1 an" -#: themes/default/templates/index.html.ep:4 themes/default/templates/index.html.ep:56 +#: themes/default/templates/index.html.ep:4 themes/default/templates/index.html.ep:65 msgid "24 hours" msgstr "24 oras" @@ -41,27 +67,15 @@ msgstr "24 oras" msgid ":" msgstr " :" -#: themes/default/templates/about.html.ep:16 -msgid "" -"A thank you with a photo of kitten on Diaspora* or " -"Mastodon " -"is cool too ;-)" -msgstr "" -"Un mercé amb una fotografia de caton sus Diaspora* o " -"Mastodon " -"conven tanben ;-)" - #: themes/default/templates/render.html.ep:42 msgid "Abort" msgstr "Anullar" -#: themes/default/templates/layouts/default.html.ep:48 themes/default/templates/layouts/default.html.ep:76 +#: themes/default/templates/layouts/default.html.ep:53 themes/default/templates/layouts/default.html.ep:86 msgid "About" msgstr "A prepaus" -#: themes/default/templates/index.html.ep:88 +#: themes/default/templates/index.html.ep:107 msgid "Add a password to file(s)" msgstr "Apondre un senhal al(s) fichièr(s)" @@ -73,7 +87,19 @@ msgstr "" "Ajustar d’URL pas ligadas a aquesta instància pel còr o subjècte d’un " "corrièl es defendut." -#: themes/default/templates/about.html.ep:18 +#: themes/default/templates/partial/invitations.js.ep:3 +msgid "Are you sure you want to delete the selected invitations?" +msgstr "Volètz vertadièrament suprimir l’invitacion seleccionada ?" + +#: themes/default/templates/partial/invitations.js.ep:4 +msgid "" +"Are you sure you want to resend the invitation mail for the selected " +"invitations?" +msgstr "" +"Volètz vertadièrament tornar mandar lo corrièl d’invitacion per las " +"invitacions seleccionadas ?" + +#: themes/default/templates/about.html.ep:17 msgid "" "As Lufi is a free software licensed under of the terms of the AGPLv3, you can " @@ -91,7 +117,7 @@ msgstr "" msgid "Asking for file part XX1 of %1" msgstr "Demanda del tròç XX1 sus %1 del fichièr" -#: themes/default/templates/about.html.ep:21 +#: themes/default/templates/about.html.ep:20 msgid "Back to homepage" msgstr "Tornar a la pagina d’acuèlh" @@ -99,7 +125,7 @@ msgstr "Tornar a la pagina d’acuèlh" msgid "Bad CSRF token!" msgstr "Marrit geton CSRF !" -#: lib/Lufi/Controller/Auth.pm:22 lib/Lufi/Controller/Auth.pm:39 +#: lib/Lufi/Controller/Auth.pm:27 lib/Lufi/Controller/Auth.pm:49 msgid "Bad CSRF token." msgstr "Marrit geton CSRF." @@ -108,11 +134,15 @@ msgid "Click here to refresh the page and restart the download." msgstr "" "Clicatz aquí per actualizar la pagina e tornar començar lo telecargament." -#: themes/default/templates/index.html.ep:98 +#: themes/default/templates/invitations/invite.mail.ep:8 +msgid "Click on the following URL to upload files on Lufi:" +msgstr "Clicatz l’URL seguenta per mandar de fichièrs sus Lufi :" + +#: themes/default/templates/index.html.ep:126 msgid "Click to open the file browser" msgstr "Clicatz per dobrir lo navigador de fichièr" -#: themes/default/templates/delays.html.ep:38 +#: themes/default/templates/delays.html.ep:38 themes/default/templates/invitations/my_invitations.html.ep:80 msgid "Close" msgstr "Tampar" @@ -120,25 +150,29 @@ msgstr "Tampar" msgid "Comma-separated email addresses" msgstr "Adreças de corrièl separadas per de virgulas" -#: themes/default/templates/partial/index.js.ep:14 +#: themes/default/templates/index.html.ep:140 +msgid "Compressing zip file…" +msgstr "Compression del fichièr zip…" + +#: themes/default/templates/partial/index.js.ep:15 msgid "Copy all links to clipboard" msgstr "Copiar totes los ligams al quicha-papièrs" -#: themes/default/templates/partial/index.js.ep:17 +#: themes/default/templates/partial/index.js.ep:18 msgid "Copy to clipboard" msgstr "Copiar al quicha-papièrs" -#: lib/Lufi/Controller/Files.pm:460 +#: lib/Lufi/Controller/Files.pm:507 msgid "Could not delete the file. You are not authenticated." msgstr "Impossible de suprimir lo fichièr. Sètz pas connectat-ada." -#: lib/Lufi/Controller/Files.pm:442 +#: lib/Lufi/Controller/Files.pm:489 msgid "Could not find the file. Are you sure of the URL and the token?" msgstr "" "Impossible de trobar lo fichièr. Sètz segur-a que l’URL e lo geton son " "bons ?" -#: lib/Lufi/Controller/Files.pm:353 +#: lib/Lufi/Controller/Files.pm:400 msgid "Could not find the file. Are you sure of the URL?" msgstr "Impossible de trobar lo fichièr. Sètz segur-a que l’URL es bona ?" @@ -146,7 +180,19 @@ msgstr "Impossible de trobar lo fichièr. Sètz segur-a que l’URL es bona ?" msgid "Counter" msgstr "Comptador" -#: themes/default/templates/files.html.ep:29 themes/default/templates/index.html.ep:81 +#: themes/default/templates/index.html.ep:100 +msgid "Create a zip archive with the files before uploading?" +msgstr "Crear un archiu zip amb los fichièr abans lo mandadís ?" + +#: themes/default/templates/invitations/my_invitations.html.ep:26 +msgid "Created at" +msgstr "Creada lo" + +#: themes/default/templates/invitations/my_invitations.html.ep:14 +msgid "Delete" +msgstr "Suprimir" + +#: themes/default/templates/files.html.ep:29 themes/default/templates/index.html.ep:90 msgid "Delete at first download?" msgstr "Suprimir aprèp lo primièr telecargament ?" @@ -154,7 +200,7 @@ msgstr "Suprimir aprèp lo primièr telecargament ?" msgid "Delete selected files" msgstr "Suprimir los fichièrs seleccionats" -#: themes/default/templates/files.html.ep:32 themes/default/templates/partial/index.js.ep:18 +#: themes/default/templates/files.html.ep:32 themes/default/templates/partial/index.js.ep:19 msgid "Deletion link" msgstr "Ligam de supression" @@ -167,7 +213,7 @@ msgstr "" "abans son expiracion e que lo telecargament s’acaba aprèp l’expiracion, " "utilizaire poirà recuperar lo fichièr." -#: themes/default/templates/partial/index.js.ep:20 themes/default/templates/render.html.ep:28 +#: themes/default/templates/partial/index.js.ep:21 themes/default/templates/render.html.ep:28 msgid "Download" msgstr "Telecargar" @@ -175,7 +221,7 @@ msgstr "Telecargar" msgid "Download aborted." msgstr "Telecargament abandonat." -#: themes/default/templates/files.html.ep:27 themes/default/templates/partial/index.js.ep:19 +#: themes/default/templates/files.html.ep:27 themes/default/templates/partial/index.js.ep:20 msgid "Download link" msgstr "Ligam de telecargament" @@ -192,10 +238,14 @@ msgstr "" "chifrats e mandats al servidor. Recuperaretz dos ligams per fichièr : un " "ligam de telecargament e un ligam per suprimir lo fichièr quand o volètz." -#: themes/default/templates/index.html.ep:94 +#: themes/default/templates/index.html.ep:122 msgid "Drop files here" msgstr "Lisatz vòstres fichièrs aquí" +#: themes/default/templates/invitations/invite.html.ep:40 +msgid "Email address of your guest" +msgstr "Adreça electronica de vòstre convidat" + #: themes/default/templates/mail.html.ep:39 msgid "Email body" msgstr "Còs del corrièl" @@ -208,26 +258,30 @@ msgstr "Subjècte del corrièl" msgid "Emails" msgstr "Corrièl" -#: themes/default/templates/partial/index.js.ep:21 +#: themes/default/templates/partial/index.js.ep:22 msgid "Encrypting part XX1 of XX2" msgstr "Chiframent del tròç XX1 sus XX2" -#: lib/Lufi/Controller/Files.pm:242 +#: lib/Lufi/Controller/Files.pm:289 msgid "Error: the file existed but was deleted." msgstr "Error : lo fichièr existissiá mas es estat suprimit" -#: lib/Lufi/Controller/Files.pm:322 +#: lib/Lufi/Controller/Files.pm:369 msgid "Error: the file has not been sent entirely." msgstr "Error : lo fichièr es pas estat mandat completament" -#: lib/Lufi/Controller/Files.pm:332 +#: lib/Lufi/Controller/Files.pm:379 msgid "Error: unable to find the file. Are you sure of your URL?" msgstr "Error : impossible de trobar lo fichièr. Sètz segur-a de l’URL ?" -#: themes/default/templates/partial/index.js.ep:22 +#: themes/default/templates/partial/index.js.ep:23 msgid "Expiration:" msgstr "Expiracion :" +#: themes/default/templates/invitations/my_invitations.html.ep:27 +msgid "Expire at" +msgstr "Expira lo" + #: themes/default/templates/files.html.ep:31 msgid "Expires at" msgstr "Expira lo" @@ -236,7 +290,7 @@ msgstr "Expira lo" msgid "Export localStorage data" msgstr "Exportar las donadas localStorage" -#: lib/Lufi/Controller/Files.pm:424 +#: lib/Lufi/Controller/Files.pm:471 msgid "File deleted" msgstr "Fichièr suprimit" @@ -244,15 +298,27 @@ msgstr "Fichièr suprimit" msgid "File name" msgstr "Nom del fichièr" -#: themes/default/templates/index.html.ep:71 +#: themes/default/templates/invitations/my_invitations.html.ep:61 +msgid "Files" +msgstr "Fichièrs" + +#: themes/default/templates/index.html.ep:80 msgid "Files deleted at first download" msgstr "Fichièr suprimit al primièr telecargament" +#: themes/default/templates/invitations/my_invitations.html.ep:28 +msgid "Files sent at" +msgstr "Fichièrs mandats a" + +#: themes/default/templates/partial/invitations.js.ep:8 +msgid "Files sent in invitation XX1 by XX2" +msgstr "Fichièrs mandats dins l’invitacion XX1 per XX2" + #: themes/default/templates/partial/render.js.ep:8 msgid "Get the file" msgstr "Recuperar lo fichièr" -#: themes/default/templates/about.html.ep:19 +#: themes/default/templates/about.html.ep:18 msgid "" "Get the source code on the official repository or on its depaus oficial o sus son miralh Github." +#: themes/default/templates/invitations/my_invitations.html.ep:24 +msgid "Guest mail" +msgstr "Corrièl del convidat" + +#. (ucfirst(stash('invitation') +#: themes/default/templates/invitations/notification_files_sent.mail.ep:6 +msgid "Hello %1," +msgstr "Adiu %1," + +#: themes/default/templates/invitations/invite.mail.ep:4 +msgid "Hello," +msgstr "Adiu," + #: themes/default/templates/partial/mail.js.ep:35 msgid "Hello,\\n\\nHere's some files I want to share with you:\\n" msgstr "Bonjorn,\\n\\nVaquí qualques fichièrs que desiri partejar amb tu :\\n" @@ -270,13 +349,17 @@ msgstr "Bonjorn,\\n\\nVaquí qualques fichièrs que desiri partejar amb tu :\\ msgid "Here's some files" msgstr "Vaquí qualques fichièrs" -#: themes/default/templates/partial/index.js.ep:24 +#: themes/default/templates/partial/invitations.js.ep:7 +msgid "Hide hidden invitations" +msgstr "Amagar las invitacions amagadas" + +#: themes/default/templates/partial/index.js.ep:25 msgid "Hit Enter, then Ctrl+C to copy all the download links" msgstr "" "Quichatz Entrada puèi fasètz Ctrl+C per copiar totes los ligams per " "telecargar" -#: themes/default/templates/partial/index.js.ep:23 +#: themes/default/templates/partial/index.js.ep:24 msgid "Hit Enter, then Ctrl+C to copy the download link" msgstr "" "Quichatz Entrada puèi fasètz Ctrl+C per copiar lo ligam per telecargar" @@ -285,7 +368,11 @@ msgstr "" msgid "How does it work?" msgstr "Cossí aquò fonciona ?" -#: themes/default/templates/about.html.ep:17 +#: themes/default/templates/invitations/invite.html.ep:46 +msgid "How many days would you like the invitation to be valid?" +msgstr "Quant de jorns volriatz que l’invitacion siá valida ?" + +#: themes/default/templates/about.html.ep:16 msgid "How to install the software on my server?" msgstr "Cossí installar lo logicial sus mon servidor ?" @@ -310,7 +397,7 @@ msgstr "" msgid "Import localStorage data" msgstr "Importar las donadas localStorage" -#: themes/default/templates/index.html.ep:44 +#: themes/default/templates/index.html.ep:53 msgid "Important: more information on delays" msgstr "Important : mai d’informacions suls relambis" @@ -318,6 +405,24 @@ msgstr "Important : mai d’informacions suls relambis" msgid "Information about delays" msgstr "Informacion suls relambis" +#: themes/default/templates/invitations/my_invitations.html.ep:12 +msgid "Invert selection" +msgstr "Invertir la seleccion" + +#. ($i->guest_mail, $url) +#: lib/Lufi/Controller/Invitation.pm:171 +msgid "Invitation resent to %1.
URL: %2" +msgstr "Invitacion mandada tornamai a %1.
URL : %2" + +#. ($invitation->guest_mail, $url) +#: lib/Lufi/Controller/Invitation.pm:87 +msgid "Invitation sent to %1.
URL: %2" +msgstr "Invitacion mandada a %1.
URL : %2" + +#: themes/default/templates/invitations/invite.html.ep:27 themes/default/templates/layouts/default.html.ep:36 themes/default/templates/layouts/default.html.ep:69 +msgid "Invite a guest" +msgstr "Convidar qualqu’un" + #: themes/default/templates/partial/render.js.ep:6 msgid "" "It seems that the key in your URL is incorrect. Please, verify your URL." @@ -329,7 +434,7 @@ msgstr "" msgid "Javascript is disabled. You won't be able to use Lufi." msgstr "Javascript es desactivat. Lufi foncionarà pas." -#: themes/default/templates/layouts/default.html.ep:40 themes/default/templates/layouts/default.html.ep:68 +#: themes/default/templates/layouts/default.html.ep:44 themes/default/templates/layouts/default.html.ep:46 themes/default/templates/layouts/default.html.ep:77 themes/default/templates/layouts/default.html.ep:79 msgid "Language" msgstr "Lenga" @@ -337,7 +442,7 @@ msgstr "Lenga" msgid "Login" msgstr "Identificant" -#: themes/default/templates/layouts/default.html.ep:53 themes/default/templates/layouts/default.html.ep:78 +#: themes/default/templates/layouts/default.html.ep:58 themes/default/templates/layouts/default.html.ep:91 msgid "Logout" msgstr "Desconnexion" @@ -349,18 +454,33 @@ msgstr "Lufi es un logicial liure d’albèrgament de fichièrs." msgid "Mail" msgstr "Corrièl" -#: themes/default/templates/files.html.ep:3 themes/default/templates/layouts/default.html.ep:34 themes/default/templates/layouts/default.html.ep:62 +#: themes/default/templates/files.html.ep:3 themes/default/templates/layouts/default.html.ep:34 themes/default/templates/layouts/default.html.ep:67 msgid "My files" msgstr "Mos fichièrs" +#: themes/default/templates/invitations/my_invitations.html.ep:5 themes/default/templates/layouts/default.html.ep:37 themes/default/templates/layouts/default.html.ep:70 +msgid "My invitations" +msgstr "Mas invitacions" + +#: themes/default/templates/invitations/notification_files_sent.mail.ep:17 +msgid "" +"NB: this list includes the list of files that have already been sent to you." +msgstr "" +"NB : aquesta lista inclutz la lista dels fichièrs que vos son estats mandats." +"" + +#: themes/default/templates/index.html.ep:115 +msgid "Name of the zip file" +msgstr "Nom del fichièr zip" + #. (format_bytes($json->{size}) -#: lib/Lufi/Controller/Files.pm:79 +#: lib/Lufi/Controller/Files.pm:108 msgid "No enough space available on the server for this file (size: %1)." msgstr "" "Espaci disc insufisent sul servidor per aqueste fichièr (talha del fichièr :" " \"%1)." -#: themes/default/templates/partial/files.js.ep:10 themes/default/templates/partial/index.js.ep:27 +#: themes/default/templates/partial/files.js.ep:10 themes/default/templates/partial/index.js.ep:28 msgid "No expiration delay" msgstr "Pas cap de relambi d’expiracion" @@ -374,7 +494,7 @@ msgstr "" "informacions son gardadas en localStorage : se suprimissètz vòstras donadas " "localStorage, perdretz aquelas informacions." -#: themes/default/templates/index.html.ep:87 themes/default/templates/login.html.ep:21 themes/default/templates/render.html.ep:26 +#: themes/default/templates/index.html.ep:106 themes/default/templates/login.html.ep:21 themes/default/templates/render.html.ep:26 msgid "Password" msgstr "Senhal" @@ -392,7 +512,7 @@ msgstr "" "primièr recuperar e deschifrar totes los tròces abans que poscatz o " "telecargar." -#: lib/Lufi/Controller/Auth.pm:28 +#: lib/Lufi/Controller/Auth.pm:38 msgid "" "Please, check your credentials or your right to access this service: unable " "to authenticate." @@ -408,20 +528,40 @@ msgstr "Confidencialitat" msgid "Purge expired files from localStorage" msgstr "Suprimir del localStorage los fichièrs expirats" -#: themes/default/templates/layouts/default.html.ep:31 themes/default/templates/layouts/default.html.ep:59 +#: themes/default/templates/invitations/notification_files_sent.mail.ep:20 +msgid "Regards," +msgstr "Coralament," + +#: themes/default/templates/invitations/invite.mail.ep:15 +msgid "Regards." +msgstr "Coralament." + +#: themes/default/templates/layouts/default.html.ep:31 themes/default/templates/layouts/default.html.ep:64 msgid "Report file" msgstr "Senhalar un fichièr" +#: themes/default/templates/invitations/my_invitations.html.ep:15 +msgid "Resend invitation mail" +msgstr "Tornar mandar lo corrièl d’invitacion" + +#: themes/default/templates/invitations/my_invitations.html.ep:9 +msgid "Rows in purple mean that the invitations have expired." +msgstr "Las linhas en violet representan las invitacions expiradas." + #: themes/default/templates/files.html.ep:9 msgid "" "Rows in red mean that the files have expired and are no longer available." msgstr "" "Las linhas en roge indican que lo fichièr a expirat e es pas mai disponible." -#: themes/default/templates/partial/index.js.ep:25 +#: themes/default/templates/partial/index.js.ep:26 msgid "Send all links by email" msgstr "Mandar totes los ligams per corrièl" +#: themes/default/templates/invitations/invite.html.ep:50 +msgid "Send the invitation" +msgstr "Mandar l’invitacion" + #: themes/default/templates/mail.html.ep:46 msgid "Send with this server" msgstr "Mandar amb aqueste servidor" @@ -430,7 +570,7 @@ msgstr "Mandar amb aqueste servidor" msgid "Send with your own mail software" msgstr "Mandar amb vòstre pròpri logicial de corrièl" -#: themes/default/templates/partial/index.js.ep:28 +#: themes/default/templates/partial/index.js.ep:29 msgid "" "Sending part XX1 of XX2. Please, be patient, the progress bar can take a " "while to move." @@ -443,20 +583,51 @@ msgstr "" msgid "Share your files in total privacy on %1" msgstr "Partejatz vòstres fichièrs en tota confidencialitat sus %1" -#: themes/default/templates/layouts/default.html.ep:36 themes/default/templates/layouts/default.html.ep:64 themes/default/templates/login.html.ep:27 themes/default/templates/logout.html.ep:17 +#: themes/default/templates/invitations/my_invitations.html.ep:13 themes/default/templates/partial/invitations.js.ep:9 +msgid "Show hidden invitations" +msgstr "Far veire las invitacions amagadas" + +#: themes/default/templates/partial/render.js.ep:11 +msgid "Show zip content" +msgstr "Mostrar lo contengut del zip" + +#: themes/default/templates/layouts/default.html.ep:40 themes/default/templates/layouts/default.html.ep:73 themes/default/templates/login.html.ep:28 themes/default/templates/logout.html.ep:17 msgid "Signin" msgstr "Connexion" -#: themes/default/templates/index.html.ep:37 +#: lib/Lufi/Controller/Invitation.pm:283 themes/default/templates/invitations/exception.html.ep:16 +msgid "" +"Sorry, the invitation doesn’t exist. Are you sure you are on the right URL?" +msgstr "O planhèm, l’invitacion existís pas. Avètz utilizat la bona URL ?" + +#: themes/default/templates/index.html.ep:46 msgid "Sorry, the uploading is currently disabled. Please try again later." msgstr "" "O planhèm, la foncion per mandar de fichièr es desactivada pel moment. " "Mercés de tornar ensajar mai tard." -#: lib/Lufi/Controller/Files.pm:53 +#: lib/Lufi/Controller/Files.pm:82 msgid "Sorry, uploading is disabled." msgstr "O planhèm, la foncion per mandar de fichièr es desactivada." +#: themes/default/templates/invitations/exception.html.ep:7 +msgid "Sorry, your invitation has expired or has been deleted." +msgstr "O planhèm, vòstra invitacion a expirada o es estada suprimida." + +#. ($invit->ldap_user_mail) +#: lib/Lufi/Controller/Files.pm:122 +msgid "" +"Sorry, your invitation has expired or has been deleted. Please contact %1 to " +"have another invitation." +msgstr "" +"O planhèm, vòstra invitacion a expirada o es estada suprimida. Contactatz %1 " +"per n’aver una autra." + +#. ($invitation->ldap_user_mail) +#: lib/Lufi/Controller/Invitation.pm:276 +msgid "The URLs of your files have been sent by email to %1." +msgstr "Las URL de vòstres fichièrs son estadas mandadas per corrièl a %1." + #: themes/default/templates/about.html.ep:7 msgid "" "The administrator can only see the file's name, its size and its mimetype " @@ -485,7 +656,12 @@ msgstr "Lo contengut del corrièl pòt pas èsser void." msgid "The email subject can't be empty." msgstr "Lo subjècte del corrièl pòt pas èsser void." -#: lib/Lufi/Controller/Files.pm:421 +#. ($expire_at, $max_expire_at) +#: lib/Lufi/Controller/Invitation.pm:51 +msgid "The expiration delay (%1) is not between 1 and %2 days." +msgstr "Lo relambi d’expiracion (%1) es pas entre 1 e %2 jorns." + +#: lib/Lufi/Controller/Files.pm:468 msgid "The file has already been deleted" msgstr "Lo fichièr es ja estat suprimit" @@ -503,10 +679,49 @@ msgstr "" msgid "The following email addresses are not valid: %1" msgstr "Las adreças de corrièl seguentas son pas validas : %1" -#: themes/default/templates/partial/index.js.ep:15 +#. ($guest_mail) +#: lib/Lufi/Controller/Invitation.pm:48 +msgid "The guest email address (%1) is unvalid." +msgstr "L’adreça del convida (%1) es pas valida." + +#. ($i->token, $i->guest_mail) +#: lib/Lufi/Controller/Invitation.pm:150 +msgid "" +"The invitation %1 can’t be resent: %2 has already sent files.
Please " +"create a new invitation." +msgstr "" +"L’invitacion %1 pòt pas èsser tornada manda : %2 mandèt ja de fichièrs." +"
Volgatz crear una nòva invitacion." + +#. ($i->token) +#: lib/Lufi/Controller/Invitation.pm:130 +msgid "The invitation %1 has been deleted." +msgstr "L’invitacion %1 es estada suprimida." + +#. (stash('user_mail') +#: themes/default/templates/invitations/invite.html.ep:34 +msgid "The invitation mail will be send from your email address (%1)." +msgstr "Lo messatge d’invitacion serà mandat a vòstra adreça electronica (%1)" + +#: themes/default/templates/partial/index.js.ep:16 msgid "The link(s) has been copied to your clipboard" msgstr "Lo(s) ligam(s) es/son estat(s) copiat(s) al quicha-papièrs" +#. (stash('invitation') +#: themes/default/templates/index.html.ep:30 +msgid "" +"The link(s) of your file(s) will automatically be sent by mail to %1 (%2)" +msgstr "" +"Los ligams de vòstre(s) fichièr(s) seràn automaticament mandats per corrièl " +"a %1 (%2)" + +#. (stash('ldap_user') +#: themes/default/templates/invitations/invite.mail.ep:11 +msgid "The links of your file(s) will automatically be sent by mail to %1." +msgstr "" +"Los ligams de vòstre(s) fichièr(s) seràn automaticament mandats per corrièl " +"a %1." + #: lib/Lufi/Controller/Mail.pm:97 msgid "The mail has been sent." msgstr "Lo corrièl es estat mandat." @@ -514,18 +729,12 @@ msgstr "Lo corrièl es estat mandat." #: themes/default/templates/about.html.ep:15 msgid "" "The original (and only for now) author is Luc Didry. If you want to support him, you can do it " -"via Tipeee " -"or via Liberapay." +"class=\"classic\">Luc Didry." msgstr "" "L’autor original (e pel moment, lo sol) es Luc Didry. S’avètz enveja de lo sostenir, podètz o far " -"via Tipeee " -"o via Liberapay." -"" +"class=\"classic\">Luc Didry." -#: lib/Lufi/Controller/Files.pm:189 +#: lib/Lufi/Controller/Files.pm:236 msgid "" "The server was unable to find the file record to add your file part to. " "Please, contact the administrator." @@ -534,13 +743,22 @@ msgstr "" "que li cal ajustar vòstre tròç de fichièr. Mercés de contactar " "l’administrator." -#: lib/Lufi/Controller/Files.pm:248 +#: lib/Lufi/Controller/Files.pm:295 msgid "" "This file has been deactivated by the admins. Contact them to know why." msgstr "" "Aqueste fichièr es estat desactivat pels administrators. Contactatz-los per " "saber perque." +#: themes/default/templates/invitations/my_invitations.html.ep:46 themes/default/templates/partial/invitations.js.ep:6 +msgid "This invitation is normally hidden" +msgstr "Aquesta invitacion es normalament amagada" + +#. (stash('expires') +#: themes/default/templates/invitations/invite.mail.ep:13 +msgid "This invitation is valid until %1." +msgstr "L’invitacion es valida fins al %1." + #: themes/default/templates/delays.html.ep:10 msgid "" "This server sets limitations according to the file size. The expiration " @@ -551,12 +769,20 @@ msgstr "" "relambi d’expiracion de vòstre fichièr serà lo minimum entre çò qu’avètz " "causit e los limits seguents :" -#: themes/default/templates/partial/index.js.ep:16 +#: themes/default/templates/invitations/my_invitations.html.ep:16 +msgid "Toggle visibility" +msgstr "Bascular la visibilitat" + +#: themes/default/templates/invitations/my_invitations.html.ep:25 +msgid "URL" +msgstr "URL" + +#: themes/default/templates/partial/index.js.ep:17 msgid "Unable to copy the link(s) to your clipboard" msgstr "Impossible de copiar lo(s) ligams(s) al quicha-papièrs" #. ($short) -#: lib/Lufi/Controller/Files.pm:392 +#: lib/Lufi/Controller/Files.pm:439 msgid "" "Unable to get counter for %1. The file does not exists. It will be removed " "from your localStorage." @@ -565,28 +791,32 @@ msgstr "" "levat de vòstre localStorage." #. ($short) -#: lib/Lufi/Controller/Files.pm:382 +#: lib/Lufi/Controller/Files.pm:429 msgid "Unable to get counter for %1. The token is invalid." msgstr "Impossible de recuperar lo comptador per %1. Lo geton es invalid." #. ($short) -#: lib/Lufi/Controller/Files.pm:402 +#: lib/Lufi/Controller/Files.pm:449 msgid "Unable to get counter for %1. You are not authenticated." msgstr "Impossible de recuperar lo comptador per %1. Sètz pas connectat·ada." -#: themes/default/templates/layouts/default.html.ep:33 themes/default/templates/layouts/default.html.ep:61 +#: themes/default/templates/layouts/default.html.ep:33 themes/default/templates/layouts/default.html.ep:66 msgid "Upload files" msgstr "Mandar de fichièrs" +#: themes/default/templates/index.html.ep:145 +msgid "Upload generated zip file" +msgstr "Mandar lo fichièr zip generat" + #: themes/default/templates/files.html.ep:30 msgid "Uploaded at" msgstr "Mandat lo" -#: themes/default/templates/index.html.ep:103 +#: themes/default/templates/index.html.ep:153 msgid "Uploaded files" msgstr "Fichièrs mandats" -#: themes/default/templates/partial/index.js.ep:29 +#: themes/default/templates/partial/index.js.ep:30 msgid "Websocket communication error" msgstr "Error de comunicacion WebSocket" @@ -598,6 +828,18 @@ msgstr "Qu’es aquò Lufi ?" msgid "Who wrote this software?" msgstr "Qual escriguèt aqueste logicial ?" +#: themes/default/templates/partial/index.js.ep:13 +msgid "XXX file has been added to upload queue." +msgstr "XXX fichièrs son estats ajustats a la fila de mandadís" + +#: themes/default/templates/invitations/invite.html.ep:30 +msgid "" +"You can invite someone to send you files through this Lufi instance even if " +"they don’t have an account on it." +msgstr "" +"Podètz convidar qualqu’un per que vos mande de fichièrs per aquesta " +"instància Lufi encara qu’ajan pas de compte sus aquela d’aquí." + #: themes/default/templates/about.html.ep:11 msgid "" "You can see the list of your files by clicking on the \"My files\" link at " @@ -637,7 +879,7 @@ msgstr "" "Ensajatz de partir de la pagina. Lo telecargament serà anullat. Sètz segur-" "a ?" -#: themes/default/templates/partial/index.js.ep:13 +#: themes/default/templates/partial/index.js.ep:14 msgid "" "You have attempted to leave this page. The upload will be canceled. Are you " "sure?" @@ -652,22 +894,23 @@ msgstr "Sètz ben estat desconnectat." msgid "You must give email addresses." msgstr "Vos cal donar d’adreças." -#: themes/default/templates/index.html.ep:29 +#: themes/default/templates/index.html.ep:38 msgid "" -"Your browser has not enough entropy to generate a strong encryption key. " -"Please wait (it's better if you do things on your computer while waiting)." +"Your browser does not have enough entropy to generate a strong encryption " +"key. Please wait (it's better if you do things on your computer while " +"waiting)." msgstr "" "Vòstre navigador a pas pro d’entropia per generar una clau de chiframent " "fòrta. Mercés d’esperar (es de bon far de realizar de causas a l’ordenador " "d’aquel temps)." #. (format_bytes($json->{size}) -#: lib/Lufi/Controller/Files.pm:66 +#: lib/Lufi/Controller/Files.pm:95 msgid "Your file is too big: %1 (maximum size allowed: %2)" msgstr "" "Vòstre fichièr es tròp voluminós : %1 (la talha maximum autorizada es %2)" -#: lib/Lufi/Controller/Files.pm:304 +#: lib/Lufi/Controller/Files.pm:351 msgid "Your password is not valid. Please refresh the page to retry." msgstr "" "Lo senhal es pas valid. Mercés d’actualizar la pagina e ensajar tornamai." @@ -686,6 +929,10 @@ msgstr "entre %1 e %2, lo fichièr serà gardat per totjorn. ;" msgid "deadline: " msgstr "darrièr relambi per telecargar :" +#: themes/default/templates/partial/invitations.js.ep:5 +msgid "expires on XXX" +msgstr "expira lo XXX" + #. (format_bytes($keys[$i]) #: themes/default/templates/delays.html.ep:26 msgid "for %1 and more, the file will be kept %2 day(s)" @@ -700,6 +947,11 @@ msgstr "per %1 e mai, lo fichièr serà gardat per totjorn." msgid "no time limit" msgstr "Pas cap de relambi d’expiracion" -#: themes/default/templates/index.html.ep:96 +#: themes/default/templates/index.html.ep:124 msgid "or" msgstr "o" + +#. ($e->{name}, format_bytes($e->{size}) +#: themes/default/templates/invitations/notification_files_sent.mail.ep:12 +msgid "— %1 (%2), that will expire on %3" +msgstr "— %1 (%2), qu’expirarà lo %3" diff --git a/themes/default/lib/Lufi/I18N/pt.po b/themes/default/lib/Lufi/I18N/pt.po index a1c5c9b..291baa4 100644 --- a/themes/default/lib/Lufi/I18N/pt.po +++ b/themes/default/lib/Lufi/I18N/pt.po @@ -4,6 +4,7 @@ # Luc Didry , 2015. # Jéssica Da Cunha , 2017. # Luc Didry , 2018. #zanata +# Luc Didry , 2019. #zanata msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" @@ -11,20 +12,44 @@ msgstr "" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"PO-Revision-Date: 2018-10-28 01:57+0000\n" +"PO-Revision-Date: 2019-04-19 04:58+0000\n" "Language-Team: \n" "X-Generator: Zanata 4.6.2\n" -"Last-Translator: Copied by Zanata \n" +"Last-Translator: Luc Didry \n" "Plural-Forms: nplurals=2; plural=(n > 1);\n" "Language: pt\n" #. ($delay) #. (max_delay) -#: themes/default/templates/index.html.ep:47 themes/default/templates/index.html.ep:56 themes/default/templates/index.html.ep:57 +#: themes/default/templates/index.html.ep:56 themes/default/templates/index.html.ep:65 themes/default/templates/index.html.ep:66 msgid "%1 days" msgstr "%1 dias" -#: themes/default/templates/partial/index.js.ep:26 +#. (stash('ldap_user') +#: themes/default/templates/invitations/invite.mail.ep:2 +msgid "%1 invites you to send him/her files" +msgstr "" + +#. (stash('ldap_user') +#: themes/default/templates/invitations/invite.mail.ep:6 +msgid "%1 invites you to send him/her files through Lufi." +msgstr "" + +#. (stash('invitation') +#: themes/default/templates/invitations/notification_files_sent.mail.ep:4 +msgid "%1 sent you files" +msgstr "" + +#. (stash('invitation') +#: themes/default/templates/invitations/notification_files_sent.mail.ep:8 +msgid "%1 used your invitation to send you files:" +msgstr "" + +#: lib/Lufi/Controller/Invitation.pm:159 lib/Lufi/Controller/Invitation.pm:84 themes/default/templates/invitations/my_invitations.html.ep:51 themes/default/templates/invitations/my_invitations.html.ep:52 themes/default/templates/invitations/my_invitations.html.ep:53 themes/default/templates/invitations/notification_files_sent.mail.ep:12 +msgid "%A %d %B %Y at %T" +msgstr "" + +#: themes/default/templates/partial/index.js.ep:27 msgid "(max size: XXX)" msgstr "" @@ -32,7 +57,7 @@ msgstr "" msgid "1 year" msgstr "1 ano" -#: themes/default/templates/index.html.ep:4 themes/default/templates/index.html.ep:56 +#: themes/default/templates/index.html.ep:4 themes/default/templates/index.html.ep:65 msgid "24 hours" msgstr "24 horas" @@ -40,27 +65,15 @@ msgstr "24 horas" msgid ":" msgstr " :" -#: themes/default/templates/about.html.ep:16 -msgid "" -"A thank you with a photo of kitten on Diaspora* or " -"Mastodon " -"is cool too ;-)" -msgstr "" -"Um obrigado com a foto de um gatinho no Diaspora* ou " -"Mastodon é " -"também porreiro ;-)" - #: themes/default/templates/render.html.ep:42 msgid "Abort" msgstr "Interromper" -#: themes/default/templates/layouts/default.html.ep:48 themes/default/templates/layouts/default.html.ep:76 +#: themes/default/templates/layouts/default.html.ep:53 themes/default/templates/layouts/default.html.ep:86 msgid "About" msgstr "Sobre" -#: themes/default/templates/index.html.ep:88 +#: themes/default/templates/index.html.ep:107 msgid "Add a password to file(s)" msgstr "" @@ -70,7 +83,17 @@ msgid "" "prohibited." msgstr "" -#: themes/default/templates/about.html.ep:18 +#: themes/default/templates/partial/invitations.js.ep:3 +msgid "Are you sure you want to delete the selected invitations?" +msgstr "" + +#: themes/default/templates/partial/invitations.js.ep:4 +msgid "" +"Are you sure you want to resend the invitation mail for the selected " +"invitations?" +msgstr "" + +#: themes/default/templates/about.html.ep:17 msgid "" "As Lufi is a free software licensed under of the terms of the AGPLv3, you can " @@ -88,7 +111,7 @@ msgstr "" msgid "Asking for file part XX1 of %1" msgstr "Pedido de recuperação de um fragmento do ficheiro XX1 de %1" -#: themes/default/templates/about.html.ep:21 +#: themes/default/templates/about.html.ep:20 msgid "Back to homepage" msgstr "Voltar à página inicial" @@ -96,7 +119,7 @@ msgstr "Voltar à página inicial" msgid "Bad CSRF token!" msgstr "Símbolo errado CSRF !" -#: lib/Lufi/Controller/Auth.pm:22 lib/Lufi/Controller/Auth.pm:39 +#: lib/Lufi/Controller/Auth.pm:27 lib/Lufi/Controller/Auth.pm:49 msgid "Bad CSRF token." msgstr "" @@ -104,11 +127,15 @@ msgstr "" msgid "Click here to refresh the page and restart the download." msgstr "Clique aqui para atualizar a página e começar o download." -#: themes/default/templates/index.html.ep:98 +#: themes/default/templates/invitations/invite.mail.ep:8 +msgid "Click on the following URL to upload files on Lufi:" +msgstr "" + +#: themes/default/templates/index.html.ep:126 msgid "Click to open the file browser" msgstr "Clique para abrir o navegador de ficheiros" -#: themes/default/templates/delays.html.ep:38 +#: themes/default/templates/delays.html.ep:38 themes/default/templates/invitations/my_invitations.html.ep:80 msgid "Close" msgstr "Fechar" @@ -116,25 +143,29 @@ msgstr "Fechar" msgid "Comma-separated email addresses" msgstr "Os e-mails devem ser separados por vírgulas" -#: themes/default/templates/partial/index.js.ep:14 +#: themes/default/templates/index.html.ep:140 +msgid "Compressing zip file…" +msgstr "" + +#: themes/default/templates/partial/index.js.ep:15 msgid "Copy all links to clipboard" msgstr "Copiar todos os links para a área de transferência" -#: themes/default/templates/partial/index.js.ep:17 +#: themes/default/templates/partial/index.js.ep:18 msgid "Copy to clipboard" msgstr "Copiar para a área de transferência" -#: lib/Lufi/Controller/Files.pm:460 +#: lib/Lufi/Controller/Files.pm:507 msgid "Could not delete the file. You are not authenticated." msgstr "Impossível apagar o ficheiro. Não está conectado." -#: lib/Lufi/Controller/Files.pm:442 +#: lib/Lufi/Controller/Files.pm:489 msgid "Could not find the file. Are you sure of the URL and the token?" msgstr "" "Impossível encontrar o ficheiro.Tem a certeza que o URL e os símbolos estão " "corretos?" -#: lib/Lufi/Controller/Files.pm:353 +#: lib/Lufi/Controller/Files.pm:400 msgid "Could not find the file. Are you sure of the URL?" msgstr "" "Impossível encontar o ficheiro. Tem a certeza de que o URL está correto?" @@ -143,7 +174,19 @@ msgstr "" msgid "Counter" msgstr "Contador" -#: themes/default/templates/files.html.ep:29 themes/default/templates/index.html.ep:81 +#: themes/default/templates/index.html.ep:100 +msgid "Create a zip archive with the files before uploading?" +msgstr "" + +#: themes/default/templates/invitations/my_invitations.html.ep:26 +msgid "Created at" +msgstr "" + +#: themes/default/templates/invitations/my_invitations.html.ep:14 +msgid "Delete" +msgstr "" + +#: themes/default/templates/files.html.ep:29 themes/default/templates/index.html.ep:90 msgid "Delete at first download?" msgstr "Apagar após o primeiro download?" @@ -151,7 +194,7 @@ msgstr "Apagar após o primeiro download?" msgid "Delete selected files" msgstr "" -#: themes/default/templates/files.html.ep:32 themes/default/templates/partial/index.js.ep:18 +#: themes/default/templates/files.html.ep:32 themes/default/templates/partial/index.js.ep:19 msgid "Deletion link" msgstr "Eliminar o link" @@ -164,7 +207,7 @@ msgstr "" "sua expiração e que o descarregamento acaba após a sua expiração, o " "utilizador pode ainda assim recuperar o ficheiro." -#: themes/default/templates/partial/index.js.ep:20 themes/default/templates/render.html.ep:28 +#: themes/default/templates/partial/index.js.ep:21 themes/default/templates/render.html.ep:28 msgid "Download" msgstr "Download" @@ -172,7 +215,7 @@ msgstr "Download" msgid "Download aborted." msgstr "Download interrompido." -#: themes/default/templates/files.html.ep:27 themes/default/templates/partial/index.js.ep:19 +#: themes/default/templates/files.html.ep:27 themes/default/templates/partial/index.js.ep:20 msgid "Download link" msgstr "Link para o download" @@ -189,10 +232,14 @@ msgstr "" "enviados ao servidor. Vai receber dois links por ficheiro: um é de download " "e o outro para poder apagar o ficheiro quando quiser." -#: themes/default/templates/index.html.ep:94 +#: themes/default/templates/index.html.ep:122 msgid "Drop files here" msgstr "" +#: themes/default/templates/invitations/invite.html.ep:40 +msgid "Email address of your guest" +msgstr "" + #: themes/default/templates/mail.html.ep:39 msgid "Email body" msgstr "Conteúdo do e-mail" @@ -205,26 +252,30 @@ msgstr "Assunto do e-mail" msgid "Emails" msgstr "E-mails" -#: themes/default/templates/partial/index.js.ep:21 +#: themes/default/templates/partial/index.js.ep:22 msgid "Encrypting part XX1 of XX2" msgstr "Codificação do fragmento XX1 de XX2" -#: lib/Lufi/Controller/Files.pm:242 +#: lib/Lufi/Controller/Files.pm:289 msgid "Error: the file existed but was deleted." msgstr "Erro: o ficheiro existia mas foi apagado." -#: lib/Lufi/Controller/Files.pm:322 +#: lib/Lufi/Controller/Files.pm:369 msgid "Error: the file has not been sent entirely." msgstr "Erro: o ficheiro não foi enviado na totalidade." -#: lib/Lufi/Controller/Files.pm:332 +#: lib/Lufi/Controller/Files.pm:379 msgid "Error: unable to find the file. Are you sure of your URL?" msgstr "Erro: impossível encontrar o ficheiro. Tem a certeza do URL?" -#: themes/default/templates/partial/index.js.ep:22 +#: themes/default/templates/partial/index.js.ep:23 msgid "Expiration:" msgstr "Expiração:" +#: themes/default/templates/invitations/my_invitations.html.ep:27 +msgid "Expire at" +msgstr "" + #: themes/default/templates/files.html.ep:31 msgid "Expires at" msgstr "Expira no" @@ -233,7 +284,7 @@ msgstr "Expira no" msgid "Export localStorage data" msgstr "Exportar os dados localStorage" -#: lib/Lufi/Controller/Files.pm:424 +#: lib/Lufi/Controller/Files.pm:471 msgid "File deleted" msgstr "Ficheiro apagado" @@ -241,15 +292,27 @@ msgstr "Ficheiro apagado" msgid "File name" msgstr "Nome do ficheiro" -#: themes/default/templates/index.html.ep:71 +#: themes/default/templates/invitations/my_invitations.html.ep:61 +msgid "Files" +msgstr "" + +#: themes/default/templates/index.html.ep:80 msgid "Files deleted at first download" msgstr "" +#: themes/default/templates/invitations/my_invitations.html.ep:28 +msgid "Files sent at" +msgstr "" + +#: themes/default/templates/partial/invitations.js.ep:8 +msgid "Files sent in invitation XX1 by XX2" +msgstr "" + #: themes/default/templates/partial/render.js.ep:8 msgid "Get the file" msgstr "Recuperar o ficheiro" -#: themes/default/templates/about.html.ep:19 +#: themes/default/templates/about.html.ep:18 msgid "" "Get the source code on the official repository or on its o depósito oficial ou então réplicas Github." +#: themes/default/templates/invitations/my_invitations.html.ep:24 +msgid "Guest mail" +msgstr "" + +#. (ucfirst(stash('invitation') +#: themes/default/templates/invitations/notification_files_sent.mail.ep:6 +msgid "Hello %1," +msgstr "" + +#: themes/default/templates/invitations/invite.mail.ep:4 +msgid "Hello," +msgstr "" + #: themes/default/templates/partial/mail.js.ep:35 msgid "Hello,\\n\\nHere's some files I want to share with you:\\n" msgstr "" @@ -268,12 +344,16 @@ msgstr "" msgid "Here's some files" msgstr "Aqui estão alguns ficheiros" -#: themes/default/templates/partial/index.js.ep:24 +#: themes/default/templates/partial/invitations.js.ep:7 +msgid "Hide hidden invitations" +msgstr "" + +#: themes/default/templates/partial/index.js.ep:25 msgid "Hit Enter, then Ctrl+C to copy all the download links" msgstr "" "Clique no Enter e depois Ctrl+C para copiar todos os links de download" -#: themes/default/templates/partial/index.js.ep:23 +#: themes/default/templates/partial/index.js.ep:24 msgid "Hit Enter, then Ctrl+C to copy the download link" msgstr "Clique no Enter e depois Ctrl+C para copiar o link de download" @@ -281,7 +361,11 @@ msgstr "Clique no Enter e depois Ctrl+C para copiar o link de download" msgid "How does it work?" msgstr "Como funciona?" -#: themes/default/templates/about.html.ep:17 +#: themes/default/templates/invitations/invite.html.ep:46 +msgid "How many days would you like the invitation to be valid?" +msgstr "" + +#: themes/default/templates/about.html.ep:16 msgid "How to install the software on my server?" msgstr "" @@ -305,7 +389,7 @@ msgstr "" msgid "Import localStorage data" msgstr "Importar os dados localStorage" -#: themes/default/templates/index.html.ep:44 +#: themes/default/templates/index.html.ep:53 msgid "Important: more information on delays" msgstr "Importante: mais informações sobre os prazos" @@ -313,6 +397,24 @@ msgstr "Importante: mais informações sobre os prazos" msgid "Information about delays" msgstr "Informação sobre os prazos" +#: themes/default/templates/invitations/my_invitations.html.ep:12 +msgid "Invert selection" +msgstr "" + +#. ($i->guest_mail, $url) +#: lib/Lufi/Controller/Invitation.pm:171 +msgid "Invitation resent to %1.
URL: %2" +msgstr "" + +#. ($invitation->guest_mail, $url) +#: lib/Lufi/Controller/Invitation.pm:87 +msgid "Invitation sent to %1.
URL: %2" +msgstr "" + +#: themes/default/templates/invitations/invite.html.ep:27 themes/default/templates/layouts/default.html.ep:36 themes/default/templates/layouts/default.html.ep:69 +msgid "Invite a guest" +msgstr "" + #: themes/default/templates/partial/render.js.ep:6 msgid "" "It seems that the key in your URL is incorrect. Please, verify your URL." @@ -323,7 +425,7 @@ msgstr "" msgid "Javascript is disabled. You won't be able to use Lufi." msgstr "Javascript está desativado. Lufi não funcionará." -#: themes/default/templates/layouts/default.html.ep:40 themes/default/templates/layouts/default.html.ep:68 +#: themes/default/templates/layouts/default.html.ep:44 themes/default/templates/layouts/default.html.ep:46 themes/default/templates/layouts/default.html.ep:77 themes/default/templates/layouts/default.html.ep:79 msgid "Language" msgstr "" @@ -331,7 +433,7 @@ msgstr "" msgid "Login" msgstr "Utilizador" -#: themes/default/templates/layouts/default.html.ep:53 themes/default/templates/layouts/default.html.ep:78 +#: themes/default/templates/layouts/default.html.ep:58 themes/default/templates/layouts/default.html.ep:91 msgid "Logout" msgstr "Encerrar sessão" @@ -345,17 +447,30 @@ msgstr "" msgid "Mail" msgstr "" -#: themes/default/templates/files.html.ep:3 themes/default/templates/layouts/default.html.ep:34 themes/default/templates/layouts/default.html.ep:62 +#: themes/default/templates/files.html.ep:3 themes/default/templates/layouts/default.html.ep:34 themes/default/templates/layouts/default.html.ep:67 msgid "My files" msgstr "Meus ficheiros" +#: themes/default/templates/invitations/my_invitations.html.ep:5 themes/default/templates/layouts/default.html.ep:37 themes/default/templates/layouts/default.html.ep:70 +msgid "My invitations" +msgstr "" + +#: themes/default/templates/invitations/notification_files_sent.mail.ep:17 +msgid "" +"NB: this list includes the list of files that have already been sent to you." +msgstr "" + +#: themes/default/templates/index.html.ep:115 +msgid "Name of the zip file" +msgstr "" + #. (format_bytes($json->{size}) -#: lib/Lufi/Controller/Files.pm:79 +#: lib/Lufi/Controller/Files.pm:108 msgid "No enough space available on the server for this file (size: %1)." msgstr "" "O servidor não tem espaço suficiente para este ficheiro (tamanho: %1)." -#: themes/default/templates/partial/files.js.ep:10 themes/default/templates/partial/index.js.ep:27 +#: themes/default/templates/partial/files.js.ep:10 themes/default/templates/partial/index.js.ep:28 msgid "No expiration delay" msgstr "Não tem prazo de expiração" @@ -369,7 +484,7 @@ msgstr "" "informações são armazenadas no localStorage : se apagar os seus dados no " "LocalStorage, poedrá perder essa informação." -#: themes/default/templates/index.html.ep:87 themes/default/templates/login.html.ep:21 themes/default/templates/render.html.ep:26 +#: themes/default/templates/index.html.ep:106 themes/default/templates/login.html.ep:21 themes/default/templates/render.html.ep:26 msgid "Password" msgstr "Palavra-passe" @@ -387,7 +502,7 @@ msgstr "" "recuperar e descodificar todos os fragmentos e depois poderá descarregar o " "ficheiro." -#: lib/Lufi/Controller/Auth.pm:28 +#: lib/Lufi/Controller/Auth.pm:38 msgid "" "Please, check your credentials or your right to access this service: unable " "to authenticate." @@ -401,10 +516,26 @@ msgstr "Privacidade" msgid "Purge expired files from localStorage" msgstr "Apagar do localStorage os ficheiros expirados" -#: themes/default/templates/layouts/default.html.ep:31 themes/default/templates/layouts/default.html.ep:59 +#: themes/default/templates/invitations/notification_files_sent.mail.ep:20 +msgid "Regards," +msgstr "" + +#: themes/default/templates/invitations/invite.mail.ep:15 +msgid "Regards." +msgstr "" + +#: themes/default/templates/layouts/default.html.ep:31 themes/default/templates/layouts/default.html.ep:64 msgid "Report file" msgstr "" +#: themes/default/templates/invitations/my_invitations.html.ep:15 +msgid "Resend invitation mail" +msgstr "" + +#: themes/default/templates/invitations/my_invitations.html.ep:9 +msgid "Rows in purple mean that the invitations have expired." +msgstr "" + #: themes/default/templates/files.html.ep:9 msgid "" "Rows in red mean that the files have expired and are no longer available." @@ -412,10 +543,14 @@ msgstr "" "As linhas a vermelho indicam que o ficheiro expirou e já não está disponível." "" -#: themes/default/templates/partial/index.js.ep:25 +#: themes/default/templates/partial/index.js.ep:26 msgid "Send all links by email" msgstr "Enviar todos os links por e-mail" +#: themes/default/templates/invitations/invite.html.ep:50 +msgid "Send the invitation" +msgstr "" + #: themes/default/templates/mail.html.ep:46 msgid "Send with this server" msgstr "Enviar com este servidor" @@ -424,7 +559,7 @@ msgstr "Enviar com este servidor" msgid "Send with your own mail software" msgstr "Enviar com o seu e-mail pessoal" -#: themes/default/templates/partial/index.js.ep:28 +#: themes/default/templates/partial/index.js.ep:29 msgid "" "Sending part XX1 of XX2. Please, be patient, the progress bar can take a " "while to move." @@ -437,19 +572,48 @@ msgstr "" msgid "Share your files in total privacy on %1" msgstr "Partilhe os seus ficheiros com toda a privacidade em %1" -#: themes/default/templates/layouts/default.html.ep:36 themes/default/templates/layouts/default.html.ep:64 themes/default/templates/login.html.ep:27 themes/default/templates/logout.html.ep:17 +#: themes/default/templates/invitations/my_invitations.html.ep:13 themes/default/templates/partial/invitations.js.ep:9 +msgid "Show hidden invitations" +msgstr "" + +#: themes/default/templates/partial/render.js.ep:11 +msgid "Show zip content" +msgstr "" + +#: themes/default/templates/layouts/default.html.ep:40 themes/default/templates/layouts/default.html.ep:73 themes/default/templates/login.html.ep:28 themes/default/templates/logout.html.ep:17 msgid "Signin" msgstr "Conexão" -#: themes/default/templates/index.html.ep:37 +#: lib/Lufi/Controller/Invitation.pm:283 themes/default/templates/invitations/exception.html.ep:16 +msgid "" +"Sorry, the invitation doesn’t exist. Are you sure you are on the right URL?" +msgstr "" + +#: themes/default/templates/index.html.ep:46 msgid "Sorry, the uploading is currently disabled. Please try again later." msgstr "" "Desculpe, o envio do ficheiro está atualmente desativado. Tente mais tarde." -#: lib/Lufi/Controller/Files.pm:53 +#: lib/Lufi/Controller/Files.pm:82 msgid "Sorry, uploading is disabled." msgstr "Desculpe, o envio do ficheiro está desativado." +#: themes/default/templates/invitations/exception.html.ep:7 +msgid "Sorry, your invitation has expired or has been deleted." +msgstr "" + +#. ($invit->ldap_user_mail) +#: lib/Lufi/Controller/Files.pm:122 +msgid "" +"Sorry, your invitation has expired or has been deleted. Please contact %1 to " +"have another invitation." +msgstr "" + +#. ($invitation->ldap_user_mail) +#: lib/Lufi/Controller/Invitation.pm:276 +msgid "The URLs of your files have been sent by email to %1." +msgstr "" + #: themes/default/templates/about.html.ep:7 msgid "" "The administrator can only see the file's name, its size and its mimetype " @@ -476,7 +640,12 @@ msgstr "A mensagem do e-mail não pode estar vazia." msgid "The email subject can't be empty." msgstr "O assunto do e-mail não pode estar vazio." -#: lib/Lufi/Controller/Files.pm:421 +#. ($expire_at, $max_expire_at) +#: lib/Lufi/Controller/Invitation.pm:51 +msgid "The expiration delay (%1) is not between 1 and %2 days." +msgstr "" + +#: lib/Lufi/Controller/Files.pm:468 msgid "The file has already been deleted" msgstr "O ficheiro já foi apagado" @@ -493,10 +662,43 @@ msgstr "" msgid "The following email addresses are not valid: %1" msgstr "Os e-mails seguintes não são válidos: %1" -#: themes/default/templates/partial/index.js.ep:15 +#. ($guest_mail) +#: lib/Lufi/Controller/Invitation.pm:48 +msgid "The guest email address (%1) is unvalid." +msgstr "" + +#. ($i->token, $i->guest_mail) +#: lib/Lufi/Controller/Invitation.pm:150 +msgid "" +"The invitation %1 can’t be resent: %2 has already sent files.
Please " +"create a new invitation." +msgstr "" + +#. ($i->token) +#: lib/Lufi/Controller/Invitation.pm:130 +msgid "The invitation %1 has been deleted." +msgstr "" + +#. (stash('user_mail') +#: themes/default/templates/invitations/invite.html.ep:34 +msgid "The invitation mail will be send from your email address (%1)." +msgstr "" + +#: themes/default/templates/partial/index.js.ep:16 msgid "The link(s) has been copied to your clipboard" msgstr "O(s) link(s) foi/foram copiados para a área de transferência" +#. (stash('invitation') +#: themes/default/templates/index.html.ep:30 +msgid "" +"The link(s) of your file(s) will automatically be sent by mail to %1 (%2)" +msgstr "" + +#. (stash('ldap_user') +#: themes/default/templates/invitations/invite.mail.ep:11 +msgid "The links of your file(s) will automatically be sent by mail to %1." +msgstr "" + #: lib/Lufi/Controller/Mail.pm:97 msgid "The mail has been sent." msgstr "O e-mail foi enviado." @@ -504,17 +706,12 @@ msgstr "O e-mail foi enviado." #: themes/default/templates/about.html.ep:15 msgid "" "The original (and only for now) author is Luc Didry. If you want to support him, you can do it " -"via Tipeee " -"or via Liberapay." +"class=\"classic\">Luc Didry." msgstr "" "O autor original (e por agora, o único) é Luc Didry. Se o desejar apoiar pode fazer-lo via Tipeee ou via " -"Liberapay." +"class=\"classic\">Luc Didry." -#: lib/Lufi/Controller/Files.pm:189 +#: lib/Lufi/Controller/Files.pm:236 msgid "" "The server was unable to find the file record to add your file part to. " "Please, contact the administrator." @@ -522,11 +719,20 @@ msgstr "" "O servidor foi incapaz de encontrar o registo do ficheiro no qual devia-se " "juntar o fragmento do seu ficheiro. Contacte o administrador." -#: lib/Lufi/Controller/Files.pm:248 +#: lib/Lufi/Controller/Files.pm:295 msgid "" "This file has been deactivated by the admins. Contact them to know why." msgstr "" +#: themes/default/templates/invitations/my_invitations.html.ep:46 themes/default/templates/partial/invitations.js.ep:6 +msgid "This invitation is normally hidden" +msgstr "" + +#. (stash('expires') +#: themes/default/templates/invitations/invite.mail.ep:13 +msgid "This invitation is valid until %1." +msgstr "" + #: themes/default/templates/delays.html.ep:10 msgid "" "This server sets limitations according to the file size. The expiration " @@ -537,12 +743,20 @@ msgstr "" "expiração dos seu ficheiro sera o minimo entre o que você escolheu e os " "limites seguintes:" -#: themes/default/templates/partial/index.js.ep:16 +#: themes/default/templates/invitations/my_invitations.html.ep:16 +msgid "Toggle visibility" +msgstr "" + +#: themes/default/templates/invitations/my_invitations.html.ep:25 +msgid "URL" +msgstr "" + +#: themes/default/templates/partial/index.js.ep:17 msgid "Unable to copy the link(s) to your clipboard" msgstr "Impossível copiar o(s) link(s) na sua área de transferência" #. ($short) -#: lib/Lufi/Controller/Files.pm:392 +#: lib/Lufi/Controller/Files.pm:439 msgid "" "Unable to get counter for %1. The file does not exists. It will be removed " "from your localStorage." @@ -551,28 +765,32 @@ msgstr "" "apagar a sua localStorage." #. ($short) -#: lib/Lufi/Controller/Files.pm:382 +#: lib/Lufi/Controller/Files.pm:429 msgid "Unable to get counter for %1. The token is invalid." msgstr "Impossível recuperar o contador para %1. O símbolo é inválido." #. ($short) -#: lib/Lufi/Controller/Files.pm:402 +#: lib/Lufi/Controller/Files.pm:449 msgid "Unable to get counter for %1. You are not authenticated." msgstr "Impossível recuperar o contador para %1. Não está conectado." -#: themes/default/templates/layouts/default.html.ep:33 themes/default/templates/layouts/default.html.ep:61 +#: themes/default/templates/layouts/default.html.ep:33 themes/default/templates/layouts/default.html.ep:66 msgid "Upload files" msgstr "Enviar os ficheiros" +#: themes/default/templates/index.html.ep:145 +msgid "Upload generated zip file" +msgstr "" + #: themes/default/templates/files.html.ep:30 msgid "Uploaded at" msgstr "Enviar a" -#: themes/default/templates/index.html.ep:103 +#: themes/default/templates/index.html.ep:153 msgid "Uploaded files" msgstr "Ficheiros enviados" -#: themes/default/templates/partial/index.js.ep:29 +#: themes/default/templates/partial/index.js.ep:30 msgid "Websocket communication error" msgstr "Erro de comunicação com WebSocket" @@ -584,6 +802,16 @@ msgstr "O que é o Lufi?" msgid "Who wrote this software?" msgstr "" +#: themes/default/templates/partial/index.js.ep:13 +msgid "XXX file has been added to upload queue." +msgstr "" + +#: themes/default/templates/invitations/invite.html.ep:30 +msgid "" +"You can invite someone to send you files through this Lufi instance even if " +"they don’t have an account on it." +msgstr "" + #: themes/default/templates/about.html.ep:11 msgid "" "You can see the list of your files by clicking on the \"My files\" link at " @@ -620,7 +848,7 @@ msgid "" "you sure?" msgstr "Está a tentar sair da página. O download será anulado. Tem a certeza?" -#: themes/default/templates/partial/index.js.ep:13 +#: themes/default/templates/partial/index.js.ep:14 msgid "" "You have attempted to leave this page. The upload will be canceled. Are you " "sure?" @@ -634,19 +862,20 @@ msgstr "Foi desconectado com sucesso." msgid "You must give email addresses." msgstr "Deve escrever os e-mails." -#: themes/default/templates/index.html.ep:29 +#: themes/default/templates/index.html.ep:38 msgid "" -"Your browser has not enough entropy to generate a strong encryption key. " -"Please wait (it's better if you do things on your computer while waiting)." +"Your browser does not have enough entropy to generate a strong encryption " +"key. Please wait (it's better if you do things on your computer while " +"waiting)." msgstr "" #. (format_bytes($json->{size}) -#: lib/Lufi/Controller/Files.pm:66 +#: lib/Lufi/Controller/Files.pm:95 msgid "Your file is too big: %1 (maximum size allowed: %2)" msgstr "" "O seu ficheiro é grande de mais: %1 (o tamanho máximo autorizado é de %2)" -#: lib/Lufi/Controller/Files.pm:304 +#: lib/Lufi/Controller/Files.pm:351 msgid "Your password is not valid. Please refresh the page to retry." msgstr "" @@ -664,6 +893,10 @@ msgstr "entre %1 e %2, o ficheiro será conservado por um tempo indeterminado." msgid "deadline: " msgstr "Data-limite: " +#: themes/default/templates/partial/invitations.js.ep:5 +msgid "expires on XXX" +msgstr "" + #. (format_bytes($keys[$i]) #: themes/default/templates/delays.html.ep:26 msgid "for %1 and more, the file will be kept %2 day(s)" @@ -679,6 +912,11 @@ msgstr "" msgid "no time limit" msgstr "Não tem limite de expiração" -#: themes/default/templates/index.html.ep:96 +#: themes/default/templates/index.html.ep:124 msgid "or" msgstr "ou" + +#. ($e->{name}, format_bytes($e->{size}) +#: themes/default/templates/invitations/notification_files_sent.mail.ep:12 +msgid "— %1 (%2), that will expire on %3" +msgstr "" diff --git a/themes/default/public/css/lufi.css b/themes/default/public/css/lufi.css index 7b96ea9..ea2433f 100644 --- a/themes/default/public/css/lufi.css +++ b/themes/default/public/css/lufi.css @@ -140,6 +140,89 @@ nav .btn-flat { nav .btn-flat:focus { background-color: rgba(0,0,0,0.1) !important; } + +/* pulse animation from Materialize CSS 1.0.0 */ +button.pulse { + margin-left: 1em; +} +.pulse { + overflow: initial; + position: relative; +} +.pulse::before { + content: ''; + display: block; + position: absolute; + width: 100%; + height: 100%; + top: 0; + left: 0; + background-color: inherit; + border-radius: inherit; + -webkit-transition: opacity .3s, -webkit-transform .3s; + transition: opacity .3s, -webkit-transform .3s; + transition: opacity .3s, transform .3s; + transition: opacity .3s, transform .3s, -webkit-transform .3s; + -webkit-animation: pulse-animation 1s cubic-bezier(0.24, 0, 0.38, 1) infinite; + animation: pulse-animation 1s cubic-bezier(0.24, 0, 0.38, 1) infinite; + z-index: -1; +} + +@-webkit-keyframes pulse-animation { + 0% { + opacity: 1; + -webkit-transform: scale(1); + transform: scale(1); + } + 50% { + opacity: 0; + -webkit-transform: scale(1.5); + transform: scale(1.5); + } + 100% { + opacity: 0; + -webkit-transform: scale(1.5); + transform: scale(1.5); + } +} + +@keyframes pulse-animation { + 0% { + opacity: 1; + -webkit-transform: scale(1); + transform: scale(1); + } + 50% { + opacity: 0; + -webkit-transform: scale(1.5); + transform: scale(1.5); + } + 100% { + opacity: 0; + -webkit-transform: scale(1.5); + transform: scale(1.5); + } +} +.margin-bottom-35 { + margin-bottom: 35px; +} +.toast.teal.accent-3, +.toast.red.accent-2 { + color: black; +} +.offscreen { + clip-path: inset(100%); + clip: rect(1px 1px 1px 1px); /* IE 6/7 */ + clip: rect(1px, 1px, 1px, 1px); + height: 1px; + overflow: hidden; + position: absolute; + white-space: nowrap; /* added line */ + width: 1px; +} +.small-h1 { + font-size: 2.2rem; +} .white-background { background-color: #FFF; } diff --git a/themes/default/public/js/jszip.js b/themes/default/public/js/jszip.js new file mode 100644 index 0000000..e53c056 --- /dev/null +++ b/themes/default/public/js/jszip.js @@ -0,0 +1,11367 @@ +/*! + +JSZip v3.2.1 - A JavaScript class for generating and reading zip files + + +(c) 2009-2016 Stuart Knightley +Dual licenced under the MIT license or GPLv3. See https://raw.github.com/Stuk/jszip/master/LICENSE.markdown. + +JSZip uses the library pako released under the MIT license : +https://github.com/nodeca/pako/blob/master/LICENSE +*/ + +(function(f){if(typeof exports==="object"&&typeof module!=="undefined"){module.exports=f()}else if(typeof define==="function"&&define.amd){define([],f)}else{var g;if(typeof window!=="undefined"){g=window}else if(typeof global!=="undefined"){g=global}else if(typeof self!=="undefined"){g=self}else{g=this}g.JSZip = f()}})(function(){var define,module,exports;return (function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o> 2; + enc2 = ((chr1 & 3) << 4) | (chr2 >> 4); + enc3 = remainingBytes > 1 ? (((chr2 & 15) << 2) | (chr3 >> 6)) : 64; + enc4 = remainingBytes > 2 ? (chr3 & 63) : 64; + + output.push(_keyStr.charAt(enc1) + _keyStr.charAt(enc2) + _keyStr.charAt(enc3) + _keyStr.charAt(enc4)); + + } + + return output.join(""); +}; + +// public method for decoding +exports.decode = function(input) { + var chr1, chr2, chr3; + var enc1, enc2, enc3, enc4; + var i = 0, resultIndex = 0; + + var dataUrlPrefix = "data:"; + + if (input.substr(0, dataUrlPrefix.length) === dataUrlPrefix) { + // This is a common error: people give a data url + // (data:image/png;base64,iVBOR...) with a {base64: true} and + // wonders why things don't work. + // We can detect that the string input looks like a data url but we + // *can't* be sure it is one: removing everything up to the comma would + // be too dangerous. + throw new Error("Invalid base64 input, it looks like a data url."); + } + + input = input.replace(/[^A-Za-z0-9\+\/\=]/g, ""); + + var totalLength = input.length * 3 / 4; + if(input.charAt(input.length - 1) === _keyStr.charAt(64)) { + totalLength--; + } + if(input.charAt(input.length - 2) === _keyStr.charAt(64)) { + totalLength--; + } + if (totalLength % 1 !== 0) { + // totalLength is not an integer, the length does not match a valid + // base64 content. That can happen if: + // - the input is not a base64 content + // - the input is *almost* a base64 content, with a extra chars at the + // beginning or at the end + // - the input uses a base64 variant (base64url for example) + throw new Error("Invalid base64 input, bad content length."); + } + var output; + if (support.uint8array) { + output = new Uint8Array(totalLength|0); + } else { + output = new Array(totalLength|0); + } + + while (i < input.length) { + + enc1 = _keyStr.indexOf(input.charAt(i++)); + enc2 = _keyStr.indexOf(input.charAt(i++)); + enc3 = _keyStr.indexOf(input.charAt(i++)); + enc4 = _keyStr.indexOf(input.charAt(i++)); + + chr1 = (enc1 << 2) | (enc2 >> 4); + chr2 = ((enc2 & 15) << 4) | (enc3 >> 2); + chr3 = ((enc3 & 3) << 6) | enc4; + + output[resultIndex++] = chr1; + + if (enc3 !== 64) { + output[resultIndex++] = chr2; + } + if (enc4 !== 64) { + output[resultIndex++] = chr3; + } + + } + + return output; +}; + +},{"./support":30,"./utils":32}],2:[function(require,module,exports){ +'use strict'; + +var external = require("./external"); +var DataWorker = require('./stream/DataWorker'); +var DataLengthProbe = require('./stream/DataLengthProbe'); +var Crc32Probe = require('./stream/Crc32Probe'); +var DataLengthProbe = require('./stream/DataLengthProbe'); + +/** + * Represent a compressed object, with everything needed to decompress it. + * @constructor + * @param {number} compressedSize the size of the data compressed. + * @param {number} uncompressedSize the size of the data after decompression. + * @param {number} crc32 the crc32 of the decompressed file. + * @param {object} compression the type of compression, see lib/compressions.js. + * @param {String|ArrayBuffer|Uint8Array|Buffer} data the compressed data. + */ +function CompressedObject(compressedSize, uncompressedSize, crc32, compression, data) { + this.compressedSize = compressedSize; + this.uncompressedSize = uncompressedSize; + this.crc32 = crc32; + this.compression = compression; + this.compressedContent = data; +} + +CompressedObject.prototype = { + /** + * Create a worker to get the uncompressed content. + * @return {GenericWorker} the worker. + */ + getContentWorker : function () { + var worker = new DataWorker(external.Promise.resolve(this.compressedContent)) + .pipe(this.compression.uncompressWorker()) + .pipe(new DataLengthProbe("data_length")); + + var that = this; + worker.on("end", function () { + if(this.streamInfo['data_length'] !== that.uncompressedSize) { + throw new Error("Bug : uncompressed data size mismatch"); + } + }); + return worker; + }, + /** + * Create a worker to get the compressed content. + * @return {GenericWorker} the worker. + */ + getCompressedWorker : function () { + return new DataWorker(external.Promise.resolve(this.compressedContent)) + .withStreamInfo("compressedSize", this.compressedSize) + .withStreamInfo("uncompressedSize", this.uncompressedSize) + .withStreamInfo("crc32", this.crc32) + .withStreamInfo("compression", this.compression) + ; + } +}; + +/** + * Chain the given worker with other workers to compress the content with the + * given compresion. + * @param {GenericWorker} uncompressedWorker the worker to pipe. + * @param {Object} compression the compression object. + * @param {Object} compressionOptions the options to use when compressing. + * @return {GenericWorker} the new worker compressing the content. + */ +CompressedObject.createWorkerFrom = function (uncompressedWorker, compression, compressionOptions) { + return uncompressedWorker + .pipe(new Crc32Probe()) + .pipe(new DataLengthProbe("uncompressedSize")) + .pipe(compression.compressWorker(compressionOptions)) + .pipe(new DataLengthProbe("compressedSize")) + .withStreamInfo("compression", compression); +}; + +module.exports = CompressedObject; + +},{"./external":6,"./stream/Crc32Probe":25,"./stream/DataLengthProbe":26,"./stream/DataWorker":27}],3:[function(require,module,exports){ +'use strict'; + +var GenericWorker = require("./stream/GenericWorker"); + +exports.STORE = { + magic: "\x00\x00", + compressWorker : function (compressionOptions) { + return new GenericWorker("STORE compression"); + }, + uncompressWorker : function () { + return new GenericWorker("STORE decompression"); + } +}; +exports.DEFLATE = require('./flate'); + +},{"./flate":7,"./stream/GenericWorker":28}],4:[function(require,module,exports){ +'use strict'; + +var utils = require('./utils'); + +/** + * The following functions come from pako, from pako/lib/zlib/crc32.js + * released under the MIT license, see pako https://github.com/nodeca/pako/ + */ + +// Use ordinary array, since untyped makes no boost here +function makeTable() { + var c, table = []; + + for(var n =0; n < 256; n++){ + c = n; + for(var k =0; k < 8; k++){ + c = ((c&1) ? (0xEDB88320 ^ (c >>> 1)) : (c >>> 1)); + } + table[n] = c; + } + + return table; +} + +// Create table on load. Just 255 signed longs. Not a problem. +var crcTable = makeTable(); + + +function crc32(crc, buf, len, pos) { + var t = crcTable, end = pos + len; + + crc = crc ^ (-1); + + for (var i = pos; i < end; i++ ) { + crc = (crc >>> 8) ^ t[(crc ^ buf[i]) & 0xFF]; + } + + return (crc ^ (-1)); // >>> 0; +} + +// That's all for the pako functions. + +/** + * Compute the crc32 of a string. + * This is almost the same as the function crc32, but for strings. Using the + * same function for the two use cases leads to horrible performances. + * @param {Number} crc the starting value of the crc. + * @param {String} str the string to use. + * @param {Number} len the length of the string. + * @param {Number} pos the starting position for the crc32 computation. + * @return {Number} the computed crc32. + */ +function crc32str(crc, str, len, pos) { + var t = crcTable, end = pos + len; + + crc = crc ^ (-1); + + for (var i = pos; i < end; i++ ) { + crc = (crc >>> 8) ^ t[(crc ^ str.charCodeAt(i)) & 0xFF]; + } + + return (crc ^ (-1)); // >>> 0; +} + +module.exports = function crc32wrapper(input, crc) { + if (typeof input === "undefined" || !input.length) { + return 0; + } + + var isArray = utils.getTypeOf(input) !== "string"; + + if(isArray) { + return crc32(crc|0, input, input.length, 0); + } else { + return crc32str(crc|0, input, input.length, 0); + } +}; + +},{"./utils":32}],5:[function(require,module,exports){ +'use strict'; +exports.base64 = false; +exports.binary = false; +exports.dir = false; +exports.createFolders = true; +exports.date = null; +exports.compression = null; +exports.compressionOptions = null; +exports.comment = null; +exports.unixPermissions = null; +exports.dosPermissions = null; + +},{}],6:[function(require,module,exports){ +/* global Promise */ +'use strict'; + +// load the global object first: +// - it should be better integrated in the system (unhandledRejection in node) +// - the environment may have a custom Promise implementation (see zone.js) +var ES6Promise = null; +if (typeof Promise !== "undefined") { + ES6Promise = Promise; +} else { + ES6Promise = require("lie"); +} + +/** + * Let the user use/change some implementations. + */ +module.exports = { + Promise: ES6Promise +}; + +},{"lie":37}],7:[function(require,module,exports){ +'use strict'; +var USE_TYPEDARRAY = (typeof Uint8Array !== 'undefined') && (typeof Uint16Array !== 'undefined') && (typeof Uint32Array !== 'undefined'); + +var pako = require("pako"); +var utils = require("./utils"); +var GenericWorker = require("./stream/GenericWorker"); + +var ARRAY_TYPE = USE_TYPEDARRAY ? "uint8array" : "array"; + +exports.magic = "\x08\x00"; + +/** + * Create a worker that uses pako to inflate/deflate. + * @constructor + * @param {String} action the name of the pako function to call : either "Deflate" or "Inflate". + * @param {Object} options the options to use when (de)compressing. + */ +function FlateWorker(action, options) { + GenericWorker.call(this, "FlateWorker/" + action); + + this._pako = null; + this._pakoAction = action; + this._pakoOptions = options; + // the `meta` object from the last chunk received + // this allow this worker to pass around metadata + this.meta = {}; +} + +utils.inherits(FlateWorker, GenericWorker); + +/** + * @see GenericWorker.processChunk + */ +FlateWorker.prototype.processChunk = function (chunk) { + this.meta = chunk.meta; + if (this._pako === null) { + this._createPako(); + } + this._pako.push(utils.transformTo(ARRAY_TYPE, chunk.data), false); +}; + +/** + * @see GenericWorker.flush + */ +FlateWorker.prototype.flush = function () { + GenericWorker.prototype.flush.call(this); + if (this._pako === null) { + this._createPako(); + } + this._pako.push([], true); +}; +/** + * @see GenericWorker.cleanUp + */ +FlateWorker.prototype.cleanUp = function () { + GenericWorker.prototype.cleanUp.call(this); + this._pako = null; +}; + +/** + * Create the _pako object. + * TODO: lazy-loading this object isn't the best solution but it's the + * quickest. The best solution is to lazy-load the worker list. See also the + * issue #446. + */ +FlateWorker.prototype._createPako = function () { + this._pako = new pako[this._pakoAction]({ + raw: true, + level: this._pakoOptions.level || -1 // default compression + }); + var self = this; + this._pako.onData = function(data) { + self.push({ + data : data, + meta : self.meta + }); + }; +}; + +exports.compressWorker = function (compressionOptions) { + return new FlateWorker("Deflate", compressionOptions); +}; +exports.uncompressWorker = function () { + return new FlateWorker("Inflate", {}); +}; + +},{"./stream/GenericWorker":28,"./utils":32,"pako":38}],8:[function(require,module,exports){ +'use strict'; + +var utils = require('../utils'); +var GenericWorker = require('../stream/GenericWorker'); +var utf8 = require('../utf8'); +var crc32 = require('../crc32'); +var signature = require('../signature'); + +/** + * Transform an integer into a string in hexadecimal. + * @private + * @param {number} dec the number to convert. + * @param {number} bytes the number of bytes to generate. + * @returns {string} the result. + */ +var decToHex = function(dec, bytes) { + var hex = "", i; + for (i = 0; i < bytes; i++) { + hex += String.fromCharCode(dec & 0xff); + dec = dec >>> 8; + } + return hex; +}; + +/** + * Generate the UNIX part of the external file attributes. + * @param {Object} unixPermissions the unix permissions or null. + * @param {Boolean} isDir true if the entry is a directory, false otherwise. + * @return {Number} a 32 bit integer. + * + * adapted from http://unix.stackexchange.com/questions/14705/the-zip-formats-external-file-attribute : + * + * TTTTsstrwxrwxrwx0000000000ADVSHR + * ^^^^____________________________ file type, see zipinfo.c (UNX_*) + * ^^^_________________________ setuid, setgid, sticky + * ^^^^^^^^^________________ permissions + * ^^^^^^^^^^______ not used ? + * ^^^^^^ DOS attribute bits : Archive, Directory, Volume label, System file, Hidden, Read only + */ +var generateUnixExternalFileAttr = function (unixPermissions, isDir) { + + var result = unixPermissions; + if (!unixPermissions) { + // I can't use octal values in strict mode, hence the hexa. + // 040775 => 0x41fd + // 0100664 => 0x81b4 + result = isDir ? 0x41fd : 0x81b4; + } + return (result & 0xFFFF) << 16; +}; + +/** + * Generate the DOS part of the external file attributes. + * @param {Object} dosPermissions the dos permissions or null. + * @param {Boolean} isDir true if the entry is a directory, false otherwise. + * @return {Number} a 32 bit integer. + * + * Bit 0 Read-Only + * Bit 1 Hidden + * Bit 2 System + * Bit 3 Volume Label + * Bit 4 Directory + * Bit 5 Archive + */ +var generateDosExternalFileAttr = function (dosPermissions, isDir) { + + // the dir flag is already set for compatibility + return (dosPermissions || 0) & 0x3F; +}; + +/** + * Generate the various parts used in the construction of the final zip file. + * @param {Object} streamInfo the hash with informations about the compressed file. + * @param {Boolean} streamedContent is the content streamed ? + * @param {Boolean} streamingEnded is the stream finished ? + * @param {number} offset the current offset from the start of the zip file. + * @param {String} platform let's pretend we are this platform (change platform dependents fields) + * @param {Function} encodeFileName the function to encode the file name / comment. + * @return {Object} the zip parts. + */ +var generateZipParts = function(streamInfo, streamedContent, streamingEnded, offset, platform, encodeFileName) { + var file = streamInfo['file'], + compression = streamInfo['compression'], + useCustomEncoding = encodeFileName !== utf8.utf8encode, + encodedFileName = utils.transformTo("string", encodeFileName(file.name)), + utfEncodedFileName = utils.transformTo("string", utf8.utf8encode(file.name)), + comment = file.comment, + encodedComment = utils.transformTo("string", encodeFileName(comment)), + utfEncodedComment = utils.transformTo("string", utf8.utf8encode(comment)), + useUTF8ForFileName = utfEncodedFileName.length !== file.name.length, + useUTF8ForComment = utfEncodedComment.length !== comment.length, + dosTime, + dosDate, + extraFields = "", + unicodePathExtraField = "", + unicodeCommentExtraField = "", + dir = file.dir, + date = file.date; + + + var dataInfo = { + crc32 : 0, + compressedSize : 0, + uncompressedSize : 0 + }; + + // if the content is streamed, the sizes/crc32 are only available AFTER + // the end of the stream. + if (!streamedContent || streamingEnded) { + dataInfo.crc32 = streamInfo['crc32']; + dataInfo.compressedSize = streamInfo['compressedSize']; + dataInfo.uncompressedSize = streamInfo['uncompressedSize']; + } + + var bitflag = 0; + if (streamedContent) { + // Bit 3: the sizes/crc32 are set to zero in the local header. + // The correct values are put in the data descriptor immediately + // following the compressed data. + bitflag |= 0x0008; + } + if (!useCustomEncoding && (useUTF8ForFileName || useUTF8ForComment)) { + // Bit 11: Language encoding flag (EFS). + bitflag |= 0x0800; + } + + + var extFileAttr = 0; + var versionMadeBy = 0; + if (dir) { + // dos or unix, we set the dos dir flag + extFileAttr |= 0x00010; + } + if(platform === "UNIX") { + versionMadeBy = 0x031E; // UNIX, version 3.0 + extFileAttr |= generateUnixExternalFileAttr(file.unixPermissions, dir); + } else { // DOS or other, fallback to DOS + versionMadeBy = 0x0014; // DOS, version 2.0 + extFileAttr |= generateDosExternalFileAttr(file.dosPermissions, dir); + } + + // date + // @see http://www.delorie.com/djgpp/doc/rbinter/it/52/13.html + // @see http://www.delorie.com/djgpp/doc/rbinter/it/65/16.html + // @see http://www.delorie.com/djgpp/doc/rbinter/it/66/16.html + + dosTime = date.getUTCHours(); + dosTime = dosTime << 6; + dosTime = dosTime | date.getUTCMinutes(); + dosTime = dosTime << 5; + dosTime = dosTime | date.getUTCSeconds() / 2; + + dosDate = date.getUTCFullYear() - 1980; + dosDate = dosDate << 4; + dosDate = dosDate | (date.getUTCMonth() + 1); + dosDate = dosDate << 5; + dosDate = dosDate | date.getUTCDate(); + + if (useUTF8ForFileName) { + // set the unicode path extra field. unzip needs at least one extra + // field to correctly handle unicode path, so using the path is as good + // as any other information. This could improve the situation with + // other archive managers too. + // This field is usually used without the utf8 flag, with a non + // unicode path in the header (winrar, winzip). This helps (a bit) + // with the messy Windows' default compressed folders feature but + // breaks on p7zip which doesn't seek the unicode path extra field. + // So for now, UTF-8 everywhere ! + unicodePathExtraField = + // Version + decToHex(1, 1) + + // NameCRC32 + decToHex(crc32(encodedFileName), 4) + + // UnicodeName + utfEncodedFileName; + + extraFields += + // Info-ZIP Unicode Path Extra Field + "\x75\x70" + + // size + decToHex(unicodePathExtraField.length, 2) + + // content + unicodePathExtraField; + } + + if(useUTF8ForComment) { + + unicodeCommentExtraField = + // Version + decToHex(1, 1) + + // CommentCRC32 + decToHex(crc32(encodedComment), 4) + + // UnicodeName + utfEncodedComment; + + extraFields += + // Info-ZIP Unicode Path Extra Field + "\x75\x63" + + // size + decToHex(unicodeCommentExtraField.length, 2) + + // content + unicodeCommentExtraField; + } + + var header = ""; + + // version needed to extract + header += "\x0A\x00"; + // general purpose bit flag + header += decToHex(bitflag, 2); + // compression method + header += compression.magic; + // last mod file time + header += decToHex(dosTime, 2); + // last mod file date + header += decToHex(dosDate, 2); + // crc-32 + header += decToHex(dataInfo.crc32, 4); + // compressed size + header += decToHex(dataInfo.compressedSize, 4); + // uncompressed size + header += decToHex(dataInfo.uncompressedSize, 4); + // file name length + header += decToHex(encodedFileName.length, 2); + // extra field length + header += decToHex(extraFields.length, 2); + + + var fileRecord = signature.LOCAL_FILE_HEADER + header + encodedFileName + extraFields; + + var dirRecord = signature.CENTRAL_FILE_HEADER + + // version made by (00: DOS) + decToHex(versionMadeBy, 2) + + // file header (common to file and central directory) + header + + // file comment length + decToHex(encodedComment.length, 2) + + // disk number start + "\x00\x00" + + // internal file attributes TODO + "\x00\x00" + + // external file attributes + decToHex(extFileAttr, 4) + + // relative offset of local header + decToHex(offset, 4) + + // file name + encodedFileName + + // extra field + extraFields + + // file comment + encodedComment; + + return { + fileRecord: fileRecord, + dirRecord: dirRecord + }; +}; + +/** + * Generate the EOCD record. + * @param {Number} entriesCount the number of entries in the zip file. + * @param {Number} centralDirLength the length (in bytes) of the central dir. + * @param {Number} localDirLength the length (in bytes) of the local dir. + * @param {String} comment the zip file comment as a binary string. + * @param {Function} encodeFileName the function to encode the comment. + * @return {String} the EOCD record. + */ +var generateCentralDirectoryEnd = function (entriesCount, centralDirLength, localDirLength, comment, encodeFileName) { + var dirEnd = ""; + var encodedComment = utils.transformTo("string", encodeFileName(comment)); + + // end of central dir signature + dirEnd = signature.CENTRAL_DIRECTORY_END + + // number of this disk + "\x00\x00" + + // number of the disk with the start of the central directory + "\x00\x00" + + // total number of entries in the central directory on this disk + decToHex(entriesCount, 2) + + // total number of entries in the central directory + decToHex(entriesCount, 2) + + // size of the central directory 4 bytes + decToHex(centralDirLength, 4) + + // offset of start of central directory with respect to the starting disk number + decToHex(localDirLength, 4) + + // .ZIP file comment length + decToHex(encodedComment.length, 2) + + // .ZIP file comment + encodedComment; + + return dirEnd; +}; + +/** + * Generate data descriptors for a file entry. + * @param {Object} streamInfo the hash generated by a worker, containing informations + * on the file entry. + * @return {String} the data descriptors. + */ +var generateDataDescriptors = function (streamInfo) { + var descriptor = ""; + descriptor = signature.DATA_DESCRIPTOR + + // crc-32 4 bytes + decToHex(streamInfo['crc32'], 4) + + // compressed size 4 bytes + decToHex(streamInfo['compressedSize'], 4) + + // uncompressed size 4 bytes + decToHex(streamInfo['uncompressedSize'], 4); + + return descriptor; +}; + + +/** + * A worker to concatenate other workers to create a zip file. + * @param {Boolean} streamFiles `true` to stream the content of the files, + * `false` to accumulate it. + * @param {String} comment the comment to use. + * @param {String} platform the platform to use, "UNIX" or "DOS". + * @param {Function} encodeFileName the function to encode file names and comments. + */ +function ZipFileWorker(streamFiles, comment, platform, encodeFileName) { + GenericWorker.call(this, "ZipFileWorker"); + // The number of bytes written so far. This doesn't count accumulated chunks. + this.bytesWritten = 0; + // The comment of the zip file + this.zipComment = comment; + // The platform "generating" the zip file. + this.zipPlatform = platform; + // the function to encode file names and comments. + this.encodeFileName = encodeFileName; + // Should we stream the content of the files ? + this.streamFiles = streamFiles; + // If `streamFiles` is false, we will need to accumulate the content of the + // files to calculate sizes / crc32 (and write them *before* the content). + // This boolean indicates if we are accumulating chunks (it will change a lot + // during the lifetime of this worker). + this.accumulate = false; + // The buffer receiving chunks when accumulating content. + this.contentBuffer = []; + // The list of generated directory records. + this.dirRecords = []; + // The offset (in bytes) from the beginning of the zip file for the current source. + this.currentSourceOffset = 0; + // The total number of entries in this zip file. + this.entriesCount = 0; + // the name of the file currently being added, null when handling the end of the zip file. + // Used for the emited metadata. + this.currentFile = null; + + + + this._sources = []; +} +utils.inherits(ZipFileWorker, GenericWorker); + +/** + * @see GenericWorker.push + */ +ZipFileWorker.prototype.push = function (chunk) { + + var currentFilePercent = chunk.meta.percent || 0; + var entriesCount = this.entriesCount; + var remainingFiles = this._sources.length; + + if(this.accumulate) { + this.contentBuffer.push(chunk); + } else { + this.bytesWritten += chunk.data.length; + + GenericWorker.prototype.push.call(this, { + data : chunk.data, + meta : { + currentFile : this.currentFile, + percent : entriesCount ? (currentFilePercent + 100 * (entriesCount - remainingFiles - 1)) / entriesCount : 100 + } + }); + } +}; + +/** + * The worker started a new source (an other worker). + * @param {Object} streamInfo the streamInfo object from the new source. + */ +ZipFileWorker.prototype.openedSource = function (streamInfo) { + this.currentSourceOffset = this.bytesWritten; + this.currentFile = streamInfo['file'].name; + + var streamedContent = this.streamFiles && !streamInfo['file'].dir; + + // don't stream folders (because they don't have any content) + if(streamedContent) { + var record = generateZipParts(streamInfo, streamedContent, false, this.currentSourceOffset, this.zipPlatform, this.encodeFileName); + this.push({ + data : record.fileRecord, + meta : {percent:0} + }); + } else { + // we need to wait for the whole file before pushing anything + this.accumulate = true; + } +}; + +/** + * The worker finished a source (an other worker). + * @param {Object} streamInfo the streamInfo object from the finished source. + */ +ZipFileWorker.prototype.closedSource = function (streamInfo) { + this.accumulate = false; + var streamedContent = this.streamFiles && !streamInfo['file'].dir; + var record = generateZipParts(streamInfo, streamedContent, true, this.currentSourceOffset, this.zipPlatform, this.encodeFileName); + + this.dirRecords.push(record.dirRecord); + if(streamedContent) { + // after the streamed file, we put data descriptors + this.push({ + data : generateDataDescriptors(streamInfo), + meta : {percent:100} + }); + } else { + // the content wasn't streamed, we need to push everything now + // first the file record, then the content + this.push({ + data : record.fileRecord, + meta : {percent:0} + }); + while(this.contentBuffer.length) { + this.push(this.contentBuffer.shift()); + } + } + this.currentFile = null; +}; + +/** + * @see GenericWorker.flush + */ +ZipFileWorker.prototype.flush = function () { + + var localDirLength = this.bytesWritten; + for(var i = 0; i < this.dirRecords.length; i++) { + this.push({ + data : this.dirRecords[i], + meta : {percent:100} + }); + } + var centralDirLength = this.bytesWritten - localDirLength; + + var dirEnd = generateCentralDirectoryEnd(this.dirRecords.length, centralDirLength, localDirLength, this.zipComment, this.encodeFileName); + + this.push({ + data : dirEnd, + meta : {percent:100} + }); +}; + +/** + * Prepare the next source to be read. + */ +ZipFileWorker.prototype.prepareNextSource = function () { + this.previous = this._sources.shift(); + this.openedSource(this.previous.streamInfo); + if (this.isPaused) { + this.previous.pause(); + } else { + this.previous.resume(); + } +}; + +/** + * @see GenericWorker.registerPrevious + */ +ZipFileWorker.prototype.registerPrevious = function (previous) { + this._sources.push(previous); + var self = this; + + previous.on('data', function (chunk) { + self.processChunk(chunk); + }); + previous.on('end', function () { + self.closedSource(self.previous.streamInfo); + if(self._sources.length) { + self.prepareNextSource(); + } else { + self.end(); + } + }); + previous.on('error', function (e) { + self.error(e); + }); + return this; +}; + +/** + * @see GenericWorker.resume + */ +ZipFileWorker.prototype.resume = function () { + if(!GenericWorker.prototype.resume.call(this)) { + return false; + } + + if (!this.previous && this._sources.length) { + this.prepareNextSource(); + return true; + } + if (!this.previous && !this._sources.length && !this.generatedError) { + this.end(); + return true; + } +}; + +/** + * @see GenericWorker.error + */ +ZipFileWorker.prototype.error = function (e) { + var sources = this._sources; + if(!GenericWorker.prototype.error.call(this, e)) { + return false; + } + for(var i = 0; i < sources.length; i++) { + try { + sources[i].error(e); + } catch(e) { + // the `error` exploded, nothing to do + } + } + return true; +}; + +/** + * @see GenericWorker.lock + */ +ZipFileWorker.prototype.lock = function () { + GenericWorker.prototype.lock.call(this); + var sources = this._sources; + for(var i = 0; i < sources.length; i++) { + sources[i].lock(); + } +}; + +module.exports = ZipFileWorker; + +},{"../crc32":4,"../signature":23,"../stream/GenericWorker":28,"../utf8":31,"../utils":32}],9:[function(require,module,exports){ +'use strict'; + +var compressions = require('../compressions'); +var ZipFileWorker = require('./ZipFileWorker'); + +/** + * Find the compression to use. + * @param {String} fileCompression the compression defined at the file level, if any. + * @param {String} zipCompression the compression defined at the load() level. + * @return {Object} the compression object to use. + */ +var getCompression = function (fileCompression, zipCompression) { + + var compressionName = fileCompression || zipCompression; + var compression = compressions[compressionName]; + if (!compression) { + throw new Error(compressionName + " is not a valid compression method !"); + } + return compression; +}; + +/** + * Create a worker to generate a zip file. + * @param {JSZip} zip the JSZip instance at the right root level. + * @param {Object} options to generate the zip file. + * @param {String} comment the comment to use. + */ +exports.generateWorker = function (zip, options, comment) { + + var zipFileWorker = new ZipFileWorker(options.streamFiles, comment, options.platform, options.encodeFileName); + var entriesCount = 0; + try { + + zip.forEach(function (relativePath, file) { + entriesCount++; + var compression = getCompression(file.options.compression, options.compression); + var compressionOptions = file.options.compressionOptions || options.compressionOptions || {}; + var dir = file.dir, date = file.date; + + file._compressWorker(compression, compressionOptions) + .withStreamInfo("file", { + name : relativePath, + dir : dir, + date : date, + comment : file.comment || "", + unixPermissions : file.unixPermissions, + dosPermissions : file.dosPermissions + }) + .pipe(zipFileWorker); + }); + zipFileWorker.entriesCount = entriesCount; + } catch (e) { + zipFileWorker.error(e); + } + + return zipFileWorker; +}; + +},{"../compressions":3,"./ZipFileWorker":8}],10:[function(require,module,exports){ +'use strict'; + +/** + * Representation a of zip file in js + * @constructor + */ +function JSZip() { + // if this constructor is used without `new`, it adds `new` before itself: + if(!(this instanceof JSZip)) { + return new JSZip(); + } + + if(arguments.length) { + throw new Error("The constructor with parameters has been removed in JSZip 3.0, please check the upgrade guide."); + } + + // object containing the files : + // { + // "folder/" : {...}, + // "folder/data.txt" : {...} + // } + this.files = {}; + + this.comment = null; + + // Where we are in the hierarchy + this.root = ""; + this.clone = function() { + var newObj = new JSZip(); + for (var i in this) { + if (typeof this[i] !== "function") { + newObj[i] = this[i]; + } + } + return newObj; + }; +} +JSZip.prototype = require('./object'); +JSZip.prototype.loadAsync = require('./load'); +JSZip.support = require('./support'); +JSZip.defaults = require('./defaults'); + +// TODO find a better way to handle this version, +// a require('package.json').version doesn't work with webpack, see #327 +JSZip.version = "3.2.0"; + +JSZip.loadAsync = function (content, options) { + return new JSZip().loadAsync(content, options); +}; + +JSZip.external = require("./external"); +module.exports = JSZip; + +},{"./defaults":5,"./external":6,"./load":11,"./object":15,"./support":30}],11:[function(require,module,exports){ +'use strict'; +var utils = require('./utils'); +var external = require("./external"); +var utf8 = require('./utf8'); +var utils = require('./utils'); +var ZipEntries = require('./zipEntries'); +var Crc32Probe = require('./stream/Crc32Probe'); +var nodejsUtils = require("./nodejsUtils"); + +/** + * Check the CRC32 of an entry. + * @param {ZipEntry} zipEntry the zip entry to check. + * @return {Promise} the result. + */ +function checkEntryCRC32(zipEntry) { + return new external.Promise(function (resolve, reject) { + var worker = zipEntry.decompressed.getContentWorker().pipe(new Crc32Probe()); + worker.on("error", function (e) { + reject(e); + }) + .on("end", function () { + if (worker.streamInfo.crc32 !== zipEntry.decompressed.crc32) { + reject(new Error("Corrupted zip : CRC32 mismatch")); + } else { + resolve(); + } + }) + .resume(); + }); +} + +module.exports = function(data, options) { + var zip = this; + options = utils.extend(options || {}, { + base64: false, + checkCRC32: false, + optimizedBinaryString: false, + createFolders: false, + decodeFileName: utf8.utf8decode + }); + + if (nodejsUtils.isNode && nodejsUtils.isStream(data)) { + return external.Promise.reject(new Error("JSZip can't accept a stream when loading a zip file.")); + } + + return utils.prepareContent("the loaded zip file", data, true, options.optimizedBinaryString, options.base64) + .then(function(data) { + var zipEntries = new ZipEntries(options); + zipEntries.load(data); + return zipEntries; + }).then(function checkCRC32(zipEntries) { + var promises = [external.Promise.resolve(zipEntries)]; + var files = zipEntries.files; + if (options.checkCRC32) { + for (var i = 0; i < files.length; i++) { + promises.push(checkEntryCRC32(files[i])); + } + } + return external.Promise.all(promises); + }).then(function addFiles(results) { + var zipEntries = results.shift(); + var files = zipEntries.files; + for (var i = 0; i < files.length; i++) { + var input = files[i]; + zip.file(input.fileNameStr, input.decompressed, { + binary: true, + optimizedBinaryString: true, + date: input.date, + dir: input.dir, + comment : input.fileCommentStr.length ? input.fileCommentStr : null, + unixPermissions : input.unixPermissions, + dosPermissions : input.dosPermissions, + createFolders: options.createFolders + }); + } + if (zipEntries.zipComment.length) { + zip.comment = zipEntries.zipComment; + } + + return zip; + }); +}; + +},{"./external":6,"./nodejsUtils":14,"./stream/Crc32Probe":25,"./utf8":31,"./utils":32,"./zipEntries":33}],12:[function(require,module,exports){ +"use strict"; + +var utils = require('../utils'); +var GenericWorker = require('../stream/GenericWorker'); + +/** + * A worker that use a nodejs stream as source. + * @constructor + * @param {String} filename the name of the file entry for this stream. + * @param {Readable} stream the nodejs stream. + */ +function NodejsStreamInputAdapter(filename, stream) { + GenericWorker.call(this, "Nodejs stream input adapter for " + filename); + this._upstreamEnded = false; + this._bindStream(stream); +} + +utils.inherits(NodejsStreamInputAdapter, GenericWorker); + +/** + * Prepare the stream and bind the callbacks on it. + * Do this ASAP on node 0.10 ! A lazy binding doesn't always work. + * @param {Stream} stream the nodejs stream to use. + */ +NodejsStreamInputAdapter.prototype._bindStream = function (stream) { + var self = this; + this._stream = stream; + stream.pause(); + stream + .on("data", function (chunk) { + self.push({ + data: chunk, + meta : { + percent : 0 + } + }); + }) + .on("error", function (e) { + if(self.isPaused) { + this.generatedError = e; + } else { + self.error(e); + } + }) + .on("end", function () { + if(self.isPaused) { + self._upstreamEnded = true; + } else { + self.end(); + } + }); +}; +NodejsStreamInputAdapter.prototype.pause = function () { + if(!GenericWorker.prototype.pause.call(this)) { + return false; + } + this._stream.pause(); + return true; +}; +NodejsStreamInputAdapter.prototype.resume = function () { + if(!GenericWorker.prototype.resume.call(this)) { + return false; + } + + if(this._upstreamEnded) { + this.end(); + } else { + this._stream.resume(); + } + + return true; +}; + +module.exports = NodejsStreamInputAdapter; + +},{"../stream/GenericWorker":28,"../utils":32}],13:[function(require,module,exports){ +'use strict'; + +var Readable = require('readable-stream').Readable; + +var utils = require('../utils'); +utils.inherits(NodejsStreamOutputAdapter, Readable); + +/** +* A nodejs stream using a worker as source. +* @see the SourceWrapper in http://nodejs.org/api/stream.html +* @constructor +* @param {StreamHelper} helper the helper wrapping the worker +* @param {Object} options the nodejs stream options +* @param {Function} updateCb the update callback. +*/ +function NodejsStreamOutputAdapter(helper, options, updateCb) { + Readable.call(this, options); + this._helper = helper; + + var self = this; + helper.on("data", function (data, meta) { + if (!self.push(data)) { + self._helper.pause(); + } + if(updateCb) { + updateCb(meta); + } + }) + .on("error", function(e) { + self.emit('error', e); + }) + .on("end", function () { + self.push(null); + }); +} + + +NodejsStreamOutputAdapter.prototype._read = function() { + this._helper.resume(); +}; + +module.exports = NodejsStreamOutputAdapter; + +},{"../utils":32,"readable-stream":16}],14:[function(require,module,exports){ +'use strict'; + +module.exports = { + /** + * True if this is running in Nodejs, will be undefined in a browser. + * In a browser, browserify won't include this file and the whole module + * will be resolved an empty object. + */ + isNode : typeof Buffer !== "undefined", + /** + * Create a new nodejs Buffer from an existing content. + * @param {Object} data the data to pass to the constructor. + * @param {String} encoding the encoding to use. + * @return {Buffer} a new Buffer. + */ + newBufferFrom: function(data, encoding) { + if (Buffer.from && Buffer.from !== Uint8Array.from) { + return Buffer.from(data, encoding); + } else { + if (typeof data === "number") { + // Safeguard for old Node.js versions. On newer versions, + // Buffer.from(number) / Buffer(number, encoding) already throw. + throw new Error("The \"data\" argument must not be a number"); + } + return new Buffer(data, encoding); + } + }, + /** + * Create a new nodejs Buffer with the specified size. + * @param {Integer} size the size of the buffer. + * @return {Buffer} a new Buffer. + */ + allocBuffer: function (size) { + if (Buffer.alloc) { + return Buffer.alloc(size); + } else { + var buf = new Buffer(size); + buf.fill(0); + return buf; + } + }, + /** + * Find out if an object is a Buffer. + * @param {Object} b the object to test. + * @return {Boolean} true if the object is a Buffer, false otherwise. + */ + isBuffer : function(b){ + return Buffer.isBuffer(b); + }, + + isStream : function (obj) { + return obj && + typeof obj.on === "function" && + typeof obj.pause === "function" && + typeof obj.resume === "function"; + } +}; + +},{}],15:[function(require,module,exports){ +'use strict'; +var utf8 = require('./utf8'); +var utils = require('./utils'); +var GenericWorker = require('./stream/GenericWorker'); +var StreamHelper = require('./stream/StreamHelper'); +var defaults = require('./defaults'); +var CompressedObject = require('./compressedObject'); +var ZipObject = require('./zipObject'); +var generate = require("./generate"); +var nodejsUtils = require("./nodejsUtils"); +var NodejsStreamInputAdapter = require("./nodejs/NodejsStreamInputAdapter"); + + +/** + * Add a file in the current folder. + * @private + * @param {string} name the name of the file + * @param {String|ArrayBuffer|Uint8Array|Buffer} data the data of the file + * @param {Object} originalOptions the options of the file + * @return {Object} the new file. + */ +var fileAdd = function(name, data, originalOptions) { + // be sure sub folders exist + var dataType = utils.getTypeOf(data), + parent; + + + /* + * Correct options. + */ + + var o = utils.extend(originalOptions || {}, defaults); + o.date = o.date || new Date(); + if (o.compression !== null) { + o.compression = o.compression.toUpperCase(); + } + + if (typeof o.unixPermissions === "string") { + o.unixPermissions = parseInt(o.unixPermissions, 8); + } + + // UNX_IFDIR 0040000 see zipinfo.c + if (o.unixPermissions && (o.unixPermissions & 0x4000)) { + o.dir = true; + } + // Bit 4 Directory + if (o.dosPermissions && (o.dosPermissions & 0x0010)) { + o.dir = true; + } + + if (o.dir) { + name = forceTrailingSlash(name); + } + if (o.createFolders && (parent = parentFolder(name))) { + folderAdd.call(this, parent, true); + } + + var isUnicodeString = dataType === "string" && o.binary === false && o.base64 === false; + if (!originalOptions || typeof originalOptions.binary === "undefined") { + o.binary = !isUnicodeString; + } + + + var isCompressedEmpty = (data instanceof CompressedObject) && data.uncompressedSize === 0; + + if (isCompressedEmpty || o.dir || !data || data.length === 0) { + o.base64 = false; + o.binary = true; + data = ""; + o.compression = "STORE"; + dataType = "string"; + } + + /* + * Convert content to fit. + */ + + var zipObjectContent = null; + if (data instanceof CompressedObject || data instanceof GenericWorker) { + zipObjectContent = data; + } else if (nodejsUtils.isNode && nodejsUtils.isStream(data)) { + zipObjectContent = new NodejsStreamInputAdapter(name, data); + } else { + zipObjectContent = utils.prepareContent(name, data, o.binary, o.optimizedBinaryString, o.base64); + } + + var object = new ZipObject(name, zipObjectContent, o); + this.files[name] = object; + /* + TODO: we can't throw an exception because we have async promises + (we can have a promise of a Date() for example) but returning a + promise is useless because file(name, data) returns the JSZip + object for chaining. Should we break that to allow the user + to catch the error ? + + return external.Promise.resolve(zipObjectContent) + .then(function () { + return object; + }); + */ +}; + +/** + * Find the parent folder of the path. + * @private + * @param {string} path the path to use + * @return {string} the parent folder, or "" + */ +var parentFolder = function (path) { + if (path.slice(-1) === '/') { + path = path.substring(0, path.length - 1); + } + var lastSlash = path.lastIndexOf('/'); + return (lastSlash > 0) ? path.substring(0, lastSlash) : ""; +}; + +/** + * Returns the path with a slash at the end. + * @private + * @param {String} path the path to check. + * @return {String} the path with a trailing slash. + */ +var forceTrailingSlash = function(path) { + // Check the name ends with a / + if (path.slice(-1) !== "/") { + path += "/"; // IE doesn't like substr(-1) + } + return path; +}; + +/** + * Add a (sub) folder in the current folder. + * @private + * @param {string} name the folder's name + * @param {boolean=} [createFolders] If true, automatically create sub + * folders. Defaults to false. + * @return {Object} the new folder. + */ +var folderAdd = function(name, createFolders) { + createFolders = (typeof createFolders !== 'undefined') ? createFolders : defaults.createFolders; + + name = forceTrailingSlash(name); + + // Does this folder already exist? + if (!this.files[name]) { + fileAdd.call(this, name, null, { + dir: true, + createFolders: createFolders + }); + } + return this.files[name]; +}; + +/** +* Cross-window, cross-Node-context regular expression detection +* @param {Object} object Anything +* @return {Boolean} true if the object is a regular expression, +* false otherwise +*/ +function isRegExp(object) { + return Object.prototype.toString.call(object) === "[object RegExp]"; +} + +// return the actual prototype of JSZip +var out = { + /** + * @see loadAsync + */ + load: function() { + throw new Error("This method has been removed in JSZip 3.0, please check the upgrade guide."); + }, + + + /** + * Call a callback function for each entry at this folder level. + * @param {Function} cb the callback function: + * function (relativePath, file) {...} + * It takes 2 arguments : the relative path and the file. + */ + forEach: function(cb) { + var filename, relativePath, file; + for (filename in this.files) { + if (!this.files.hasOwnProperty(filename)) { + continue; + } + file = this.files[filename]; + relativePath = filename.slice(this.root.length, filename.length); + if (relativePath && filename.slice(0, this.root.length) === this.root) { // the file is in the current root + cb(relativePath, file); // TODO reverse the parameters ? need to be clean AND consistent with the filter search fn... + } + } + }, + + /** + * Filter nested files/folders with the specified function. + * @param {Function} search the predicate to use : + * function (relativePath, file) {...} + * It takes 2 arguments : the relative path and the file. + * @return {Array} An array of matching elements. + */ + filter: function(search) { + var result = []; + this.forEach(function (relativePath, entry) { + if (search(relativePath, entry)) { // the file matches the function + result.push(entry); + } + + }); + return result; + }, + + /** + * Add a file to the zip file, or search a file. + * @param {string|RegExp} name The name of the file to add (if data is defined), + * the name of the file to find (if no data) or a regex to match files. + * @param {String|ArrayBuffer|Uint8Array|Buffer} data The file data, either raw or base64 encoded + * @param {Object} o File options + * @return {JSZip|Object|Array} this JSZip object (when adding a file), + * a file (when searching by string) or an array of files (when searching by regex). + */ + file: function(name, data, o) { + if (arguments.length === 1) { + if (isRegExp(name)) { + var regexp = name; + return this.filter(function(relativePath, file) { + return !file.dir && regexp.test(relativePath); + }); + } + else { // text + var obj = this.files[this.root + name]; + if (obj && !obj.dir) { + return obj; + } else { + return null; + } + } + } + else { // more than one argument : we have data ! + name = this.root + name; + fileAdd.call(this, name, data, o); + } + return this; + }, + + /** + * Add a directory to the zip file, or search. + * @param {String|RegExp} arg The name of the directory to add, or a regex to search folders. + * @return {JSZip} an object with the new directory as the root, or an array containing matching folders. + */ + folder: function(arg) { + if (!arg) { + return this; + } + + if (isRegExp(arg)) { + return this.filter(function(relativePath, file) { + return file.dir && arg.test(relativePath); + }); + } + + // else, name is a new folder + var name = this.root + arg; + var newFolder = folderAdd.call(this, name); + + // Allow chaining by returning a new object with this folder as the root + var ret = this.clone(); + ret.root = newFolder.name; + return ret; + }, + + /** + * Delete a file, or a directory and all sub-files, from the zip + * @param {string} name the name of the file to delete + * @return {JSZip} this JSZip object + */ + remove: function(name) { + name = this.root + name; + var file = this.files[name]; + if (!file) { + // Look for any folders + if (name.slice(-1) !== "/") { + name += "/"; + } + file = this.files[name]; + } + + if (file && !file.dir) { + // file + delete this.files[name]; + } else { + // maybe a folder, delete recursively + var kids = this.filter(function(relativePath, file) { + return file.name.slice(0, name.length) === name; + }); + for (var i = 0; i < kids.length; i++) { + delete this.files[kids[i].name]; + } + } + + return this; + }, + + /** + * Generate the complete zip file + * @param {Object} options the options to generate the zip file : + * - compression, "STORE" by default. + * - type, "base64" by default. Values are : string, base64, uint8array, arraybuffer, blob. + * @return {String|Uint8Array|ArrayBuffer|Buffer|Blob} the zip file + */ + generate: function(options) { + throw new Error("This method has been removed in JSZip 3.0, please check the upgrade guide."); + }, + + /** + * Generate the complete zip file as an internal stream. + * @param {Object} options the options to generate the zip file : + * - compression, "STORE" by default. + * - type, "base64" by default. Values are : string, base64, uint8array, arraybuffer, blob. + * @return {StreamHelper} the streamed zip file. + */ + generateInternalStream: function(options) { + var worker, opts = {}; + try { + opts = utils.extend(options || {}, { + streamFiles: false, + compression: "STORE", + compressionOptions : null, + type: "", + platform: "DOS", + comment: null, + mimeType: 'application/zip', + encodeFileName: utf8.utf8encode + }); + + opts.type = opts.type.toLowerCase(); + opts.compression = opts.compression.toUpperCase(); + + // "binarystring" is prefered but the internals use "string". + if(opts.type === "binarystring") { + opts.type = "string"; + } + + if (!opts.type) { + throw new Error("No output type specified."); + } + + utils.checkSupport(opts.type); + + // accept nodejs `process.platform` + if( + opts.platform === 'darwin' || + opts.platform === 'freebsd' || + opts.platform === 'linux' || + opts.platform === 'sunos' + ) { + opts.platform = "UNIX"; + } + if (opts.platform === 'win32') { + opts.platform = "DOS"; + } + + var comment = opts.comment || this.comment || ""; + worker = generate.generateWorker(this, opts, comment); + } catch (e) { + worker = new GenericWorker("error"); + worker.error(e); + } + return new StreamHelper(worker, opts.type || "string", opts.mimeType); + }, + /** + * Generate the complete zip file asynchronously. + * @see generateInternalStream + */ + generateAsync: function(options, onUpdate) { + return this.generateInternalStream(options).accumulate(onUpdate); + }, + /** + * Generate the complete zip file asynchronously. + * @see generateInternalStream + */ + generateNodeStream: function(options, onUpdate) { + options = options || {}; + if (!options.type) { + options.type = "nodebuffer"; + } + return this.generateInternalStream(options).toNodejsStream(onUpdate); + } +}; +module.exports = out; + +},{"./compressedObject":2,"./defaults":5,"./generate":9,"./nodejs/NodejsStreamInputAdapter":12,"./nodejsUtils":14,"./stream/GenericWorker":28,"./stream/StreamHelper":29,"./utf8":31,"./utils":32,"./zipObject":35}],16:[function(require,module,exports){ +/* + * This file is used by module bundlers (browserify/webpack/etc) when + * including a stream implementation. We use "readable-stream" to get a + * consistent behavior between nodejs versions but bundlers often have a shim + * for "stream". Using this shim greatly improve the compatibility and greatly + * reduce the final size of the bundle (only one stream implementation, not + * two). + */ +module.exports = require("stream"); + +},{"stream":undefined}],17:[function(require,module,exports){ +'use strict'; +var DataReader = require('./DataReader'); +var utils = require('../utils'); + +function ArrayReader(data) { + DataReader.call(this, data); + for(var i = 0; i < this.data.length; i++) { + data[i] = data[i] & 0xFF; + } +} +utils.inherits(ArrayReader, DataReader); +/** + * @see DataReader.byteAt + */ +ArrayReader.prototype.byteAt = function(i) { + return this.data[this.zero + i]; +}; +/** + * @see DataReader.lastIndexOfSignature + */ +ArrayReader.prototype.lastIndexOfSignature = function(sig) { + var sig0 = sig.charCodeAt(0), + sig1 = sig.charCodeAt(1), + sig2 = sig.charCodeAt(2), + sig3 = sig.charCodeAt(3); + for (var i = this.length - 4; i >= 0; --i) { + if (this.data[i] === sig0 && this.data[i + 1] === sig1 && this.data[i + 2] === sig2 && this.data[i + 3] === sig3) { + return i - this.zero; + } + } + + return -1; +}; +/** + * @see DataReader.readAndCheckSignature + */ +ArrayReader.prototype.readAndCheckSignature = function (sig) { + var sig0 = sig.charCodeAt(0), + sig1 = sig.charCodeAt(1), + sig2 = sig.charCodeAt(2), + sig3 = sig.charCodeAt(3), + data = this.readData(4); + return sig0 === data[0] && sig1 === data[1] && sig2 === data[2] && sig3 === data[3]; +}; +/** + * @see DataReader.readData + */ +ArrayReader.prototype.readData = function(size) { + this.checkOffset(size); + if(size === 0) { + return []; + } + var result = this.data.slice(this.zero + this.index, this.zero + this.index + size); + this.index += size; + return result; +}; +module.exports = ArrayReader; + +},{"../utils":32,"./DataReader":18}],18:[function(require,module,exports){ +'use strict'; +var utils = require('../utils'); + +function DataReader(data) { + this.data = data; // type : see implementation + this.length = data.length; + this.index = 0; + this.zero = 0; +} +DataReader.prototype = { + /** + * Check that the offset will not go too far. + * @param {string} offset the additional offset to check. + * @throws {Error} an Error if the offset is out of bounds. + */ + checkOffset: function(offset) { + this.checkIndex(this.index + offset); + }, + /** + * Check that the specified index will not be too far. + * @param {string} newIndex the index to check. + * @throws {Error} an Error if the index is out of bounds. + */ + checkIndex: function(newIndex) { + if (this.length < this.zero + newIndex || newIndex < 0) { + throw new Error("End of data reached (data length = " + this.length + ", asked index = " + (newIndex) + "). Corrupted zip ?"); + } + }, + /** + * Change the index. + * @param {number} newIndex The new index. + * @throws {Error} if the new index is out of the data. + */ + setIndex: function(newIndex) { + this.checkIndex(newIndex); + this.index = newIndex; + }, + /** + * Skip the next n bytes. + * @param {number} n the number of bytes to skip. + * @throws {Error} if the new index is out of the data. + */ + skip: function(n) { + this.setIndex(this.index + n); + }, + /** + * Get the byte at the specified index. + * @param {number} i the index to use. + * @return {number} a byte. + */ + byteAt: function(i) { + // see implementations + }, + /** + * Get the next number with a given byte size. + * @param {number} size the number of bytes to read. + * @return {number} the corresponding number. + */ + readInt: function(size) { + var result = 0, + i; + this.checkOffset(size); + for (i = this.index + size - 1; i >= this.index; i--) { + result = (result << 8) + this.byteAt(i); + } + this.index += size; + return result; + }, + /** + * Get the next string with a given byte size. + * @param {number} size the number of bytes to read. + * @return {string} the corresponding string. + */ + readString: function(size) { + return utils.transformTo("string", this.readData(size)); + }, + /** + * Get raw data without conversion, bytes. + * @param {number} size the number of bytes to read. + * @return {Object} the raw data, implementation specific. + */ + readData: function(size) { + // see implementations + }, + /** + * Find the last occurence of a zip signature (4 bytes). + * @param {string} sig the signature to find. + * @return {number} the index of the last occurence, -1 if not found. + */ + lastIndexOfSignature: function(sig) { + // see implementations + }, + /** + * Read the signature (4 bytes) at the current position and compare it with sig. + * @param {string} sig the expected signature + * @return {boolean} true if the signature matches, false otherwise. + */ + readAndCheckSignature: function(sig) { + // see implementations + }, + /** + * Get the next date. + * @return {Date} the date. + */ + readDate: function() { + var dostime = this.readInt(4); + return new Date(Date.UTC( + ((dostime >> 25) & 0x7f) + 1980, // year + ((dostime >> 21) & 0x0f) - 1, // month + (dostime >> 16) & 0x1f, // day + (dostime >> 11) & 0x1f, // hour + (dostime >> 5) & 0x3f, // minute + (dostime & 0x1f) << 1)); // second + } +}; +module.exports = DataReader; + +},{"../utils":32}],19:[function(require,module,exports){ +'use strict'; +var Uint8ArrayReader = require('./Uint8ArrayReader'); +var utils = require('../utils'); + +function NodeBufferReader(data) { + Uint8ArrayReader.call(this, data); +} +utils.inherits(NodeBufferReader, Uint8ArrayReader); + +/** + * @see DataReader.readData + */ +NodeBufferReader.prototype.readData = function(size) { + this.checkOffset(size); + var result = this.data.slice(this.zero + this.index, this.zero + this.index + size); + this.index += size; + return result; +}; +module.exports = NodeBufferReader; + +},{"../utils":32,"./Uint8ArrayReader":21}],20:[function(require,module,exports){ +'use strict'; +var DataReader = require('./DataReader'); +var utils = require('../utils'); + +function StringReader(data) { + DataReader.call(this, data); +} +utils.inherits(StringReader, DataReader); +/** + * @see DataReader.byteAt + */ +StringReader.prototype.byteAt = function(i) { + return this.data.charCodeAt(this.zero + i); +}; +/** + * @see DataReader.lastIndexOfSignature + */ +StringReader.prototype.lastIndexOfSignature = function(sig) { + return this.data.lastIndexOf(sig) - this.zero; +}; +/** + * @see DataReader.readAndCheckSignature + */ +StringReader.prototype.readAndCheckSignature = function (sig) { + var data = this.readData(4); + return sig === data; +}; +/** + * @see DataReader.readData + */ +StringReader.prototype.readData = function(size) { + this.checkOffset(size); + // this will work because the constructor applied the "& 0xff" mask. + var result = this.data.slice(this.zero + this.index, this.zero + this.index + size); + this.index += size; + return result; +}; +module.exports = StringReader; + +},{"../utils":32,"./DataReader":18}],21:[function(require,module,exports){ +'use strict'; +var ArrayReader = require('./ArrayReader'); +var utils = require('../utils'); + +function Uint8ArrayReader(data) { + ArrayReader.call(this, data); +} +utils.inherits(Uint8ArrayReader, ArrayReader); +/** + * @see DataReader.readData + */ +Uint8ArrayReader.prototype.readData = function(size) { + this.checkOffset(size); + if(size === 0) { + // in IE10, when using subarray(idx, idx), we get the array [0x00] instead of []. + return new Uint8Array(0); + } + var result = this.data.subarray(this.zero + this.index, this.zero + this.index + size); + this.index += size; + return result; +}; +module.exports = Uint8ArrayReader; + +},{"../utils":32,"./ArrayReader":17}],22:[function(require,module,exports){ +'use strict'; + +var utils = require('../utils'); +var support = require('../support'); +var ArrayReader = require('./ArrayReader'); +var StringReader = require('./StringReader'); +var NodeBufferReader = require('./NodeBufferReader'); +var Uint8ArrayReader = require('./Uint8ArrayReader'); + +/** + * Create a reader adapted to the data. + * @param {String|ArrayBuffer|Uint8Array|Buffer} data the data to read. + * @return {DataReader} the data reader. + */ +module.exports = function (data) { + var type = utils.getTypeOf(data); + utils.checkSupport(type); + if (type === "string" && !support.uint8array) { + return new StringReader(data); + } + if (type === "nodebuffer") { + return new NodeBufferReader(data); + } + if (support.uint8array) { + return new Uint8ArrayReader(utils.transformTo("uint8array", data)); + } + return new ArrayReader(utils.transformTo("array", data)); +}; + +},{"../support":30,"../utils":32,"./ArrayReader":17,"./NodeBufferReader":19,"./StringReader":20,"./Uint8ArrayReader":21}],23:[function(require,module,exports){ +'use strict'; +exports.LOCAL_FILE_HEADER = "PK\x03\x04"; +exports.CENTRAL_FILE_HEADER = "PK\x01\x02"; +exports.CENTRAL_DIRECTORY_END = "PK\x05\x06"; +exports.ZIP64_CENTRAL_DIRECTORY_LOCATOR = "PK\x06\x07"; +exports.ZIP64_CENTRAL_DIRECTORY_END = "PK\x06\x06"; +exports.DATA_DESCRIPTOR = "PK\x07\x08"; + +},{}],24:[function(require,module,exports){ +'use strict'; + +var GenericWorker = require('./GenericWorker'); +var utils = require('../utils'); + +/** + * A worker which convert chunks to a specified type. + * @constructor + * @param {String} destType the destination type. + */ +function ConvertWorker(destType) { + GenericWorker.call(this, "ConvertWorker to " + destType); + this.destType = destType; +} +utils.inherits(ConvertWorker, GenericWorker); + +/** + * @see GenericWorker.processChunk + */ +ConvertWorker.prototype.processChunk = function (chunk) { + this.push({ + data : utils.transformTo(this.destType, chunk.data), + meta : chunk.meta + }); +}; +module.exports = ConvertWorker; + +},{"../utils":32,"./GenericWorker":28}],25:[function(require,module,exports){ +'use strict'; + +var GenericWorker = require('./GenericWorker'); +var crc32 = require('../crc32'); +var utils = require('../utils'); + +/** + * A worker which calculate the crc32 of the data flowing through. + * @constructor + */ +function Crc32Probe() { + GenericWorker.call(this, "Crc32Probe"); + this.withStreamInfo("crc32", 0); +} +utils.inherits(Crc32Probe, GenericWorker); + +/** + * @see GenericWorker.processChunk + */ +Crc32Probe.prototype.processChunk = function (chunk) { + this.streamInfo.crc32 = crc32(chunk.data, this.streamInfo.crc32 || 0); + this.push(chunk); +}; +module.exports = Crc32Probe; + +},{"../crc32":4,"../utils":32,"./GenericWorker":28}],26:[function(require,module,exports){ +'use strict'; + +var utils = require('../utils'); +var GenericWorker = require('./GenericWorker'); + +/** + * A worker which calculate the total length of the data flowing through. + * @constructor + * @param {String} propName the name used to expose the length + */ +function DataLengthProbe(propName) { + GenericWorker.call(this, "DataLengthProbe for " + propName); + this.propName = propName; + this.withStreamInfo(propName, 0); +} +utils.inherits(DataLengthProbe, GenericWorker); + +/** + * @see GenericWorker.processChunk + */ +DataLengthProbe.prototype.processChunk = function (chunk) { + if(chunk) { + var length = this.streamInfo[this.propName] || 0; + this.streamInfo[this.propName] = length + chunk.data.length; + } + GenericWorker.prototype.processChunk.call(this, chunk); +}; +module.exports = DataLengthProbe; + + +},{"../utils":32,"./GenericWorker":28}],27:[function(require,module,exports){ +'use strict'; + +var utils = require('../utils'); +var GenericWorker = require('./GenericWorker'); + +// the size of the generated chunks +// TODO expose this as a public variable +var DEFAULT_BLOCK_SIZE = 16 * 1024; + +/** + * A worker that reads a content and emits chunks. + * @constructor + * @param {Promise} dataP the promise of the data to split + */ +function DataWorker(dataP) { + GenericWorker.call(this, "DataWorker"); + var self = this; + this.dataIsReady = false; + this.index = 0; + this.max = 0; + this.data = null; + this.type = ""; + + this._tickScheduled = false; + + dataP.then(function (data) { + self.dataIsReady = true; + self.data = data; + self.max = data && data.length || 0; + self.type = utils.getTypeOf(data); + if(!self.isPaused) { + self._tickAndRepeat(); + } + }, function (e) { + self.error(e); + }); +} + +utils.inherits(DataWorker, GenericWorker); + +/** + * @see GenericWorker.cleanUp + */ +DataWorker.prototype.cleanUp = function () { + GenericWorker.prototype.cleanUp.call(this); + this.data = null; +}; + +/** + * @see GenericWorker.resume + */ +DataWorker.prototype.resume = function () { + if(!GenericWorker.prototype.resume.call(this)) { + return false; + } + + if (!this._tickScheduled && this.dataIsReady) { + this._tickScheduled = true; + utils.delay(this._tickAndRepeat, [], this); + } + return true; +}; + +/** + * Trigger a tick a schedule an other call to this function. + */ +DataWorker.prototype._tickAndRepeat = function() { + this._tickScheduled = false; + if(this.isPaused || this.isFinished) { + return; + } + this._tick(); + if(!this.isFinished) { + utils.delay(this._tickAndRepeat, [], this); + this._tickScheduled = true; + } +}; + +/** + * Read and push a chunk. + */ +DataWorker.prototype._tick = function() { + + if(this.isPaused || this.isFinished) { + return false; + } + + var size = DEFAULT_BLOCK_SIZE; + var data = null, nextIndex = Math.min(this.max, this.index + size); + if (this.index >= this.max) { + // EOF + return this.end(); + } else { + switch(this.type) { + case "string": + data = this.data.substring(this.index, nextIndex); + break; + case "uint8array": + data = this.data.subarray(this.index, nextIndex); + break; + case "array": + case "nodebuffer": + data = this.data.slice(this.index, nextIndex); + break; + } + this.index = nextIndex; + return this.push({ + data : data, + meta : { + percent : this.max ? this.index / this.max * 100 : 0 + } + }); + } +}; + +module.exports = DataWorker; + +},{"../utils":32,"./GenericWorker":28}],28:[function(require,module,exports){ +'use strict'; + +/** + * A worker that does nothing but passing chunks to the next one. This is like + * a nodejs stream but with some differences. On the good side : + * - it works on IE 6-9 without any issue / polyfill + * - it weights less than the full dependencies bundled with browserify + * - it forwards errors (no need to declare an error handler EVERYWHERE) + * + * A chunk is an object with 2 attributes : `meta` and `data`. The former is an + * object containing anything (`percent` for example), see each worker for more + * details. The latter is the real data (String, Uint8Array, etc). + * + * @constructor + * @param {String} name the name of the stream (mainly used for debugging purposes) + */ +function GenericWorker(name) { + // the name of the worker + this.name = name || "default"; + // an object containing metadata about the workers chain + this.streamInfo = {}; + // an error which happened when the worker was paused + this.generatedError = null; + // an object containing metadata to be merged by this worker into the general metadata + this.extraStreamInfo = {}; + // true if the stream is paused (and should not do anything), false otherwise + this.isPaused = true; + // true if the stream is finished (and should not do anything), false otherwise + this.isFinished = false; + // true if the stream is locked to prevent further structure updates (pipe), false otherwise + this.isLocked = false; + // the event listeners + this._listeners = { + 'data':[], + 'end':[], + 'error':[] + }; + // the previous worker, if any + this.previous = null; +} + +GenericWorker.prototype = { + /** + * Push a chunk to the next workers. + * @param {Object} chunk the chunk to push + */ + push : function (chunk) { + this.emit("data", chunk); + }, + /** + * End the stream. + * @return {Boolean} true if this call ended the worker, false otherwise. + */ + end : function () { + if (this.isFinished) { + return false; + } + + this.flush(); + try { + this.emit("end"); + this.cleanUp(); + this.isFinished = true; + } catch (e) { + this.emit("error", e); + } + return true; + }, + /** + * End the stream with an error. + * @param {Error} e the error which caused the premature end. + * @return {Boolean} true if this call ended the worker with an error, false otherwise. + */ + error : function (e) { + if (this.isFinished) { + return false; + } + + if(this.isPaused) { + this.generatedError = e; + } else { + this.isFinished = true; + + this.emit("error", e); + + // in the workers chain exploded in the middle of the chain, + // the error event will go downward but we also need to notify + // workers upward that there has been an error. + if(this.previous) { + this.previous.error(e); + } + + this.cleanUp(); + } + return true; + }, + /** + * Add a callback on an event. + * @param {String} name the name of the event (data, end, error) + * @param {Function} listener the function to call when the event is triggered + * @return {GenericWorker} the current object for chainability + */ + on : function (name, listener) { + this._listeners[name].push(listener); + return this; + }, + /** + * Clean any references when a worker is ending. + */ + cleanUp : function () { + this.streamInfo = this.generatedError = this.extraStreamInfo = null; + this._listeners = []; + }, + /** + * Trigger an event. This will call registered callback with the provided arg. + * @param {String} name the name of the event (data, end, error) + * @param {Object} arg the argument to call the callback with. + */ + emit : function (name, arg) { + if (this._listeners[name]) { + for(var i = 0; i < this._listeners[name].length; i++) { + this._listeners[name][i].call(this, arg); + } + } + }, + /** + * Chain a worker with an other. + * @param {Worker} next the worker receiving events from the current one. + * @return {worker} the next worker for chainability + */ + pipe : function (next) { + return next.registerPrevious(this); + }, + /** + * Same as `pipe` in the other direction. + * Using an API with `pipe(next)` is very easy. + * Implementing the API with the point of view of the next one registering + * a source is easier, see the ZipFileWorker. + * @param {Worker} previous the previous worker, sending events to this one + * @return {Worker} the current worker for chainability + */ + registerPrevious : function (previous) { + if (this.isLocked) { + throw new Error("The stream '" + this + "' has already been used."); + } + + // sharing the streamInfo... + this.streamInfo = previous.streamInfo; + // ... and adding our own bits + this.mergeStreamInfo(); + this.previous = previous; + var self = this; + previous.on('data', function (chunk) { + self.processChunk(chunk); + }); + previous.on('end', function () { + self.end(); + }); + previous.on('error', function (e) { + self.error(e); + }); + return this; + }, + /** + * Pause the stream so it doesn't send events anymore. + * @return {Boolean} true if this call paused the worker, false otherwise. + */ + pause : function () { + if(this.isPaused || this.isFinished) { + return false; + } + this.isPaused = true; + + if(this.previous) { + this.previous.pause(); + } + return true; + }, + /** + * Resume a paused stream. + * @return {Boolean} true if this call resumed the worker, false otherwise. + */ + resume : function () { + if(!this.isPaused || this.isFinished) { + return false; + } + this.isPaused = false; + + // if true, the worker tried to resume but failed + var withError = false; + if(this.generatedError) { + this.error(this.generatedError); + withError = true; + } + if(this.previous) { + this.previous.resume(); + } + + return !withError; + }, + /** + * Flush any remaining bytes as the stream is ending. + */ + flush : function () {}, + /** + * Process a chunk. This is usually the method overridden. + * @param {Object} chunk the chunk to process. + */ + processChunk : function(chunk) { + this.push(chunk); + }, + /** + * Add a key/value to be added in the workers chain streamInfo once activated. + * @param {String} key the key to use + * @param {Object} value the associated value + * @return {Worker} the current worker for chainability + */ + withStreamInfo : function (key, value) { + this.extraStreamInfo[key] = value; + this.mergeStreamInfo(); + return this; + }, + /** + * Merge this worker's streamInfo into the chain's streamInfo. + */ + mergeStreamInfo : function () { + for(var key in this.extraStreamInfo) { + if (!this.extraStreamInfo.hasOwnProperty(key)) { + continue; + } + this.streamInfo[key] = this.extraStreamInfo[key]; + } + }, + + /** + * Lock the stream to prevent further updates on the workers chain. + * After calling this method, all calls to pipe will fail. + */ + lock: function () { + if (this.isLocked) { + throw new Error("The stream '" + this + "' has already been used."); + } + this.isLocked = true; + if (this.previous) { + this.previous.lock(); + } + }, + + /** + * + * Pretty print the workers chain. + */ + toString : function () { + var me = "Worker " + this.name; + if (this.previous) { + return this.previous + " -> " + me; + } else { + return me; + } + } +}; + +module.exports = GenericWorker; + +},{}],29:[function(require,module,exports){ +'use strict'; + +var utils = require('../utils'); +var ConvertWorker = require('./ConvertWorker'); +var GenericWorker = require('./GenericWorker'); +var base64 = require('../base64'); +var support = require("../support"); +var external = require("../external"); + +var NodejsStreamOutputAdapter = null; +if (support.nodestream) { + try { + NodejsStreamOutputAdapter = require('../nodejs/NodejsStreamOutputAdapter'); + } catch(e) {} +} + +/** + * Apply the final transformation of the data. If the user wants a Blob for + * example, it's easier to work with an U8intArray and finally do the + * ArrayBuffer/Blob conversion. + * @param {String} type the name of the final type + * @param {String|Uint8Array|Buffer} content the content to transform + * @param {String} mimeType the mime type of the content, if applicable. + * @return {String|Uint8Array|ArrayBuffer|Buffer|Blob} the content in the right format. + */ +function transformZipOutput(type, content, mimeType) { + switch(type) { + case "blob" : + return utils.newBlob(utils.transformTo("arraybuffer", content), mimeType); + case "base64" : + return base64.encode(content); + default : + return utils.transformTo(type, content); + } +} + +/** + * Concatenate an array of data of the given type. + * @param {String} type the type of the data in the given array. + * @param {Array} dataArray the array containing the data chunks to concatenate + * @return {String|Uint8Array|Buffer} the concatenated data + * @throws Error if the asked type is unsupported + */ +function concat (type, dataArray) { + var i, index = 0, res = null, totalLength = 0; + for(i = 0; i < dataArray.length; i++) { + totalLength += dataArray[i].length; + } + switch(type) { + case "string": + return dataArray.join(""); + case "array": + return Array.prototype.concat.apply([], dataArray); + case "uint8array": + res = new Uint8Array(totalLength); + for(i = 0; i < dataArray.length; i++) { + res.set(dataArray[i], index); + index += dataArray[i].length; + } + return res; + case "nodebuffer": + return Buffer.concat(dataArray); + default: + throw new Error("concat : unsupported type '" + type + "'"); + } +} + +/** + * Listen a StreamHelper, accumulate its content and concatenate it into a + * complete block. + * @param {StreamHelper} helper the helper to use. + * @param {Function} updateCallback a callback called on each update. Called + * with one arg : + * - the metadata linked to the update received. + * @return Promise the promise for the accumulation. + */ +function accumulate(helper, updateCallback) { + return new external.Promise(function (resolve, reject){ + var dataArray = []; + var chunkType = helper._internalType, + resultType = helper._outputType, + mimeType = helper._mimeType; + helper + .on('data', function (data, meta) { + dataArray.push(data); + if(updateCallback) { + updateCallback(meta); + } + }) + .on('error', function(err) { + dataArray = []; + reject(err); + }) + .on('end', function (){ + try { + var result = transformZipOutput(resultType, concat(chunkType, dataArray), mimeType); + resolve(result); + } catch (e) { + reject(e); + } + dataArray = []; + }) + .resume(); + }); +} + +/** + * An helper to easily use workers outside of JSZip. + * @constructor + * @param {Worker} worker the worker to wrap + * @param {String} outputType the type of data expected by the use + * @param {String} mimeType the mime type of the content, if applicable. + */ +function StreamHelper(worker, outputType, mimeType) { + var internalType = outputType; + switch(outputType) { + case "blob": + case "arraybuffer": + internalType = "uint8array"; + break; + case "base64": + internalType = "string"; + break; + } + + try { + // the type used internally + this._internalType = internalType; + // the type used to output results + this._outputType = outputType; + // the mime type + this._mimeType = mimeType; + utils.checkSupport(internalType); + this._worker = worker.pipe(new ConvertWorker(internalType)); + // the last workers can be rewired without issues but we need to + // prevent any updates on previous workers. + worker.lock(); + } catch(e) { + this._worker = new GenericWorker("error"); + this._worker.error(e); + } +} + +StreamHelper.prototype = { + /** + * Listen a StreamHelper, accumulate its content and concatenate it into a + * complete block. + * @param {Function} updateCb the update callback. + * @return Promise the promise for the accumulation. + */ + accumulate : function (updateCb) { + return accumulate(this, updateCb); + }, + /** + * Add a listener on an event triggered on a stream. + * @param {String} evt the name of the event + * @param {Function} fn the listener + * @return {StreamHelper} the current helper. + */ + on : function (evt, fn) { + var self = this; + + if(evt === "data") { + this._worker.on(evt, function (chunk) { + fn.call(self, chunk.data, chunk.meta); + }); + } else { + this._worker.on(evt, function () { + utils.delay(fn, arguments, self); + }); + } + return this; + }, + /** + * Resume the flow of chunks. + * @return {StreamHelper} the current helper. + */ + resume : function () { + utils.delay(this._worker.resume, [], this._worker); + return this; + }, + /** + * Pause the flow of chunks. + * @return {StreamHelper} the current helper. + */ + pause : function () { + this._worker.pause(); + return this; + }, + /** + * Return a nodejs stream for this helper. + * @param {Function} updateCb the update callback. + * @return {NodejsStreamOutputAdapter} the nodejs stream. + */ + toNodejsStream : function (updateCb) { + utils.checkSupport("nodestream"); + if (this._outputType !== "nodebuffer") { + // an object stream containing blob/arraybuffer/uint8array/string + // is strange and I don't know if it would be useful. + // I you find this comment and have a good usecase, please open a + // bug report ! + throw new Error(this._outputType + " is not supported by this method"); + } + + return new NodejsStreamOutputAdapter(this, { + objectMode : this._outputType !== "nodebuffer" + }, updateCb); + } +}; + + +module.exports = StreamHelper; + +},{"../base64":1,"../external":6,"../nodejs/NodejsStreamOutputAdapter":13,"../support":30,"../utils":32,"./ConvertWorker":24,"./GenericWorker":28}],30:[function(require,module,exports){ +'use strict'; + +exports.base64 = true; +exports.array = true; +exports.string = true; +exports.arraybuffer = typeof ArrayBuffer !== "undefined" && typeof Uint8Array !== "undefined"; +exports.nodebuffer = typeof Buffer !== "undefined"; +// contains true if JSZip can read/generate Uint8Array, false otherwise. +exports.uint8array = typeof Uint8Array !== "undefined"; + +if (typeof ArrayBuffer === "undefined") { + exports.blob = false; +} +else { + var buffer = new ArrayBuffer(0); + try { + exports.blob = new Blob([buffer], { + type: "application/zip" + }).size === 0; + } + catch (e) { + try { + var Builder = self.BlobBuilder || self.WebKitBlobBuilder || self.MozBlobBuilder || self.MSBlobBuilder; + var builder = new Builder(); + builder.append(buffer); + exports.blob = builder.getBlob('application/zip').size === 0; + } + catch (e) { + exports.blob = false; + } + } +} + +try { + exports.nodestream = !!require('readable-stream').Readable; +} catch(e) { + exports.nodestream = false; +} + +},{"readable-stream":16}],31:[function(require,module,exports){ +'use strict'; + +var utils = require('./utils'); +var support = require('./support'); +var nodejsUtils = require('./nodejsUtils'); +var GenericWorker = require('./stream/GenericWorker'); + +/** + * The following functions come from pako, from pako/lib/utils/strings + * released under the MIT license, see pako https://github.com/nodeca/pako/ + */ + +// Table with utf8 lengths (calculated by first byte of sequence) +// Note, that 5 & 6-byte values and some 4-byte values can not be represented in JS, +// because max possible codepoint is 0x10ffff +var _utf8len = new Array(256); +for (var i=0; i<256; i++) { + _utf8len[i] = (i >= 252 ? 6 : i >= 248 ? 5 : i >= 240 ? 4 : i >= 224 ? 3 : i >= 192 ? 2 : 1); +} +_utf8len[254]=_utf8len[254]=1; // Invalid sequence start + +// convert string to array (typed, when possible) +var string2buf = function (str) { + var buf, c, c2, m_pos, i, str_len = str.length, buf_len = 0; + + // count binary size + for (m_pos = 0; m_pos < str_len; m_pos++) { + c = str.charCodeAt(m_pos); + if ((c & 0xfc00) === 0xd800 && (m_pos+1 < str_len)) { + c2 = str.charCodeAt(m_pos+1); + if ((c2 & 0xfc00) === 0xdc00) { + c = 0x10000 + ((c - 0xd800) << 10) + (c2 - 0xdc00); + m_pos++; + } + } + buf_len += c < 0x80 ? 1 : c < 0x800 ? 2 : c < 0x10000 ? 3 : 4; + } + + // allocate buffer + if (support.uint8array) { + buf = new Uint8Array(buf_len); + } else { + buf = new Array(buf_len); + } + + // convert + for (i=0, m_pos = 0; i < buf_len; m_pos++) { + c = str.charCodeAt(m_pos); + if ((c & 0xfc00) === 0xd800 && (m_pos+1 < str_len)) { + c2 = str.charCodeAt(m_pos+1); + if ((c2 & 0xfc00) === 0xdc00) { + c = 0x10000 + ((c - 0xd800) << 10) + (c2 - 0xdc00); + m_pos++; + } + } + if (c < 0x80) { + /* one byte */ + buf[i++] = c; + } else if (c < 0x800) { + /* two bytes */ + buf[i++] = 0xC0 | (c >>> 6); + buf[i++] = 0x80 | (c & 0x3f); + } else if (c < 0x10000) { + /* three bytes */ + buf[i++] = 0xE0 | (c >>> 12); + buf[i++] = 0x80 | (c >>> 6 & 0x3f); + buf[i++] = 0x80 | (c & 0x3f); + } else { + /* four bytes */ + buf[i++] = 0xf0 | (c >>> 18); + buf[i++] = 0x80 | (c >>> 12 & 0x3f); + buf[i++] = 0x80 | (c >>> 6 & 0x3f); + buf[i++] = 0x80 | (c & 0x3f); + } + } + + return buf; +}; + +// Calculate max possible position in utf8 buffer, +// that will not break sequence. If that's not possible +// - (very small limits) return max size as is. +// +// buf[] - utf8 bytes array +// max - length limit (mandatory); +var utf8border = function(buf, max) { + var pos; + + max = max || buf.length; + if (max > buf.length) { max = buf.length; } + + // go back from last position, until start of sequence found + pos = max-1; + while (pos >= 0 && (buf[pos] & 0xC0) === 0x80) { pos--; } + + // Fuckup - very small and broken sequence, + // return max, because we should return something anyway. + if (pos < 0) { return max; } + + // If we came to start of buffer - that means vuffer is too small, + // return max too. + if (pos === 0) { return max; } + + return (pos + _utf8len[buf[pos]] > max) ? pos : max; +}; + +// convert array to string +var buf2string = function (buf) { + var str, i, out, c, c_len; + var len = buf.length; + + // Reserve max possible length (2 words per char) + // NB: by unknown reasons, Array is significantly faster for + // String.fromCharCode.apply than Uint16Array. + var utf16buf = new Array(len*2); + + for (out=0, i=0; i 4) { utf16buf[out++] = 0xfffd; i += c_len-1; continue; } + + // apply mask on first byte + c &= c_len === 2 ? 0x1f : c_len === 3 ? 0x0f : 0x07; + // join the rest + while (c_len > 1 && i < len) { + c = (c << 6) | (buf[i++] & 0x3f); + c_len--; + } + + // terminated by end of string? + if (c_len > 1) { utf16buf[out++] = 0xfffd; continue; } + + if (c < 0x10000) { + utf16buf[out++] = c; + } else { + c -= 0x10000; + utf16buf[out++] = 0xd800 | ((c >> 10) & 0x3ff); + utf16buf[out++] = 0xdc00 | (c & 0x3ff); + } + } + + // shrinkBuf(utf16buf, out) + if (utf16buf.length !== out) { + if(utf16buf.subarray) { + utf16buf = utf16buf.subarray(0, out); + } else { + utf16buf.length = out; + } + } + + // return String.fromCharCode.apply(null, utf16buf); + return utils.applyFromCharCode(utf16buf); +}; + + +// That's all for the pako functions. + + +/** + * Transform a javascript string into an array (typed if possible) of bytes, + * UTF-8 encoded. + * @param {String} str the string to encode + * @return {Array|Uint8Array|Buffer} the UTF-8 encoded string. + */ +exports.utf8encode = function utf8encode(str) { + if (support.nodebuffer) { + return nodejsUtils.newBufferFrom(str, "utf-8"); + } + + return string2buf(str); +}; + + +/** + * Transform a bytes array (or a representation) representing an UTF-8 encoded + * string into a javascript string. + * @param {Array|Uint8Array|Buffer} buf the data de decode + * @return {String} the decoded string. + */ +exports.utf8decode = function utf8decode(buf) { + if (support.nodebuffer) { + return utils.transformTo("nodebuffer", buf).toString("utf-8"); + } + + buf = utils.transformTo(support.uint8array ? "uint8array" : "array", buf); + + return buf2string(buf); +}; + +/** + * A worker to decode utf8 encoded binary chunks into string chunks. + * @constructor + */ +function Utf8DecodeWorker() { + GenericWorker.call(this, "utf-8 decode"); + // the last bytes if a chunk didn't end with a complete codepoint. + this.leftOver = null; +} +utils.inherits(Utf8DecodeWorker, GenericWorker); + +/** + * @see GenericWorker.processChunk + */ +Utf8DecodeWorker.prototype.processChunk = function (chunk) { + + var data = utils.transformTo(support.uint8array ? "uint8array" : "array", chunk.data); + + // 1st step, re-use what's left of the previous chunk + if (this.leftOver && this.leftOver.length) { + if(support.uint8array) { + var previousData = data; + data = new Uint8Array(previousData.length + this.leftOver.length); + data.set(this.leftOver, 0); + data.set(previousData, this.leftOver.length); + } else { + data = this.leftOver.concat(data); + } + this.leftOver = null; + } + + var nextBoundary = utf8border(data); + var usableData = data; + if (nextBoundary !== data.length) { + if (support.uint8array) { + usableData = data.subarray(0, nextBoundary); + this.leftOver = data.subarray(nextBoundary, data.length); + } else { + usableData = data.slice(0, nextBoundary); + this.leftOver = data.slice(nextBoundary, data.length); + } + } + + this.push({ + data : exports.utf8decode(usableData), + meta : chunk.meta + }); +}; + +/** + * @see GenericWorker.flush + */ +Utf8DecodeWorker.prototype.flush = function () { + if(this.leftOver && this.leftOver.length) { + this.push({ + data : exports.utf8decode(this.leftOver), + meta : {} + }); + this.leftOver = null; + } +}; +exports.Utf8DecodeWorker = Utf8DecodeWorker; + +/** + * A worker to endcode string chunks into utf8 encoded binary chunks. + * @constructor + */ +function Utf8EncodeWorker() { + GenericWorker.call(this, "utf-8 encode"); +} +utils.inherits(Utf8EncodeWorker, GenericWorker); + +/** + * @see GenericWorker.processChunk + */ +Utf8EncodeWorker.prototype.processChunk = function (chunk) { + this.push({ + data : exports.utf8encode(chunk.data), + meta : chunk.meta + }); +}; +exports.Utf8EncodeWorker = Utf8EncodeWorker; + +},{"./nodejsUtils":14,"./stream/GenericWorker":28,"./support":30,"./utils":32}],32:[function(require,module,exports){ +'use strict'; + +var support = require('./support'); +var base64 = require('./base64'); +var nodejsUtils = require('./nodejsUtils'); +var setImmediate = require('set-immediate-shim'); +var external = require("./external"); + + +/** + * Convert a string that pass as a "binary string": it should represent a byte + * array but may have > 255 char codes. Be sure to take only the first byte + * and returns the byte array. + * @param {String} str the string to transform. + * @return {Array|Uint8Array} the string in a binary format. + */ +function string2binary(str) { + var result = null; + if (support.uint8array) { + result = new Uint8Array(str.length); + } else { + result = new Array(str.length); + } + return stringToArrayLike(str, result); +} + +/** + * Create a new blob with the given content and the given type. + * @param {String|ArrayBuffer} part the content to put in the blob. DO NOT use + * an Uint8Array because the stock browser of android 4 won't accept it (it + * will be silently converted to a string, "[object Uint8Array]"). + * + * Use only ONE part to build the blob to avoid a memory leak in IE11 / Edge: + * when a large amount of Array is used to create the Blob, the amount of + * memory consumed is nearly 100 times the original data amount. + * + * @param {String} type the mime type of the blob. + * @return {Blob} the created blob. + */ +exports.newBlob = function(part, type) { + exports.checkSupport("blob"); + + try { + // Blob constructor + return new Blob([part], { + type: type + }); + } + catch (e) { + + try { + // deprecated, browser only, old way + var Builder = self.BlobBuilder || self.WebKitBlobBuilder || self.MozBlobBuilder || self.MSBlobBuilder; + var builder = new Builder(); + builder.append(part); + return builder.getBlob(type); + } + catch (e) { + + // well, fuck ?! + throw new Error("Bug : can't construct the Blob."); + } + } + + +}; +/** + * The identity function. + * @param {Object} input the input. + * @return {Object} the same input. + */ +function identity(input) { + return input; +} + +/** + * Fill in an array with a string. + * @param {String} str the string to use. + * @param {Array|ArrayBuffer|Uint8Array|Buffer} array the array to fill in (will be mutated). + * @return {Array|ArrayBuffer|Uint8Array|Buffer} the updated array. + */ +function stringToArrayLike(str, array) { + for (var i = 0; i < str.length; ++i) { + array[i] = str.charCodeAt(i) & 0xFF; + } + return array; +} + +/** + * An helper for the function arrayLikeToString. + * This contains static informations and functions that + * can be optimized by the browser JIT compiler. + */ +var arrayToStringHelper = { + /** + * Transform an array of int into a string, chunk by chunk. + * See the performances notes on arrayLikeToString. + * @param {Array|ArrayBuffer|Uint8Array|Buffer} array the array to transform. + * @param {String} type the type of the array. + * @param {Integer} chunk the chunk size. + * @return {String} the resulting string. + * @throws Error if the chunk is too big for the stack. + */ + stringifyByChunk: function(array, type, chunk) { + var result = [], k = 0, len = array.length; + // shortcut + if (len <= chunk) { + return String.fromCharCode.apply(null, array); + } + while (k < len) { + if (type === "array" || type === "nodebuffer") { + result.push(String.fromCharCode.apply(null, array.slice(k, Math.min(k + chunk, len)))); + } + else { + result.push(String.fromCharCode.apply(null, array.subarray(k, Math.min(k + chunk, len)))); + } + k += chunk; + } + return result.join(""); + }, + /** + * Call String.fromCharCode on every item in the array. + * This is the naive implementation, which generate A LOT of intermediate string. + * This should be used when everything else fail. + * @param {Array|ArrayBuffer|Uint8Array|Buffer} array the array to transform. + * @return {String} the result. + */ + stringifyByChar: function(array){ + var resultStr = ""; + for(var i = 0; i < array.length; i++) { + resultStr += String.fromCharCode(array[i]); + } + return resultStr; + }, + applyCanBeUsed : { + /** + * true if the browser accepts to use String.fromCharCode on Uint8Array + */ + uint8array : (function () { + try { + return support.uint8array && String.fromCharCode.apply(null, new Uint8Array(1)).length === 1; + } catch (e) { + return false; + } + })(), + /** + * true if the browser accepts to use String.fromCharCode on nodejs Buffer. + */ + nodebuffer : (function () { + try { + return support.nodebuffer && String.fromCharCode.apply(null, nodejsUtils.allocBuffer(1)).length === 1; + } catch (e) { + return false; + } + })() + } +}; + +/** + * Transform an array-like object to a string. + * @param {Array|ArrayBuffer|Uint8Array|Buffer} array the array to transform. + * @return {String} the result. + */ +function arrayLikeToString(array) { + // Performances notes : + // -------------------- + // String.fromCharCode.apply(null, array) is the fastest, see + // see http://jsperf.com/converting-a-uint8array-to-a-string/2 + // but the stack is limited (and we can get huge arrays !). + // + // result += String.fromCharCode(array[i]); generate too many strings ! + // + // This code is inspired by http://jsperf.com/arraybuffer-to-string-apply-performance/2 + // TODO : we now have workers that split the work. Do we still need that ? + var chunk = 65536, + type = exports.getTypeOf(array), + canUseApply = true; + if (type === "uint8array") { + canUseApply = arrayToStringHelper.applyCanBeUsed.uint8array; + } else if (type === "nodebuffer") { + canUseApply = arrayToStringHelper.applyCanBeUsed.nodebuffer; + } + + if (canUseApply) { + while (chunk > 1) { + try { + return arrayToStringHelper.stringifyByChunk(array, type, chunk); + } catch (e) { + chunk = Math.floor(chunk / 2); + } + } + } + + // no apply or chunk error : slow and painful algorithm + // default browser on android 4.* + return arrayToStringHelper.stringifyByChar(array); +} + +exports.applyFromCharCode = arrayLikeToString; + + +/** + * Copy the data from an array-like to an other array-like. + * @param {Array|ArrayBuffer|Uint8Array|Buffer} arrayFrom the origin array. + * @param {Array|ArrayBuffer|Uint8Array|Buffer} arrayTo the destination array which will be mutated. + * @return {Array|ArrayBuffer|Uint8Array|Buffer} the updated destination array. + */ +function arrayLikeToArrayLike(arrayFrom, arrayTo) { + for (var i = 0; i < arrayFrom.length; i++) { + arrayTo[i] = arrayFrom[i]; + } + return arrayTo; +} + +// a matrix containing functions to transform everything into everything. +var transform = {}; + +// string to ? +transform["string"] = { + "string": identity, + "array": function(input) { + return stringToArrayLike(input, new Array(input.length)); + }, + "arraybuffer": function(input) { + return transform["string"]["uint8array"](input).buffer; + }, + "uint8array": function(input) { + return stringToArrayLike(input, new Uint8Array(input.length)); + }, + "nodebuffer": function(input) { + return stringToArrayLike(input, nodejsUtils.allocBuffer(input.length)); + } +}; + +// array to ? +transform["array"] = { + "string": arrayLikeToString, + "array": identity, + "arraybuffer": function(input) { + return (new Uint8Array(input)).buffer; + }, + "uint8array": function(input) { + return new Uint8Array(input); + }, + "nodebuffer": function(input) { + return nodejsUtils.newBufferFrom(input); + } +}; + +// arraybuffer to ? +transform["arraybuffer"] = { + "string": function(input) { + return arrayLikeToString(new Uint8Array(input)); + }, + "array": function(input) { + return arrayLikeToArrayLike(new Uint8Array(input), new Array(input.byteLength)); + }, + "arraybuffer": identity, + "uint8array": function(input) { + return new Uint8Array(input); + }, + "nodebuffer": function(input) { + return nodejsUtils.newBufferFrom(new Uint8Array(input)); + } +}; + +// uint8array to ? +transform["uint8array"] = { + "string": arrayLikeToString, + "array": function(input) { + return arrayLikeToArrayLike(input, new Array(input.length)); + }, + "arraybuffer": function(input) { + return input.buffer; + }, + "uint8array": identity, + "nodebuffer": function(input) { + return nodejsUtils.newBufferFrom(input); + } +}; + +// nodebuffer to ? +transform["nodebuffer"] = { + "string": arrayLikeToString, + "array": function(input) { + return arrayLikeToArrayLike(input, new Array(input.length)); + }, + "arraybuffer": function(input) { + return transform["nodebuffer"]["uint8array"](input).buffer; + }, + "uint8array": function(input) { + return arrayLikeToArrayLike(input, new Uint8Array(input.length)); + }, + "nodebuffer": identity +}; + +/** + * Transform an input into any type. + * The supported output type are : string, array, uint8array, arraybuffer, nodebuffer. + * If no output type is specified, the unmodified input will be returned. + * @param {String} outputType the output type. + * @param {String|Array|ArrayBuffer|Uint8Array|Buffer} input the input to convert. + * @throws {Error} an Error if the browser doesn't support the requested output type. + */ +exports.transformTo = function(outputType, input) { + if (!input) { + // undefined, null, etc + // an empty string won't harm. + input = ""; + } + if (!outputType) { + return input; + } + exports.checkSupport(outputType); + var inputType = exports.getTypeOf(input); + var result = transform[inputType][outputType](input); + return result; +}; + +/** + * Return the type of the input. + * The type will be in a format valid for JSZip.utils.transformTo : string, array, uint8array, arraybuffer. + * @param {Object} input the input to identify. + * @return {String} the (lowercase) type of the input. + */ +exports.getTypeOf = function(input) { + if (typeof input === "string") { + return "string"; + } + if (Object.prototype.toString.call(input) === "[object Array]") { + return "array"; + } + if (support.nodebuffer && nodejsUtils.isBuffer(input)) { + return "nodebuffer"; + } + if (support.uint8array && input instanceof Uint8Array) { + return "uint8array"; + } + if (support.arraybuffer && input instanceof ArrayBuffer) { + return "arraybuffer"; + } +}; + +/** + * Throw an exception if the type is not supported. + * @param {String} type the type to check. + * @throws {Error} an Error if the browser doesn't support the requested type. + */ +exports.checkSupport = function(type) { + var supported = support[type.toLowerCase()]; + if (!supported) { + throw new Error(type + " is not supported by this platform"); + } +}; + +exports.MAX_VALUE_16BITS = 65535; +exports.MAX_VALUE_32BITS = -1; // well, "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" is parsed as -1 + +/** + * Prettify a string read as binary. + * @param {string} str the string to prettify. + * @return {string} a pretty string. + */ +exports.pretty = function(str) { + var res = '', + code, i; + for (i = 0; i < (str || "").length; i++) { + code = str.charCodeAt(i); + res += '\\x' + (code < 16 ? "0" : "") + code.toString(16).toUpperCase(); + } + return res; +}; + +/** + * Defer the call of a function. + * @param {Function} callback the function to call asynchronously. + * @param {Array} args the arguments to give to the callback. + */ +exports.delay = function(callback, args, self) { + setImmediate(function () { + callback.apply(self || null, args || []); + }); +}; + +/** + * Extends a prototype with an other, without calling a constructor with + * side effects. Inspired by nodejs' `utils.inherits` + * @param {Function} ctor the constructor to augment + * @param {Function} superCtor the parent constructor to use + */ +exports.inherits = function (ctor, superCtor) { + var Obj = function() {}; + Obj.prototype = superCtor.prototype; + ctor.prototype = new Obj(); +}; + +/** + * Merge the objects passed as parameters into a new one. + * @private + * @param {...Object} var_args All objects to merge. + * @return {Object} a new object with the data of the others. + */ +exports.extend = function() { + var result = {}, i, attr; + for (i = 0; i < arguments.length; i++) { // arguments is not enumerable in some browsers + for (attr in arguments[i]) { + if (arguments[i].hasOwnProperty(attr) && typeof result[attr] === "undefined") { + result[attr] = arguments[i][attr]; + } + } + } + return result; +}; + +/** + * Transform arbitrary content into a Promise. + * @param {String} name a name for the content being processed. + * @param {Object} inputData the content to process. + * @param {Boolean} isBinary true if the content is not an unicode string + * @param {Boolean} isOptimizedBinaryString true if the string content only has one byte per character. + * @param {Boolean} isBase64 true if the string content is encoded with base64. + * @return {Promise} a promise in a format usable by JSZip. + */ +exports.prepareContent = function(name, inputData, isBinary, isOptimizedBinaryString, isBase64) { + + // if inputData is already a promise, this flatten it. + var promise = external.Promise.resolve(inputData).then(function(data) { + + + var isBlob = support.blob && (data instanceof Blob || ['[object File]', '[object Blob]'].indexOf(Object.prototype.toString.call(data)) !== -1); + + if (isBlob && typeof FileReader !== "undefined") { + return new external.Promise(function (resolve, reject) { + var reader = new FileReader(); + + reader.onload = function(e) { + resolve(e.target.result); + }; + reader.onerror = function(e) { + reject(e.target.error); + }; + reader.readAsArrayBuffer(data); + }); + } else { + return data; + } + }); + + return promise.then(function(data) { + var dataType = exports.getTypeOf(data); + + if (!dataType) { + return external.Promise.reject( + new Error("Can't read the data of '" + name + "'. Is it " + + "in a supported JavaScript type (String, Blob, ArrayBuffer, etc) ?") + ); + } + // special case : it's way easier to work with Uint8Array than with ArrayBuffer + if (dataType === "arraybuffer") { + data = exports.transformTo("uint8array", data); + } else if (dataType === "string") { + if (isBase64) { + data = base64.decode(data); + } + else if (isBinary) { + // optimizedBinaryString === true means that the file has already been filtered with a 0xFF mask + if (isOptimizedBinaryString !== true) { + // this is a string, not in a base64 format. + // Be sure that this is a correct "binary string" + data = string2binary(data); + } + } + } + return data; + }); +}; + +},{"./base64":1,"./external":6,"./nodejsUtils":14,"./support":30,"set-immediate-shim":54}],33:[function(require,module,exports){ +'use strict'; +var readerFor = require('./reader/readerFor'); +var utils = require('./utils'); +var sig = require('./signature'); +var ZipEntry = require('./zipEntry'); +var utf8 = require('./utf8'); +var support = require('./support'); +// class ZipEntries {{{ +/** + * All the entries in the zip file. + * @constructor + * @param {Object} loadOptions Options for loading the stream. + */ +function ZipEntries(loadOptions) { + this.files = []; + this.loadOptions = loadOptions; +} +ZipEntries.prototype = { + /** + * Check that the reader is on the specified signature. + * @param {string} expectedSignature the expected signature. + * @throws {Error} if it is an other signature. + */ + checkSignature: function(expectedSignature) { + if (!this.reader.readAndCheckSignature(expectedSignature)) { + this.reader.index -= 4; + var signature = this.reader.readString(4); + throw new Error("Corrupted zip or bug: unexpected signature " + "(" + utils.pretty(signature) + ", expected " + utils.pretty(expectedSignature) + ")"); + } + }, + /** + * Check if the given signature is at the given index. + * @param {number} askedIndex the index to check. + * @param {string} expectedSignature the signature to expect. + * @return {boolean} true if the signature is here, false otherwise. + */ + isSignature: function(askedIndex, expectedSignature) { + var currentIndex = this.reader.index; + this.reader.setIndex(askedIndex); + var signature = this.reader.readString(4); + var result = signature === expectedSignature; + this.reader.setIndex(currentIndex); + return result; + }, + /** + * Read the end of the central directory. + */ + readBlockEndOfCentral: function() { + this.diskNumber = this.reader.readInt(2); + this.diskWithCentralDirStart = this.reader.readInt(2); + this.centralDirRecordsOnThisDisk = this.reader.readInt(2); + this.centralDirRecords = this.reader.readInt(2); + this.centralDirSize = this.reader.readInt(4); + this.centralDirOffset = this.reader.readInt(4); + + this.zipCommentLength = this.reader.readInt(2); + // warning : the encoding depends of the system locale + // On a linux machine with LANG=en_US.utf8, this field is utf8 encoded. + // On a windows machine, this field is encoded with the localized windows code page. + var zipComment = this.reader.readData(this.zipCommentLength); + var decodeParamType = support.uint8array ? "uint8array" : "array"; + // To get consistent behavior with the generation part, we will assume that + // this is utf8 encoded unless specified otherwise. + var decodeContent = utils.transformTo(decodeParamType, zipComment); + this.zipComment = this.loadOptions.decodeFileName(decodeContent); + }, + /** + * Read the end of the Zip 64 central directory. + * Not merged with the method readEndOfCentral : + * The end of central can coexist with its Zip64 brother, + * I don't want to read the wrong number of bytes ! + */ + readBlockZip64EndOfCentral: function() { + this.zip64EndOfCentralSize = this.reader.readInt(8); + this.reader.skip(4); + // this.versionMadeBy = this.reader.readString(2); + // this.versionNeeded = this.reader.readInt(2); + this.diskNumber = this.reader.readInt(4); + this.diskWithCentralDirStart = this.reader.readInt(4); + this.centralDirRecordsOnThisDisk = this.reader.readInt(8); + this.centralDirRecords = this.reader.readInt(8); + this.centralDirSize = this.reader.readInt(8); + this.centralDirOffset = this.reader.readInt(8); + + this.zip64ExtensibleData = {}; + var extraDataSize = this.zip64EndOfCentralSize - 44, + index = 0, + extraFieldId, + extraFieldLength, + extraFieldValue; + while (index < extraDataSize) { + extraFieldId = this.reader.readInt(2); + extraFieldLength = this.reader.readInt(4); + extraFieldValue = this.reader.readData(extraFieldLength); + this.zip64ExtensibleData[extraFieldId] = { + id: extraFieldId, + length: extraFieldLength, + value: extraFieldValue + }; + } + }, + /** + * Read the end of the Zip 64 central directory locator. + */ + readBlockZip64EndOfCentralLocator: function() { + this.diskWithZip64CentralDirStart = this.reader.readInt(4); + this.relativeOffsetEndOfZip64CentralDir = this.reader.readInt(8); + this.disksCount = this.reader.readInt(4); + if (this.disksCount > 1) { + throw new Error("Multi-volumes zip are not supported"); + } + }, + /** + * Read the local files, based on the offset read in the central part. + */ + readLocalFiles: function() { + var i, file; + for (i = 0; i < this.files.length; i++) { + file = this.files[i]; + this.reader.setIndex(file.localHeaderOffset); + this.checkSignature(sig.LOCAL_FILE_HEADER); + file.readLocalPart(this.reader); + file.handleUTF8(); + file.processAttributes(); + } + }, + /** + * Read the central directory. + */ + readCentralDir: function() { + var file; + + this.reader.setIndex(this.centralDirOffset); + while (this.reader.readAndCheckSignature(sig.CENTRAL_FILE_HEADER)) { + file = new ZipEntry({ + zip64: this.zip64 + }, this.loadOptions); + file.readCentralPart(this.reader); + this.files.push(file); + } + + if (this.centralDirRecords !== this.files.length) { + if (this.centralDirRecords !== 0 && this.files.length === 0) { + // We expected some records but couldn't find ANY. + // This is really suspicious, as if something went wrong. + throw new Error("Corrupted zip or bug: expected " + this.centralDirRecords + " records in central dir, got " + this.files.length); + } else { + // We found some records but not all. + // Something is wrong but we got something for the user: no error here. + // console.warn("expected", this.centralDirRecords, "records in central dir, got", this.files.length); + } + } + }, + /** + * Read the end of central directory. + */ + readEndOfCentral: function() { + var offset = this.reader.lastIndexOfSignature(sig.CENTRAL_DIRECTORY_END); + if (offset < 0) { + // Check if the content is a truncated zip or complete garbage. + // A "LOCAL_FILE_HEADER" is not required at the beginning (auto + // extractible zip for example) but it can give a good hint. + // If an ajax request was used without responseType, we will also + // get unreadable data. + var isGarbage = !this.isSignature(0, sig.LOCAL_FILE_HEADER); + + if (isGarbage) { + throw new Error("Can't find end of central directory : is this a zip file ? " + + "If it is, see https://stuk.github.io/jszip/documentation/howto/read_zip.html"); + } else { + throw new Error("Corrupted zip: can't find end of central directory"); + } + + } + this.reader.setIndex(offset); + var endOfCentralDirOffset = offset; + this.checkSignature(sig.CENTRAL_DIRECTORY_END); + this.readBlockEndOfCentral(); + + + /* extract from the zip spec : + 4) If one of the fields in the end of central directory + record is too small to hold required data, the field + should be set to -1 (0xFFFF or 0xFFFFFFFF) and the + ZIP64 format record should be created. + 5) The end of central directory record and the + Zip64 end of central directory locator record must + reside on the same disk when splitting or spanning + an archive. + */ + if (this.diskNumber === utils.MAX_VALUE_16BITS || this.diskWithCentralDirStart === utils.MAX_VALUE_16BITS || this.centralDirRecordsOnThisDisk === utils.MAX_VALUE_16BITS || this.centralDirRecords === utils.MAX_VALUE_16BITS || this.centralDirSize === utils.MAX_VALUE_32BITS || this.centralDirOffset === utils.MAX_VALUE_32BITS) { + this.zip64 = true; + + /* + Warning : the zip64 extension is supported, but ONLY if the 64bits integer read from + the zip file can fit into a 32bits integer. This cannot be solved : JavaScript represents + all numbers as 64-bit double precision IEEE 754 floating point numbers. + So, we have 53bits for integers and bitwise operations treat everything as 32bits. + see https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Operators/Bitwise_Operators + and http://www.ecma-international.org/publications/files/ECMA-ST/ECMA-262.pdf section 8.5 + */ + + // should look for a zip64 EOCD locator + offset = this.reader.lastIndexOfSignature(sig.ZIP64_CENTRAL_DIRECTORY_LOCATOR); + if (offset < 0) { + throw new Error("Corrupted zip: can't find the ZIP64 end of central directory locator"); + } + this.reader.setIndex(offset); + this.checkSignature(sig.ZIP64_CENTRAL_DIRECTORY_LOCATOR); + this.readBlockZip64EndOfCentralLocator(); + + // now the zip64 EOCD record + if (!this.isSignature(this.relativeOffsetEndOfZip64CentralDir, sig.ZIP64_CENTRAL_DIRECTORY_END)) { + // console.warn("ZIP64 end of central directory not where expected."); + this.relativeOffsetEndOfZip64CentralDir = this.reader.lastIndexOfSignature(sig.ZIP64_CENTRAL_DIRECTORY_END); + if (this.relativeOffsetEndOfZip64CentralDir < 0) { + throw new Error("Corrupted zip: can't find the ZIP64 end of central directory"); + } + } + this.reader.setIndex(this.relativeOffsetEndOfZip64CentralDir); + this.checkSignature(sig.ZIP64_CENTRAL_DIRECTORY_END); + this.readBlockZip64EndOfCentral(); + } + + var expectedEndOfCentralDirOffset = this.centralDirOffset + this.centralDirSize; + if (this.zip64) { + expectedEndOfCentralDirOffset += 20; // end of central dir 64 locator + expectedEndOfCentralDirOffset += 12 /* should not include the leading 12 bytes */ + this.zip64EndOfCentralSize; + } + + var extraBytes = endOfCentralDirOffset - expectedEndOfCentralDirOffset; + + if (extraBytes > 0) { + // console.warn(extraBytes, "extra bytes at beginning or within zipfile"); + if (this.isSignature(endOfCentralDirOffset, sig.CENTRAL_FILE_HEADER)) { + // The offsets seem wrong, but we have something at the specified offset. + // So… we keep it. + } else { + // the offset is wrong, update the "zero" of the reader + // this happens if data has been prepended (crx files for example) + this.reader.zero = extraBytes; + } + } else if (extraBytes < 0) { + throw new Error("Corrupted zip: missing " + Math.abs(extraBytes) + " bytes."); + } + }, + prepareReader: function(data) { + this.reader = readerFor(data); + }, + /** + * Read a zip file and create ZipEntries. + * @param {String|ArrayBuffer|Uint8Array|Buffer} data the binary string representing a zip file. + */ + load: function(data) { + this.prepareReader(data); + this.readEndOfCentral(); + this.readCentralDir(); + this.readLocalFiles(); + } +}; +// }}} end of ZipEntries +module.exports = ZipEntries; + +},{"./reader/readerFor":22,"./signature":23,"./support":30,"./utf8":31,"./utils":32,"./zipEntry":34}],34:[function(require,module,exports){ +'use strict'; +var readerFor = require('./reader/readerFor'); +var utils = require('./utils'); +var CompressedObject = require('./compressedObject'); +var crc32fn = require('./crc32'); +var utf8 = require('./utf8'); +var compressions = require('./compressions'); +var support = require('./support'); + +var MADE_BY_DOS = 0x00; +var MADE_BY_UNIX = 0x03; + +/** + * Find a compression registered in JSZip. + * @param {string} compressionMethod the method magic to find. + * @return {Object|null} the JSZip compression object, null if none found. + */ +var findCompression = function(compressionMethod) { + for (var method in compressions) { + if (!compressions.hasOwnProperty(method)) { + continue; + } + if (compressions[method].magic === compressionMethod) { + return compressions[method]; + } + } + return null; +}; + +// class ZipEntry {{{ +/** + * An entry in the zip file. + * @constructor + * @param {Object} options Options of the current file. + * @param {Object} loadOptions Options for loading the stream. + */ +function ZipEntry(options, loadOptions) { + this.options = options; + this.loadOptions = loadOptions; +} +ZipEntry.prototype = { + /** + * say if the file is encrypted. + * @return {boolean} true if the file is encrypted, false otherwise. + */ + isEncrypted: function() { + // bit 1 is set + return (this.bitFlag & 0x0001) === 0x0001; + }, + /** + * say if the file has utf-8 filename/comment. + * @return {boolean} true if the filename/comment is in utf-8, false otherwise. + */ + useUTF8: function() { + // bit 11 is set + return (this.bitFlag & 0x0800) === 0x0800; + }, + /** + * Read the local part of a zip file and add the info in this object. + * @param {DataReader} reader the reader to use. + */ + readLocalPart: function(reader) { + var compression, localExtraFieldsLength; + + // we already know everything from the central dir ! + // If the central dir data are false, we are doomed. + // On the bright side, the local part is scary : zip64, data descriptors, both, etc. + // The less data we get here, the more reliable this should be. + // Let's skip the whole header and dash to the data ! + reader.skip(22); + // in some zip created on windows, the filename stored in the central dir contains \ instead of /. + // Strangely, the filename here is OK. + // I would love to treat these zip files as corrupted (see http://www.info-zip.org/FAQ.html#backslashes + // or APPNOTE#4.4.17.1, "All slashes MUST be forward slashes '/'") but there are a lot of bad zip generators... + // Search "unzip mismatching "local" filename continuing with "central" filename version" on + // the internet. + // + // I think I see the logic here : the central directory is used to display + // content and the local directory is used to extract the files. Mixing / and \ + // may be used to display \ to windows users and use / when extracting the files. + // Unfortunately, this lead also to some issues : http://seclists.org/fulldisclosure/2009/Sep/394 + this.fileNameLength = reader.readInt(2); + localExtraFieldsLength = reader.readInt(2); // can't be sure this will be the same as the central dir + // the fileName is stored as binary data, the handleUTF8 method will take care of the encoding. + this.fileName = reader.readData(this.fileNameLength); + reader.skip(localExtraFieldsLength); + + if (this.compressedSize === -1 || this.uncompressedSize === -1) { + throw new Error("Bug or corrupted zip : didn't get enough informations from the central directory " + "(compressedSize === -1 || uncompressedSize === -1)"); + } + + compression = findCompression(this.compressionMethod); + if (compression === null) { // no compression found + throw new Error("Corrupted zip : compression " + utils.pretty(this.compressionMethod) + " unknown (inner file : " + utils.transformTo("string", this.fileName) + ")"); + } + this.decompressed = new CompressedObject(this.compressedSize, this.uncompressedSize, this.crc32, compression, reader.readData(this.compressedSize)); + }, + + /** + * Read the central part of a zip file and add the info in this object. + * @param {DataReader} reader the reader to use. + */ + readCentralPart: function(reader) { + this.versionMadeBy = reader.readInt(2); + reader.skip(2); + // this.versionNeeded = reader.readInt(2); + this.bitFlag = reader.readInt(2); + this.compressionMethod = reader.readString(2); + this.date = reader.readDate(); + this.crc32 = reader.readInt(4); + this.compressedSize = reader.readInt(4); + this.uncompressedSize = reader.readInt(4); + var fileNameLength = reader.readInt(2); + this.extraFieldsLength = reader.readInt(2); + this.fileCommentLength = reader.readInt(2); + this.diskNumberStart = reader.readInt(2); + this.internalFileAttributes = reader.readInt(2); + this.externalFileAttributes = reader.readInt(4); + this.localHeaderOffset = reader.readInt(4); + + if (this.isEncrypted()) { + throw new Error("Encrypted zip are not supported"); + } + + // will be read in the local part, see the comments there + reader.skip(fileNameLength); + this.readExtraFields(reader); + this.parseZIP64ExtraField(reader); + this.fileComment = reader.readData(this.fileCommentLength); + }, + + /** + * Parse the external file attributes and get the unix/dos permissions. + */ + processAttributes: function () { + this.unixPermissions = null; + this.dosPermissions = null; + var madeBy = this.versionMadeBy >> 8; + + // Check if we have the DOS directory flag set. + // We look for it in the DOS and UNIX permissions + // but some unknown platform could set it as a compatibility flag. + this.dir = this.externalFileAttributes & 0x0010 ? true : false; + + if(madeBy === MADE_BY_DOS) { + // first 6 bits (0 to 5) + this.dosPermissions = this.externalFileAttributes & 0x3F; + } + + if(madeBy === MADE_BY_UNIX) { + this.unixPermissions = (this.externalFileAttributes >> 16) & 0xFFFF; + // the octal permissions are in (this.unixPermissions & 0x01FF).toString(8); + } + + // fail safe : if the name ends with a / it probably means a folder + if (!this.dir && this.fileNameStr.slice(-1) === '/') { + this.dir = true; + } + }, + + /** + * Parse the ZIP64 extra field and merge the info in the current ZipEntry. + * @param {DataReader} reader the reader to use. + */ + parseZIP64ExtraField: function(reader) { + + if (!this.extraFields[0x0001]) { + return; + } + + // should be something, preparing the extra reader + var extraReader = readerFor(this.extraFields[0x0001].value); + + // I really hope that these 64bits integer can fit in 32 bits integer, because js + // won't let us have more. + if (this.uncompressedSize === utils.MAX_VALUE_32BITS) { + this.uncompressedSize = extraReader.readInt(8); + } + if (this.compressedSize === utils.MAX_VALUE_32BITS) { + this.compressedSize = extraReader.readInt(8); + } + if (this.localHeaderOffset === utils.MAX_VALUE_32BITS) { + this.localHeaderOffset = extraReader.readInt(8); + } + if (this.diskNumberStart === utils.MAX_VALUE_32BITS) { + this.diskNumberStart = extraReader.readInt(4); + } + }, + /** + * Read the central part of a zip file and add the info in this object. + * @param {DataReader} reader the reader to use. + */ + readExtraFields: function(reader) { + var end = reader.index + this.extraFieldsLength, + extraFieldId, + extraFieldLength, + extraFieldValue; + + if (!this.extraFields) { + this.extraFields = {}; + } + + while (reader.index < end) { + extraFieldId = reader.readInt(2); + extraFieldLength = reader.readInt(2); + extraFieldValue = reader.readData(extraFieldLength); + + this.extraFields[extraFieldId] = { + id: extraFieldId, + length: extraFieldLength, + value: extraFieldValue + }; + } + }, + /** + * Apply an UTF8 transformation if needed. + */ + handleUTF8: function() { + var decodeParamType = support.uint8array ? "uint8array" : "array"; + if (this.useUTF8()) { + this.fileNameStr = utf8.utf8decode(this.fileName); + this.fileCommentStr = utf8.utf8decode(this.fileComment); + } else { + var upath = this.findExtraFieldUnicodePath(); + if (upath !== null) { + this.fileNameStr = upath; + } else { + // ASCII text or unsupported code page + var fileNameByteArray = utils.transformTo(decodeParamType, this.fileName); + this.fileNameStr = this.loadOptions.decodeFileName(fileNameByteArray); + } + + var ucomment = this.findExtraFieldUnicodeComment(); + if (ucomment !== null) { + this.fileCommentStr = ucomment; + } else { + // ASCII text or unsupported code page + var commentByteArray = utils.transformTo(decodeParamType, this.fileComment); + this.fileCommentStr = this.loadOptions.decodeFileName(commentByteArray); + } + } + }, + + /** + * Find the unicode path declared in the extra field, if any. + * @return {String} the unicode path, null otherwise. + */ + findExtraFieldUnicodePath: function() { + var upathField = this.extraFields[0x7075]; + if (upathField) { + var extraReader = readerFor(upathField.value); + + // wrong version + if (extraReader.readInt(1) !== 1) { + return null; + } + + // the crc of the filename changed, this field is out of date. + if (crc32fn(this.fileName) !== extraReader.readInt(4)) { + return null; + } + + return utf8.utf8decode(extraReader.readData(upathField.length - 5)); + } + return null; + }, + + /** + * Find the unicode comment declared in the extra field, if any. + * @return {String} the unicode comment, null otherwise. + */ + findExtraFieldUnicodeComment: function() { + var ucommentField = this.extraFields[0x6375]; + if (ucommentField) { + var extraReader = readerFor(ucommentField.value); + + // wrong version + if (extraReader.readInt(1) !== 1) { + return null; + } + + // the crc of the comment changed, this field is out of date. + if (crc32fn(this.fileComment) !== extraReader.readInt(4)) { + return null; + } + + return utf8.utf8decode(extraReader.readData(ucommentField.length - 5)); + } + return null; + } +}; +module.exports = ZipEntry; + +},{"./compressedObject":2,"./compressions":3,"./crc32":4,"./reader/readerFor":22,"./support":30,"./utf8":31,"./utils":32}],35:[function(require,module,exports){ +'use strict'; + +var StreamHelper = require('./stream/StreamHelper'); +var DataWorker = require('./stream/DataWorker'); +var utf8 = require('./utf8'); +var CompressedObject = require('./compressedObject'); +var GenericWorker = require('./stream/GenericWorker'); + +/** + * A simple object representing a file in the zip file. + * @constructor + * @param {string} name the name of the file + * @param {String|ArrayBuffer|Uint8Array|Buffer} data the data + * @param {Object} options the options of the file + */ +var ZipObject = function(name, data, options) { + this.name = name; + this.dir = options.dir; + this.date = options.date; + this.comment = options.comment; + this.unixPermissions = options.unixPermissions; + this.dosPermissions = options.dosPermissions; + + this._data = data; + this._dataBinary = options.binary; + // keep only the compression + this.options = { + compression : options.compression, + compressionOptions : options.compressionOptions + }; +}; + +ZipObject.prototype = { + /** + * Create an internal stream for the content of this object. + * @param {String} type the type of each chunk. + * @return StreamHelper the stream. + */ + internalStream: function (type) { + var result = null, outputType = "string"; + try { + if (!type) { + throw new Error("No output type specified."); + } + outputType = type.toLowerCase(); + var askUnicodeString = outputType === "string" || outputType === "text"; + if (outputType === "binarystring" || outputType === "text") { + outputType = "string"; + } + result = this._decompressWorker(); + + var isUnicodeString = !this._dataBinary; + + if (isUnicodeString && !askUnicodeString) { + result = result.pipe(new utf8.Utf8EncodeWorker()); + } + if (!isUnicodeString && askUnicodeString) { + result = result.pipe(new utf8.Utf8DecodeWorker()); + } + } catch (e) { + result = new GenericWorker("error"); + result.error(e); + } + + return new StreamHelper(result, outputType, ""); + }, + + /** + * Prepare the content in the asked type. + * @param {String} type the type of the result. + * @param {Function} onUpdate a function to call on each internal update. + * @return Promise the promise of the result. + */ + async: function (type, onUpdate) { + return this.internalStream(type).accumulate(onUpdate); + }, + + /** + * Prepare the content as a nodejs stream. + * @param {String} type the type of each chunk. + * @param {Function} onUpdate a function to call on each internal update. + * @return Stream the stream. + */ + nodeStream: function (type, onUpdate) { + return this.internalStream(type || "nodebuffer").toNodejsStream(onUpdate); + }, + + /** + * Return a worker for the compressed content. + * @private + * @param {Object} compression the compression object to use. + * @param {Object} compressionOptions the options to use when compressing. + * @return Worker the worker. + */ + _compressWorker: function (compression, compressionOptions) { + if ( + this._data instanceof CompressedObject && + this._data.compression.magic === compression.magic + ) { + return this._data.getCompressedWorker(); + } else { + var result = this._decompressWorker(); + if(!this._dataBinary) { + result = result.pipe(new utf8.Utf8EncodeWorker()); + } + return CompressedObject.createWorkerFrom(result, compression, compressionOptions); + } + }, + /** + * Return a worker for the decompressed content. + * @private + * @return Worker the worker. + */ + _decompressWorker : function () { + if (this._data instanceof CompressedObject) { + return this._data.getContentWorker(); + } else if (this._data instanceof GenericWorker) { + return this._data; + } else { + return new DataWorker(this._data); + } + } +}; + +var removedMethods = ["asText", "asBinary", "asNodeBuffer", "asUint8Array", "asArrayBuffer"]; +var removedFn = function () { + throw new Error("This method has been removed in JSZip 3.0, please check the upgrade guide."); +}; + +for(var i = 0; i < removedMethods.length; i++) { + ZipObject.prototype[removedMethods[i]] = removedFn; +} +module.exports = ZipObject; + +},{"./compressedObject":2,"./stream/DataWorker":27,"./stream/GenericWorker":28,"./stream/StreamHelper":29,"./utf8":31}],36:[function(require,module,exports){ +(function (global){ +'use strict'; +var Mutation = global.MutationObserver || global.WebKitMutationObserver; + +var scheduleDrain; + +{ + if (Mutation) { + var called = 0; + var observer = new Mutation(nextTick); + var element = global.document.createTextNode(''); + observer.observe(element, { + characterData: true + }); + scheduleDrain = function () { + element.data = (called = ++called % 2); + }; + } else if (!global.setImmediate && typeof global.MessageChannel !== 'undefined') { + var channel = new global.MessageChannel(); + channel.port1.onmessage = nextTick; + scheduleDrain = function () { + channel.port2.postMessage(0); + }; + } else if ('document' in global && 'onreadystatechange' in global.document.createElement('script')) { + scheduleDrain = function () { + + // Create a