newlines get interpreted as what they mean in the output textfield now

This commit is contained in:
alpcentaur 2024-10-03 19:20:25 +02:00
parent 8921bed15f
commit 6761fa2496
59 changed files with 5102 additions and 4 deletions

View file

@ -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

View file

@ -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"

View file

@ -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.

View file

@ -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

View file

@ -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

View file

@ -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<61>sur des bases de donn<6E>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."
}
}

View file

@ -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."
}
}

View file

@ -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
);

View file

@ -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
);

View file

@ -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
);

View file

@ -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
Farfetchd
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

View file

@ -0,0 +1 @@
reorder_imports = true

View file

@ -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!();
}
}
}

View file

@ -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,
}
}
}

View file

@ -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")
})?)
}

View file

@ -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,
//}

View file

@ -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!();
}
}
}

View file

@ -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,
}
}
}

View file

@ -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")
})?)
}

View file

@ -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);
}
}

View file

@ -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
}

View file

@ -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)
})?,
))
}
}

View file

@ -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)
})?,
))
}
}

File diff suppressed because one or more lines are too long

View file

@ -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);
}
}

View file

@ -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 ©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;
}

View file

@ -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>

After

Width:  |  Height:  |  Size: 211 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.8 KiB

View file

@ -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);
}
}

View file

@ -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);
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 111 KiB

View file

@ -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>

View file

@ -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&ccedil;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&colon;&sol;&sol;foorms&period;digitalcourage&period;de&sol;" 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>

View file

@ -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&ccedil;ais</option>
<option lang="en" selected value="en">English</option>
<option lang="oc" value="oc">Occitan</option>
<option lang="es" value="es">Espa&ntilde;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&colon;&sol;&sol;foorms&period;digitalcourage&period;de&sol;" 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>

View file

@ -84,7 +84,7 @@ async fn getdadata(info: web::Json<Info>, req: HttpRequest) -> HttpResponse {
//let infobytes = info.0.as_bytes()
let mut client = Client::new();
let mut res = client.post("http://rust-proto-fastapi-server:8000/datext")
let mut res = client.post("http://tf-fastapi-server:8000/datext")
// set a header
//.header("Content-Type: application/json".as_bytes())
.body(&mut info.Text.as_str().as_bytes())

View file

@ -49,7 +49,6 @@ async fn main() -> std::io::Result<()> {
// )
.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| {

View file

@ -148,7 +148,7 @@
result = xhr1.response.split('?&?&')
document.getElementById("output1").value = result[0].substring(3, result[0].length).replace(/\n/g, "<br>");
document.getElementById("output1").value = result[0].substring(3, result[0].length).replace(/\\\\n/g, "\n");
document.getElementById("output2").value = result[1].substring(0, result[1].length - 3);
document.getElementById("datextarea").value = text;
};

View file

@ -34,7 +34,7 @@ RUN pip install --upgrade pip && pip install fastapi uvicorn[standard]
# to let the container running:
CMD uvicorn --host 0.0.0.0 fastapi_server:app
CMD uvicorn --host 0.0.0.0 fastapi_server:app --reload
#ENTRYPOINT ["tail"]
#CMD ["-f","/dev/null"]