feat/flask: reworked downloads page
- new table styles - info about last version and build date - "viruses" warning paragraph
This commit is contained in:
@@ -1,30 +1,23 @@
|
||||
import threading
|
||||
import time
|
||||
import json
|
||||
import re
|
||||
|
||||
from os import path, listdir
|
||||
from datetime import date, datetime, timezone
|
||||
from datetime import date
|
||||
from configparser import ConfigParser
|
||||
from urllib.request import urlopen
|
||||
|
||||
import sass
|
||||
|
||||
from flask import (
|
||||
Flask,
|
||||
redirect,
|
||||
render_template,
|
||||
request,
|
||||
url_for,
|
||||
g,
|
||||
Response
|
||||
)
|
||||
from flask import Flask, redirect, render_template, request, url_for, g, Response
|
||||
|
||||
|
||||
# ---------- ENV VARS --------------------------------------------------------
|
||||
|
||||
|
||||
GIT_URL = "https://git.kolibrios.org/api/v1/repos/KolibriOS/kolibrios/branches/main"
|
||||
GIT_FETCH_DELAY_SEC = 300 # 5 minutes
|
||||
STATUS_URL = "https://builds.kolibrios.org/status.html"
|
||||
STATUS_FETCH_DELAY_SEC = 300 # 5 minutes
|
||||
|
||||
|
||||
# ---------- APP CONFIG ------------------------------------------------------
|
||||
|
||||
@@ -40,18 +33,64 @@ with open("static/style.css", "w", encoding="utf-8") as f:
|
||||
|
||||
|
||||
autobuild_date = "DD.MM.YYYY"
|
||||
autobuild_vers = "0.0.0.0+0000-0000000"
|
||||
|
||||
|
||||
def _refresh_build_date_once():
|
||||
global autobuild_date
|
||||
global autobuild_date, autobuild_vers
|
||||
try:
|
||||
with urlopen(GIT_URL, timeout=10) as r:
|
||||
b = json.load(r)
|
||||
c = b.get("commit", {}) or {}
|
||||
ts = c.get("timestamp")
|
||||
if ts:
|
||||
dt = datetime.fromisoformat(ts.replace("Z", "+00:00")).astimezone(timezone.utc)
|
||||
autobuild_date = dt.strftime("%d.%m.%Y")
|
||||
from urllib.request import Request, urlopen
|
||||
import re
|
||||
|
||||
req = Request(
|
||||
STATUS_URL,
|
||||
headers={
|
||||
"User-Agent": "Mozilla/5.0",
|
||||
"Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8",
|
||||
},
|
||||
)
|
||||
with urlopen(req, timeout=10) as r:
|
||||
html = r.read().decode(
|
||||
r.headers.get_content_charset() or "utf-8", "replace"
|
||||
)
|
||||
|
||||
rows = re.findall(r"(<tr\b[^>]*>.*?</tr>)", html, flags=re.I | re.S)
|
||||
if not rows:
|
||||
return
|
||||
|
||||
last_commit_ver = None
|
||||
|
||||
for row in rows:
|
||||
cls = re.search(r'class\s*=\s*"([^"]*)"', row, flags=re.I)
|
||||
classes = cls.group(1).lower() if cls else ""
|
||||
|
||||
text = re.sub(r"<[^>]+>", " ", row)
|
||||
|
||||
if "commit" in classes:
|
||||
mver = re.search(
|
||||
r"\b(\d+\.\d+\.\d+\.\d+\+\d{3,8}-[0-9a-fA-F]{7,40})\b", row
|
||||
)
|
||||
if mver:
|
||||
last_commit_ver = mver.group(1)
|
||||
|
||||
elif "success" in classes:
|
||||
mts = re.search(
|
||||
r"\b(\d{4})\.(\d{2})\.(\d{2})\s+\d{2}:\d{2}:\d{2}\b", text
|
||||
)
|
||||
if not mts:
|
||||
mds = re.search(r"\b(\d{2})\.(\d{2})\.(\d{4})\b", text)
|
||||
if mds:
|
||||
autobuild_date = f"{mds.group(1)}.{mds.group(2)}.{mds.group(3)}"
|
||||
else:
|
||||
return
|
||||
else:
|
||||
y, mo, d = mts.groups()
|
||||
autobuild_date = f"{d}.{mo}.{y}"
|
||||
|
||||
if last_commit_ver:
|
||||
autobuild_vers = last_commit_ver
|
||||
return
|
||||
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
@@ -59,7 +98,7 @@ def _refresh_build_date_once():
|
||||
def _updater_loop():
|
||||
while True:
|
||||
_refresh_build_date_once()
|
||||
time.sleep(GIT_FETCH_DELAY_SEC)
|
||||
time.sleep(STATUS_FETCH_DELAY_SEC)
|
||||
|
||||
|
||||
_started = False
|
||||
@@ -68,6 +107,7 @@ _refresh_build_date_once()
|
||||
# Flask 3.x fix: start updater lazily on first request (since before_first_request is removed)
|
||||
_updater_lock = threading.Lock()
|
||||
|
||||
|
||||
@app.before_request
|
||||
def _ensure_updater_started():
|
||||
global _started
|
||||
@@ -78,6 +118,11 @@ def _ensure_updater_started():
|
||||
threading.Thread(target=_updater_loop, daemon=True).start()
|
||||
|
||||
|
||||
@app.context_processor
|
||||
def _inject_autobuild_vers():
|
||||
return {"autobuild_vers": autobuild_vers}
|
||||
|
||||
|
||||
@app.context_processor
|
||||
def _inject_autobuild_date():
|
||||
return {"autobuild_date": autobuild_date}
|
||||
@@ -90,7 +135,7 @@ def load_all_locales():
|
||||
translations = {}
|
||||
locales_dir = "locales"
|
||||
|
||||
locales_code_default = ('en', 'ru', 'es')
|
||||
locales_code_default = ("en", "ru", "es")
|
||||
locales_code_extra = []
|
||||
locales_code = ()
|
||||
|
||||
@@ -109,7 +154,7 @@ def load_all_locales():
|
||||
}
|
||||
|
||||
locales_code = locales_code_default + tuple(sorted(locales_code_extra))
|
||||
locales_name = {l: translations[l]['title']['language'] for l in locales_code}
|
||||
locales_name = {l: translations[l]["title"]["language"] for l in locales_code}
|
||||
|
||||
return translations, locales_name, locales_code
|
||||
|
||||
@@ -137,7 +182,7 @@ def render_localized_template(lang, template_name):
|
||||
@app.before_request
|
||||
def before_request():
|
||||
if args := request.view_args:
|
||||
g.locale = args.get('lang', 'en')
|
||||
g.locale = args.get("lang", "en")
|
||||
g.translations = translations.get(g.locale, get_best_lang())
|
||||
g.locales_name = locales_name
|
||||
|
||||
@@ -147,16 +192,14 @@ def inject_translations():
|
||||
def translate(text, **kwargs):
|
||||
section, key = text.split(":", 1)
|
||||
|
||||
template = g.translations \
|
||||
.get(section, {}) \
|
||||
.get(key, f"${section}: {key}$")
|
||||
template = g.translations.get(section, {}).get(key, f"${section}: {key}$")
|
||||
|
||||
try:
|
||||
return template.format(**kwargs)
|
||||
except Exception:
|
||||
return template
|
||||
|
||||
return {'_': translate}
|
||||
return {"_": translate}
|
||||
|
||||
|
||||
# ---------- MAIN PAGES ------------------------------------------------------
|
||||
|
||||
@@ -44,6 +44,9 @@ p_subscription = Wir hoffen, es gefällt Ihnen!
|
||||
[downloads]
|
||||
header = Herunterladen
|
||||
|
||||
version = Version:
|
||||
date = Build-Datum:
|
||||
|
||||
img-descr = Disketten-Image
|
||||
iso-descr = LiveCD-Abbild
|
||||
distr-descr = Universal Flash/Multi-Boot-Abbild
|
||||
@@ -68,6 +71,10 @@ download_description = Auf dieser Seite können Sie die nächtlichen Build-Distr
|
||||
{git} verfügbar.
|
||||
git-server = Git-Server
|
||||
|
||||
download_warn = Gelegentlich stufen einige Antivirenprogramme das {kolibrios}-Image fälschlicherweise als Bedrohung ein.
|
||||
Das ist ein Fehlalarm.
|
||||
{kolibrios} ist vollständig quelloffen, und Sie können es jederzeit selbst erstellen, um sicherzugehen, dass es vollständig sicher ist.
|
||||
|
||||
[screenshots]
|
||||
header = Bildschirmfotos
|
||||
|
||||
|
||||
@@ -45,6 +45,9 @@ p_subscription = We hope you will enjoy it!
|
||||
[downloads]
|
||||
header = Downloads
|
||||
|
||||
version = Version:
|
||||
date = Build date:
|
||||
|
||||
img-descr = Floppy disk image
|
||||
iso-descr = LiveCD image
|
||||
distr-descr = Universal Flash/Multi-boot image
|
||||
@@ -66,6 +69,10 @@ download_description = On this page you can download the nightly builds distribu
|
||||
distributed under {gpl} license, its source code is available on our {git}.
|
||||
git-server = Git server
|
||||
|
||||
download_warn = Occasionally, some antivirus software may incorrectly flag the {kolibrios} image as a threat.
|
||||
This is a false positive.
|
||||
{kolibrios} is fully open source, and you can always build it yourself to be sure that it is completely safe.
|
||||
|
||||
[screenshots]
|
||||
header = Screenshots
|
||||
|
||||
|
||||
@@ -41,6 +41,9 @@ p_subscription = ¡Esperamos que lo disfrutes!
|
||||
[downloads]
|
||||
header = Descargas
|
||||
|
||||
version = Versión:
|
||||
date = Fecha de compilación:
|
||||
|
||||
img-descr = Imagen de disquete
|
||||
iso-descr = Imagen LiveCD
|
||||
distr-descr = Imagen Flash Universal/Multi-boot
|
||||
@@ -63,6 +66,10 @@ download_description = En esta página puedes descargar la distribución de comp
|
||||
disponible en nuestro {git}.
|
||||
git-server = servidor Git
|
||||
|
||||
download_warn = Ocasionalmente, algunos programas antivirus pueden marcar incorrectamente la imagen de {kolibrios} como una amenaza.
|
||||
Esto es un falso positivo.
|
||||
{kolibrios} es completamente de código abierto y siempre puedes compilarlo tú mismo para asegurarte de que es totalmente seguro.
|
||||
|
||||
[screenshots]
|
||||
header = Pantallas
|
||||
|
||||
|
||||
@@ -48,6 +48,9 @@ p_subscription = Nous espérons que vous l`apprécierez !
|
||||
[downloads]
|
||||
header = Téléchargements
|
||||
|
||||
version = Version :
|
||||
date = Date de compilation :
|
||||
|
||||
img-descr = Image de la disquette
|
||||
iso-descr = Image du LiveCD
|
||||
distr-descr = Image Universal Flash/Multi-boot
|
||||
@@ -66,6 +69,10 @@ download_help = Pour un débutant, le LiveCD est le meilleur.\n\
|
||||
download_description = Sur cette page, vous pouvez télécharger la distribution des builds nocturnes, ce qui signifie qu`ils contiennent toujours les modifications les plus récentes du système et peuvent donc être instables. Tous les fichiers sont compressés avec {zip}. {kolibrios} est distribué sous licence {gpl} et son code source est disponible sur notre {git}.
|
||||
git-server = serveur Git
|
||||
|
||||
download_warn = Il arrive que certains antivirus signalent à tort l’image {kolibrios} comme une menace.
|
||||
Il s’agit d’un faux positif.
|
||||
{kolibrios} est entièrement open source et vous pouvez toujours le compiler vous-même pour vous assurer qu’il est totalement sûr.
|
||||
|
||||
[screenshots]
|
||||
header = Captures d`écran
|
||||
|
||||
|
||||
@@ -40,6 +40,9 @@ p_subscription = Ci auguriamo che vi piaccia!
|
||||
[downloads]
|
||||
header = Scaricamento
|
||||
|
||||
version = Versione:
|
||||
date = Data di compilazione:
|
||||
|
||||
img-descr = Immagine su dischetto
|
||||
iso-descr = Immagine LiveCD
|
||||
distr-descr = Immagine universale Flash/MultiBoot
|
||||
@@ -62,6 +65,10 @@ download_description = In questa pagina puoi scaricare la distribuzione delle bu
|
||||
disponibile sul nostro {git}.
|
||||
git-server = server Git
|
||||
|
||||
download_warn = Occasionalmente, alcuni software antivirus potrebbero segnalare erroneamente l’immagine di {kolibrios} come una minaccia.
|
||||
Si tratta di un falso positivo.
|
||||
{kolibrios} è completamente open source e puoi sempre compilarlo tu stesso per essere certo che è del tutto sicuro.
|
||||
|
||||
[screenshots]
|
||||
header = Captures d`écran
|
||||
|
||||
|
||||
@@ -46,6 +46,9 @@ p_subscription = We hopen dat je ervan zult genieten!
|
||||
[downloads]
|
||||
header = Downloads
|
||||
|
||||
version = Versie:
|
||||
date = Builddatum:
|
||||
|
||||
img-descr = Afbeelding op diskette
|
||||
iso-descr = LiveCD-afbeelding
|
||||
distr-descr = Universele Flash/Multi-boot image
|
||||
@@ -67,6 +70,10 @@ download_description = Op deze pagina kunt u de nightly builds-distributie downl
|
||||
verspreid onder de {gpl}-licentie, en de broncode is beschikbaar op onze {git}.
|
||||
git-server = Git-server
|
||||
|
||||
download_warn = Af en toe kunnen sommige antivirusprogramma’s het {kolibrios}-image ten onrechte als een bedreiging aanmerken.
|
||||
Dit is een vals-positief.
|
||||
{kolibrios} is volledig open-source en je kunt het altijd zelf bouwen om er zeker van te zijn dat het volledig veilig is.
|
||||
|
||||
[screenshots]
|
||||
header = Schermafbeeldingen
|
||||
|
||||
|
||||
+8
-1
@@ -24,7 +24,7 @@ p1 = {kolibrios} — это крошечная, но невероятно мощ
|
||||
мегабайт места на диске, процессора i586 и 12 МБ оперативной памяти. При
|
||||
этом она содержит богатый набор приложений, таких как текстовый редактор,
|
||||
просмотрщик изображений, графический редактор, веб-браузер и более 30
|
||||
захватывающих игр.Имеется полная поддержка файловых систем FAT12/16/32,
|
||||
захватывающих игр. Имеется полная поддержка файловых систем FAT12/16/32,
|
||||
только на чтение доступны NTFS, exFAT, ISO9660 и Ext2/3/4, а также
|
||||
присутсвтвует обширный набор {drivers} для популярных звуковых, сетевых и графических карт.
|
||||
drivers = драйверов
|
||||
@@ -48,6 +48,9 @@ p_subscription = Надеемся, вам понравится!
|
||||
[downloads]
|
||||
header = Скачать
|
||||
|
||||
version = Версия:
|
||||
date = Дата сборки:
|
||||
|
||||
img-descr = Образ дискеты
|
||||
iso-descr = Образ LiveCD
|
||||
distr-descr = Универсальный образ Flash/Multi-boot
|
||||
@@ -72,6 +75,10 @@ download_description = На этой странице вы можете скач
|
||||
исходный код доступен на нашем {git}.
|
||||
git-server = Git-сервере
|
||||
|
||||
download_warn = Иногда антивирусы могут по ошибке помечать образ {kolibrios} как угрозу.
|
||||
Это ложное срабатывание.
|
||||
{kolibrios} имеет полностью открытый исходный код, и вы всегда можете собрать её самостоятельно, чтобы убедиться, что она абсолютно безопасна.
|
||||
|
||||
[screenshots]
|
||||
header = Скриншоты
|
||||
|
||||
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 612 B |
Binary file not shown.
|
After Width: | Height: | Size: 1.5 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 542 B |
+43
-29
@@ -223,6 +223,13 @@ p {
|
||||
&-subscription {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
&-warn {
|
||||
img {
|
||||
margin-bottom: -2px;
|
||||
margin-right: 0.25em;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* DOWNLOADS */
|
||||
@@ -247,13 +254,21 @@ tr {
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
.tr-margin {
|
||||
&-bot > td {
|
||||
padding-bottom: 1em;
|
||||
.tr {
|
||||
&-header {
|
||||
b {
|
||||
display: inline;
|
||||
}
|
||||
}
|
||||
|
||||
&-top > td {
|
||||
padding-top: 1em;
|
||||
&-margin {
|
||||
&-bot > td {
|
||||
padding-bottom: 1em;
|
||||
}
|
||||
|
||||
&-top > td {
|
||||
padding-top: 1em;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -284,18 +299,17 @@ td {
|
||||
}
|
||||
}
|
||||
|
||||
&-date {
|
||||
text-align: right;
|
||||
padding-right: 2em;
|
||||
}
|
||||
|
||||
&-languages {
|
||||
text-align: right;
|
||||
padding-left: 2em;
|
||||
|
||||
a {
|
||||
display: inline-block;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
a + a {
|
||||
display: inline;
|
||||
margin-left: 1em;
|
||||
margin-top: 0em;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -307,16 +321,10 @@ hr {
|
||||
.help-button {
|
||||
cursor: pointer;
|
||||
display: inline-block;
|
||||
background: #FF9800;
|
||||
border-radius: 4px;
|
||||
box-shadow: inset 0 1px rgba(254,181,94,0.9), inset 0 -2px rgba(0,0,0,0.04);
|
||||
color: #FFFFFF;
|
||||
padding: 0.5em 1em;
|
||||
text-align: center;
|
||||
transition: 0.5s;
|
||||
color: #609A21;
|
||||
|
||||
&:hover {
|
||||
background: #F6920B;
|
||||
img {
|
||||
padding-bottom: 1px;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -444,22 +452,28 @@ iframe {
|
||||
margin: 0 0.5em;
|
||||
}
|
||||
|
||||
.tr {
|
||||
&-header {
|
||||
b {
|
||||
display: block;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.td {
|
||||
&-description {
|
||||
width: auto;
|
||||
}
|
||||
|
||||
&-date {
|
||||
display: none;
|
||||
padding-right: 1em;
|
||||
padding-left: 1em;
|
||||
}
|
||||
&-languages a {
|
||||
display: block;
|
||||
width: max-content;
|
||||
margin-left: auto;
|
||||
}
|
||||
|
||||
&-languages a + a {
|
||||
display: inline-block;
|
||||
white-space: nowrap;
|
||||
margin-top: 0.5em;
|
||||
margin-left: 0em;
|
||||
margin-left: auto;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+55
-17
@@ -11,7 +11,28 @@
|
||||
|
||||
<div id="article">
|
||||
<h1>{{ _('downloads:header') }}</h1>
|
||||
|
||||
<table>
|
||||
<tr class="tr-margin-bot">
|
||||
<td colspan="3"><hr /></td>
|
||||
</tr>
|
||||
|
||||
<tr class="tr-margin-bot tr-header">
|
||||
<td class="td-image" width="40">
|
||||
<img src="{{ url_for('static', filename='img/icons/i_kolibrios.png') }}" alt="kolibrios">
|
||||
</td>
|
||||
<td class="td-description">
|
||||
{{ _('downloads:version') }} <b>{{ autobuild_vers }}</b>
|
||||
</td>
|
||||
<td class="td-languages">
|
||||
{{ _('downloads:date') }} <b>{{ autobuild_date }}</b>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<tr class="tr-margin-bot">
|
||||
<td colspan="3"><hr /></td>
|
||||
</tr>
|
||||
|
||||
{% for ext, alt in (
|
||||
('img', 'floppy'),
|
||||
('iso', 'cd'),
|
||||
@@ -28,9 +49,6 @@
|
||||
<span class="beta">BETA</span>
|
||||
{% endif %}
|
||||
</td>
|
||||
<td class="td-date">
|
||||
{{ autobuild_date }}
|
||||
</td>
|
||||
<td class="td-languages">
|
||||
{% for l, lang in (
|
||||
('en_US', 'English'),
|
||||
@@ -38,27 +56,42 @@
|
||||
('es_ES', 'Español')
|
||||
) %}
|
||||
<a href="//builds.kolibrios.org/{{ l }}/latest-{{ ext }}.7z"
|
||||
title="ver. $autobuild_cmtid_{{ l }}, $autobuild_size_{{ l }}_{{ ext }}">{{ lang }}</a>
|
||||
title="ver. $autobuild_cmtid_{{ l }}, $autobuild_size_{{ l }}_{{ ext }}"
|
||||
class="button">
|
||||
{{ lang }}
|
||||
{% if l == 'en_US' %}
|
||||
<img src="{{ url_for('static', filename='img/flags/en.png') }}" alt="{{ lang }}">
|
||||
{% elif l == 'ru_RU' %}
|
||||
<img src="{{ url_for('static', filename='img/flags/ru.png') }}" alt="{{ lang }}">
|
||||
{% elif l == 'es_ES' %}
|
||||
<img src="{{ url_for('static', filename='img/flags/es.png') }}" alt="{{ lang }}">
|
||||
{% endif %}</a>
|
||||
{% endfor %}
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
|
||||
<tr>
|
||||
<td colspan="4">
|
||||
<hr />
|
||||
</td>
|
||||
<td colspan="3"><hr /></td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
<table>
|
||||
<tr class="tr-margin-top">
|
||||
<td class="td-description" colspan="2">
|
||||
<div role="button" class="help-button"
|
||||
onclick="alert('{{ _('downloads:download_help') }}');">
|
||||
{{ _('downloads:download_choice') }}
|
||||
onclick="alert('{{ _('downloads:download_help') }}');">
|
||||
<img src="{{ url_for('static', filename='img/icons/i_info.png') }}" alt="Info">
|
||||
<u>{{ _('downloads:download_choice') }}</u>
|
||||
</div>
|
||||
<td class="td-date">
|
||||
<a href="//archive.kolibrios.org/ru/">{{ _('downloads:prev_rev') }}</a>
|
||||
</td>
|
||||
<td class="td-languages">
|
||||
<a href="//builds.kolibrios.org/">{{ _('downloads:all_rev') }}</a>
|
||||
<a href="//archive.kolibrios.org/ru/">
|
||||
{{ _('downloads:prev_rev') }}
|
||||
</a>
|
||||
<a href="//builds.kolibrios.org/">
|
||||
{{ _('downloads:all_rev') }}
|
||||
</a>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
@@ -66,12 +99,17 @@
|
||||
<p>
|
||||
{{ _(
|
||||
'downloads:download_description',
|
||||
kolibrios="<b>{0}</b>"
|
||||
.format(_('title:index')),
|
||||
kolibrios="<b>{0}</b>".format(_('title:index')),
|
||||
zip="<a href='http://www.7-zip.org' target='_blank'>7zip</a>",
|
||||
gpl="<a href='http://www.gnu.org/licenses/gpl-2.0.html' target='_blank'>GPLv2</a>",
|
||||
git="<a href='https://git.kolibrios.org'>{0}</a>"
|
||||
.format(_('downloads:git-server'))
|
||||
git="<a href='https://git.kolibrios.org'>{0}</a>".format(_('downloads:git-server'))
|
||||
) | safe }}
|
||||
</p>
|
||||
|
||||
<p class="p-warn">
|
||||
<img src="{{ url_for('static', filename='img/icons/i_warn.png') }}" alt="Warn">{{ _(
|
||||
'downloads:download_warn',
|
||||
kolibrios="<b>{0}</b>".format(_('title:index'))
|
||||
) | safe }}
|
||||
</p>
|
||||
|
||||
@@ -96,4 +134,4 @@
|
||||
{% include 'tmpl/_footer.htm' %}
|
||||
</body>
|
||||
|
||||
</html>
|
||||
</html>
|
||||
|
||||
Reference in New Issue
Block a user