@ -0,0 +1,13 @@ | |||
# ---> Rust | |||
# Generated by Cargo | |||
# will have compiled files and executables | |||
/target/ | |||
# Remove Cargo.lock from gitignore if creating an executable, leave it for libraries | |||
# More information here https://doc.rust-lang.org/cargo/guide/cargo-toml-vs-cargo-lock.html | |||
Cargo.lock | |||
# These are backup files generated by rustfmt | |||
**/*.rs.bk | |||
/config.toml |
@ -0,0 +1,31 @@ | |||
[package] | |||
name = "pluriton" | |||
version = "1.0.0" | |||
authors = ["CorsaroNero"] | |||
edition = "2018" | |||
[features] | |||
default = [ "diesel/mysql" ] | |||
[dependencies] | |||
actix-rt = "2.2.0" | |||
actix-web = "3.3.2" | |||
actix-files = "0.5.0" | |||
actix-session = "0.4" | |||
diesel = { version = "1.4", features = ["r2d2", "chrono"] } | |||
diesel_migrations = "1.4" | |||
url = "2.2" | |||
toml = "0.5" | |||
lazy_static = "1.4" | |||
serde = "1.0" | |||
serde_json = "1.0" | |||
serde_derive = "1.0" | |||
chrono = { version = "0.4", features = ["serde"] } | |||
regex = "1.3" | |||
base64 = "0.13" | |||
rand = "0.8" | |||
askama = "0.10" | |||
json = "0.12" | |||
futures = "0.3" | |||
percent-encoding = "2.1" | |||
hyper = "0.9" |
@ -0,0 +1,312 @@ | |||
Mozilla Public License Version 2.0 | |||
1. Definitions | |||
1.1. "Contributor" means each individual or legal entity that creates, contributes | |||
to the creation of, or owns Covered Software. | |||
1.2. "Contributor Version" means the combination of the Contributions of others | |||
(if any) used by a Contributor and that particular Contributor's Contribution. | |||
1.3. "Contribution" means Covered Software of a particular Contributor. | |||
1.4. "Covered Software" means Source Code Form to which the initial Contributor | |||
has attached the notice in Exhibit A, the Executable Form of such Source Code | |||
Form, and Modifications of such Source Code Form, in each case including portions | |||
thereof. | |||
1.5. "Incompatible With Secondary Licenses" means | |||
(a) that the initial Contributor has attached the notice described in Exhibit | |||
B to the Covered Software; or | |||
(b) that the Covered Software was made available under the terms of version | |||
1.1 or earlier of the License, but not also under the terms of a Secondary | |||
License. | |||
1.6. "Executable Form" means any form of the work other than Source Code Form. | |||
1.7. "Larger Work" means a work that combines Covered Software with other | |||
material, in a separate file or files, that is not Covered Software. | |||
1.8. "License" means this document. | |||
1.9. "Licensable" means having the right to grant, to the maximum extent possible, | |||
whether at the time of the initial grant or subsequently, any and all of the | |||
rights conveyed by this License. | |||
1.10. "Modifications" means any of the following: | |||
(a) any file in Source Code Form that results from an addition to, deletion | |||
from, or modification of the contents of Covered Software; or | |||
(b) any new file in Source Code Form that contains any Covered Software. | |||
1.11. "Patent Claims" of a Contributor means any patent claim(s), including | |||
without limitation, method, process, and apparatus claims, in any patent Licensable | |||
by such Contributor that would be infringed, but for the grant of the License, | |||
by the making, using, selling, offering for sale, having made, import, or | |||
transfer of either its Contributions or its Contributor Version. | |||
1.12. "Secondary License" means either the GNU General Public License, Version | |||
2.0, the GNU Lesser General Public License, Version 2.1, the GNU Affero General | |||
Public License, Version 3.0, or any later versions of those licenses. | |||
1.13. "Source Code Form" means the form of the work preferred for making modifications. | |||
1.14. "You" (or "Your") means an individual or a legal entity exercising rights | |||
under this License. For legal entities, "You" includes any entity that controls, | |||
is controlled by, or is under common control with You. For purposes of this | |||
definition, "control" means (a) the power, direct or indirect, to cause the | |||
direction or management of such entity, whether by contract or otherwise, | |||
or (b) ownership of more than fifty percent (50%) of the outstanding shares | |||
or beneficial ownership of such entity. | |||
2. License Grants and Conditions | |||
2.1. Grants | |||
Each Contributor hereby grants You a world-wide, royalty-free, non-exclusive | |||
license: | |||
(a) under intellectual property rights (other than patent or trademark) Licensable | |||
by such Contributor to use, reproduce, make available, modify, display, perform, | |||
distribute, and otherwise exploit its Contributions, either on an unmodified | |||
basis, with Modifications, or as part of a Larger Work; and | |||
(b) under Patent Claims of such Contributor to make, use, sell, offer for | |||
sale, have made, import, and otherwise transfer either its Contributions or | |||
its Contributor Version. | |||
2.2. Effective Date | |||
The licenses granted in Section 2.1 with respect to any Contribution become | |||
effective for each Contribution on the date the Contributor first distributes | |||
such Contribution. | |||
2.3. Limitations on Grant Scope | |||
The licenses granted in this Section 2 are the only rights granted under this | |||
License. No additional rights or licenses will be implied from the distribution | |||
or licensing of Covered Software under this License. Notwithstanding Section | |||
2.1(b) above, no patent license is granted by a Contributor: | |||
(a) for any code that a Contributor has removed from Covered Software; or | |||
(b) for infringements caused by: (i) Your and any other third party's modifications | |||
of Covered Software, or (ii) the combination of its Contributions with other | |||
software (except as part of its Contributor Version); or | |||
(c) under Patent Claims infringed by Covered Software in the absence of its | |||
Contributions. | |||
This License does not grant any rights in the trademarks, service marks, or | |||
logos of any Contributor (except as may be necessary to comply with the notice | |||
requirements in Section 3.4). | |||
2.4. Subsequent Licenses | |||
No Contributor makes additional grants as a result of Your choice to distribute | |||
the Covered Software under a subsequent version of this License (see Section | |||
10.2) or under the terms of a Secondary License (if permitted under the terms | |||
of Section 3.3). | |||
2.5. Representation | |||
Each Contributor represents that the Contributor believes its Contributions | |||
are its original creation(s) or it has sufficient rights to grant the rights | |||
to its Contributions conveyed by this License. | |||
2.6. Fair Use | |||
This License is not intended to limit any rights You have under applicable | |||
copyright doctrines of fair use, fair dealing, or other equivalents. | |||
2.7. Conditions | |||
Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted in | |||
Section 2.1. | |||
3. Responsibilities | |||
3.1. Distribution of Source Form | |||
All distribution of Covered Software in Source Code Form, including any Modifications | |||
that You create or to which You contribute, must be under the terms of this | |||
License. You must inform recipients that the Source Code Form of the Covered | |||
Software is governed by the terms of this License, and how they can obtain | |||
a copy of this License. You may not attempt to alter or restrict the recipients' | |||
rights in the Source Code Form. | |||
3.2. Distribution of Executable Form | |||
If You distribute Covered Software in Executable Form then: | |||
(a) such Covered Software must also be made available in Source Code Form, | |||
as described in Section 3.1, and You must inform recipients of the Executable | |||
Form how they can obtain a copy of such Source Code Form by reasonable means | |||
in a timely manner, at a charge no more than the cost of distribution to the | |||
recipient; and | |||
(b) You may distribute such Executable Form under the terms of this License, | |||
or sublicense it under different terms, provided that the license for the | |||
Executable Form does not attempt to limit or alter the recipients' rights | |||
in the Source Code Form under this License. | |||
3.3. Distribution of a Larger Work | |||
You may create and distribute a Larger Work under terms of Your choice, provided | |||
that You also comply with the requirements of this License for the Covered | |||
Software. If the Larger Work is a combination of Covered Software with a work | |||
governed by one or more Secondary Licenses, and the Covered Software is not | |||
Incompatible With Secondary Licenses, this License permits You to additionally | |||
distribute such Covered Software under the terms of such Secondary License(s), | |||
so that the recipient of the Larger Work may, at their option, further distribute | |||
the Covered Software under the terms of either this License or such Secondary | |||
License(s). | |||
3.4. Notices | |||
You may not remove or alter the substance of any license notices (including | |||
copyright notices, patent notices, disclaimers of warranty, or limitations | |||
of liability) contained within the Source Code Form of the Covered Software, | |||
except that You may alter any license notices to the extent required to remedy | |||
known factual inaccuracies. | |||
3.5. Application of Additional Terms | |||
You may choose to offer, and to charge a fee for, warranty, support, indemnity | |||
or liability obligations to one or more recipients of Covered Software. However, | |||
You may do so only on Your own behalf, and not on behalf of any Contributor. | |||
You must make it absolutely clear that any such warranty, support, indemnity, | |||
or liability obligation is offered by You alone, and You hereby agree to indemnify | |||
every Contributor for any liability incurred by such Contributor as a result | |||
of warranty, support, indemnity or liability terms You offer. You may include | |||
additional disclaimers of warranty and limitations of liability specific to | |||
any jurisdiction. | |||
4. Inability to Comply Due to Statute or Regulation | |||
If it is impossible for You to comply with any of the terms of this License | |||
with respect to some or all of the Covered Software due to statute, judicial | |||
order, or regulation then You must: (a) comply with the terms of this License | |||
to the maximum extent possible; and (b) describe the limitations and the code | |||
they affect. Such description must be placed in a text file included with | |||
all distributions of the Covered Software under this License. Except to the | |||
extent prohibited by statute or regulation, such description must be sufficiently | |||
detailed for a recipient of ordinary skill to be able to understand it. | |||
5. Termination | |||
5.1. The rights granted under this License will terminate automatically if | |||
You fail to comply with any of its terms. However, if You become compliant, | |||
then the rights granted under this License from a particular Contributor are | |||
reinstated (a) provisionally, unless and until such Contributor explicitly | |||
and finally terminates Your grants, and (b) on an ongoing basis, if such Contributor | |||
fails to notify You of the non-compliance by some reasonable means prior to | |||
60 days after You have come back into compliance. Moreover, Your grants from | |||
a particular Contributor are reinstated on an ongoing basis if such Contributor | |||
notifies You of the non-compliance by some reasonable means, this is the first | |||
time You have received notice of non-compliance with this License from such | |||
Contributor, and You become compliant prior to 30 days after Your receipt | |||
of the notice. | |||
5.2. If You initiate litigation against any entity by asserting a patent infringement | |||
claim (excluding declaratory judgment actions, counter-claims, and cross-claims) | |||
alleging that a Contributor Version directly or indirectly infringes any patent, | |||
then the rights granted to You by any and all Contributors for the Covered | |||
Software under Section 2.1 of this License shall terminate. | |||
5.3. In the event of termination under Sections 5.1 or 5.2 above, all end | |||
user license agreements (excluding distributors and resellers) which have | |||
been validly granted by You or Your distributors under this License prior | |||
to termination shall survive termination. | |||
6. Disclaimer of Warranty | |||
Covered Software is provided under this License on an "as is" basis, without | |||
warranty of any kind, either expressed, implied, or statutory, including, | |||
without limitation, warranties that the Covered Software is free of defects, | |||
merchantable, fit for a particular purpose or non-infringing. The entire risk | |||
as to the quality and performance of the Covered Software is with You. Should | |||
any Covered Software prove defective in any respect, You (not any Contributor) | |||
assume the cost of any necessary servicing, repair, or correction. This disclaimer | |||
of warranty constitutes an essential part of this License. No use of any Covered | |||
Software is authorized under this License except under this disclaimer. | |||
7. Limitation of Liability | |||
Under no circumstances and under no legal theory, whether tort (including | |||
negligence), contract, or otherwise, shall any Contributor, or anyone who | |||
distributes Covered Software as permitted above, be liable to You for any | |||
direct, indirect, special, incidental, or consequential damages of any character | |||
including, without limitation, damages for lost profits, loss of goodwill, | |||
work stoppage, computer failure or malfunction, or any and all other commercial | |||
damages or losses, even if such party shall have been informed of the possibility | |||
of such damages. This limitation of liability shall not apply to liability | |||
for death or personal injury resulting from such party's negligence to the | |||
extent applicable law prohibits such limitation. Some jurisdictions do not | |||
allow the exclusion or limitation of incidental or consequential damages, | |||
so this exclusion and limitation may not apply to You. | |||
8. Litigation | |||
Any litigation relating to this License may be brought only in the courts | |||
of a jurisdiction where the defendant maintains its principal place of business | |||
and such litigation shall be governed by laws of that jurisdiction, without | |||
reference to its conflict-of-law provisions. Nothing in this Section shall | |||
prevent a party's ability to bring cross-claims or counter-claims. | |||
9. Miscellaneous | |||
This License represents the complete agreement concerning the subject matter | |||
hereof. If any provision of this License is held to be unenforceable, such | |||
provision shall be reformed only to the extent necessary to make it enforceable. | |||
Any law or regulation which provides that the language of a contract shall | |||
be construed against the drafter shall not be used to construe this License | |||
against a Contributor. | |||
10. Versions of the License | |||
10.1. New Versions | |||
Mozilla Foundation is the license steward. Except as provided in Section 10.3, | |||
no one other than the license steward has the right to modify or publish new | |||
versions of this License. Each version will be given a distinguishing version | |||
number. | |||
10.2. Effect of New Versions | |||
You may distribute the Covered Software under the terms of the version of | |||
the License under which You originally received the Covered Software, or under | |||
the terms of any subsequent version published by the license steward. | |||
10.3. Modified Versions | |||
If you create software not governed by this License, and you want to create | |||
a new license for such software, you may create and use a modified version | |||
of this License if you rename the license and remove any references to the | |||
name of the license steward (except to note that such modified license differs | |||
from this License). | |||
10.4. Distributing Source Code Form that is Incompatible With Secondary Licenses | |||
If You choose to distribute Source Code Form that is Incompatible With Secondary | |||
Licenses under the terms of this version of the License, the notice described | |||
in Exhibit B of this License must be attached. Exhibit A - Source Code Form | |||
License Notice | |||
This Source Code Form is subject to the terms of the Mozilla Public License, | |||
v. 2.0. If a copy of the MPL was not distributed with this file, You can obtain | |||
one at http://mozilla.org/MPL/2.0/. | |||
If it is not possible or desirable to put the notice in a particular file, | |||
then You may include the notice in a location (such as a LICENSE file in a | |||
relevant directory) where a recipient would be likely to look for such a notice. | |||
You may add additional accurate notices of copyright ownership. | |||
Exhibit B - "Incompatible With Secondary Licenses" Notice | |||
This Source Code Form is "Incompatible With Secondary Licenses", as defined | |||
by the Mozilla Public License, v. 2.0. |
@ -0,0 +1,141 @@ | |||
Adorable | |||
Adventurous | |||
Aggressive | |||
Agreeable | |||
Ambitious | |||
Amused | |||
Annoying | |||
Ashy | |||
Attractive | |||
Beautiful | |||
Better | |||
Black | |||
Blue | |||
Blushing | |||
Brave | |||
Bright | |||
Busy | |||
Calm | |||
Careful | |||
Cautious | |||
Charming | |||
Cheerful | |||
Clever | |||
Clumsy | |||
Colorful | |||
Combative | |||
Confident | |||
Cooperative | |||
Courageous | |||
Crazy | |||
Creative | |||
Creepy | |||
Cruel | |||
Curious | |||
Cute | |||
Dangerous | |||
Dark | |||
Dazzling | |||
Delightful | |||
Determined | |||
Different | |||
Distinct | |||
Dizzy | |||
Eager | |||
Elegant | |||
Embarrassed | |||
Enchanting | |||
Encouraging | |||
Energetic | |||
Enthusiastic | |||
Evil | |||
Excited | |||
Fair | |||
Faithful | |||
Famous | |||
Fancy | |||
Fantastic | |||
Fierce | |||
Fine | |||
Friendly | |||
Funny | |||
Gentle | |||
Gifted | |||
Glad | |||
Glamorous | |||
Gleaming | |||
Glorious | |||
Good | |||
Gorgeous | |||
Graceful | |||
Gray | |||
Green | |||
Grumpy | |||
Handsome | |||
Happy | |||
Hardy | |||
Healthy | |||
Helpful | |||
Hilarious | |||
Homely | |||
Hungry | |||
Icy | |||
Important | |||
Innocent | |||
Inquisitive | |||
Jolly | |||
Joyful | |||
Joyous | |||
Kind | |||
Lazy | |||
Light | |||
Lively | |||
Lovely | |||
Loving | |||
Lucky | |||
Magnificent | |||
Marvelous | |||
Misty | |||
Modern | |||
Muscular | |||
Mysterious | |||
Nice | |||
Obedient | |||
Optimistic | |||
Orange | |||
Outstanding | |||
Perfect | |||
Pleasant | |||
Polite | |||
Powerful | |||
Precious | |||
Proud | |||
Purple | |||
Red | |||
Scary | |||
Shiny | |||
Shy | |||
Silly | |||
Skinny | |||
Sleepy | |||
Smiling | |||
Sparkling | |||
Splendid | |||
Spotless | |||
Stormy | |||
Strange | |||
Super | |||
Talented | |||
Tenacious | |||
Thankful | |||
Thoughtful | |||
Tired | |||
Tough | |||
Unusual | |||
Victorious | |||
Vivacious | |||
White | |||
Wild | |||
Witty | |||
Wonderful | |||
Yellow |
@ -0,0 +1,19 @@ | |||
# The address and port sncf will listen | |||
listening_address = "0.0.0.0" | |||
listening_port = 8000 | |||
# Public-facing domain for sncf. | |||
# includes protocol, FQDN and port, without the trailing slash. | |||
website_url = "http://localhost:8000" | |||
# SQLite: path to the SQLite DB | |||
# PostgreSQL: postgres://user:password@address:port/database | |||
# MySQL: mysql://user:password@address:port/database | |||
database_path = "" | |||
# Displays route names and a lot of information | |||
debug_mode = true | |||
# Don't touch this unless you know what you're doing | |||
config_version = 2 |
@ -0,0 +1,558 @@ | |||
{ | |||
"lang_code": { | |||
"en": "en", | |||
"fr": "fr", | |||
"de": "de", | |||
"it": "it" | |||
}, | |||
"lang_full": { | |||
"en": "English", | |||
"fr": "Français", | |||
"de": "Deutsch", | |||
"it": "Italiano" | |||
}, | |||
"meta_description": { | |||
"en": "pluriton : don't do work twice", | |||
"fr": "pluriton : ne faites pas double travail", | |||
"de": "pluriton : das Werkzeug gegen Monotonie und doppelte Arbeit", | |||
"it": "pluriton : per non fare doppio lavoro" | |||
}, | |||
"impressum_donations": { | |||
"en": "Donations", | |||
"fr": "Dons", | |||
"de": "Spenden", | |||
"it": "Donazioni" | |||
}, | |||
"impressum_privacy": { | |||
"en": "Privacy", | |||
"fr": "Protection des données", | |||
"de": "Datenschutz", | |||
"it": "Protezione dati" | |||
}, | |||
"index_title": { | |||
"en": "basabuuka", | |||
"fr": "basabuuka", | |||
"de": "basabuuka", | |||
"it": "basabuuka" | |||
}, | |||
"index_title2": { | |||
"en": "Open Language?", | |||
"fr": "ouvrir la langue?", | |||
"de": "Sprache oeffnen?", | |||
"it": "Aprire le lingue?" | |||
}, | |||
"index_title3": { | |||
"en": "How does pluriton work?", | |||
"fr": "Comme pluriton functionne?", | |||
"de": "Wie funktioniert pluriton?", | |||
"it": "Come funziona pluriton?" | |||
}, | |||
"index_description": { | |||
"en": "Based on databases of wikidata, and open source LLMs, you are invited to simplify your read.", | |||
"fr": "Bas�sur des bases de donn�es wikidata, et des LLM open source, vous etes invites simplifier votre lecture.", | |||
"de": "Basierend auf Datenbanken aus wikidata, und open source LLMs, bist du eingeladen dein Lesen zu vereinfachen.", | |||
"it": "Basandosi su database di wikidata e su LLM open source, siete invitati a semplificare la vostra lettura ." | |||
}, | |||
"index_description2": { | |||
"en": "Enter the text you want to translate - and klick on translate.", | |||
"fr": "Saisissez le texte que vous souhaitez traduire - et clickez sur traduire.", | |||
"de": "Geben Sie den Text ein, den sie übersetzen möchten - und klicken sie auf übersetzen.", | |||
"it": "Inserisci il testo che vuoi tradurre - e clicca su tradurre." | |||
}, | |||
"index_description3": { | |||
"en": "algorithmic translation", | |||
"fr": "traduction algorithmique", | |||
"de": "algorithmische Uebersetzung", | |||
"it": "traduzione algorithmica" | |||
}, | |||
"index_description4": { | |||
"en": "Corrected Translation", | |||
"fr": "traduction corrige", | |||
"de": "verbesserte Uebersetzung", | |||
"it": "La traduzione corretta" | |||
}, | |||
"index_beta_tag": { | |||
"en": "BETA", | |||
"fr": "BETA", | |||
"de": "BETA" | |||
}, | |||
"index_nojs": { | |||
"en": "Please enable JavaScript in your browser!", | |||
"fr": "Veuillez activer JavaScript dans votre navigateur !", | |||
"de": "Bitte aktivieren Sie JavaScript in ihrem Browser!", | |||
"it": "Si prega di attivare Javascript nel tuo browser!" | |||
}, | |||
"index_search_button": { | |||
"en": "Translate", | |||
"fr": "Traduire", | |||
"de": "Übersetzen", | |||
"it": "Traduci" | |||
}, | |||
"index_continueform_button": { | |||
"en": "Access your forms", | |||
"fr": "Accéder à vos formulaires", | |||
"de": "Zu deinen Umfragen" | |||
}, | |||
"index_beta_banner_title": { | |||
"en": "Warning: Service in beta.", | |||
"fr": "Attention : Service en bêta.", | |||
"de": "Achtung: Seite in Beta Version" | |||
}, | |||
"index_beta_banner_desc1": { | |||
"en": "This service is currently under development and might behave in an unexpected way.", | |||
"fr": "Ce service est en cours de développement et pourrait se comporter de manière inattendue.", | |||
"de": "Diese Seite ist in Entwicklung und könnte sich unerwartet verhalten." | |||
}, | |||
"index_beta_banner_desc2": { | |||
"en": "Feel free to send feedbacks on our ", | |||
"fr": "Vous pouvez nous envoyer vos retours sur ", | |||
"de": "Feedback gerne an " | |||
}, | |||
"index_beta_banner_desc_link": { | |||
"en": "our contact page", | |||
"fr": "notre page de contact", | |||
"de": "unsere Kontaktseite" | |||
}, | |||
"index_disclaimer_title": { | |||
"en": "Who keeps basabuuka running?", | |||
"fr": "Qui a organisé basabuuka?", | |||
"de": "Wer betreibt basabuuka?", | |||
"it": "Chi opera basabuuka?" | |||
}, | |||
"index_disclaimer1": { | |||
"en": "This service is being built for you from ", | |||
"fr": "Ce service vous est produit de ", | |||
"de": "Diese Seite wird von ", | |||
"it": "Questo sito viene costruito di " | |||
}, | |||
"index_disclaimer2": { | |||
"en": " ", | |||
"fr": " .", | |||
"de": " ", | |||
"it": " ." | |||
}, | |||
"index_disclaimer2_link_org": { | |||
"en": " basabuuka - to open language ", | |||
"fr": " basabuuka - ouvrir langue ", | |||
"de": " basabuuka - Sprache öffnen ", | |||
"it": " basabuuka - aprire linguaggi " | |||
}, | |||
"index_disclaimer2_but": { | |||
"en": " ", | |||
"fr": " ", | |||
"de": " ", | |||
"it": " " | |||
}, | |||
"index_disclaimer2_link_don": { | |||
"en": "", | |||
"fr": "", | |||
"de": "", | |||
"it": "" | |||
}, | |||
"index_disclaimer3": { | |||
"en": "Get in touch with ", | |||
"fr": "Contactez ", | |||
"de": "Kontaktiert ", | |||
"it": "Contatta " | |||
}, | |||
"index_disclaimer3_link": { | |||
"en": "basabuuka, ", | |||
"fr": "basabuuka, ", | |||
"de": "basabuuka, ", | |||
"it": "basabuuka, " | |||
}, | |||
"index_disclaimer4": { | |||
"en": " if you have ideas or data to contribute!", | |||
"fr": " si vous avez des idées ou des données à nous contribuer!", | |||
"de": " wenn Sie mit Ideen oder Daten beitragen möchten!", | |||
"it": " se hai delle idee o dei dati da contribuire!" | |||
}, | |||
"index_panel1_title": { | |||
"en": "How does foorms work?", | |||
"fr": "Comme foorms functionne?", | |||
"de": "Wie funktioniert foorms?" | |||
}, | |||
"index_panel1_desc1": { | |||
"en": "Are you searching for a privacy-friendly alternative to Google Forms while keeping its ease of use?", | |||
"fr": "Cherchez-vous une alternative éthique à Google Forms qui reste simple d'utilisation ?", | |||
"de": "Suchen Sie eine ethisch sinnvolle Alternative zu Google Forms, welche gleichzeitig einfach in der Bedienung ist?" | |||
}, | |||
"index_panel1_desc2": { | |||
"en": "You've just found it.", | |||
"fr": "Vous venez de la trouver.", | |||
"de": "Sie haben sie gefunden." | |||
}, | |||
"index_panel2_title": { | |||
"en": "Choose and order your fields", | |||
"fr": "Choisissez et ordonnez vos champs", | |||
"de": "Wählen und Ordnen Sie ihre Felder" | |||
}, | |||
"index_panel2_desc1": { | |||
"en": "The software currently supports seven field types.", | |||
"fr": "Pour le moment, le logiciel supporte sept types de champs.", | |||
"de": "Im Moment unterstützt die Software sieben Typen von Feldern." | |||
}, | |||
"index_panel2_desc2": { | |||
"en": "New field types are ", | |||
"fr": "De nouveaux types de champs sont ", | |||
"de": "Neue Typen von Feldern sind " | |||
}, | |||
"index_panel2_desc2_link": { | |||
"en": "currently in the works", | |||
"fr": "en cours d'élaboration", | |||
"de": "momentan in Bearbeitung" | |||
}, | |||
"index_panel3_title": { | |||
"en": "Analyze the answers", | |||
"fr": "Analysez les réponses", | |||
"de": "Analysieren Sie die Antworten" | |||
}, | |||
"index_panel3_desc1": { | |||
"en": "See detailed graphs of the answers to your form.", | |||
"fr": "Visualisez les réponses à votre formulaire avec un graphique.", | |||
"de": "Visualisieren Sie die Antworten Ihrer Umfrage graphisch." | |||
}, | |||
"index_panel4_title": { | |||
"en": "Export the answers", | |||
"fr": "Exportez les réponses", | |||
"de": "Export der Antworten" | |||
}, | |||
"index_panel4_desc1": { | |||
"en": "Export the raw data of your form in CSV format to integrate the answers in other software (e.g. LibreOffice Calc or Microsoft Excel).", | |||
"fr": "Exportez les données brutes de votre formulaire en format CSV pour intégrer les réponses dans d'autres logiciels (ex. LibreOffice Calc ou Microsoft Excel).", | |||
"de": "Exportieren Sie die Rohdaten Ihrer Umfrage im CSV Format um die Antworten in anderer Software zu integrieren( z.B. LibreOffice Calc)" | |||
}, | |||
"index_panel5_title": { | |||
"en": "Edit your form's settings", | |||
"fr": "Paramétrez vos formulaires", | |||
"de": "Einstellungen Ihrer Umfragen" | |||
}, | |||
"index_panel5_desc1": { | |||
"en": "Use the share link to send your form to other people.", | |||
"fr": "Utilisez le lien de partage pour envoyer votre formulaire à d'autres personnes.", | |||
"de": "Nutzen Sie den Teilen Link um Ihre Umfrage anderen Menschen zu schicken." | |||
}, | |||
"index_panel5_desc2": { | |||
"en": "You can also define an expiration date for your form.", | |||
"fr": "Vous pouvez également définir une date d'expiration pour votre formulaire.", | |||
"de": "Sie können auch ein Ablaufdatum für ihre Umfrage festsetzen." | |||
}, | |||
"index_panel6_title": { | |||
"en": "All your forms in one place", | |||
"fr": "Tous vos formulaires au même endroit", | |||
"de": "Alle Ihre Umfragen an einem Ort" | |||
}, | |||
"index_panel6_desc1": { | |||
"en": "Find all your forms in the same panel.", | |||
"fr": "Retrouvez tous vos formulaires sur un même panel.", | |||
"de": "Finde alle deine Umfragen in einem Panel." | |||
}, | |||
"index_bottom_docs": { | |||
"en": "Documentation", | |||
"fr": "Documentation", | |||
"de": "Dokumentation", | |||
"it": "Documentazione" | |||
}, | |||
"index_bottom_source": { | |||
"en": "Source code", | |||
"fr": "Code source", | |||
"de": "Quellcode", | |||
"it": "Codice" | |||
}, | |||
"index_bottom_lic": { | |||
"en": "License", | |||
"fr": "Licence", | |||
"de": "Lizenz", | |||
"it": "Licenza" | |||
}, | |||
"index_credits_title": { | |||
"en": "Credits", | |||
"fr": "Crédits", | |||
"de": "Credits" | |||
}, | |||
"index_credits_desc1": { | |||
"en": "The Nextcloud software suite and the Nextcloud Forms application has been developed by ", | |||
"fr": "La suite logicielle Nextcloud et l'application Nextcloud Forms a été développée par ", | |||
"de": "Die Nextcloud Software Sammlung und die Nextcloud Forms Applikation wurden entwickelt von " | |||
}, | |||
"index_credits_desc1_link": { | |||
"en": "the Nextcloud team", | |||
"fr": "l'équipe Nextcloud", | |||
"de": "dem Nextcloud Team" | |||
}, | |||
"index_credits_desc1_a": { | |||
"en": " and its contributors.", | |||
"fr": " et ses contributeur·ices.", | |||
"de": " und ihren Kontributor*innen" | |||
}, | |||
"index_credits_desc2": { | |||
"en": "The Simple Nextcloud Forms software, which simplifies the form creation process, has been developed by ", | |||
"fr": "Le logiciel Simple Nextcloud Forms, qui simplifie la création de formulaires, a été développé par ", | |||
"de": "Die Simple Nextcloud Forms Software, welche die Erstellung von Umfragen erleichtert, wurde entwickelt von " | |||
}, | |||
"index_credits_desc2_for": { | |||
"en": " for ", | |||
"fr": " pour ", | |||
"de": " für " | |||
}, | |||
"index_credits_desc2_org": { | |||
"en": "the 42l association", | |||
"fr": "l'association 42l", | |||
"de": "die 42l Assoziation" | |||
}, | |||
"index_credits_desc3": { | |||
"en": "source code", | |||
"fr": "code source", | |||
"de": "Quellcode" | |||
}, | |||
"link_title": { | |||
"en": "Link created", | |||
"fr": "Lien créé", | |||
"de": "Link erstellt" | |||
}, | |||
"link_desc1_1": { | |||
"en": "Here's an <b>administration link</b>, which will allow you to access all", | |||
"fr": "Voici un <b>lien d'administration</b>, qui vous permettra d'accéder à tous", | |||
"de": "Hier ist ein <b>Administrations Link</b>, der es ermöglicht wieder zu" | |||
}, | |||
"link_desc1_2": { | |||
"en": "your forms and check your answers.", | |||
"fr": "vos formulaires et de consulter vos réponses.", | |||
"de": "ihren Umfragen zu gelangen und die Antworten einzusehen." | |||
}, | |||
"link_desc2_1": { | |||
"en": "<b>Keep it</b> carefully and don't give it away", | |||
"fr": "<b>Conservez-le</B> bien précieusement et ne le donnez pas", | |||
"de": "<b>Bewahren Sie diese</b> gut und sicher auf" | |||
}, | |||
"link_desc2_2": { | |||
"en": "(it'd be the same as giving out your password!).", | |||
"fr": "(cela reviendrait à donner un mot de passe!).", | |||
"de": "(Die Weitergabe entspricht der Weitergabe eines Passwortes!)." | |||
}, | |||
"link_desc3_1": { | |||
"en": "Once your link copied, click on the button below to", | |||
"fr": "Une fois votre lien copié, cliquez sur le bouton ci-dessous pour", | |||
"de": "Ist der Link kopiert, drücken sie auf den unteren Button um" | |||
}, | |||
"link_desc3_2": { | |||
"en": "start editing your forms.", | |||
"fr": "commencer à éditer vos formulaires.", | |||
"de": "Umfragen zu erstellen oder zu bearbeiten." | |||
}, | |||
"link_access_btn": { | |||
"en": "to foorms", | |||
"fr": "Accéder foorms", | |||
"de": "zu foorms" | |||
}, | |||
"link_note": { | |||
"en": "Note: If you don't use your administration link during more than ", | |||
"fr": "Note : Si vous n'utilisez pas votre lien d'administration pendant plus de ", | |||
"de": "Notiz: Wenn Sie den Administrations Link für länger als " | |||
}, | |||
"link_note2": { | |||
"en": " days, your forms will be automatically deleted.", | |||
"fr": " jours, vos formulaires seront automatiquement supprimés.", | |||
"de": " Tage nicht benutzen, werden ihre Umfragen automatisch gelöscht." | |||
}, | |||
"link_copy": { | |||
"en": "Copy link", | |||
"fr": "Copier le lien", | |||
"de": "Link kopieren" | |||
}, | |||
"link_copied": { | |||
"en": "Link copied!", | |||
"fr": "Lien copié !", | |||
"de": "Link kopiert !" | |||
}, | |||
"link_mail": { | |||
"en": "send Link", | |||
"fr": "envoyer lien", | |||
"de": "Link senden" | |||
}, | |||
"error_title": { | |||
"en": "Oops!...", | |||
"fr": "Oups !...", | |||
"de": "Ups !..." | |||
}, | |||
"error_description": { | |||
"en": "The application encountered a problem:", | |||
"fr": "L'application a rencontré un problème :", | |||
"de": "Die Anwendung hat ein Problem festgestellt:" | |||
}, | |||
"error_back": { | |||
"en": "Back to the main page", | |||
"fr": "Retour à la page principale", | |||
"de": "Zurück zur Hauptseite" | |||
}, | |||
"error_note1": { | |||
"en": "We are (probably) aware of this bug, but feel free to contact us if you need assistance.", | |||
"fr": "Nous sommes (probablement) au courant, mais n'hésitez pas à nous contacter si vous avez besoin d'aide.", | |||
"de": "Wir sind uns (wahrscheinlich) bewusst, was diesen Fehler angeht. Fühlen sie sich frei uns zu kontaktieren, wenn Sie Hilfe benötigen." | |||
}, | |||
"error_note2": { | |||
"en": "Sorry for the inconvenience.", | |||
"fr": "Désolés pour les désagréments occasionnés.", | |||
"de": "Entschuldigen Sie die Störung." | |||
}, | |||
"error_forward_req": { | |||
"en": "Error while connecting to the Nextcloud instance.", | |||
"fr": "Erreur lors de la connexion à l'instance Nextcloud.", | |||
"de": "Fehler beim Verbinden zur Nextcloud Instanz." | |||
}, | |||
"error_forward_resp": { | |||
"en": "Error while reading Nextcloud instance's response.", | |||
"fr": "Erreur lors de la lecture de la réponse de l'instance Nextcloud.", | |||
"de": "Feher beim Lesen der Antwort der Nextcloud Instanz." | |||
}, | |||
"error_forward_isanon": { | |||
"en": "Couldn't set the form's isAnonymous value.", | |||
"fr": "Échec lors de la définition de la valeur isAnonymous du formulaire.", | |||
"de": "Es ist nicht möglich, die isAnonymous Wert des Formulars zu setzen." | |||
}, | |||
"error_forward_clientresp_newform": { | |||
"en": "Failed to send the response body (new form).", | |||
"fr": "Échec lors de l'envoi du corps de la réponse (nouveau formulaire).", | |||
"de": "Fehler beim senden des Response body (neues Formular)." | |||
}, | |||
"error_forward_clientresp_std": { | |||
"en": "Failed to send the response body.", | |||
"fr": "Échec lors de l'envoi du corps de la réponse.", | |||
"de": "Fehler beim Senden des Response Body." | |||
}, | |||
"error_forwardlogin_db": { | |||
"en": "Couldn't connect to the local database.", | |||
"fr": "Échec lors de la connexion à la base de données locale.", | |||
"de": "Fehler beim verbinden zur lokalen Datenbank." | |||
}, | |||
"error_forwardlogin_db_get": { | |||
"en": "Error during information retrieval from the local database.", | |||
"fr": "Erreur lors de la récupération des informations dans la base de données locale.", | |||
"de": "Fehler beim Empfangen von Daten der lokalen Datenbank." | |||
}, | |||
"error_forwardlogin_notfound": { | |||
"en": "The specified token doesn't exist in local database.", | |||
"fr": "Le token spécifié n'existe pas dans la base de données locale.", | |||
"de": "Der gesetzte Token existiert nicht in der lokalen Datenbank." | |||
}, | |||
"error_login_get": { | |||
"en": "The account creation request (GET) to Nextcloud has failed.", | |||
"fr": "La requête de création de compte (GET) vers l'instance Nextcloud a échoué.", | |||
"de": "Das Account Erstellungs Request (GET) zu Nextcloud hat nicht funktioniert." | |||
}, | |||
"error_login_get_body": { | |||
"en": "Reading response from the account creation request to Nextcloud has failed.", | |||
"fr": "La lecture de la réponse à la requête de création de compte vers l'instance Nextcloud a échoué.", | |||
"de": "Das Lesen der Response vom Account Erstellungs Request zu Nextcloud hat nicht funktioniert." | |||
}, | |||
"error_login_post": { | |||
"en": "The account creation request (POST) to Nextcloud has failed.", | |||
"fr": "La requête de création de compte (POST) vers l'instance Nextcloud a échoué.", | |||
"de": "Der Account Erstellungs Request (POST) zu Nextcloud hat nicht funktioniert. " | |||
}, | |||
"error_login_redir": { | |||
"en": "Redirection to Nextcloud account failed.", | |||
"fr": "La redirection vers le compte Nextcloud a échoué.", | |||
"de": "Die Weiterleitung zum Nextcloud account hat nicht funktioniert." | |||
}, | |||
"error_createaccount_post": { | |||
"en": "Account creation: connection to the Nextcloud API failed.", | |||
"fr": "Création de compte : la connexion à l'API Nextcloud a échoué.", | |||
"de": "Account Erstellung: Verbindung zur Nextcloud API hat nicht funktioniert." | |||
}, | |||
"error_createaccount_post_body": { | |||
"en": "Account creation: reading the answer from the Nextcloud API failed.", | |||
"fr": "Création de compte : le traitement de la réponse de l'API Nextcloud a échoué.", | |||
"de": "Account Erstellung : das Lesen der Antwort der Nextcloud API hat nicht funktioniert." | |||
}, | |||
"error_createaccount_status": { | |||
"en": "The Nextcloud instance responded with an unexpected status code.", | |||
"fr": "L'instance Nextcloud a répondu avec un code de statut inattendu.", | |||
"de": "Die Nextcloud Instanz hat mit einem unexpected status code geantwortet." | |||
}, | |||
"error_createaccount_ncstatus": { | |||
"en": "The Nextcloud API responded with an unexpected status code.", | |||
"fr": "L'API Nextcloud a répondu avec un code de statut inattendu.", | |||
"de": "Die Nextcloud API hat mit unexpected ncstatus geantwortet." | |||
}, | |||
"error_createaccount_ncstatus_parse": { | |||
"en": "Error parsing Nextcloud API's status code.", | |||
"fr": "Erreur lors de la lecture du code de statut de l'API Nextcloud.", | |||
"de": "Fehler beim Lesen des Nextcloud API status codes." | |||
}, | |||
"error_forwardregister_pool": { | |||
"en": "Error while connecting to the local database.", | |||
"fr": "Erreur lors de la connexion à la base de données locale.", | |||
"de": "Fehler beim Verbinden zu der lokalen Datenbank." | |||
}, | |||
"error_forwardregister_db": { | |||
"en": "Failed adding the Nextcloud account in the local database.", | |||
"fr": "L'ajout du compte Nextcloud dans la base de données locale a échoué.", | |||
"de": "Fehlre beim Hinzufügen des Nextcloud Accounts zur lokalen Datenbank." | |||
}, | |||
"error_forwardregister_tokenparse": { | |||
"en": "Failed parsing the admin token.", | |||
"fr": "Échec lors de la lecture du token administrateur.", | |||
"de": "Fehler beim Parsen des Admin Tokens." | |||
}, | |||
"error_login_cookiepair": { | |||
"en": "Couldn't read cookies.", | |||
"fr": "Échec lors de la lecture de cookies.", | |||
"de": "Fehler beim Lesen der Cookies" | |||
}, | |||
"error_login_regex": { | |||
"en": "Couldn't read the CSRF token.", | |||
"fr": "Échec lors de la lecture du token CSRF.", | |||
"de": "Fehler beim Lesen des CSRF Tokens." | |||
}, | |||
"error_login_setcookie": { | |||
"en": "Error during cookies transfer.", | |||
"fr": "Erreur lors du transfert de cookies.", | |||
"de": "Feheler beim Transfer der Cookies." | |||
}, | |||
"error_form_insert": { | |||
"en": "The local database couldn't be reached.", | |||
"fr": "Échec de la connexion avec la base de données locale.", | |||
"de": "Die lokale Datenbank ist nicht erreichbar." | |||
}, | |||
"error_createaccount": { | |||
"en": "The Nextcloud API returned an unexpected result.", | |||
"fr": "L'API de Nextcloud a retourné un résultat inattendu.", | |||
"de": "Die Nextcloud API hat ein unerwartetes Resultat zurückgesendet." | |||
}, | |||
"error_redirect": { | |||
"en": "Failed to redirect.", | |||
"fr": "La redirection a échoué.", | |||
"de": "Weiterleitung (Redirect) hat nicht funktioniert." | |||
}, | |||
"error_csrf_cookie": { | |||
"en": "Your CSRF token (cookie) seems incorrect, please retry.", | |||
"fr": "Votre token CSRF (cookie) semble incorrect, veuillez réessayer.", | |||
"de": "Dein CSRF Token (Cookie) scheint inkorrekt, versuchen Sie es erneut." | |||
}, | |||
"error_csrf_token": { | |||
"en": "Your CSRF token seems incorrect, please retry.", | |||
"fr": "Votre token CSRF semble incorrect, veuillez réessayer.", | |||
"de": "Ihr CSRF Token scheint nicht korrekt, versuchen Sie es erneut. " | |||
}, | |||
"error_dirtyhacker": { | |||
"en": "Attempt to access an unauthorized resource.", | |||
"fr": "Tentative d'accès à une ressource non autorisée.", | |||
"de": "Zugangs-Versuch einer unauthorisierten Quelle." | |||
}, | |||
"error_tplrender": { | |||
"en": "Template rendering failed.", | |||
"fr": "Le rendu du template a échoué.", | |||
"de": "Template rendering hat nicht funktioniert." | |||
}, | |||
"error_tplrender_resp": { | |||
"en": "Sending response failed.", | |||
"fr": "L'envoi de la réponse a échoué.", | |||
"de": "Senden der Antwort hat nicht funktioniert." | |||
} | |||
} |
@ -0,0 +1,559 @@ | |||
{ | |||
"lang_code": { | |||
"en": "en", | |||
"fr": "fr", | |||
"de": "de", | |||
"it": "it" | |||
}, | |||
"lang_full": { | |||
"en": "English", | |||
"fr": "Français", | |||
"de": "Deutsch", | |||
"it": "Italiano" | |||
}, | |||
"meta_description": { | |||
"en": "pluriton : don't do work twice", | |||
"fr": "pluriton : ne faites pas double travail", | |||
"de": "pluriton : das Werkzeug gegen Monotonie und doppelte Arbeit", | |||
"it": "pluriton : per non fare doppio lavoro" | |||
}, | |||
"impressum_donations": { | |||
"en": "Donations", | |||
"fr": "Dons", | |||
"de": "Spenden", | |||
"it": "Donazioni" | |||
}, | |||
"impressum_privacy": { | |||
"en": "Privacy", | |||
"fr": "Protection des données", | |||
"de": "Datenschutz", | |||
"it": "Protezione dati" | |||
}, | |||
"index_title": { | |||
"en": "pluriton", | |||
"fr": "pluriton", | |||
"de": "pluriton", | |||
"it": "pluriton" | |||
}, | |||
"index_title2": { | |||
"en": "What is pluriton?", | |||
"fr": "Qu'est-ce que c'est pluriton?", | |||
"de": "Was ist pluriton?", | |||
"it": "Che cosa è pluriton?" | |||
}, | |||
"index_title3": { | |||
"en": "How does pluriton work?", | |||
"fr": "Comme pluriton functionne?", | |||
"de": "Wie funktioniert pluriton?", | |||
"it": "Come funziona pluriton?" | |||
}, | |||
"index_description": { | |||
"en": "Duplicate work is annoying. Repetition quickly becomes monotonous. This is exactly where cognitive automation should come in. Therefore Pluriton instead of Monoton. As a first step towards the prototype of easy language.", | |||
"fr": "Le double travail est ennuyeux. La répétition devient vite monotone. C'est exactement là que l'automatisation cognitive doit intervenir. Donc Pluriton au lieu de Monoton. Comme un premier pas vers le prototype de langage facile.", | |||
"de": "Doppelte Arbeit nervt. Wiederholung wird schnell monoton. Genau dort sollte die kognitive Automation ansetzen. Deshalb Pluriton statt Monoton. Als erster Schritt in Richtung Prototyp Leichte Sprache.", | |||
"it": "Il doppio lavoro è fastidioso. La ripetizione diventa rapidamente monotona. È proprio qui che dovrebbe entrare in gioco l'automazione cognitiva. Quindi Pluriton invece di Monoton. Come primo passo verso il prototipo del linguaggio facile." | |||
}, | |||
"index_description2": { | |||
"en": "Enter the text you want to translate - and search for the nearest, already translated text.", | |||
"fr": "Saisissez le texte que vous souhaitez traduire - et recherchez le texte plus suivant, déjà traduit. ", | |||
"de": "Geben Sie den Text ein, den sie übersetzen möchten - und suchen Sie nach dem nächsten Text, der bereits übersetzt worden ist.", | |||
"it": "Inserisci il testo che vuoi tradurre - e cerca il testo più vicino che è già stato tradotto" | |||
}, | |||
"index_description3": { | |||
"en": "Nearest Text", | |||
"fr": "Le texte plus suivant", | |||
"de": "Der nächste Text", | |||
"it": "Il testo più vicino" | |||
}, | |||
"index_description4": { | |||
"en": "The nearest translation", | |||
"fr": "La traduction plus suivante", | |||
"de": "Die nächste Übersetzung", | |||
"it": "La traduzione piú vicina" | |||
}, | |||
"index_beta_tag": { | |||
"en": "BETA", | |||
"fr": "BETA", | |||
"de": "BETA" | |||
}, | |||
"index_nojs": { | |||
"en": "Please enable JavaScript in your browser!", | |||
"fr": "Veuillez activer JavaScript dans votre navigateur !", | |||
"de": "Bitte aktivieren Sie JavaScript in ihrem Browser!", | |||
"it": "Si prega di attivare Javascript nel tuo browser!" | |||
}, | |||
"index_search_button": { | |||
"en": "Search", | |||
"fr": "Rechercher", | |||
"de": "Suchen", | |||
"it": "Cerca" | |||
}, | |||
"index_continueform_button": { | |||
"en": "Access your forms", | |||
"fr": "Accéder à vos formulaires", | |||
"de": "Zu deinen Umfragen" | |||
}, | |||
"index_beta_banner_title": { | |||
"en": "Warning: Service in beta.", | |||
"fr": "Attention : Service en bêta.", | |||
"de": "Achtung: Seite in Beta Version" | |||
}, | |||
"index_beta_banner_desc1": { | |||
"en": "This service is currently under development and might behave in an unexpected way.", | |||
"fr": "Ce service est en cours de développement et pourrait se comporter de manière inattendue.", | |||
"de": "Diese Seite ist in Entwicklung und könnte sich unerwartet verhalten." | |||
}, | |||
"index_beta_banner_desc2": { | |||
"en": "Feel free to send feedbacks on our ", | |||
"fr": "Vous pouvez nous envoyer vos retours sur ", | |||
"de": "Feedback gerne an " | |||
}, | |||
"index_beta_banner_desc_link": { | |||
"en": "our contact page", | |||
"fr": "notre page de contact", | |||
"de": "unsere Kontaktseite" | |||
}, | |||
"index_disclaimer_title": { | |||
"en": "Who keeps pluriton running?", | |||
"fr": "Qui a organisé pluriton?", | |||
"de": "Wer betreibt pluriton?", | |||
"it": "Chi opera pluriton?" | |||
}, | |||
"index_disclaimer1": { | |||
"en": "This service is maintained for you from ", | |||
"fr": "Ce service vous est fourni gratuitement de ", | |||
"de": "Diese Seite wird von ", | |||
"it": "Questo sito e mantenuto gratuitamente di " | |||
}, | |||
"index_disclaimer2": { | |||
"en": " for free.", | |||
"fr": " .", | |||
"de": " für Sie kostenlos angeboten", | |||
"it": " ." | |||
}, | |||
"index_disclaimer2_link_org": { | |||
"en": " basabuuka - to open language ", | |||
"fr": " basabuuka - ouvrir langue ", | |||
"de": " basabuuka - Sprache öffnen ", | |||
"it": " basabuuka - aprire linguaggi " | |||
}, | |||
"index_disclaimer2_but": { | |||
"en": " But you have the possibility to ", | |||
"fr": " Mais vous avez la possibilité de ", | |||
"de": " Aber Sie können gern ", | |||
"it": " Pero hai la possibilità di " | |||
}, | |||
"index_disclaimer2_link_don": { | |||
"en": "donate.", | |||
"fr": "faire une donation.", | |||
"de": "spenden.", | |||
"it": "fare una donazione" | |||
}, | |||
"index_disclaimer3": { | |||
"en": "Or get in touch with ", | |||
"fr": "Ou contactez ", | |||
"de": "Oder schreiben Sie ", | |||
"it": "Oppure contatta " | |||
}, | |||
"index_disclaimer3_link": { | |||
"en": "basabuuka, ", | |||
"fr": "basabuuka, ", | |||
"de": "basabuuka, ", | |||
"it": "basabuuka, " | |||
}, | |||
"index_disclaimer4": { | |||
"en": " if you have ideas or data to contribute!", | |||
"fr": " si vous avez des idées ou des données à nous contribuer!", | |||
"de": " wenn Sie mit Ideen oder Daten beitragen möchten!", | |||
"it": " se hai delle idee o dei dati da contribuire!" | |||
}, | |||
"index_panel1_title": { | |||
"en": "How does foorms work?", | |||
"fr": "Comme foorms functionne?", | |||
"de": "Wie funktioniert foorms?" | |||
}, | |||
"index_panel1_desc1": { | |||
"en": "Are you searching for a privacy-friendly alternative to Google Forms while keeping its ease of use?", | |||
"fr": "Cherchez-vous une alternative éthique à Google Forms qui reste simple d'utilisation ?", | |||
"de": "Suchen Sie eine ethisch sinnvolle Alternative zu Google Forms, welche gleichzeitig einfach in der Bedienung ist?" | |||
}, | |||
"index_panel1_desc2": { | |||
"en": "You've just found it.", | |||
"fr": "Vous venez de la trouver.", | |||
"de": "Sie haben sie gefunden." | |||
}, | |||
"index_panel2_title": { | |||
"en": "Choose and order your fields", | |||
"fr": "Choisissez et ordonnez vos champs", | |||
"de": "Wählen und Ordnen Sie ihre Felder" | |||
}, | |||
"index_panel2_desc1": { | |||
"en": "The software currently supports seven field types.", | |||
"fr": "Pour le moment, le logiciel supporte sept types de champs.", | |||
"de": "Im Moment unterstützt die Software sieben Typen von Feldern." | |||
}, | |||
"index_panel2_desc2": { | |||
"en": "New field types are ", | |||
"fr": "De nouveaux types de champs sont ", | |||
"de": "Neue Typen von Feldern sind " | |||
}, | |||
"index_panel2_desc2_link": { | |||
"en": "currently in the works", | |||
"fr": "en cours d'élaboration", | |||
"de": "momentan in Bearbeitung" | |||
}, | |||
"index_panel3_title": { | |||
"en": "Analyze the answers", | |||
"fr": "Analysez les réponses", | |||
"de": "Analysieren Sie die Antworten" | |||
}, | |||
"index_panel3_desc1": { | |||
"en": "See detailed graphs of the answers to your form.", | |||
"fr": "Visualisez les réponses à votre formulaire avec un graphique.", | |||
"de": "Visualisieren Sie die Antworten Ihrer Umfrage graphisch." | |||
}, | |||
"index_panel4_title": { | |||
"en": "Export the answers", | |||
"fr": "Exportez les réponses", | |||
"de": "Export der Antworten" | |||
}, | |||
"index_panel4_desc1": { | |||
"en": "Export the raw data of your form in CSV format to integrate the answers in other software (e.g. LibreOffice Calc or Microsoft Excel).", | |||
"fr": "Exportez les données brutes de votre formulaire en format CSV pour intégrer les réponses dans d'autres logiciels (ex. LibreOffice Calc ou Microsoft Excel).", | |||
"de": "Exportieren Sie die Rohdaten Ihrer Umfrage im CSV Format um die Antworten in anderer Software zu integrieren( z.B. LibreOffice Calc)" | |||
}, | |||
"index_panel5_title": { | |||
"en": "Edit your form's settings", | |||
"fr": "Paramétrez vos formulaires", | |||
"de": "Einstellungen Ihrer Umfragen" | |||
}, | |||
"index_panel5_desc1": { | |||
"en": "Use the share link to send your form to other people.", | |||
"fr": "Utilisez le lien de partage pour envoyer votre formulaire à d'autres personnes.", | |||
"de": "Nutzen Sie den Teilen Link um Ihre Umfrage anderen Menschen zu schicken." | |||
}, | |||
"index_panel5_desc2": { | |||
"en": "You can also define an expiration date for your form.", | |||
"fr": "Vous pouvez également définir une date d'expiration pour votre formulaire.", | |||
"de": "Sie können auch ein Ablaufdatum für ihre Umfrage festsetzen." | |||
}, | |||
"index_panel6_title": { | |||
"en": "All your forms in one place", | |||
"fr": "Tous vos formulaires au même endroit", | |||
"de": "Alle Ihre Umfragen an einem Ort" | |||
}, | |||
"index_panel6_desc1": { | |||
"en": "Find all your forms in the same panel.", | |||
"fr": "Retrouvez tous vos formulaires sur un même panel.", | |||
"de": "Finde alle deine Umfragen in einem Panel." | |||
}, | |||
"index_bottom_docs": { | |||
"en": "Documentation", | |||
"fr": "Documentation", | |||
"de": "Dokumentation", | |||
"it": "Documentazione" | |||
}, | |||
"index_bottom_source": { | |||
"en": "Source code", | |||
"fr": "Code source", | |||
"de": "Quellcode", | |||
"it": "Codice" | |||
}, | |||
"index_bottom_lic": { | |||
"en": "License", | |||
"fr": "Licence", | |||
"de": "Lizenz", | |||
"it": "Licenza" | |||
}, | |||
"index_credits_title": { | |||
"en": "Credits", | |||
"fr": "Crédits", | |||
"de": "Credits" | |||
}, | |||
"index_credits_desc1": { | |||
"en": "The Nextcloud software suite and the Nextcloud Forms application has been developed by ", | |||
"fr": "La suite logicielle Nextcloud et l'application Nextcloud Forms a été développée par ", | |||
"de": "Die Nextcloud Software Sammlung und die Nextcloud Forms Applikation wurden entwickelt von " | |||
}, | |||
"index_credits_desc1_link": { | |||
"en": "the Nextcloud team", | |||
"fr": "l'équipe Nextcloud", | |||
"de": "dem Nextcloud Team" | |||
}, | |||
"index_credits_desc1_a": { | |||
"en": " and its contributors.", | |||
"fr": " et ses contributeur·ices.", | |||
"de": " und ihren Kontributor*innen" | |||
}, | |||
"index_credits_desc2": { | |||
"en": "The Simple Nextcloud Forms software, which simplifies the form creation process, has been developed by ", | |||
"fr": "Le logiciel Simple Nextcloud Forms, qui simplifie la création de formulaires, a été développé par ", | |||
"de": "Die Simple Nextcloud Forms Software, welche die Erstellung von Umfragen erleichtert, wurde entwickelt von " | |||
}, | |||
"index_credits_desc2_for": { | |||
"en": " for ", | |||
"fr": " pour ", | |||
"de": " für " | |||
}, | |||
"index_credits_desc2_org": { | |||
"en": "the 42l association", | |||
"fr": "l'association 42l", | |||
"de": "die 42l Assoziation" | |||
}, | |||
"index_credits_desc3": { | |||
"en": "source code", | |||
"fr": "code source", | |||
"de": "Quellcode" | |||
}, | |||
"link_title": { | |||
"en": "Link created", | |||
"fr": "Lien créé", | |||
"de": "Link erstellt" | |||
}, | |||
"link_desc1_1": { | |||
"en": "Here's an <b>administration link</b>, which will allow you to access all", | |||
"fr": "Voici un <b>lien d'administration</b>, qui vous permettra d'accéder à tous", | |||
"de": "Hier ist ein <b>Administrations Link</b>, der es ermöglicht wieder zu" | |||
}, | |||
"link_desc1_2": { | |||
"en": "your forms and check your answers.", | |||
"fr": "vos formulaires et de consulter vos réponses.", | |||
"de": "ihren Umfragen zu gelangen und die Antworten einzusehen." | |||
}, | |||
"link_desc2_1": { | |||
"en": "<b>Keep it</b> carefully and don't give it away", | |||
"fr": "<b>Conservez-le</B> bien précieusement et ne le donnez pas", | |||
"de": "<b>Bewahren Sie diese</b> gut und sicher auf" | |||
}, | |||
"link_desc2_2": { | |||
"en": "(it'd be the same as giving out your password!).", | |||
"fr": "(cela reviendrait à donner un mot de passe!).", | |||
"de": "(Die Weitergabe entspricht der Weitergabe eines Passwortes!)." | |||
}, | |||
"link_desc3_1": { | |||
"en": "Once your link copied, click on the button below to", | |||
"fr": "Une fois votre lien copié, cliquez sur le bouton ci-dessous pour", | |||
"de": "Ist der Link kopiert, drücken sie auf den unteren Button um" | |||
}, | |||
"link_desc3_2": { | |||
"en": "start editing your forms.", | |||
"fr": "commencer à éditer vos formulaires.", | |||
"de": "Umfragen zu erstellen oder zu bearbeiten." | |||
}, | |||
"link_access_btn": { | |||
"en": "to foorms", | |||
"fr": "Accéder foorms", | |||
"de": "zu foorms" | |||
}, | |||
"link_note": { | |||
"en": "Note: If you don't use your administration link during more than ", | |||
"fr": "Note : Si vous n'utilisez pas votre lien d'administration pendant plus de ", | |||
"de": "Notiz: Wenn Sie den Administrations Link für länger als " | |||
}, | |||
"link_note2": { | |||
"en": " days, your forms will be automatically deleted.", | |||
"fr": " jours, vos formulaires seront automatiquement supprimés.", | |||
"de": " Tage nicht benutzen, werden ihre Umfragen automatisch gelöscht." | |||
}, | |||
"link_copy": { | |||
"en": "Copy link", | |||
"fr": "Copier le lien", | |||
"de": "Link kopieren" | |||
}, | |||
"link_copied": { | |||
"en": "Link copied!", | |||
"fr": "Lien copié !", | |||
"de": "Link kopiert !" | |||
}, | |||
"link_mail": { | |||
"en": "send Link", | |||
"fr": "envoyer lien", | |||
"de": "Link senden" | |||
}, | |||
"error_title": { | |||
"en": "Oops!...", | |||
"fr": "Oups !...", | |||
"de": "Ups !..." | |||
}, | |||
"error_description": { | |||
"en": "The application encountered a problem:", | |||
"fr": "L'application a rencontré un problème :", | |||
"de": "Die Anwendung hat ein Problem festgestellt:" | |||
}, | |||
"error_back": { | |||
"en": "Back to the main page", | |||
"fr": "Retour à la page principale", | |||
"de": "Zurück zur Hauptseite" | |||
}, | |||
"error_note1": { | |||
"en": "We are (probably) aware of this bug, but feel free to contact us if you need assistance.", | |||
"fr": "Nous sommes (probablement) au courant, mais n'hésitez pas à nous contacter si vous avez besoin d'aide.", | |||
"de": "Wir sind uns (wahrscheinlich) bewusst, was diesen Fehler angeht. Fühlen sie sich frei uns zu kontaktieren, wenn Sie Hilfe benötigen." | |||
}, | |||
"error_note2": { | |||
"en": "Sorry for the inconvenience.", | |||
"fr": "Désolés pour les désagréments occasionnés.", | |||
"de": "Entschuldigen Sie die Störung." | |||
}, | |||
"error_forward_req": { | |||
"en": "Error while connecting to the Nextcloud instance.", | |||
"fr": "Erreur lors de la connexion à l'instance Nextcloud.", | |||
"de": "Fehler beim Verbinden zur Nextcloud Instanz." | |||
}, | |||
"error_forward_resp": { | |||
"en": "Error while reading Nextcloud instance's response.", | |||
"fr": "Erreur lors de la lecture de la réponse de l'instance Nextcloud.", | |||
"de": "Feher beim Lesen der Antwort der Nextcloud Instanz." | |||
}, | |||
"error_forward_isanon": { | |||
"en": "Couldn't set the form's isAnonymous value.", | |||
"fr": "Échec lors de la définition de la valeur isAnonymous du formulaire.", | |||
"de": "Es ist nicht möglich, die isAnonymous Wert des Formulars zu setzen." | |||
}, | |||
"error_forward_clientresp_newform": { | |||
"en": "Failed to send the response body (new form).", | |||
"fr": "Échec lors de l'envoi du corps de la réponse (nouveau formulaire).", | |||
"de": "Fehler beim senden des Response body (neues Formular)." | |||
}, | |||
"error_forward_clientresp_std": { | |||
"en": "Failed to send the response body.", | |||
"fr": "Échec lors de l'envoi du corps de la réponse.", | |||
"de": "Fehler beim Senden des Response Body." | |||
}, | |||
"error_forwardlogin_db": { | |||
"en": "Couldn't connect to the local database.", | |||
"fr": "Échec lors de la connexion à la base de données locale.", | |||
"de": "Fehler beim verbinden zur lokalen Datenbank." | |||
}, | |||
"error_forwardlogin_db_get": { | |||
"en": "Error during information retrieval from the local database.", | |||
"fr": "Erreur lors de la récupération des informations dans la base de données locale.", | |||
"de": "Fehler beim Empfangen von Daten der lokalen Datenbank." | |||
}, | |||
"error_forwardlogin_notfound": { | |||
"en": "The specified token doesn't exist in local database.", | |||
"fr": "Le token spécifié n'existe pas dans la base de données locale.", | |||
"de": "Der gesetzte Token existiert nicht in der lokalen Datenbank." | |||
}, | |||
"error_login_get": { | |||
"en": "The account creation request (GET) to Nextcloud has failed.", | |||
"fr": "La requête de création de compte (GET) vers l'instance Nextcloud a échoué.", | |||
"de": "Das Account Erstellungs Request (GET) zu Nextcloud hat nicht funktioniert." | |||
}, | |||
"error_login_get_body": { | |||
"en": "Reading response from the account creation request to Nextcloud has failed.", | |||
"fr": "La lecture de la réponse à la requête de création de compte vers l'instance Nextcloud a échoué.", | |||
"de": "Das Lesen der Response vom Account Erstellungs Request zu Nextcloud hat nicht funktioniert." | |||
}, | |||
"error_login_post": { | |||
"en": "The account creation request (POST) to Nextcloud has failed.", | |||
"fr": "La requête de création de compte (POST) vers l'instance Nextcloud a échoué.", | |||
"de": "Der Account Erstellungs Request (POST) zu Nextcloud hat nicht funktioniert. " | |||
}, | |||
"error_login_redir": { | |||
"en": "Redirection to Nextcloud account failed.", | |||
"fr": "La redirection vers le compte Nextcloud a échoué.", | |||
"de": "Die Weiterleitung zum Nextcloud account hat nicht funktioniert." | |||
}, | |||
"error_createaccount_post": { | |||
"en": "Account creation: connection to the Nextcloud API failed.", | |||
"fr": "Création de compte : la connexion à l'API Nextcloud a échoué.", | |||
"de": "Account Erstellung: Verbindung zur Nextcloud API hat nicht funktioniert." | |||
}, | |||
"error_createaccount_post_body": { | |||
"en": "Account creation: reading the answer from the Nextcloud API failed.", | |||
"fr": "Création de compte : le traitement de la réponse de l'API Nextcloud a échoué.", | |||
"de": "Account Erstellung : das Lesen der Antwort der Nextcloud API hat nicht funktioniert." | |||
}, | |||
"error_createaccount_status": { | |||
"en": "The Nextcloud instance responded with an unexpected status code.", | |||
"fr": "L'instance Nextcloud a répondu avec un code de statut inattendu.", | |||
"de": "Die Nextcloud Instanz hat mit einem unexpected status code geantwortet." | |||
}, | |||
"error_createaccount_ncstatus": { | |||
"en": "The Nextcloud API responded with an unexpected status code.", | |||
"fr": "L'API Nextcloud a répondu avec un code de statut inattendu.", | |||
"de": "Die Nextcloud API hat mit unexpected ncstatus geantwortet." | |||
}, | |||
"error_createaccount_ncstatus_parse": { | |||
"en": "Error parsing Nextcloud API's status code.", | |||
"fr": "Erreur lors de la lecture du code de statut de l'API Nextcloud.", | |||
"de": "Fehler beim Lesen des Nextcloud API status codes." | |||
}, | |||
"error_forwardregister_pool": { | |||
"en": "Error while connecting to the local database.", | |||
"fr": "Erreur lors de la connexion à la base de données locale.", | |||
"de": "Fehler beim Verbinden zu der lokalen Datenbank." | |||
}, | |||
"error_forwardregister_db": { | |||
"en": "Failed adding the Nextcloud account in the local database.", | |||
"fr": "L'ajout du compte Nextcloud dans la base de données locale a échoué.", | |||
"de": "Fehlre beim Hinzufügen des Nextcloud Accounts zur lokalen Datenbank." | |||
}, | |||
"error_forwardregister_tokenparse": { | |||
"en": "Failed parsing the admin token.", | |||
"fr": "Échec lors de la lecture du token administrateur.", | |||
"de": "Fehler beim Parsen des Admin Tokens." | |||
}, | |||
"error_login_cookiepair": { | |||
"en": "Couldn't read cookies.", | |||
"fr": "Échec lors de la lecture de cookies.", | |||
"de": "Fehler beim Lesen der Cookies" | |||
}, | |||
"error_login_regex": { | |||
"en": "Couldn't read the CSRF token.", | |||
"fr": "Échec lors de la lecture du token CSRF.", | |||
"de": "Fehler beim Lesen des CSRF Tokens." | |||
}, | |||
"error_login_setcookie": { | |||
"en": "Error during cookies transfer.", | |||
"fr": "Erreur lors du transfert de cookies.", | |||
"de": "Feheler beim Transfer der Cookies." | |||
}, | |||
"error_form_insert": { | |||
"en": "The local database couldn't be reached.", | |||
"fr": "Échec de la connexion avec la base de données locale.", | |||
"de": "Die lokale Datenbank ist nicht erreichbar." | |||
}, | |||
"error_createaccount": { | |||
"en": "The Nextcloud API returned an unexpected result.", | |||
"fr": "L'API de Nextcloud a retourné un résultat inattendu.", | |||
"de": "Die Nextcloud API hat ein unerwartetes Resultat zurückgesendet." | |||
}, | |||
"error_redirect": { | |||
"en": "Failed to redirect.", | |||
"fr": "La redirection a échoué.", | |||
"de": "Weiterleitung (Redirect) hat nicht funktioniert." | |||
}, | |||
"error_csrf_cookie": { | |||
"en": "Your CSRF token (cookie) seems incorrect, please retry.", | |||
"fr": "Votre token CSRF (cookie) semble incorrect, veuillez réessayer.", | |||
"de": "Dein CSRF Token (Cookie) scheint inkorrekt, versuchen Sie es erneut." | |||
}, | |||
"error_csrf_token": { | |||
"en": "Your CSRF token seems incorrect, please retry.", | |||
"fr": "Votre token CSRF semble incorrect, veuillez réessayer.", | |||
"de": "Ihr CSRF Token scheint nicht korrekt, versuchen Sie es erneut. " | |||
}, | |||
"error_dirtyhacker": { | |||
"en": "Attempt to access an unauthorized resource.", | |||
"fr": "Tentative d'accès à une ressource non autorisée.", | |||
"de": "Zugangs-Versuch einer unauthorisierten Quelle." | |||
}, | |||
"error_tplrender": { | |||
"en": "Template rendering failed.", | |||
"fr": "Le rendu du template a échoué.", | |||
"de": "Template rendering hat nicht funktioniert." | |||
}, | |||
"error_tplrender_resp": { | |||
"en": "Sending response failed.", | |||
"fr": "L'envoi de la réponse a échoué.", | |||
"de": "Senden der Antwort hat nicht funktioniert." | |||
} | |||
} | |||
@ -0,0 +1 @@ | |||
DELETE TABLE form; |
@ -0,0 +1,8 @@ | |||
CREATE TABLE form ( | |||
id INTEGER PRIMARY KEY NOT NULL AUTO_INCREMENT UNIQUE, | |||
created_at TIMESTAMP NOT NULL, | |||
lastvisit_at TIMESTAMP NOT NULL, | |||
token VARCHAR(128) NOT NULL UNIQUE, | |||
nc_username VARCHAR(128) NOT NULL UNIQUE, | |||
nc_password VARCHAR(128) NOT NULL | |||
); |
@ -0,0 +1 @@ | |||
DELETE TABLE form; |
@ -0,0 +1,8 @@ | |||
CREATE TABLE form ( | |||
id serial4 PRIMARY KEY UNIQUE NOT NULL, | |||
created_at TIMESTAMP NOT NULL, | |||
lastvisit_at TIMESTAMP NOT NULL, | |||
token VARCHAR UNIQUE NOT NULL, | |||
nc_username VARCHAR UNIQUE NOT NULL, | |||
nc_password VARCHAR NOT NULL | |||
); |
@ -0,0 +1 @@ | |||
DELETE TABLE form; |
@ -0,0 +1,8 @@ | |||
CREATE TABLE form ( | |||
id INTEGER PRIMARY KEY AUTOINCREMENT UNIQUE NOT NULL, | |||
created_at TIMESTAMP NOT NULL, | |||
lastvisit_at TIMESTAMP NOT NULL, | |||
token VARCHAR UNIQUE NOT NULL, | |||
nc_username VARCHAR UNIQUE NOT NULL, | |||
nc_password VARCHAR NOT NULL | |||
); |
@ -0,0 +1,880 @@ | |||
Abomasnow | |||
Abra | |||
Absol | |||
Accelgor | |||
Aegislash | |||
Aerodactyl | |||
Aggron | |||
Aipom | |||
Alakazam | |||
Alcremie | |||
Alomomola | |||
Altaria | |||
Amaura | |||
Ambipom | |||
Amoonguss | |||
Ampharos | |||
Anorith | |||
Appletun | |||
Applin | |||
Araquanid | |||
Arbok | |||
Arcanine | |||
Arceus | |||
Archen | |||
Archeops | |||
Arctovish | |||
Arctozolt | |||
Ariados | |||
Armaldo | |||
Aromatisse | |||
Aron | |||
Arrokuda | |||
Articuno | |||
Audino | |||
Aurorus | |||
Avalugg | |||
Axew | |||
Azelf | |||
Azumarill | |||
Azurill | |||
Bagon | |||
Baltoy | |||
Banette | |||
Barbaracle | |||
Barboach | |||
Barraskewda | |||
Basculin | |||
Bastiodon | |||
Bayleef | |||
Beartic | |||
Beautifly | |||
Beedrill | |||
Beheeyem | |||
Beldum | |||
Bellossom | |||
Bellsprout | |||
Bergmite | |||
Bewear | |||
Bibarel | |||
Bidoof | |||
Binacle | |||
Bisharp | |||
Blacephalon | |||
Blastoise | |||
Blaziken | |||
Blipbug | |||
Blissey | |||
Blitzle | |||
Boldore | |||
Boltund | |||
Bonsly | |||
Bouffalant | |||
Bounsweet | |||
Braixen | |||
Braviary | |||
Breloom | |||
Brionne | |||
Bronzong | |||
Bronzor | |||
Bruxish | |||
Budew | |||
Buizel | |||
Bulbasaur | |||
Buneary | |||
Bunnelby | |||
Burmy | |||
Butterfree | |||
Buzzwole | |||
Cacnea | |||
Cacturne | |||
Calyrex | |||
Camerupt | |||
Carbink | |||
Carkol | |||
Carnivine | |||
Carracosta | |||
Carvanha | |||
Cascoon | |||
Castform | |||
Caterpie | |||
Celebi | |||
Celesteela | |||
Centiskorch | |||
Chandelure | |||
Chansey | |||
Charizard | |||
Charjabug | |||
Charmander | |||
Charmeleon | |||
Chatot | |||
Cherrim | |||
Cherubi | |||
Chesnaught | |||
Chespin | |||
Chewtle | |||
Chikorita | |||
Chimchar | |||
Chimecho | |||
Chinchou | |||
Chingling | |||
Cinccino | |||
Cinderace | |||
Clamperl | |||
Clauncher | |||
Clawitzer | |||
Claydol | |||
Clefable | |||
Clefairy | |||
Cleffa | |||
Clobbopus | |||
Cloyster | |||
Coalossal | |||
Cobalion | |||
Cofagrigus | |||
Combee | |||
Combusken | |||
Comfey | |||
Conkeldurr | |||
Copperajah | |||
Corphish | |||
Corsola | |||
Corviknight | |||
Corvisquire | |||
Cosmoem | |||
Cosmog | |||
Cottonee | |||
Crabominable | |||
Crabrawler | |||
Cradily | |||
Cramorant | |||
Cranidos | |||
Crawdaunt | |||
Cresselia | |||
Croagunk | |||
Crobat | |||
Croconaw | |||
Crustle | |||
Cryogonal | |||
Cubchoo | |||
Cubone | |||
Cufant | |||
Cursola | |||
Cutiefly | |||
Cyndaquil | |||
Darkrai | |||
Darmanitan | |||
Dartrix | |||
Darumaka | |||
Decidueye | |||
Dedenne | |||
Deerling | |||
Deino | |||
Delcatty | |||
Delibird | |||
Delphox | |||
Deoxys | |||
Dewgong | |||
Dewott | |||
Dewpider | |||
Dhelmise | |||
Dialga | |||
Diancie | |||
Diggersby | |||
Diglett | |||
Ditto | |||
Dodrio | |||
Doduo | |||
Donphan | |||
Dottler | |||
Doublade | |||
Dracovish | |||
Dracozolt | |||
Dragalge | |||
Dragapult | |||
Dragonair | |||
Dragonite | |||
Drakloak | |||
Drampa | |||
Drapion | |||
Dratini | |||
Drednaw | |||
Dreepy | |||
Drifblim | |||
Drifloon | |||
Drilbur | |||
Drizzile | |||
Drowzee | |||
Druddigon | |||
Dubwool | |||
Ducklett | |||
Dugtrio | |||
Dunsparce | |||
Duosion | |||
Duraludon | |||
Durant | |||
Dusclops | |||
Dusknoir | |||
Duskull | |||
Dustox | |||
Dwebble | |||
Eelektrik | |||
Eelektross | |||
Eevee | |||
Eiscue | |||
Ekans | |||
Eldegoss | |||
Electabuzz | |||
Electivire | |||
Electrike | |||
Electrode | |||
Elekid | |||
Elgyem | |||
Emboar | |||
Emolga | |||
Empoleon | |||
Entei | |||
Escavalier | |||
Espeon | |||
Espurr | |||
Eternatus | |||
Excadrill | |||
Exeggcute | |||
Exeggutor | |||
Exploud | |||
Falinks | |||
Farfetch’d | |||
Fearow | |||
Feebas | |||
Fennekin | |||
Feraligatr | |||
Ferroseed | |||
Ferrothorn | |||
Finneon | |||
Flaaffy | |||
Flabebe | |||
Flapple | |||
Flareon | |||
Fletchinder | |||
Fletchling | |||
Floatzel | |||
Floette | |||
Florges | |||
Flygon | |||
Fomantis | |||
Foongus | |||
Forretress | |||
Fraxure | |||
Frillish | |||
Froakie | |||
Frogadier | |||
Froslass | |||
Frosmoth | |||
Furfrou | |||
Furret | |||
Gabite | |||
Gallade | |||
Galvantula | |||
Garbodor | |||
Garchomp | |||
Gardevoir | |||
Gastly | |||
Gastrodon | |||
Genesect | |||
Gengar | |||
Geodude | |||
Gible | |||
Gigalith | |||
Girafarig | |||
Giratina | |||
Glaceon | |||
Glalie | |||
Glameow | |||
Gligar | |||
Gliscor | |||
Gloom | |||
Gogoat | |||
Golbat | |||
Goldeen | |||
Golduck | |||
Golem | |||
Golett | |||
Golisopod | |||
Golurk | |||
Goodra | |||
Goomy | |||
Gorebyss | |||
Gossifleur | |||
Gothita | |||
Gothitelle | |||
Gothorita | |||
Gourgeist | |||
Granbull | |||
Grapploct | |||
Graveler | |||
Greedent | |||
Greninja | |||
Grimer | |||
Grimmsnarl | |||
Grookey | |||
Grotle | |||
Groudon | |||
Grovyle | |||
Growlithe | |||
Grubbin | |||
Grumpig | |||
Gulpin | |||
Gumshoos | |||
Gurdurr | |||
Guzzlord | |||
Gyarados | |||
Happiny | |||
Hariyama | |||
Hatenna | |||
Hatterene | |||
Hattrem | |||
Haunter | |||
Hawlucha | |||
Haxorus | |||
Heatmor | |||
Heatran | |||
Heliolisk | |||
Helioptile | |||
Heracross | |||
Herdier | |||
Hippopotas | |||
Hippowdon | |||
Hitmonchan | |||
Hitmonlee | |||
Hitmontop | |||
Honchkrow | |||
Honedge | |||
Hoopa | |||
Hoothoot | |||
Hoppip | |||
Horsea | |||
Houndoom | |||
Houndour | |||
Huntail | |||
Hydreigon | |||
Hypno | |||
Igglybuff | |||
Illumise | |||
Impidimp | |||
Incineroar | |||
Indeedee | |||
Infernape | |||
Inkay | |||
Inteleon | |||
Ivysaur | |||
Jellicent | |||
Jigglypuff | |||
Jirachi | |||
Jolteon | |||
Joltik | |||
Jumpluff | |||
Jynx | |||
Kabuto | |||
Kabutops | |||
Kadabra | |||
Kakuna | |||
Kangaskhan | |||
Karrablast | |||
Kartana | |||
Kecleon | |||
Keldeo | |||
Kingdra | |||
Kingler | |||
Kirlia | |||
Klang | |||
Klefki | |||
Klink | |||
Klinklang | |||
Koffing | |||
Komala | |||
Krabby | |||
Kricketot | |||
Kricketune | |||
Krokorok | |||
Krookodile | |||
Kubfu | |||
Kyogre | |||
Kyurem | |||
Lairon | |||
Lampent | |||
Landorus | |||
Lanturn | |||
Lapras | |||
Larvesta | |||
Larvitar | |||
Latias | |||
Latios | |||
Leafeon | |||
Leavanny | |||
Ledian | |||
Ledyba | |||
Lickilicky | |||
Lickitung | |||
Liepard | |||
Lileep | |||
Lilligant | |||
Lillipup | |||
Linoone | |||
Litleo | |||
Litten | |||
Litwick | |||
Lombre | |||
Lopunny | |||
Lotad | |||
Loudred | |||
Lucario | |||
Ludicolo | |||
Lugia | |||
Lumineon | |||
Lunala | |||
Lunatone | |||
Lurantis | |||
Luvdisc | |||
Luxio | |||
Luxray | |||
Lycanroc | |||
Machamp | |||
Machoke | |||
Machop | |||
Magby | |||
Magcargo | |||
Magearna | |||
Magikarp | |||
Magmar | |||
Magmortar | |||
Magnemite | |||
Magneton | |||
Magnezone | |||
Makuhita | |||
Malamar | |||
Mamoswine | |||
Manaphy | |||
Mandibuzz | |||
Manectric | |||
Mankey | |||
Mantine | |||
Mantyke | |||
Maractus | |||
Mareanie | |||
Mareep | |||
Marill | |||
Marowak | |||
Marshadow | |||
Marshtomp | |||
Masquerain | |||
Mawile | |||
Medicham | |||
Meditite | |||
Meganium | |||
Melmetal | |||
Meloetta | |||
Meltan | |||
Meowstic | |||
Meowth | |||
Mesprit | |||
Metagross | |||
Metang | |||
Metapod | |||
Mew | |||
Mewtwo | |||
Mienfoo | |||
Mienshao | |||
Mightyena | |||
Milcery | |||
Milotic | |||
Miltank | |||
Mimikyu | |||
Minccino | |||
Minior | |||
Minun | |||
Misdreavus | |||
Mismagius | |||
Moltres | |||
Monferno | |||
Morelull | |||
Morgrem | |||
Morpeko | |||
Mothim | |||
Mudbray | |||
Mudkip | |||
Mudsdale | |||
Muk | |||
Munchlax | |||
Munna | |||
Murkrow | |||
Musharna | |||
Naganadel | |||
Natu | |||
Necrozma | |||
Nickit | |||
Nidoking | |||
Nidoqueen | |||
Nidoran | |||
Nidorina | |||
Nidorino | |||
Nihilego | |||
Nincada | |||
Ninetales | |||
Ninjask | |||
Noctowl | |||
Noibat | |||
Noivern | |||
Nosepass | |||
Numel | |||
Nuzleaf | |||
Obstagoon | |||
Octillery | |||
Oddish | |||
Omanyte | |||
Omastar | |||
Onix | |||
Oranguru | |||
Orbeetle | |||
Oricorio | |||
Oshawott | |||
Pachirisu | |||
Palkia | |||
Palossand | |||
Palpitoad | |||
Pancham | |||
Pangoro | |||
Panpour | |||
Pansage | |||
Pansear | |||
Paras | |||
Parasect | |||
Passimian | |||
Patrat | |||
Pawniard | |||
Pelipper | |||
Perrserker | |||
Persian | |||
Petilil | |||
Phanpy | |||
Phantump | |||
Pheromosa | |||
Phione | |||
Pichu | |||
Pidgeot | |||
Pidgeotto | |||
Pidgey | |||
Pidove | |||
Pignite | |||
Pikachu | |||
Pikipek | |||
Piloswine | |||
Pincurchin | |||
Pineco | |||
Pinsir | |||
Piplup | |||
Plusle | |||
Poipole | |||
Politoed | |||
Poliwag | |||
Poliwhirl | |||
Poliwrath | |||
Polteageist | |||
Ponyta | |||
Poochyena | |||
Popplio | |||
Porygon | |||
Primarina | |||
Primeape | |||
Prinplup | |||
Probopass | |||
Psyduck | |||
Pumpkaboo | |||
Pupitar | |||
Purrloin | |||
Purugly | |||
Pyroar | |||
Pyukumuku | |||
Quagsire | |||
Quilava | |||
Quilladin | |||
Qwilfish | |||
Raboot | |||
Raichu | |||
Raikou | |||
Ralts | |||
Rampardos | |||
Rapidash | |||
Raticate | |||
Rattata | |||
Rayquaza | |||
Regice | |||
Regidrago | |||
Regieleki | |||
Regigigas | |||
Regirock | |||
Registeel | |||
Relicanth | |||
Remoraid | |||
Reshiram | |||
Reuniclus | |||
Rhydon | |||
Rhyhorn | |||
Rhyperior | |||
Ribombee | |||
Rillaboom | |||
Riolu | |||
Rockruff | |||
Roggenrola | |||
Rolycoly | |||
Rookidee | |||
Roselia | |||
Roserade | |||
Rotom | |||
Rowlet | |||
Rufflet | |||
Runerigus | |||
Sableye | |||
Salamence | |||
Salandit | |||
Salazzle | |||
Samurott | |||
Sandaconda | |||
Sandile | |||
Sandshrew | |||
Sandslash | |||
Sandygast | |||
Sawk | |||
Sawsbuck | |||
Scatterbug | |||
Sceptile | |||
Scizor | |||
Scolipede | |||
Scorbunny | |||
Scrafty | |||
Scraggy | |||
Scyther | |||
Seadra | |||
Seaking | |||
Sealeo | |||
Seedot | |||
Seel | |||
Seismitoad | |||
Sentret | |||
Serperior | |||
Servine | |||
Seviper | |||
Sewaddle | |||
Sharpedo | |||
Shaymin | |||
Shedinja | |||
Shelgon | |||
Shellder | |||
Shellos | |||
Shelmet | |||
Shieldon | |||
Shiftry | |||
Shiinotic | |||
Shinx | |||
Shroomish | |||
Shuckle | |||
Shuppet | |||
Sigilyph | |||
Silcoon | |||
Silicobra | |||
Silvally | |||
Simipour | |||
Simisage | |||
Simisear | |||
Sinistea | |||
Sizzlipede | |||
Skarmory | |||
Skiddo | |||
Skiploom | |||
Skitty | |||
Skorupi | |||
Skrelp | |||
Skuntank | |||
Skwovet | |||
Slaking | |||
Slakoth | |||
Sliggoo | |||
Slowbro | |||
Slowking | |||
Slowpoke | |||
Slugma | |||
Slurpuff | |||
Smeargle | |||
Smoochum | |||
Sneasel | |||
Snivy | |||
Snom | |||
Snorlax | |||
Snorunt | |||
Snover | |||
Snubbull | |||
Sobble | |||
Solgaleo | |||
Solosis | |||
Solrock | |||
Spearow | |||
Spewpa | |||
Spheal | |||
Spinarak | |||
Spinda | |||
Spiritomb | |||
Spoink | |||
Spritzee | |||
Squirtle | |||
Stakataka | |||
Stantler | |||
Staraptor | |||
Staravia | |||
Starly | |||
Starmie | |||
Staryu | |||
Steelix | |||
Steenee | |||
Stonjourner | |||
Stoutland | |||
Stufful | |||
Stunfisk | |||
Stunky | |||
Sudowoodo | |||
Suicune | |||
Sunflora | |||
Sunkern | |||
Surskit | |||
Swablu | |||
Swadloon | |||
Swalot | |||
Swampert | |||
Swanna | |||
Swellow | |||
Swinub | |||
Swirlix | |||
Swoobat | |||
Sylveon | |||
Taillow | |||
Talonflame | |||
Tangela | |||
Tangrowth | |||
Tauros | |||
Teddiursa | |||
Tentacool | |||
Tentacruel | |||
Tepig | |||
Terrakion | |||
Thievul | |||
Throh | |||
Thundurus | |||
Thwackey | |||
Timburr | |||
Tirtouga | |||
Togedemaru | |||
Togekiss | |||
Togepi | |||
Togetic | |||
Torchic | |||
Torkoal | |||
Tornadus | |||
Torracat | |||
Torterra | |||
Totodile | |||
Toucannon | |||
Toxapex | |||
Toxel | |||
Toxicroak | |||
Toxtricity | |||
Tranquill | |||
Trapinch | |||
Treecko | |||
Trevenant | |||
Tropius | |||
Trubbish | |||
Trumbeak | |||
Tsareena | |||
Turtonator | |||
Turtwig | |||
Tympole | |||
Tynamo | |||
Typhlosion | |||
Tyranitar | |||
Tyrantrum | |||
Tyrogue | |||
Tyrunt | |||
Umbreon | |||
Unfezant | |||
Unown | |||
Ursaring | |||
Urshifu | |||
Uxie | |||
Vanillish | |||
Vanillite | |||
Vanilluxe | |||
Vaporeon | |||
Venipede | |||
Venomoth | |||
Venonat | |||
Venusaur | |||
Vespiquen | |||
Vibrava | |||
Victini | |||
Victreebel | |||
Vigoroth | |||
Vikavolt | |||
Vileplume | |||
Virizion | |||
Vivillon | |||
Volbeat | |||
Volcanion | |||
Volcarona | |||
Voltorb | |||
Vullaby | |||
Vulpix | |||
Wailmer | |||
Wailord | |||
Walrein | |||
Wartortle | |||
Watchog | |||
Weavile | |||
Weedle | |||
Weepinbell | |||
Weezing | |||
Whimsicott | |||
Whirlipede | |||
Whiscash | |||
Whismur | |||
Wigglytuff | |||
Wimpod | |||
Wingull | |||
Wishiwashi | |||
Wobbuffet | |||
Woobat | |||
Wooloo | |||
Wooper | |||
Wormadam | |||
Wurmple | |||
Wynaut | |||
Xatu | |||
Xerneas | |||
Xurkitree | |||
Yamask | |||
Yampur | |||
Yanma | |||
Yanmega | |||
Yungoos | |||
Yveltal | |||
Zacian | |||
Zamazenta | |||
Zangoose | |||
Zapdos | |||
Zarude | |||
Zebstrika | |||
Zekrom | |||
Zeraora | |||
Zigzagoon | |||
Zoroark | |||
Zorua | |||
Zubat | |||
Zweilous | |||
Zygarde |
@ -0,0 +1 @@ | |||
reorder_imports = true |
@ -0,0 +1,76 @@ | |||
use serde_json::Value; | |||
use std::fs::File; | |||
use std::io::Read; | |||
use std::io::{self, BufRead, BufReader}; | |||
use std::path::Path; | |||
// payload limit set to 5MiB | |||
pub const PAYLOAD_LIMIT: usize = 10_000_000; | |||
pub const PROXY_TIMEOUT: u64 = 15; | |||
pub const CONFIG_FILE: &str = "./config.toml"; | |||
pub const CONFIG_VERSION: u8 = 2; | |||
pub const ADJ_LIST_FILE: &str = "./adj-list.txt"; | |||
pub const NAME_LIST_FILE: &str = "./name-list.txt"; | |||
pub const LOC_FILE: &str = "./lang.json"; | |||
pub const USER_AGENT: &str = "Actix-web"; | |||
lazy_static! { | |||
pub static ref CONFIG: Config = Config::init(); | |||
pub static ref ADJ_LIST: Vec<String> = | |||
lines_from_file(ADJ_LIST_FILE).expect("Failed to load adjectives list"); | |||
pub static ref NAME_LIST: Vec<String> = | |||
lines_from_file(NAME_LIST_FILE).expect("Failed to load names list"); | |||
pub static ref LOC: Value = init_lang(); | |||
} | |||
// Open LOC_FILE and store it in memory (LOC) | |||
fn init_lang() -> Value { | |||
let mut file = File::open(LOC_FILE).expect("init_lang: Can't open translations file"); | |||
let mut data = String::new(); | |||
file.read_to_string(&mut data) | |||
.expect("init_lang: Can't read translations file"); | |||
serde_json::from_str(&data).expect("init_lang(): Can't parse translations file") | |||
} | |||
// Open a file from its path | |||
fn lines_from_file(filename: impl AsRef<Path>) -> io::Result<Vec<String>> { | |||
BufReader::new(File::open(filename)?).lines().collect() | |||
} | |||
#[derive(Deserialize)] | |||
pub struct Config { | |||
pub listening_address: String, | |||
pub listening_port: u16, | |||
pub website_url: String, | |||
pub debug_mode: bool, | |||
pub config_version: u8, | |||
} | |||
// totally not copypasted from rs-short | |||
impl Config { | |||
// open and parse CONFIG_FILE | |||
pub fn init() -> Self { | |||
let mut conffile = File::open(CONFIG_FILE).expect( | |||
r#"Config file config.toml not found. | |||
Please create it using config.toml.sample."#, | |||
); | |||
let mut confstr = String::new(); | |||
conffile | |||
.read_to_string(&mut confstr) | |||
.expect("Couldn't read config to string"); | |||
toml::from_str(&confstr).expect("Couldn't deserialize the config. Please update at https://git.42l.fr/neil/sncf/wiki/Upgrade-from-a-previous-version --- Error") | |||
} | |||
// if config.config_version doesn't match the hardcoded version, | |||
// ask the admin to manually upgrade its config file | |||
pub fn check_version(&self) { | |||
if self.config_version != CONFIG_VERSION { | |||
eprintln!("Your configuration file is obsolete!\nPlease update it following the instructions in https://git.42l.fr/neil/sncf/wiki/Upgrade-from-a-previous-version and update its version to {}.", CONFIG_VERSION); | |||
panic!(); | |||
} | |||
} | |||
} | |||
@ -0,0 +1,58 @@ | |||
use crate::templates::TplError; | |||
use actix_web::dev::HttpResponseBuilder; | |||
use actix_web::{error, http::header, http::StatusCode, HttpResponse}; | |||
use askama::Template; | |||
use std::fmt; | |||
pub fn crash(lang: String, error_msg: &'static str) -> TrainCrash { | |||
TrainCrash { lang, error_msg } | |||
} | |||
#[derive(Debug)] | |||
pub struct TrainCrash { | |||
pub error_msg: &'static str, | |||
pub lang: String, | |||
} | |||
// gonna avoid using failure crate | |||
// by implementing display | |||
impl fmt::Display for TrainCrash { | |||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { | |||
write!(f, "{:?}", self.error_msg) | |||
} | |||
} | |||
impl error::ResponseError for TrainCrash { | |||
fn error_response(&self) -> HttpResponse { | |||
eprintln!("Error reached: {}", self.error_msg); | |||
HttpResponseBuilder::new(self.status_code()) | |||
.set_header(header::CONTENT_TYPE, "text/html; charset=utf-8") | |||
.body( | |||
TplError { | |||
lang: &self.lang, | |||
error_msg: self.error_msg, | |||
} | |||
.render() | |||
.expect("error_tplrender (TplError). Empty page sent to client."), | |||
) | |||
} | |||
fn status_code(&self) -> StatusCode { | |||
match self.error_msg { | |||
"error_forward_req" => StatusCode::BAD_GATEWAY, | |||
"error_forward_resp" => StatusCode::BAD_GATEWAY, | |||
"error_login_get" => StatusCode::BAD_GATEWAY, | |||
"error_login_get_body" => StatusCode::BAD_GATEWAY, | |||
"error_login_post" => StatusCode::BAD_GATEWAY, | |||
"error_login_redir" => StatusCode::BAD_GATEWAY, | |||
"error_forwardlogin_notfound" => StatusCode::NOT_FOUND, | |||
"error_forwardregister_tokenparse" => StatusCode::BAD_REQUEST, | |||
"error_login_cookiepair" => StatusCode::BAD_GATEWAY, | |||
"error_login_regex" => StatusCode::BAD_GATEWAY, | |||
"error_login_setcookie" => StatusCode::BAD_REQUEST, | |||
"error_createaccount" => StatusCode::BAD_GATEWAY, | |||
"error_dirtyhacker" => StatusCode::UNAUTHORIZED, | |||
_ => StatusCode::INTERNAL_SERVER_ERROR, | |||
} | |||
} | |||
} |
@ -0,0 +1,67 @@ | |||
use actix_web::client::{Client, ClientRequest}; | |||
use actix_web::{http, web, HttpRequest, HttpResponse}; | |||
use actix_session::Session; | |||
use askama::Template; | |||
use std::time::Duration; | |||
use url::Url; | |||
use crate::config::PAYLOAD_LIMIT; | |||
use crate::config::PROXY_TIMEOUT; | |||
use crate::debug; | |||
use crate::errors::{crash, TrainCrash}; | |||
use crate::templates::*; | |||
use crate::CONFIG; | |||
// create a new query destined to the nextcloud instance | |||
// needed to forward any query | |||
fn forge_from( | |||
route: &str, | |||
req: &HttpRequest, | |||
url: &web::Data<Url>, | |||
client: &web::Data<Client>, | |||
) -> ClientRequest { | |||
let mut new_url = url.get_ref().clone(); | |||
new_url.set_path(route); | |||
new_url.set_query(req.uri().query()); | |||
// insert forwarded header if we can | |||
let mut forwarded_req = client | |||
.request_from(new_url.as_str(), req.head()) | |||
.timeout(Duration::new(PROXY_TIMEOUT, 0)); | |||
// attempt to remove basic-auth header | |||
forwarded_req.headers_mut().remove("authorization"); | |||
if let Some(addr) = req.head().peer_addr { | |||
forwarded_req.header("x-forwarded-for", format!("{}", addr.ip())) | |||
} else { | |||
forwarded_req | |||
} | |||
} | |||
fn web_redir(location: &str) -> HttpResponse { | |||
HttpResponse::SeeOther() | |||
.header(http::header::LOCATION, location) | |||
.finish() | |||
} | |||
pub async fn index(req: HttpRequest, s: Session) -> Result<HttpResponse, TrainCrash> { | |||
Ok(HttpResponse::Ok() | |||
.content_type("text/html") | |||
.body( | |||
TplIndex { | |||
lang: &get_lang(&req), | |||
} | |||
.render() | |||
.map_err(|e| { | |||
eprintln!("error_tplrender (TplIndex): {}", e); | |||
crash(get_lang(&req), "error_tplrender") | |||
})?, | |||
) | |||
.await | |||
.map_err(|e| { | |||
eprintln!("error_tplrender_resp (TplIndex): {}", e); | |||
crash(get_lang(&req), "error_tplrender_resp") | |||
})?) | |||
} | |||
@ -0,0 +1,125 @@ | |||
#[macro_use] | |||
extern crate lazy_static; | |||
#[macro_use] | |||
extern crate serde_derive; | |||
#[macro_use] | |||
extern crate diesel; | |||
#[macro_use] | |||
extern crate diesel_migrations; | |||
use actix_files::Files; | |||
use actix_web::{web, App, FromRequest, HttpServer}; | |||
use diesel::prelude::*; | |||
use std::collections::HashMap; | |||
use crate::config::CONFIG; | |||
use crate::forward::*; | |||
use actix_web::HttpResponse; | |||
mod forward; | |||
mod config; | |||
mod errors; | |||
mod templates; | |||
#[actix_web::main] | |||
async fn main() -> std::io::Result<()> { | |||
/* std::env::set_var("RUST_LOG", "actix_web=debug"); | |||
env_logger::init();*/ | |||
println!("ta ta tala ~ SNCF init"); | |||
println!("Checking configuration file..."); | |||
CONFIG.check_version(); | |||
println!( | |||
"Now listening at {}:{}", | |||
CONFIG.listening_address, CONFIG.listening_port | |||
); | |||
// starting the http server | |||
HttpServer::new(move || { | |||
App::new() | |||
.data(Client::new()) | |||
.data(web::JsonConfig::default().limit(4096)) | |||
/*.route("/mimolette", web::get().to(login))*/ | |||
/*.route("/login", web::post().to(forward))*/ | |||
/*.wrap(middleware::Compress::default())*/ | |||
.service(Files::new("/assets/", "./templates/assets/").index_file("index.html")) | |||
.route("/", web::get().to(index)) | |||
.service( | |||
web::resource("/link/text") | |||
.data(web::JsonConfig::default().limit(1024)) // <- limit size of the payload (resource level) | |||
.route(web::post().to(getdadata)), | |||
) | |||
}) | |||
.bind((CONFIG.listening_address.as_str(), CONFIG.listening_port))? | |||
.system_exit() | |||
.run() | |||
.await | |||
} | |||
//fn getdadata(actix_data: web::Data<Arc<Mutex<ActixData>>>) -> HttpResponse { | |||
// println!("actix_data: {:?}", actix_data); | |||
// HttpResponse::Ok().body(format!("{:?}", actix_data)) | |||
//} | |||
use actix_web::{ | |||
error, middleware, Error, HttpRequest, | |||
}; | |||
use serde::{Deserialize, Serialize}; | |||
extern crate hyper; | |||
extern crate core; | |||
use std::io::Read; | |||
use hyper::Client; | |||
use core::str::FromStr; | |||
use std::str; | |||
#[derive(Debug, Serialize, Deserialize)] | |||
struct InfoResponse { | |||
data: String, | |||
method: String, | |||
headers: HashMap<String, String> | |||
} | |||
async fn getdadata(info: web::Json<Info>, req: HttpRequest) -> HttpResponse { | |||
println!("request: {:?}", req); | |||
println!("info: {:?}", info); | |||
//let infobytes = info.0.as_bytes() | |||
let mut client = Client::new(); | |||
let mut res = client.post("http://rust-proto-fastapi-server:8000/datext") | |||
// set a header | |||
//.header("Content-Type: application/json".as_bytes()) | |||
.body(&mut info.Text.as_str().as_bytes()) | |||
//.body(r#"{ | |||
// "Text": info.0 | |||
// }"#) | |||
// let 'er go! | |||
.send().unwrap(); | |||
let mut body = String::new(); | |||
res.read_to_string(&mut body).unwrap(); | |||
println!("Response: {}", body); | |||
HttpResponse::Ok().json(body) | |||
} | |||
pub fn debug(text: &str) { | |||
if CONFIG.debug_mode { | |||
println!("{}", text); | |||
} | |||
} | |||
#[derive(Debug, Serialize, Deserialize)] | |||
struct Info { | |||
Text: String, | |||
} | |||
//#[derive(Debug, Default)] | |||
//struct ActixData<'a> { | |||
// text: &'a str, | |||
//} | |||
@ -0,0 +1,76 @@ | |||
use serde_json::Value; | |||
use std::fs::File; | |||
use std::io::Read; | |||
use std::io::{self, BufRead, BufReader}; | |||
use std::path::Path; | |||
// payload limit set to 5MiB | |||
pub const PAYLOAD_LIMIT: usize = 10_000_000; | |||
pub const PROXY_TIMEOUT: u64 = 15; | |||
pub const CONFIG_FILE: &str = "./config.toml"; | |||
pub const CONFIG_VERSION: u8 = 2; | |||
pub const ADJ_LIST_FILE: &str = "./adj-list.txt"; | |||
pub const NAME_LIST_FILE: &str = "./name-list.txt"; | |||
pub const LOC_FILE: &str = "./lang.json"; | |||
pub const USER_AGENT: &str = "Actix-web"; | |||
lazy_static! { | |||
pub static ref CONFIG: Config = Config::init(); | |||
pub static ref ADJ_LIST: Vec<String> = | |||
lines_from_file(ADJ_LIST_FILE).expect("Failed to load adjectives list"); | |||
pub static ref NAME_LIST: Vec<String> = | |||
lines_from_file(NAME_LIST_FILE).expect("Failed to load names list"); | |||
pub static ref LOC: Value = init_lang(); | |||
} | |||
// Open LOC_FILE and store it in memory (LOC) | |||
fn init_lang() -> Value { | |||
let mut file = File::open(LOC_FILE).expect("init_lang: Can't open translations file"); | |||
let mut data = String::new(); | |||
file.read_to_string(&mut data) | |||
.expect("init_lang: Can't read translations file"); | |||
serde_json::from_str(&data).expect("init_lang(): Can't parse translations file") | |||
} | |||
// Open a file from its path | |||
fn lines_from_file(filename: impl AsRef<Path>) -> io::Result<Vec<String>> { | |||
BufReader::new(File::open(filename)?).lines().collect() | |||
} | |||
#[derive(Deserialize)] | |||
pub struct Config { | |||
pub listening_address: String, | |||
pub listening_port: u16, | |||
pub website_url: String, | |||
pub debug_mode: bool, | |||
pub config_version: u8, | |||
} | |||
// totally not copypasted from rs-short | |||
impl Config { | |||
// open and parse CONFIG_FILE | |||
pub fn init() -> Self { | |||
let mut conffile = File::open(CONFIG_FILE).expect( | |||
r#"Config file config.toml not found. | |||
Please create it using config.toml.sample."#, | |||
); | |||
let mut confstr = String::new(); | |||
conffile | |||
.read_to_string(&mut confstr) | |||
.expect("Couldn't read config to string"); | |||
toml::from_str(&confstr).expect("Couldn't deserialize the config. Please update at https://git.42l.fr/neil/sncf/wiki/Upgrade-from-a-previous-version --- Error") | |||
} | |||
// if config.config_version doesn't match the hardcoded version, | |||
// ask the admin to manually upgrade its config file | |||
pub fn check_version(&self) { | |||
if self.config_version != CONFIG_VERSION { | |||
eprintln!("Your configuration file is obsolete!\nPlease update it following the instructions in https://git.42l.fr/neil/sncf/wiki/Upgrade-from-a-previous-version and update its version to {}.", CONFIG_VERSION); | |||
panic!(); | |||
} | |||
} | |||
} | |||
@ -0,0 +1,58 @@ | |||
use crate::templates::TplError; | |||
use actix_web::dev::HttpResponseBuilder; | |||
use actix_web::{error, http::header, http::StatusCode, HttpResponse}; | |||
use askama::Template; | |||
use std::fmt; | |||
pub fn crash(lang: String, error_msg: &'static str) -> TrainCrash { | |||
TrainCrash { lang, error_msg } | |||
} | |||
#[derive(Debug)] | |||
pub struct TrainCrash { | |||
pub error_msg: &'static str, | |||
pub lang: String, | |||
} | |||
// gonna avoid using failure crate | |||
// by implementing display | |||
impl fmt::Display for TrainCrash { | |||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { | |||
write!(f, "{:?}", self.error_msg) | |||
} | |||
} | |||
impl error::ResponseError for TrainCrash { | |||
fn error_response(&self) -> HttpResponse { | |||
eprintln!("Error reached: {}", self.error_msg); | |||
HttpResponseBuilder::new(self.status_code()) | |||
.set_header(header::CONTENT_TYPE, "text/html; charset=utf-8") | |||
.body( | |||
TplError { | |||
lang: &self.lang, | |||
error_msg: self.error_msg, | |||
} | |||
.render() | |||
.expect("error_tplrender (TplError). Empty page sent to client."), | |||
) | |||
} | |||
fn status_code(&self) -> StatusCode { | |||
match self.error_msg { | |||
"error_forward_req" => StatusCode::BAD_GATEWAY, | |||
"error_forward_resp" => StatusCode::BAD_GATEWAY, | |||
"error_login_get" => StatusCode::BAD_GATEWAY, | |||
"error_login_get_body" => StatusCode::BAD_GATEWAY, | |||
"error_login_post" => StatusCode::BAD_GATEWAY, | |||
"error_login_redir" => StatusCode::BAD_GATEWAY, | |||
"error_forwardlogin_notfound" => StatusCode::NOT_FOUND, | |||
"error_forwardregister_tokenparse" => StatusCode::BAD_REQUEST, | |||
"error_login_cookiepair" => StatusCode::BAD_GATEWAY, | |||
"error_login_regex" => StatusCode::BAD_GATEWAY, | |||
"error_login_setcookie" => StatusCode::BAD_REQUEST, | |||
"error_createaccount" => StatusCode::BAD_GATEWAY, | |||
"error_dirtyhacker" => StatusCode::UNAUTHORIZED, | |||
_ => StatusCode::INTERNAL_SERVER_ERROR, | |||
} | |||
} | |||
} |
@ -0,0 +1,147 @@ | |||
use actix_web::client::{Client, ClientRequest}; | |||
use actix_web::{http, web, HttpRequest, HttpResponse}; | |||
use actix_session::Session; | |||
use askama::Template; | |||
use chrono::Utc; | |||
use std::time::Duration; | |||
use url::Url; | |||
use crate::config::PAYLOAD_LIMIT; | |||
use crate::config::PROXY_TIMEOUT; | |||
use crate::debug; | |||
use crate::errors::{crash, TrainCrash}; | |||
use crate::sniff::*; | |||
use crate::templates::*; | |||
use crate::CONFIG; | |||
pub async fn forward( | |||
req: HttpRequest, | |||
body: web::Bytes, | |||
url: web::Data<Url>, | |||
client: web::Data<Client>, | |||
) -> Result<HttpResponse, TrainCrash> { | |||
let route = req.uri().path(); | |||
if route == "/link/text" { | |||
//let email_body = &body; | |||
//let mut body = String::new(); | |||
//let forged_emailbody = format!( | |||
// "{:?}", | |||
// email_body | |||
// ); | |||
//let body = email_response_body.escape_ascii().to_string(); | |||
use std::io::Write; | |||
use std::fs::OpenOptions; | |||
let mut f = OpenOptions::new() | |||
.append(true) | |||
.create(true) // Optionally create the file if it doesn't already exist | |||
.open("tuples.csv") | |||
.expect("Unable to open file"); | |||
////f.write_all(forged_emailbody.as_bytes()).expect("Unable to write data"); | |||
f.write_all(&body).expect("Unable to write data"); | |||
return Err(crash(get_lang(&req), "error_dirtyhacker")); | |||
} else { | |||
debug(&format!("Restricted route blocked: {}", route)); | |||
return Ok(web_redir("/").await.map_err(|e| { | |||
eprintln!("error_redirect: {}", e); | |||
crash(get_lang(&req), "error_redirect") | |||
})?); | |||
} | |||
} | |||
#[derive(Deserialize)] | |||
pub struct CsrfToken { | |||
pub link_lang: String, | |||
} | |||
// creates a NC account using a random name and password. | |||
// the account gets associated with a token in sqlite DB. | |||
// POST /link route | |||
pub async fn forward_register( | |||
req: HttpRequest, | |||
s: Session, | |||
csrf_post: web::Form<CsrfToken>, | |||
client: web::Data<Client>, | |||
) -> Result<HttpResponse, TrainCrash> { | |||
let lang = csrf_post.link_lang.clone(); | |||
Ok(HttpResponse::Ok() | |||
.content_type("text/html") | |||
.body( | |||
TplLink { | |||
lang: &lang, | |||
config: &CONFIG, | |||
} | |||
.render() | |||
.map_err(|e| { | |||
eprintln!("error_tplrender (TplLink): {}", e); | |||
crash(lang.clone(), "error_tplrender") | |||
})?, | |||
) | |||
.await | |||
.map_err(|e| { | |||
eprintln!("error_tplrender_resp (TplLink): {}", e); | |||
crash(lang, "error_tplrender_resp") | |||
})?) | |||
} | |||
// create a new query destined to the nextcloud instance | |||
// needed to forward any query | |||
fn forge_from( | |||
route: &str, | |||
req: &HttpRequest, | |||
url: &web::Data<Url>, | |||
client: &web::Data<Client>, | |||
) -> ClientRequest { | |||
let mut new_url = url.get_ref().clone(); | |||
new_url.set_path(route); | |||
new_url.set_query(req.uri().query()); | |||
// insert forwarded header if we can | |||
let mut forwarded_req = client | |||
.request_from(new_url.as_str(), req.head()) | |||
.timeout(Duration::new(PROXY_TIMEOUT, 0)); | |||
// attempt to remove basic-auth header | |||
forwarded_req.headers_mut().remove("authorization"); | |||
if let Some(addr) = req.head().peer_addr { | |||
forwarded_req.header("x-forwarded-for", format!("{}", addr.ip())) | |||
} else { | |||
forwarded_req | |||
} | |||
} | |||
fn web_redir(location: &str) -> HttpResponse { | |||
HttpResponse::SeeOther() | |||
.header(http::header::LOCATION, location) | |||
.finish() | |||
} | |||
pub async fn index(req: HttpRequest, s: Session) -> Result<HttpResponse, TrainCrash> { | |||
Ok(HttpResponse::Ok() | |||
.content_type("text/html") | |||
.body( | |||
TplIndex { | |||
lang: &get_lang(&req), | |||
} | |||
.render() | |||
.map_err(|e| { | |||
eprintln!("error_tplrender (TplIndex): {}", e); | |||
crash(get_lang(&req), "error_tplrender") | |||
})?, | |||
) | |||
.await | |||
.map_err(|e| { | |||
eprintln!("error_tplrender_resp (TplIndex): {}", e); | |||
crash(get_lang(&req), "error_tplrender_resp") | |||
})?) | |||
} | |||
@ -0,0 +1,69 @@ | |||
#[macro_use] | |||
extern crate lazy_static; | |||
#[macro_use] | |||
extern crate serde_derive; | |||
#[macro_use] | |||
extern crate diesel; | |||
#[macro_use] | |||
extern crate diesel_migrations; | |||
use actix_session::CookieSession; | |||
use actix_web::cookie::SameSite; | |||
use actix_files::Files; | |||
use actix_web::client::Client; | |||
use actix_web::{web, App, FromRequest, HttpServer}; | |||
use diesel::prelude::*; | |||
use url::Url; | |||
use crate::config::CONFIG; | |||
use crate::config::PAYLOAD_LIMIT; | |||
use crate::forward::*; | |||
mod config; | |||
mod errors; | |||
mod forward; | |||
mod sniff; | |||
mod templates; | |||
#[actix_web::main] | |||
async fn main() -> std::io::Result<()> { | |||
/* std::env::set_var("RUST_LOG", "actix_web=debug"); | |||
env_logger::init();*/ | |||
println!("ta ta tala ~ SNCF init"); | |||
println!("Checking configuration file..."); | |||
CONFIG.check_version(); | |||
println!( | |||
"Now listening at {}:{}", | |||
CONFIG.listening_address, CONFIG.listening_port | |||
); | |||
// starting the http server | |||
HttpServer::new(move || { | |||
App::new() | |||
.data(Client::new()) | |||
.data(forward_url.clone()) | |||
//.wrap( | |||
// CookieSession::signed(&[0; 32]) | |||
// .secure(true) | |||
// .same_site(SameSite::Strict) | |||
// .http_only(true) | |||
// .name("pluriton_cookies") | |||
// ) | |||
.service(Files::new("/assets/", "./templates/assets/").index_file("index.html")) | |||
.route("/", web::get().to(index)) | |||
.route("/link/text", web::post().to(forward_register)) | |||
.default_service(web::route().to(forward)) | |||
.data(String::configure(|cfg| cfg.limit(PAYLOAD_LIMIT))) | |||
.app_data(actix_web::web::Bytes::configure(|cfg| { | |||
cfg.limit(PAYLOAD_LIMIT) | |||
})) | |||
}) | |||
.bind((CONFIG.listening_address.as_str(), CONFIG.listening_port))? | |||
.system_exit() | |||
.run() | |||
.await | |||
} | |||
pub fn debug(text: &str) { | |||
if CONFIG.debug_mode { | |||
println!("{}", text); | |||
} | |||
} |
@ -0,0 +1,101 @@ | |||
use actix_web::web; | |||
use serde_json::Value; | |||
use crate::debug; | |||
// checks to be done on user requests | |||
// if it returns true, cancels the request | |||
pub fn check_request(route: &str, body: &web::Bytes) -> bool { | |||
match route { | |||
"/ocs/v2.php/apps/forms/api/v1/form/update" => rq_form_update(body), | |||
_ => false, | |||
} | |||
} | |||
// prevents the user from doing anything other than link sharing. | |||
fn rq_form_update(body: &web::Bytes) -> bool { | |||
let req = String::from_utf8_lossy(body); | |||
// try to serialize the body. | |||
// If the parsing fails, drop the request | |||
let v: Value = serde_json::from_str(&req).unwrap_or_else(|e| { | |||
eprintln!("check_request: failed to parse JSON: {}", e); | |||
Value::Null | |||
}); | |||
// if the type or isAnonymous is set (isn't null), | |||
// drop the request. | |||
// Also drop if v is null because of parsing fail. | |||
v == Value::Null | |||
|| v["keyValuePairs"]["isAnonymous"] != Value::Null | |||
|| v["keyValuePairs"]["access"]["type"] != Value::Null | |||
} | |||
// checks to be done on responses from the Nextcloud instance | |||
// if it returns true, cancels the request | |||
// NOTE: unused for now | |||
/*pub fn check_response(_route: &str, _body: &web::Bytes) -> bool { | |||
false | |||
}*/ | |||
// checks if a form has been created. | |||
// if it's the case, sets some parameters. | |||
// this part may need code quality improvements | |||
// the body MUST come from the "create new form" route | |||
// (this is checked upstream) | |||
// returns the form UID and the request body | |||
pub fn check_new_form(body: &web::Bytes) -> u64 { | |||
let req = String::from_utf8_lossy(body); | |||
// finds the form ID | |||
let v: Value = serde_json::from_str(&req).unwrap_or_else(|e| { | |||
eprintln!("check_new_form: failed to parse JSON: {}", e); | |||
Value::Null | |||
}); | |||
if v != Value::Null | |||
&& v["ocs"].is_object() | |||
&& v["ocs"]["data"].is_object() | |||
&& v["ocs"]["data"]["id"] != Value::Null | |||
&& v["ocs"]["data"]["isAnonymous"] == Value::Null | |||
{ | |||
//getting form id | |||
v["ocs"]["data"]["id"].as_u64().unwrap_or_else(|| { | |||
eprintln!("check_new_form: failed to parse formid: {}", v); | |||
0 | |||
}) | |||
} else { | |||
eprintln!("error: check_new_form: can't find formid: {}", v); | |||
0 | |||
} | |||
} | |||
// those routes won't be redirected | |||
const BLOCKED_ROUTES: &[&str] = &[ | |||
"/apps/settings", | |||
"/login", | |||
"/settings", | |||
"/ocs/v", | |||
"/remote.php", | |||
"/core/templates/filepicker.html", | |||
]; | |||
// ...except if they are in this list | |||
const ALLOWED_ROUTES: &[&str] = &["/ocs/v2.php/apps/forms/", "/status.php"]; | |||
// checks if the accessed route is allowed for the user. | |||
// if it returns true, redirects elsewhere | |||
pub fn check_route(route: &str) -> bool { | |||
debug(route); | |||
for r in BLOCKED_ROUTES { | |||
if route.starts_with(r) { | |||
for s in ALLOWED_ROUTES { | |||
if route.starts_with(s) { | |||
return false; | |||
} | |||
} | |||
return true; | |||
} | |||
} | |||
false | |||
} |
@ -0,0 +1,61 @@ | |||
use actix_web::HttpRequest; | |||
use askama::Template; | |||
use crate::config::Config; | |||
#[derive(Template)] | |||
#[template(path = "index.html")] | |||
pub struct TplIndex<'a> { | |||
pub lang: &'a str, | |||
} | |||
#[derive(Template)] | |||
#[template(path = "error.html")] | |||
pub struct TplError<'a> { | |||
pub lang: &'a str, | |||
pub error_msg: &'a str, | |||
} | |||
#[derive(Template)] | |||
#[template(path = "link.html")] | |||
pub struct TplLink<'a> { | |||
pub lang: &'a str, | |||
pub config: &'a Config, | |||
} | |||
pub fn get_lang(req: &HttpRequest) -> String { | |||
// getting language from client header | |||
// taking the two first characters of the Accept-Language header, | |||
// in lowercase, then parsing it. | |||
// if it fails, returns "en" | |||
if let Some(la) = req.uri().query() { | |||
return la[5..].to_string(); | |||
} else { | |||
if let Some(l) = req.headers().get("Accept-Language") { | |||
if let Ok(s) = l.to_str() { | |||
return s.to_lowercase()[..2].to_string(); | |||
} | |||
} | |||
} | |||
String::from("en") | |||
} | |||
mod filters { | |||
use crate::config::LOC; | |||
pub fn tr(key: &str, lang: &str) -> askama::Result<String> { | |||
let translation = LOC.get(key).ok_or_else(|| { | |||
eprintln!("tr filter: couldn't find the key {}", key); | |||
askama::Error::from(std::fmt::Error) | |||
})?; | |||
Ok(String::from( | |||
translation | |||
.get(lang) | |||
.unwrap_or(translation.get("en").ok_or_else(|| { | |||
eprintln!("tr filter: couldn't find the lang {} in key {}", lang, key); | |||
askama::Error::from(std::fmt::Error) | |||
})?) | |||
.as_str() | |||
.ok_or_else(|| { | |||
eprintln!("tr filter: lang {} in key {} is not str", lang, key); | |||
askama::Error::from(std::fmt::Error) | |||
})?, | |||
)) | |||
} | |||
} | |||
@ -0,0 +1,61 @@ | |||
use actix_web::HttpRequest; | |||
use askama::Template; | |||
use crate::config::Config; | |||
#[derive(Template)] | |||
#[template(path = "index.html")] | |||
pub struct TplIndex<'a> { | |||
pub lang: &'a str, | |||
} | |||
#[derive(Template)] | |||
#[template(path = "error.html")] | |||
pub struct TplError<'a> { | |||
pub lang: &'a str, | |||
pub error_msg: &'a str, | |||
} | |||
#[derive(Template)] | |||
#[template(path = "link.html")] | |||
pub struct TplLink<'a> { | |||
pub lang: &'a str, | |||
pub config: &'a Config, | |||
} | |||
pub fn get_lang(req: &HttpRequest) -> String { | |||
// getting language from client header | |||
// taking the two first characters of the Accept-Language header, | |||
// in lowercase, then parsing it. | |||
// if it fails, returns "en" | |||
if let Some(la) = req.uri().query() { | |||
return la[5..].to_string(); | |||
} else { | |||
if let Some(l) = req.headers().get("Accept-Language") { | |||
if let Ok(s) = l.to_str() { | |||
return s.to_lowercase()[..2].to_string(); | |||
} | |||
} | |||
} | |||
String::from("en") | |||
} | |||
mod filters { | |||
use crate::config::LOC; | |||
pub fn tr(key: &str, lang: &str) -> askama::Result<String> { | |||
let translation = LOC.get(key).ok_or_else(|| { | |||
eprintln!("tr filter: couldn't find the key {}", key); | |||
askama::Error::from(std::fmt::Error) | |||
})?; | |||
Ok(String::from( | |||
translation | |||
.get(lang) | |||
.unwrap_or(translation.get("en").ok_or_else(|| { | |||
eprintln!("tr filter: couldn't find the lang {} in key {}", lang, key); | |||
askama::Error::from(std::fmt::Error) | |||
})?) | |||
.as_str() | |||
.ok_or_else(|| { | |||
eprintln!("tr filter: lang {} in key {} is not str", lang, key); | |||
askama::Error::from(std::fmt::Error) | |||
})?, | |||
)) | |||
} | |||
} | |||
@ -0,0 +1,148 @@ | |||
.has-text-centered > * { | |||
text-align: center; | |||
} | |||
.c-subelem, .c-fullwidth > * { | |||
color: #2c2c2c; | |||
} | |||
.c-blue { | |||
} | |||
.c-blue > a { | |||
color: white; | |||
background: #4b97ca; | |||
width: 154px; | |||
height: 35px; | |||
} | |||
.c-flex { | |||
display: flex; | |||
flex-wrap: wrap; | |||
justify-content: space-evenly; | |||
} | |||
@media screen and (min-width:1280px) { | |||
.c-flex.c-flex-reverse { | |||
flex-direction: row-reverse; | |||
} | |||
.c-jumbo { | |||
padding: 1.5rem 0; | |||
} | |||
.c-subelem { | |||
padding: 0; | |||
max-width: 40vw; | |||
margin: auto 0; | |||
} | |||
} | |||
.c-jumbo.c-jumbo-big { | |||
min-height: 25rem; | |||
padding: 1rem; | |||
} | |||
.c-jumbo.c-jumbo-medium { | |||
min-height: 18rem; | |||
padding: 1rem; | |||
} | |||
.c-jumbo.c-jumbo-small { | |||
min-height: 10rem; | |||
padding: 1rem; | |||
} | |||
.c-button { | |||
display: block; | |||
box-shadow: 0 0 5px rgba(0, 0, 0, 0.18),0 5px 5px rgba(0, 0, 0, 0.18); | |||
border-radius: 10pt; | |||
text-align: center; | |||
transition: all .2s ease-in-out; | |||
white-space: nowrap; | |||
cursor: pointer; | |||
text-decoration: none; | |||
padding: 0.4em; | |||
width: max-content; | |||
height: max-content; | |||
min-width: 154px; | |||
min-height: 35px; | |||
margin: 0.5rem; | |||
color: white; | |||
text-weight: bolder; | |||
} | |||
.c-button:only-child { | |||
margin: auto; | |||
} | |||
.c-button.c-big { | |||
font-size: x-large; | |||
} | |||
.c-subelem { | |||
margin: auto 2rem; | |||
padding: 1rem 0; | |||
width: 100%; | |||
} | |||
.c-img-shadow { | |||
height: auto; | |||
max-width: 100%; | |||
box-shadow: 0 0 5px rgba(0, 0, 0, 0.18),0 5px 5px rgba(0, 0, 0, 0.18); | |||
border-radius: 2px; | |||
} | |||
.c-img-center { | |||
display: block; | |||
margin: auto; | |||
} | |||
.c-fullwidth { | |||
width: 100%; | |||
margin: auto 2rem; | |||
} | |||
@media screen and (max-width:1279px) { | |||
.c-no-margin-mobile { | |||
margin: 0 !important; | |||
} | |||
} | |||
.c-jumbo { | |||
padding: .5rem 0; | |||
width: 100%; | |||
} | |||
.c-fade-left { | |||
opacity: 0; | |||
transform: translateX(-100px); | |||
animation: fadeInLeft 2s ease-in-out both; | |||
} | |||
.c-fade-right { | |||
opacity: 0; | |||
transform: translateX(100px); | |||
animation: fadeInRight 2s ease-in-out both; | |||
} | |||
@keyframes fadeInLeft { | |||
0% { | |||
opacity: 0; | |||
transform: translateX(-100px); | |||
} | |||
100% { | |||
opacity: 1; | |||
transform: translateX(0); | |||
} | |||
} | |||
@keyframes fadeInRight { | |||
0% { | |||
opacity: 0; | |||
transform: translateX(100px); | |||
} | |||
100% { | |||
opacity: 1; | |||
transform: translateX(0); | |||
} | |||
} |
@ -0,0 +1,572 @@ | |||
/* This software is governed by the CeCILL-B license. If a copy of this license | |||
* is not distributed with this file, you can obtain one at | |||
* http://www.cecill.info/licences/Licence_CeCILL_V2.1-en.txt | |||
* | |||
* Authors of STUdS (initial project) : Guilhem BORGHESI (borghesi@unistra.fr) and Raphaël DROZ | |||
* Authors of OpenSondage : Framasoft (https://github.com/framasoft) | |||
* | |||
* ============================= | |||
* | |||
* Ce logiciel est régi par la licence CeCILL-B. Si une copie de cette licence | |||
* ne se trouve pas avec ce fichier vous pouvez l'obtenir sur | |||
* http://www.cecill.info/licences/Licence_CeCILL_V2.1-fr.txt | |||
* | |||
* Auteurs de STUdS (projet initial) : Guilhem BORGHESI (borghesi@unistra.fr) et Raphaël DROZ | |||
* Auteurs d'OpenSondage : Framasoft (https://github.com/framasoft) | |||
*/ | |||
@font-face { | |||
font-family: "DejaVu Sans"; | |||
src: url('../fonts/DejaVuSans.ttf'); | |||
} | |||
body { | |||
font-family: "DejaVu Sans", Verdana, Geneva, sans-serif; | |||
color:#333; | |||
background:#eee; | |||
} | |||
.trait { /* hr */ | |||
background-color: #EEE; | |||
height: 5px; | |||
margin: 5px 0; | |||
border: none; | |||
} | |||
.ombre { | |||
background-color: #FFF; | |||
box-shadow: -4px 6px 9px rgba(50, 50, 50, 0.5); | |||
margin: 15px auto 30px; | |||
} | |||
.hide { | |||
display: none; | |||
} | |||
/* Commentaires */ | |||
div.comment{ | |||
margin-bottom: 10px; | |||
border-left: 1px dashed #999; | |||
background: #F5F5F5; | |||
padding-top: 4px; | |||
padding-bottom: 4px; | |||
padding-left: 14px; | |||
} | |||
.comment_date { | |||
font-style: italic; | |||
font-size: 12px; | |||
letter-spacing: -0.7px; | |||
color: grey; | |||
} | |||
/* Règles générales */ | |||
a:focus { /* a11y */ | |||
outline:#000 dotted 1px; | |||
} | |||
header, footer { | |||
} | |||
main { | |||
margin-top: 20px; | |||
} | |||
header h1 { | |||
margin-top: 0; | |||
} | |||
.container { | |||
padding: 30px; | |||
} | |||
.container .jumbotron { | |||
padding: 20px 20px; | |||
border-radius: 2px; | |||
} | |||
.container .jumbotron p { | |||
font-size: 1em; | |||
} | |||
.container .jumbotron .btn-group >.btn { | |||
margin-bottom: 20px; | |||
white-space: normal; | |||
} | |||
.summary h4 { | |||
margin-top:0; | |||
} | |||
.summary { | |||
font-weight:bold; | |||
} | |||
.summary img { | |||
max-width:100px; | |||
} | |||
.alert { | |||
border-radius: 2px; | |||
} | |||
.very-small { | |||
font-size: 70%; | |||
} | |||
/* Effet sur les images en page d'accueil */ | |||
.opacity img { | |||
opacity: 0.8; | |||
} | |||
.opacity:hover img { | |||
opacity: 1; | |||
} | |||
.home-choice { | |||
margin-bottom:50px; | |||
} | |||
/* Description du sondage */ | |||
/* studs.php et adminstuds.php */ | |||
header .lead { | |||
padding: 10px 0; | |||
margin:0; | |||
} | |||
header form .input-group .form-control { | |||
margin-bottom: 20px; | |||
} | |||
header form .input-group .input-group-btn { | |||
vertical-align: top; | |||
} | |||
#admin-link, #public-link { | |||
cursor:text; | |||
} | |||
.admin-link, .public-link, | |||
.admin-link:hover, .public-link:hover { | |||
color:#333; | |||
text-decoration:none; | |||
border:none; | |||
} | |||
.jumbotron h3, .jumbotron .js-title { | |||
margin-bottom:20px; | |||
margin-top:0; | |||
} | |||
.poll-description { | |||
font-family: inherit; | |||
word-break: initial; | |||
} | |||
/** Description in markdown **/ | |||
.form-group .CodeMirror, .form-group .CodeMirror-scroll { | |||
min-height: 200px; | |||
} | |||
#description-form .CodeMirror { | |||
background-color: #f5f5f5; | |||
} | |||
.editor-toolbar { | |||
margin-top: 10px; | |||
background-color: #eee; | |||
} | |||
#poll_comments { | |||
margin-top: 10px; | |||
} | |||
h4.control-label { | |||
display: inline-block; | |||
max-width: 100%; | |||
margin-bottom: 5px; | |||
font-weight: 700; | |||
font-size: 14px; | |||
line-height: 1.42857; | |||
margin-top:0; | |||
} | |||
caption { | |||
padding: 0 10px 10px; | |||
font-weight:bold; | |||
} | |||
.results a.btn-default.btn-sm { | |||
padding: 3px 7px; | |||
font-size: 0.7em; | |||
} | |||
/* adminstuds.php */ | |||
#title-form h3 .btn-edit, | |||
#email-form .btn-edit, | |||
#description-form .btn-edit, | |||
#poll-rules-form .btn-edit, | |||
#poll-hidden-form .btn-edit, | |||
#expiration-form .btn-edit, | |||
#password-form .btn-edit, | |||
#name-form .btn-edit { | |||
position:absolute; | |||
left:-2000px; | |||
} | |||
#title-form .btn-edit:focus, | |||
#title-form h3:hover .btn-edit, | |||
#email-form .btn-edit:focus, | |||
#email-form:hover .btn-edit, | |||
#description-form .btn-edit:focus, | |||
#description-form:hover .btn-edit, | |||
#poll-rules-form .btn-edit:focus, | |||
#poll-rules-form:hover .btn-edit, | |||
#poll-hidden-form .btn-edit:focus, | |||
#poll-hidden-form:hover .btn-edit, | |||
#expiration-form .btn-edit:focus, | |||
#expiration-form:hover .btn-edit, | |||
#password-form .btn-edit:focus, | |||
#password-form:hover .btn-edit, | |||
#name-form .btn-edit:focus, | |||
#name-form:hover .btn-edit { | |||
position:relative !important; | |||
left:0; | |||
padding: 0 10px; | |||
} | |||
.js-desc textarea { | |||
margin-bottom:5px; | |||
} | |||
#author-form .form-control-static { | |||
margin-bottom:0; | |||
} | |||
#poll-rules-form p, #poll-hidden-form p, | |||
.jumbotron p.well { | |||
font-size:16px; | |||
} | |||
.jumbotron p { | |||
font-weight: normal; | |||
} | |||
/* Tableau du sondage */ | |||
#tableContainer { | |||
overflow-x:auto; | |||
margin:5px auto; | |||
} | |||
table.results { | |||
margin:0 auto; | |||
} | |||
table.results > tbody > tr:hover > td, | |||
table.results > tbody > tr:hover > th { | |||
opacity:0.85; | |||
} | |||
table.results > tbody > tr#vote-form:hover > td, | |||
table.results > tbody > tr#vote-form:hover > th { | |||
opacity:1; | |||
} | |||
table.results tbody td { | |||
text-align:center; | |||
padding:1px 5px; | |||
border-bottom: 2px solid white; | |||
border-top: 2px solid white; | |||
} | |||
table.results thead th { | |||
text-align:center; | |||
border:2px solid white; | |||
padding: 5px; | |||
min-width:40px; | |||
font-size:12px; | |||
max-width:100px; | |||
overflow:hidden; | |||
text-overflow:ellipsis; | |||
} | |||
table.results thead th img { | |||
max-width: 100%; | |||
} | |||
table.results thead .btn { | |||
margin: 0 auto; | |||
display: block; | |||
} | |||
table.results th.rbd.day, | |||
table.results th.rbd.bg-info, | |||
table.results td.rbd { | |||
border-right: 2px dotted white; | |||
} | |||
table.results th.bg-primary.month, | |||
table.results th.day, | |||
table.results th.bg-info { | |||
border-bottom:none; | |||
border-top:none; | |||
border-right: 2px dotted white; | |||
border-left: 2px dotted white; | |||
} | |||
table.results tbody th.bg-info { | |||
border-right: 2px solid white; | |||
border-left: 2px solid white; | |||
text-align:center; | |||
min-width:150px; | |||
} | |||
table.results th.bg-primary.month, | |||
table.results th.day { | |||
text-align:left; | |||
} | |||
table.results #nom { | |||
width:115px; | |||
} | |||
table.results .btn-link.btn-sm { | |||
padding:2px; | |||
} | |||
#addition { | |||
vertical-align:top; | |||
} | |||
#showChart { | |||
margin-top:30px; | |||
} | |||
#Chart { | |||
padding-right:30px; | |||
} | |||
/* Formulaire de création de sondage */ | |||
@media (max-width: 767px) { | |||
#formulaire .col-xs-12 { | |||
padding-left: 0; | |||
margin-bottom: 20px; | |||
} | |||
} | |||
/* Formulaire de vote */ | |||
#vote-form td ul, #vote-form td label { | |||
margin:0; | |||
font-size:12px; | |||
} | |||
#vote-form td label { | |||
padding: 1px 3px; | |||
} | |||
#vote-form td { | |||
border-top:2px solid white; | |||
} | |||
#vote-form td:first-child { | |||
min-width: 180px; | |||
} | |||
.yes input, .ifneedbe input,.no input { | |||
position: absolute; | |||
width: 1px; | |||
height: 1px; | |||
padding: 0; | |||
margin: -1px; | |||
overflow: hidden; | |||
clip: rect(0px, 0px, 0px, 0px); | |||
border: 0 none; | |||
} | |||
.choice input:focus + label { | |||
outline: 2px dotted #000; | |||
outline-offset: -2px; | |||
} | |||
.choice { | |||
width: 35px; | |||
margin:0 auto !important; | |||
} | |||
.choice label { | |||
cursor: pointer; | |||
} | |||
td.btn-edit { | |||
padding: 5px; | |||
} | |||
span.edit-username-left { | |||
float: right; | |||
} | |||
.yes .btn, .ifneedbe .btn, .no .btn { | |||
width: 35px; | |||
color: #555; | |||
} | |||
.yes .btn,.yes .btn:hover { | |||
border-bottom-right-radius:0 !important; | |||
border-bottom-left-radius:0 !important; | |||
margin-bottom:-1px !important; | |||
margin-top:4px !important; | |||
color: #677835; | |||
} | |||
.ifneedbe .btn,.ifneedbe .btn:hover { | |||
border-radius: 0; | |||
color: #C48A1B; | |||
} | |||
.no .btn,.no .btn:hover{ | |||
border-top-right-radius:0 !important; | |||
border-top-left-radius:0 !important; | |||
margin-bottom:4px !important; | |||
margin-top:-1px !important; | |||
color: #AD220F; | |||
} | |||
.yes input[type="radio"]:checked + label { /* =.btn-success.active */ | |||
color: #fff; | |||
background-color: #768745; | |||
border-color: #67753C; | |||
box-shadow: 0 3px 5px rgba(0, 0, 0, 0.125) inset; | |||
} | |||
.ifneedbe input[type="radio"]:checked + label { /* =.btn-warning.active */ | |||
color: #fff; | |||
background-color: #CF9800; | |||
border-color: #BD8A00; | |||
box-shadow: 0 3px 5px rgba(0, 0, 0, 0.125) inset; | |||
} | |||
.no input[type="radio"]:checked + label { /* =.btn-danger.active */ | |||
color: #fff; | |||
background-color: #BF2511; | |||
border-color: #AD220F; | |||
box-shadow: 0 3px 5px rgba(0, 0, 0, 0.125) inset; | |||
} | |||
/* Button and results "No" */ | |||
.no .btn.startunchecked { | |||
box-shadow:none !important; | |||
color:#AD220F !important; | |||
background:#fff !important; | |||
border-color:#bdbdbd !important; | |||
} | |||
.no .btn.startunchecked:hover { | |||
background-color: #E0E0E0 !important; | |||
border-color: #949494 !important; | |||
} | |||
table.results .bg-danger .glyphicon { | |||
opacity:0; | |||
-moz-animation-name: hideNoIcon; | |||
-moz-animation-iteration-count: 1; | |||
-moz-animation-timing-function: ease-in; | |||
-moz-animation-duration: 2s; | |||
-webkit-animation-name: hideNoIcon; | |||
-webkit-animation-iteration-count: 1; | |||
-webkit-animation-timing-function: ease-in; | |||
-webkit-animation-duration: 2s; | |||
animation-name: hideNoIcon; | |||
animation-iteration-count: 1; | |||
animation-timing-function: ease-in; | |||
animation-duration: 2s; | |||
} | |||
@-moz-keyframes hideNoIcon { | |||
0% { | |||
opacity:1; | |||
} | |||
100% { | |||
opacity:0; | |||
} | |||
} | |||
@-webkit-keyframes hideNoIcon { | |||
0% { | |||
opacity:1; | |||
} | |||
100% { | |||
opacity:0; | |||
} | |||
} | |||
@keyframes hideNoIcon { | |||
0% { | |||
opacity:1; | |||
} | |||
100% { | |||
opacity:0; | |||
} | |||
} | |||
table.results > tbody > tr:hover > td .glyphicon { | |||
opacity:1 | |||
} | |||
/* create_date_poll.php */ | |||
#selected-days .form-group { | |||
margin-left:0; | |||
margin-right:0; | |||
} | |||
#selected-days legend input { | |||
box-shadow: none; | |||
border-width:0; | |||
color: #333; | |||
font-size: 21px; | |||
border-radius:0; | |||
margin-bottom:-1px; | |||
background:transparent; | |||
} | |||
#selected-days legend input:hover, | |||
#selected-days legend input:focus { | |||
border-bottom-width:1px; | |||
background-color:#E6E6E6; | |||
} | |||
#selected-days legend .input-group-addon { | |||
border:none; | |||
background:transparent; | |||
} | |||
#selected-days legend .input-group-addon:last-of-type { | |||
padding-top: 0; | |||
padding-bottom: 0; | |||
} | |||
#selected-days legend { | |||
height: 33px; | |||
} | |||
/* create_classic_poll.php */ | |||
.md-a-img { | |||
text-decoration:none !important; | |||
} | |||
#md-a-imgModal .form-group { | |||
margin:10px 0; | |||
} | |||
#md-a-imgModalLabel { | |||
font-size: 24px; | |||
} | |||
/* Admin */ | |||
#poll_search { | |||
cursor: pointer; | |||
} | |||
.table-of-polls { | |||
overflow-x: scroll; | |||
margin-bottom: 0; | |||
border: 0; | |||
box-shadow: none; | |||
} | |||
/* Studs */ | |||
.password_request { | |||
padding-top: 15px; | |||
padding-bottom: 15px; | |||
} | |||
#password-form .btn-cancel { | |||
float: right; | |||
} | |||
/* Buttons */ | |||
.btn { | |||
white-space: normal; | |||
} |
@ -0,0 +1 @@ | |||
<svg id="Ebene_1" data-name="Ebene 1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 27.38 31.61"><defs><style>.cls-1{fill:#fc0;}</style></defs><polygon class="cls-1" points="0 0 27.38 15.8 0 31.61 0 0"/></svg> |
@ -0,0 +1,292 @@ | |||
@font-face { | |||
font-family: 'Ubuntu-R'; | |||
src: url('/assets/Ubuntu-R.ttf'); | |||
font-weight: normal; | |||
font-style: normal; | |||
} | |||
.hidden { | |||
display: none !important; | |||
} | |||
* { | |||
font-family: Ubuntu,"Ubuntu-R",sans-serif; | |||
} | |||
a { | |||
text-decoration: none; | |||
/*color: #2359fb;*/ | |||
} | |||
.flex { | |||
display: flex; | |||
flex-wrap: wrap; | |||
justify-content: center; | |||
} | |||
.fullheight { | |||
min-height: 100vh; | |||
} | |||
.fullheight-nav { | |||
min-height: calc(100vh - 50px); | |||
} | |||
.fullwidth { | |||
width: 100%; | |||
text-align: center; | |||
} | |||
.title { | |||
color: black; | |||
/*text-shadow: 0 0 5px rgba(0, 0, 0, 0.18),0 5px 5px rgba(0, 0, 0, 0.18);*/ | |||
} | |||
h1 { | |||
font-size: 4vw; | |||
} | |||
h2 { | |||
font-size: 2.25vw; | |||
} | |||
h3 { | |||
font-size: 17pt bold; | |||
text-align: left; | |||
} | |||
p { | |||
font-size: 15pt medium; | |||
/*line-height: 1.6;*/ | |||
text-align: left; | |||
} | |||
.beta-tag { | |||
background: #ff00ff; | |||
color: white; | |||
border-radius: 5px; | |||
font-size: 0.9rem; | |||
padding: 0.3rem; | |||
margin-left: 0.5rem; | |||
} | |||
.beta-banner a { | |||
color: #ff00ff; | |||
} | |||
.beta-banner { | |||
background: repeating-linear-gradient( 45deg, #ff00ff, #ff00ff 10px, #c44c05 10px, #c44c05 20px ); | |||
color: white; | |||
padding: 1rem; | |||
text-shadow: 0 0 5px rgba(0, 0, 0, 0.18),0 5px 5px rgba(0, 0, 0, 0.18); | |||
} | |||
.logo { | |||
width: 10vw; | |||
margin-right: 2vw; | |||
} | |||
.page-heading { | |||
background-image: url("/assets/index-background.png"); /*, linear-gradient(0deg, #1f58c6 0%, #1c66f2 100%);*/ | |||
background-position: 50% 50%; | |||
background-repeat: no-repeat; | |||
background-size: cover; | |||
background-attachment: fixed; | |||
} | |||
.page-heading-text { | |||
width: auto; | |||
margin: auto; | |||
padding: 1rem; | |||
} | |||
.page-heading > p { | |||
color: black; | |||
} | |||
.page-heading > p > a { | |||
color: #000000; | |||
} | |||
.page-heading.error { | |||
background: url("/assets/index-background.png"); /*, linear-gradient(0deg, #790000 0%, #a40000 100%)*/ | |||
} | |||
.ncstyle-button.error { | |||
background: #ee4040; | |||
} | |||
.error.ncstyle-button:hover { | |||
background: #c82323; | |||
} | |||
.navbar { | |||
height: 50px; | |||
} | |||
body, html { | |||
margin: 0; | |||
padding: 0; | |||
} | |||
.ncstyle-button { | |||
background-color: #00feca; | |||
box-shadow: 0 0 5px rgba(0, 0, 0, 0.18),0 5px 5px rgba(0, 0, 0, 0.18); | |||
border-radius: 1vw; | |||
text-decoration: none; | |||
text-shadow: 0 0 5px rgba(0, 0, 0, 0.18),0 5px 5px rgba(0, 0, 0, 0.18); | |||
white-space: nowrap; | |||
height: 48px; | |||
width: auto; | |||
line-height: 2.25rem; | |||
padding: 0.5em; | |||
background: #00feca; | |||
font-size: 20pt; | |||
min-width: 18vw; | |||
display: block; | |||
transition: all .25s ease-in-out; | |||
color: white; | |||
} | |||
.margin-bottom { | |||
margin-bottom: 1rem; | |||
} | |||
.ncstyle-button_blue:hover { | |||
background: #fbc617; | |||
} | |||
.ncstyle-button_yellow:hover { | |||
background: #fbc617; | |||
} | |||
.ncstyle-input { | |||
margin: auto; | |||
padding: 7px 6px; | |||
font-size: 16px; | |||
background-color: white; | |||
color: #454545; | |||
border: 1px solid #dbdbdb; | |||
outline: none; | |||
border-radius: 3px; | |||
cursor: text; | |||
width: 80vw; | |||
} | |||
.click { | |||
cursor: pointer; | |||
} | |||
#script-copy { | |||
display: none; | |||
} | |||
@media only screen and (max-width: 1080px) { | |||
h1 { | |||
font-size: 48px; | |||
} | |||
h2 { | |||
font-size: 32px; | |||
} | |||
h3 { | |||
font-size: 24px; | |||
} | |||
p { | |||
font-size: 16px; | |||
} | |||
.title { | |||
text-align: center; | |||
} | |||
.logo { | |||
width: 20vw; | |||
margin: 0; | |||
} | |||
.ncstyle-button_blue { | |||
font-size: 24px; | |||
} | |||
} | |||
@media only screen and (max-width: 1080px), screen and (max-height: 600px) { | |||
.scroll-down-arrow { | |||
display: none; | |||
} | |||
} | |||
.scroll-down-arrow { | |||
background-image: url(data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz48IURPQ1RZUEUgc3ZnIFBVQkxJQyAiLS8vVzNDLy9EVEQgU1ZHIDEuMS8vRU4iICJodHRwOi8vd3d3LnczLm9yZy9HcmFwaGljcy9TVkcvMS4xL0RURC9zdmcxMS5kdGQiPjxzdmcgdmVyc2lvbj0iMS4xIiBpZD0iQ2hldnJvbl90aGluX2Rvd24iIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiIHg9IjBweCIgeT0iMHB4IiB2aWV3Qm94PSIwIDAgMjAgMjAiIGVuYWJsZS1iYWNrZ3JvdW5kPSJuZXcgMCAwIDIwIDIwIiBmaWxsPSJ3aGl0ZSIgeG1sOnNwYWNlPSJwcmVzZXJ2ZSI+PHBhdGggZD0iTTE3LjQxOCw2LjEwOWMwLjI3Mi0wLjI2OCwwLjcwOS0wLjI2OCwwLjk3OSwwYzAuMjcsMC4yNjgsMC4yNzEsMC43MDEsMCwwLjk2OWwtNy45MDgsNy44M2MtMC4yNywwLjI2OC0wLjcwNywwLjI2OC0wLjk3OSwwbC03LjkwOC03LjgzYy0wLjI3LTAuMjY4LTAuMjctMC43MDEsMC0wLjk2OWMwLjI3MS0wLjI2OCwwLjcwOS0wLjI2OCwwLjk3OSwwTDEwLDEzLjI1TDE3LjQxOCw2LjEwOXoiLz48L3N2Zz4=); | |||
background-size: contain; | |||
background-repeat: no-repeat; | |||
} | |||
.scroll-down-link { | |||
cursor:pointer; | |||
height: 60px; | |||
width: 80px; | |||
margin: 0px 0 0 -40px; | |||
line-height: 60px; | |||
position: absolute; | |||
left: 50%; | |||
bottom: 10px; | |||
color: #FFF; | |||
text-align: center; | |||
font-size: 70px; | |||
z-index: 100; | |||
text-decoration: none; | |||
text-shadow: 0px 0px 3px rgba(0, 0, 0, 0.4); | |||
animation: fade_move_down 2s ease-in-out infinite; | |||
} | |||
/*animated scroll arrow animation*/ | |||
@keyframes fade_move_down { | |||
0% { transform:translate(0,-20px); opacity: 0; } | |||
50% { opacity: 1; } | |||
100% { transform:translate(0,20px); opacity: 0; } | |||
} | |||
.lds-ring { | |||
display: inline-block; | |||
position: relative; | |||
width: 80px; | |||
height: 80px; | |||
} | |||
.lds-ring div { | |||
box-sizing: border-box; | |||
display: block; | |||
position: absolute; | |||
width: 64px; | |||
height: 64px; | |||
margin: 8px; | |||
border: 8px solid #fff; | |||
border-radius: 50%; | |||
animation: lds-ring 1.2s cubic-bezier(0.5, 0, 0.5, 1) infinite; | |||
border-color: #fff transparent transparent transparent; | |||
} | |||
.lds-ring div:nth-child(1) { | |||
animation-delay: -0.45s; | |||
} | |||
.lds-ring div:nth-child(2) { | |||
animation-delay: -0.3s; | |||
} | |||
.lds-ring div:nth-child(3) { | |||
animation-delay: -0.15s; | |||
} | |||
@keyframes lds-ring { | |||
0% { | |||
transform: rotate(0deg); | |||
} | |||
100% { | |||
transform: rotate(360deg); | |||
} | |||
} | |||
@ -0,0 +1,35 @@ | |||
// on clicking Previous button in browser, reset the page | |||
// needed to get another CSRF token and remove the spinning wheel | |||
window.onpageshow = function() { | |||
if (performance.getEntriesByType("navigation")[0].type == "back_forward") { | |||
location.reload(false); | |||
} | |||
} | |||
let browse_forms_button = get('browse_forms_button'); | |||
let new_link_button = get('new_link_button'); | |||
// csrf_token is retrieved from server-side template | |||
new_link_button.addEventListener('click', function() { | |||
get("new_link").submit(); | |||
hideButtonsAndSpin(); | |||
}); | |||
if (browse_forms_button != undefined) { | |||
browse_forms_button.addEventListener('click', function () { | |||
hideButtonsAndSpin(); | |||
}); | |||
} | |||
function hideButtonsAndSpin() { | |||
new_link_button.classList.add("hidden"); | |||
// hide the access forms button if it exists | |||
if (browse_forms_button != undefined) { | |||
browse_forms_button.classList.add("hidden"); | |||
} | |||
get('loading_ring').classList.remove("hidden"); | |||
} | |||
function get(elemId) { | |||
return document.getElementById(elemId); | |||
} |
@ -0,0 +1,29 @@ | |||
<!doctype html> | |||
<html lang="{{ lang }}"> | |||
<head> | |||
<title>{{ "error_title"|tr(lang) }}</title> | |||
<meta charset="utf-8" /> | |||
<meta name="viewport" content="width=device-width, initial-scale=1"> | |||
<meta name="description" content="{{ "meta_description"|tr(lang) }}" /> | |||
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> | |||
<link rel="icon" type="image/png" sizes="48x48" href="/assets/favicon.ico" /> | |||
<link rel="stylesheet" href="/assets/index.css?v=1.0" /> | |||
<link rel="stylesheet" href="/assets/cloud.css?v=1.0" /> | |||
<body> | |||
<div class="flex page-heading error fullheight"> | |||
<div class="flex page-heading-text"> | |||
<div> | |||
<h1 class="title">{{ "error_title"|tr(lang) }}</h1> | |||
<h2 class="title">{{ "error_description"|tr(lang) }}</h2> | |||
<h3 class="title">{{ error_msg|tr(lang) }}</h3> | |||
<p class="title">{{ "error_note1"|tr(lang) }}</h3> | |||
<p class="title">{{ "error_note2"|tr(lang) }}</h3> | |||
</div> | |||
</div> | |||
<div class="flex"> | |||
<a class="ncstyle-button error c-button" href="/">{{ "error_back"|tr(lang) }}</a> | |||
</div> | |||
</div> | |||
</body> | |||
</html> | |||
@ -0,0 +1,278 @@ | |||
<div id="container"> | |||
<!doctype html> | |||
<html lang="{{ "lang_code"|tr(lang) }}"> | |||
<head> | |||
<title>{{ "index_title"|tr(lang) }} – {{ "index_description"|tr(lang) }}</title> | |||
<meta charset="utf-8" /> | |||
<meta name="viewport" content="width=device-width, initial-scale=1.0"> | |||
<meta name="description" content="{{ "meta_description"|tr(lang) }}" /> | |||
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> | |||
<link rel="icon" type="image/png" sizes="48x48" href="/assets/favicon.svg" /> | |||
<link rel="stylesheet" href="/assets/index.css?v=1.2" /> | |||
<link rel="stylesheet" href="/assets/cloud.css?v=1.0" /> | |||
<link rel="stylesheet" href="/assets/digitalcourage.css" /> | |||
<link rel="stylesheet" href="/assets/bootstrap.min.css" /> | |||
<style> | |||
.break { | |||
flex-basis: 100%; | |||
height: 0; | |||
} | |||
.grid-container { | |||
display: grid; | |||
grid-template-columns: auto auto; | |||
grid-gap: 5vw; | |||
} | |||
.grid-container2 { | |||
display: grid; | |||
grid-template-columns: auto auto auto; | |||
width: 532px; | |||
grid-gap: 35px; | |||
} | |||
@media only screen and (max-width: 768px) { | |||
/* For mobile phones: */ | |||
[class*="grid-container"] { | |||
grid-template-columns: auto; | |||
max-width: 100%; | |||
justify-content: center; | |||
} | |||
[class*="grid-container2"] { | |||
grid-template-columns: auto; | |||
max-width: 100%; | |||
justify-content: center; | |||
grid-gap: 20px; | |||
} | |||
[class*="item1"] { | |||
grid-template-columns: auto; | |||
max-width: 100%; | |||
align-items: center; | |||
width: 80vw; | |||
} | |||
[class*="item2"] { | |||
grid-template-columns: auto; | |||
max-width: 100%; | |||
align-items: center; | |||
width: 80vw; | |||
} | |||
} | |||
.div_120 { | |||
flex-basis: 100%; | |||
height: 120px; | |||
} | |||
.div_60 { | |||
flex-basis: 100%; | |||
height: 60px; | |||
} | |||
.div_45 { | |||
flex-basis: 100%; | |||
height: 45px; | |||
} | |||
.div_35 { | |||
flex-basis: 100%; | |||
height: 35px; | |||
} | |||
.div_25 { | |||
flex-basis: 100%; | |||
height: 25px; | |||
} | |||
.div_10 { | |||
flex-basis: 100%; | |||
height: 10px; | |||
} | |||
.h3 { | |||
font-size: 20pt; | |||
} | |||
h2 { | |||
font-size: 30pt; | |||
} | |||
.a1 { | |||
font-size: 20pt; | |||
} | |||
p { | |||
font-size: 14pt; | |||
} | |||
p1 { | |||
font-size: 20pt; | |||
} | |||
.downDC { | |||
height: 90px; | |||
padding: 10px; | |||
} | |||
.c-img-shadow { | |||
height: 200px; | |||
max-width: 100%; | |||
box-shadow: 0 0 5px rgba(0, 0, 0, 0.18),0 5px 5px rgba(0, 0, 0, 0.18); | |||
border-radius: 2px; | |||
} | |||
</style> | |||
<noscript><style> .jsonly { display: none } </style></noscript> | |||
<script> | |||
window.onload = function() { | |||
// retrieved from server-side template | |||
let lang = "{{ lang }}"; | |||
document.getElementById('langs').value=lang; | |||
document.getElementById("send-text").style.display = "unset"; | |||
let btn2 = document.getElementById("send-text-btn"); | |||
btn2.style.cursor = "pointer"; | |||
btn2.addEventListener('click', function() { | |||
var text = document.getElementById("datextarea").value; | |||
console.log(text); | |||
var xhr1=new XMLHttpRequest(); | |||
xhr1.open("POST",'link/text', true); | |||
xhr1.setRequestHeader('Content-Type', 'application/json'); | |||
const json = { | |||
"Text": text | |||
}; | |||
xhr1.send(JSON.stringify(json)); | |||
document.getElementById("datextarea").value = "..."; | |||
xhr1.onreadystatechange = function () { | |||
if (xhr1.readyState == 4) { | |||
result = xhr1.response.split('?&?&') | |||
document.getElementById("output1").value = result[0].substring(3, result[0].length).replace(/\n/g, "<br>"); | |||
document.getElementById("output2").value = result[1].substring(0, result[1].length - 3); | |||
document.getElementById("datextarea").value = text; | |||
}; | |||
}; | |||
document.getElementById("datextarea").value = "..."; | |||
}); | |||
} | |||
</script> | |||
</head> | |||
<body> | |||
<div class="container ombre"> | |||
<header role="banner" class="clearfix"> | |||
<form method="get" action="/" class="hidden-print"> | |||
<div class="input-group input-group-sm pull-right col-xs-12 col-sm-2 langs" style="margin-right: 8px"> | |||
<select id="langs" name="lang" class="form-control" title="Select language" > | |||
<option lang="fr" value="fr">Français</option> | |||
<option lang="en" selected value="en">English</option> | |||
<option lang="de" value="de">Deutsch</option> | |||
<option lang="it" value="it">Italiano</option> | |||
</select> | |||
<span class="input-group-btn"> | |||
<button type="submit" id="language_button" class="btn btn-default btn-sm language_button" title="Change language">OK</button> | |||
</span> | |||
</div> | |||
</form> | |||
<a href="https://foorms.digitalcourage.de/" title="Home - foorms" style="margin-left: 8px" > | |||
<img src="/assets/logo.png" alt="foorms" class="" height="95vh" /> | |||
</a> | |||
<h2 class="lead col-xs-12"></h2> <div class="trait col-xs-12" role="presentation"></div> | |||
</header> | |||
<main role="main"> | |||
<div class="div_10"></div> | |||
<div class="div_10"></div> | |||
<div class="div_10"></div> | |||
<center> | |||
<div> | |||
<h2 class="title">{{ "index_title2"|tr(lang) }}</h2> | |||
</div> | |||
<br/> | |||
<div class="div_25"></div> | |||
<div class="flex has-text-centered"> | |||
<div> | |||
<p1 class="title">{{ "index_description"|tr(lang) }}</p1> | |||
</div> | |||
</div> | |||
</center> | |||
<div class="div_60"></div> | |||
<center> | |||
<textarea id="datextarea" placeholder="{{"index_description2"|tr(lang)}}" style="font-size:18px; outline:none; resize: none; overflow:auto; width:80vw; height:40vh; border-width:2vw border-width: 2vw; border-style:solid"></textarea> | |||
<div class="div_10"></div> | |||
<div id="send-text"> | |||
<a style="width: 50vw;" id="send-text-btn" class="ncstyle-button margin-bottom">{{ "index_search_button"|tr(lang) }}</a> | |||
</div> | |||
<div class="div_10"></div> | |||
<div class="grid-container"> | |||
<div class="item1"> | |||
<textarea id="output1" placeholder="{{"index_description3"|tr(lang)}}" style="font-size:18px; outline:none; resize: none; overflow:auto; width:38vw; height:55vh; border-width:2vw border-width: 2vw; border-style:solid"></textarea> | |||
</div> | |||
<div class="item2"> | |||
<textarea id="output2" placeholder="{{"index_description4"|tr(lang)}}" style="font-size:18px; outline:none; resize: none; overflow:auto; width:38vw; height:55vh; border-width:2vw border-width: 2vw; border-style:solid"></textarea> | |||
</div> | |||
</div> | |||
<div class="div_10"></div> | |||
</center> | |||
<div class="break"></div> | |||
<div class="div_120"></div> | |||
<center> | |||
<div class="div_60"></div> | |||
<div class="flex has-text-centered"> | |||
<div class=" flex"> | |||
<noscript> | |||
<a class="ncstyle-button">{{ "index_nojs"|tr(lang) }}</a> | |||
</noscript> | |||
</div> | |||
</div> | |||
<div class="div_120"></div> | |||
<p> | |||
<div class="flex has-text-centered"> | |||
<div> | |||
<h2 class="title">{{ "index_disclaimer_title"|tr(lang) }}</h2> | |||
</div> | |||
<div class="break"></div> | |||
<div class="div_25"></div> | |||
<div> | |||
<p1 class="title">{{ "index_disclaimer1"|tr(lang) }}</p1> | |||
<a href="https://www.basabuuka.org" class="a1">{{ "index_disclaimer2_link_org"|tr(lang) }}</a1> | |||
<p1 class="title">{{ "index_disclaimer2"|tr(lang) }}</p1> | |||
</div> | |||
<br> | |||
<div class="break"></div> | |||
<div> | |||
<p1 class="title">{{ "index_disclaimer2_but"|tr(lang) }}</p1> | |||
<a href="https://www.basabuuka.org" class="a1">{{ "index_disclaimer2_link_don"|tr(lang) }}</a> | |||
</div> | |||
<br> | |||
<div class="break"></div> | |||
<div> | |||
<p1 class="title">{{ "index_disclaimer3"|tr(lang) }}</p1> | |||
<a href="https://www.basabuuka.org" class="a1">{{ "index_disclaimer3_link"|tr(lang) }}</a> | |||
<p1 class="title">{{ "index_disclaimer4"|tr(lang) }}</p1> | |||
</div> | |||
</div> | |||
</p> | |||
<div class="div_120"></div> | |||
<div class="c-blue grid-container2"> | |||
<a href="https://code.basabuuka.org/alpcentaur/basabuuka-prototype-rust-dc" style="font-size:15px;" class="c-button" target="_blank">{{ "index_bottom_source"|tr(lang) }}</a> | |||
<a href="https://code.basabuuka.org/alpcentaur/license-of-redistribution" style="font-size:15px;" class="c-button" target="_blank">{{ "index_bottom_lic"|tr(lang) }}</a> | |||
</div> | |||
<div class="div_10"></div> | |||
</center> | |||
</main> | |||
</div> <!-- .container --> | |||
</body> | |||
</html> | |||
<div class="container ombre downDC" style="display:flex;align-items:center;"> | |||
<h2 class="lead"><a target="_blank" href="https://www.basabuuka.org/">Basabuuka</a> | <a target="_blank" href="https://notebook.basabuuka.org">Notebook Prototyp</a> | <a target="_blank" href="info@cannabinieri.de">Kontakt</a> | <a target="_blank" href="">{{ "impressum_privacy"|tr(lang) }}</a> </h2> | |||
</div> | |||
@ -0,0 +1,298 @@ | |||
<!DOCTYPE html> | |||
<html lang="it"> | |||
<head> | |||
<meta charset="utf-8"> | |||
<meta name="viewport" content="width=device-width, initial-scale=1" /> | |||
<title>{{ "link_title"|tr(lang) }} – {{ "index_title"|tr(lang) }}</title> | |||
<meta name="robots" content="noindex" /> | |||
<meta name="description" content="{{ "meta_description"|tr(lang) }}" /> | |||
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> | |||
<link rel="icon" type="image/png" sizes="48x48" href="/assets/favicon.svg" /> | |||
<link rel="stylesheet" href="/assets/index.css?v=1.0" /> | |||
<link rel="stylesheet" href="/assets/cloud.css?v=1.0" /> | |||
<link rel="stylesheet" href="/assets/digitalcourage.css" /> | |||
<link rel="stylesheet" href="/assets/bootstrap.min.css" /> | |||
<script type="text/javascript"> | |||
window.onload = function () { | |||
// show link copy button if javascript is enabled | |||
document.getElementById("script-copy").style.display = "unset"; | |||
let btn = document.getElementById("script-copy-btn"); | |||
btn.style.cursor = "pointer"; | |||
let lang = "{{ lang }}"; | |||
document.getElementById('langs').value=lang; | |||
}); | |||
btn.addEventListener('click', function() { | |||
var copyText = document.getElementById("link"); | |||
/* Select the text field */ | |||
copyText.select(); | |||
copyText.setSelectionRange(0, 99999); | |||
document.execCommand("copy"); | |||
btn.innerHTML = '{{ "link_copied"|tr(lang) }}'; | |||
}); | |||
function ValidateEmail(mail) | |||
{ | |||
if (/^[a-zA-Z0-9.!#$%&'*+/=?^_`{|}~-]+@[a-zA-Z0-9-]+(?:\.[a-zA-Z0-9-]+)*$/.test(mail)) | |||
{ | |||
return (true) | |||
} | |||
alert("Die eingegebene Mail Adresse ist ungültig. Sie können sich auch anmelden, ohne den Token zugeschickt zu bekommen.") | |||
return (false) | |||
} | |||
document.getElementById("email-register").style.display = "unset"; | |||
let btn2 = document.getElementById("email-register-btn"); | |||
btn2.style.cursor = "pointer"; | |||
btn2.addEventListener('click', function() { | |||
var email = document.getElementById("email").value; | |||
var adtok = document.getElementById("link").value; | |||
console.log(email); | |||
var validation = ValidateEmail(email); | |||
/* var emailjsonstring = JSON.stringify(JSON.parse(document.getElementById('email'))); */ | |||
if (validation == true) | |||
{ | |||
var xhr1=new XMLHttpRequest(); | |||
xhr1.open("POST",'link/email', true); | |||
xhr1.send(email + ',' + adtok + '\n'); | |||
document.getElementById("email").value = "Die Email ist auf dem Weg"; | |||
} | |||
}); | |||
} | |||
function new_link(csrf) { | |||
var sel = document.getElementById('langs'); | |||
let opt = getSelectedOption(sel); | |||
let lang = opt.value; | |||
document.getElementById('langs').value = lang; | |||
document.getElementById("link_lang").value = lang; | |||
document.getElementById('new_link').submit(); | |||
document.getElementById('new_link_button').classList.add("hidden"); | |||
document.getElementById('loading_ring').classList.remove("hidden"); | |||
} | |||
function getSelectedOption(sel) { | |||
var opt; | |||
for ( var i = 0, len = sel.options.length; i < len; i++ ) { | |||
opt = sel.options[i]; | |||
if ( opt.selected === true ) { | |||
break; | |||
} | |||
} | |||
return opt; | |||
} | |||
</script> | |||
<style> | |||
.break { | |||
flex-basis: 100%; | |||
height: 0; | |||
} | |||
.div_120 { | |||
flex-basis: 100%; | |||
height: 120px; | |||
} | |||
.div_45 { | |||
flex-basis: 100%; | |||
height: 45px; | |||
} | |||
.div_35 { | |||
flex-basis: 100%; | |||
height: 35px; | |||
} | |||
.div_25 { | |||
flex-basis: 100%; | |||
height: 25px; | |||
} | |||
.div_10 { | |||
flex-basis: 100%; | |||
height: 10px; | |||
} | |||
.grid-container { | |||
display: grid; | |||
grid-template-columns: auto auto; | |||
width: 725px; | |||
grid-gap: 25px; | |||
} | |||
.grid-container2 { | |||
display: grid; | |||
grid-template-columns: auto auto auto; | |||
width: 532px; | |||
grid-gap: 20px; | |||
} | |||
@media only screen and (max-width: 768px) { | |||
/* For mobile phones: */ | |||
[class*="grid-container"] { | |||
grid-template-columns: auto; | |||
max-width: 100%; | |||
justify-content: center; | |||
} | |||
[class*="grid-container2"] { | |||
grid-template-columns: auto; | |||
max-width: 100%; | |||
justify-content: center; | |||
} | |||
[class*="item2"] { | |||
grid-template-columns: auto; | |||
max-width: 100%; | |||
align-items: center; | |||
} | |||
} | |||
.center { | |||
display: flex; | |||
justify-content: center; | |||
align-items: center; | |||
} | |||
.item1 { | |||
width: 350px; | |||
height: 200px; | |||
display: flex; | |||
justify-content: center; | |||
align-items: center; | |||
} | |||
.item2 { | |||
width: 350px; | |||
height: 200px; | |||
#display: flex; | |||
#justify-content: center; | |||
#align-items: center; | |||
} | |||
.h2 { | |||
font-size: 30pt; | |||
} | |||
p { | |||
font-size: 16pt; | |||
} | |||
.downDC { | |||
height: 90px; | |||
padding: 10px; | |||
} | |||
.c-img-shadow { | |||
height: 200px; | |||
max-width: 100%; | |||
box-shadow: 0 0 5px rgba(0, 0, 0, 0.18),0 5px 5px rgba(0, 0, 0, 0.18); | |||
border-radius: 2px; | |||
} | |||
</style> | |||
<body> | |||
<div class="container ombre"> | |||
<header role="banner" class="clearfix"> | |||
<form id="new_link" method="post" action="/link" class="hidden-print"> | |||
<div class="input-group input-group-sm pull-right col-xs-12 col-sm-2 langs" style="margin-right: 8px"> | |||
<select id="langs" name="lang" class="form-control" title="Select language" > | |||
<option lang="fr" value="fr">Français</option> | |||
<option lang="en" selected value="en">English</option> | |||
<option lang="oc" value="oc">Occitan</option> | |||
<option lang="es" value="es">Español</option> | |||
<option lang="de" value="de">Deutsch</option> | |||
<option lang="nl" value="nl">Dutch</option> | |||
<option lang="it" value="it">Italiano</option> | |||
<option lang="br" value="br">Brezhoneg</option> | |||
</select> | |||
<input id="link_lang" name="link_lang" type="text" class="hidden"> | |||
<span class="input-group-btn"> | |||
<a id="new_link_button" class="btn btn-default btn-sm language_button" title="Change language">OK</a> | |||
</span> | |||
</div> | |||
</form> | |||
<a href="https://foorms.digitalcourage.de/" title="Home - foorms" style="margin-left: 8px" > | |||
<img src="/assets/logo.svg" alt="foorms" class="" height="58vh" /> | |||
</a> | |||
<h2 class="lead col-xs-12"></h2> <div class="trait col-xs-12" role="presentation"></div> | |||
</header> | |||
<main role="main"> | |||
<center> | |||
<div class="has-text-centered"> | |||
<br /> | |||
<h2>{{ "link_title"|tr(lang) }}</h2> | |||
<div class="div_25"> </div> | |||
<p>{{ "link_desc1_1"|tr(lang)|safe }}</p> | |||
<div class="break"> </div> | |||
<p>{{ "link_desc1_2"|tr(lang)|safe }}</p> | |||
<div class="div_25"> </div> | |||
<div class="c-flex c-jumbo"> | |||
<input id="link" class="ncstyle-input" type="text" style='font-size: 16px; text-align:center' size="80" readonly value="" /> | |||
</div> | |||
<div class="div_35"> </div> | |||
<div id="script-copy"> | |||
<div class="c-flex"> | |||
<a id="script-copy-btn" class="ncstyle-button margin-bottom">{{ "link_copy"|tr(lang) }}</a> | |||
</div> | |||
</div> | |||
<div class="div_120"> </div> | |||
<p>{{ "link_desc2_1"|tr(lang)|safe }}</p> | |||
<div class="break"> </div> | |||
<p>{{ "link_desc2_2"|tr(lang)|safe }}</p> | |||
<div class="div_25"> </div> | |||
<div class="c-flex"> | |||
<input id="email" class="ncstyle-input" style="text-align:center;" type="text" value="Send_Password_Link@invalid" /> | |||
</div> | |||
<div class="div_35"> </div> | |||
<div id="email-register"> | |||
<div class="c-flex"> | |||
<a id="email-register-btn" class="ncstyle-button margin-bottom">{{ "link_mail"|tr(lang) }}</a> | |||
</div> | |||
<div class="div_120"> </div> | |||
</div> | |||
<p>{{ "link_desc3_1"|tr(lang) }}</p> | |||
<div class="break"></div> | |||
<p>{{ "link_desc3_2"|tr(lang) }}</p> | |||
<div class=div_35></div> | |||
</div> | |||
</div> | |||
<div class="div_120"></div> | |||
</center> | |||
<center> | |||
<div class="c-blue grid-container2"> | |||
<a href="https://42l.fr/Rapport-technique" style="font-size:15px;" class="c-button" target="_blank">{{ "index_bottom_docs"|tr(lang) }}</a> | |||
<a href="https://git.42l.fr/neil/sncf" style="font-size:15px;" class="c-button" target="_blank">{{ "index_bottom_source"|tr(lang) }}</a> | |||
<a href="https://git.42l.fr/neil/sncf/src/branch/root/LICENSE" style="font-size:15px;" class="c-button" target="_blank">{{ "index_bottom_lic"|tr(lang) }}</a> | |||
</div> | |||
</center> | |||
<div class="div_10"></div> | |||
<div class="div_10"></div> | |||
<div class="div_10"></div> | |||
</main> | |||
</div> <!-- .container --> | |||
<div class="container ombre downDC" style="display:flex; align-items:center;"> | |||
<h2 class="lead"><a target="_blank" href="https://digitalcourage.de/">Digitalcourage</a> | <a target="_blank" href="https://digitalcourage.de/newsletter">Newsletter</a> | <a target="_blank" href="https://digitalcourage.de/spenden">{{ "impressum_donations"|tr(lang)|safe }}</a> | <a target="_blank" href="https://digitalcourage.de/en">Impressum</a> | <a target="_blank" href="https://digitalcourage.de/en">{{ "impressum_privacy"|tr(lang)|safe }}</a> </h2> | |||
</div> | |||
</body> | |||
</html> | |||