diff --git a/pyproject.toml b/pyproject.toml index a6e55995f6..020a1f6306 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -20,6 +20,7 @@ profile = "django" [tool.djlint.per-file-ignores] "weblate_web/invoices/templates/invoice-template.html" = "H031" +"weblate_web/legal/templates/pdf/base.html" = "H030,H031" # False positives for crypto URLs "weblate_web/templates/donate.html" = "D018" # Use language-less URLs in notifications diff --git a/weblate_web/legal/management/commands/generate_terms.py b/weblate_web/legal/management/commands/generate_terms.py new file mode 100644 index 0000000000..f7afbd13f7 --- /dev/null +++ b/weblate_web/legal/management/commands/generate_terms.py @@ -0,0 +1,47 @@ +# +# Copyright © Michal Čihař +# +# This file is part of Weblate +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . +# + +from pathlib import Path + +from django.core.management.base import BaseCommand +from django.template.loader import render_to_string + +from weblate_web.pdf import render_pdf + +HOSTED_ACCOUNT = "Hosted Weblate account" + +OUT_DIR = Path(__file__).parent.parent.parent / "static" + + +class Command(BaseCommand): + help = "generates legal PDFs" + client = None + + def handle(self, *args, **options): + render_pdf( + html=render_to_string( + "pdf/terms.html", {"title": "General Terms and Conditions"} + ), + output=OUT_DIR / "Weblate_General_Terms_and_Conditions.pdf", + ) + + render_pdf( + html=render_to_string("pdf/privacy.html", {"title": "Privacy Policy"}), + output=OUT_DIR / "Weblate_Privacy_Policy.pdf", + ) diff --git a/weblate_web/legal/static/Weblate_General_Terms_and_Conditions.pdf b/weblate_web/legal/static/Weblate_General_Terms_and_Conditions.pdf new file mode 100644 index 0000000000..7919b57f4c Binary files /dev/null and b/weblate_web/legal/static/Weblate_General_Terms_and_Conditions.pdf differ diff --git a/weblate_web/legal/static/Weblate_Privacy_Policy.pdf b/weblate_web/legal/static/Weblate_Privacy_Policy.pdf new file mode 100644 index 0000000000..338e7ad1a5 Binary files /dev/null and b/weblate_web/legal/static/Weblate_Privacy_Policy.pdf differ diff --git a/weblate_web/legal/templates/pdf/base.html b/weblate_web/legal/templates/pdf/base.html new file mode 100644 index 0000000000..658347fff4 --- /dev/null +++ b/weblate_web/legal/templates/pdf/base.html @@ -0,0 +1,22 @@ + + + + + + Weblate {{ title }} + + + + + +
+ +
+ +

{{ title }}

+ + {% block content %} + {% endblock content %} + + + diff --git a/weblate_web/legal/templates/pdf/legal.css b/weblate_web/legal/templates/pdf/legal.css new file mode 100644 index 0000000000..30e398c94c --- /dev/null +++ b/weblate_web/legal/templates/pdf/legal.css @@ -0,0 +1,117 @@ +@page { + margin: 3.5cm 1cm 3cm 1cm; + @bottom-right { + content: "Page " counter(page) " of " counter(pages); + } +} + +html, +a { + color: #14213d; + font-family: Source Sans Pro; + font-size: 11pt; + line-height: 1.6; +} +body { + margin: 0; + padding: 0; +} +header { + display: block; + padding-bottom: 1.3cm; + position: fixed; + left: 0; + right: 0; + top: -2.5cm; + height: 2.5cm; +} + +h1 { + font-size: 18pt; + padding-top: 3mm; +} +img { + position: absolute; + max-width: 30%; + margin: 0; + padding: 0; + right: 0; +} +.tos { + counter-reset: tos_chapter; +} + +.tos h2::before { + counter-increment: tos_chapter; + content: counter(tos_chapter) ". "; +} + +.tos h2 { + counter-reset: tos_item; +} + +.tos p.item, +.tos p.subitem, +.tos p.subsubitem { + position: relative; +} +.tos p, +.tos dl { + padding: 0 3.5em; +} +.tos p.item { + counter-reset: tos_subitem; +} +.tos p.subitem { + counter-reset: tos_subsubitem; +} +.tos p.subsubitem { + padding: 0 4.2em; +} + +.tos p.item::before { + counter-increment: tos_item; + content: counter(tos_chapter) "." counter(tos_item) ". "; + position: absolute; + left: 0; +} + +.tos p.subitem::before { + counter-increment: tos_subitem; + content: counter(tos_chapter) "." counter(tos_item) "." counter(tos_subitem) + ". "; + position: absolute; + left: 0; +} + +.tos p.subsubitem::before { + counter-increment: tos_subsubitem; + content: counter(tos_chapter) "." counter(tos_item) "." counter(tos_subitem) + "." counter(tos_subsubitem) ". "; + position: absolute; + left: 0; +} + +.tos ol { + counter-reset: tos_list; + padding: 0 5em; +} +.tos ol > li { + list-style: none; + position: relative; +} +.tos ol > li:before { + counter-increment: tos_list; + content: counter(tos_list, lower-alpha) ") "; + position: absolute; + left: -1.4em; +} + +.tos a { + color: #2eccaa; + text-decoration: underline; +} + +.tos a:hover { + text-decoration: none; +} diff --git a/weblate_web/legal/templates/pdf/privacy.html b/weblate_web/legal/templates/pdf/privacy.html new file mode 100644 index 0000000000..7843201bc8 --- /dev/null +++ b/weblate_web/legal/templates/pdf/privacy.html @@ -0,0 +1,5 @@ +{% extends "pdf/base.html" %} + +{% block content %} +
{% include "legal/documents/privacy.html" with hide_english=True %}
+{% endblock content %} diff --git a/weblate_web/legal/templates/pdf/terms.html b/weblate_web/legal/templates/pdf/terms.html new file mode 100644 index 0000000000..e76234cec5 --- /dev/null +++ b/weblate_web/legal/templates/pdf/terms.html @@ -0,0 +1,7 @@ +{% extends "pdf/base.html" %} + +{% block content %} +
+ {% include "legal/documents/tos.html" with hide_english=True privacy_url="https://weblate.org/privacy/" %} +
+{% endblock content %} diff --git a/weblate_web/pdf.py b/weblate_web/pdf.py index 244bea121d..c04f52f190 100644 --- a/weblate_web/pdf.py +++ b/weblate_web/pdf.py @@ -25,15 +25,19 @@ from weasyprint.text.fonts import FontConfiguration INVOICES_URL = "invoices:" +LEGAL_URL = "legal:" STATIC_URL = "static:" -TEMPLATES_PATH = Path(__file__).parent / "invoices" / "templates" +INVOICES_TEMPLATES_PATH = Path(__file__).parent / "invoices" / "templates" +LEGAL_TEMPLATES_PATH = Path(__file__).parent / "legal" / "templates" def url_fetcher(url: str) -> dict[str, str | bytes]: path_obj: Path result: dict[str, str | bytes] if url.startswith(INVOICES_URL): - path_obj = TEMPLATES_PATH / url.removeprefix(INVOICES_URL) + path_obj = INVOICES_TEMPLATES_PATH / url.removeprefix(INVOICES_URL) + elif url.startswith(LEGAL_URL): + path_obj = LEGAL_TEMPLATES_PATH / url.removeprefix(LEGAL_URL) elif url.startswith(STATIC_URL): fullname = url.removeprefix(STATIC_URL) match = finders.find(fullname) diff --git a/weblate_web/templates/privacy.html b/weblate_web/templates/privacy.html index c97a30a681..471cec3edb 100644 --- a/weblate_web/templates/privacy.html +++ b/weblate_web/templates/privacy.html @@ -1,6 +1,7 @@ {% extends "base.html" %} {% load i18n %} +{% load static %} {% block content %}
@@ -23,6 +24,7 @@

{% if LANGUAGE_CODE != "en" %} {% trans "View English version" %} {% endif %} + {% trans "Download as PDF" %} diff --git a/weblate_web/templates/terms.html b/weblate_web/templates/terms.html index c54e3320ec..c92e196fc9 100644 --- a/weblate_web/templates/terms.html +++ b/weblate_web/templates/terms.html @@ -1,6 +1,7 @@ {% extends "base.html" %} {% load i18n %} +{% load static %} {% block content %}
@@ -23,6 +24,8 @@

{% if LANGUAGE_CODE != "en" %} {% trans "View English version" %} {% endif %} + {% trans "Download as PDF" %}