Build custom container images / build (map[base_image:php:8-fpm-alpine build_args:PHP_VERSION=8
context:php8-pgsql fingerprint_command:{ apk info -v | LC_ALL=C sort; find /usr/local/lib/php/extensions /usr/local/etc/php/conf.d -type f -exec sha256sum {} + | LC_ALL=C sort; }
name:ph… (push) Successful in 48s
Build custom container images / build (map[base_image:postgres:18 build_args:PG_VERSION=18
POSTGIS_VERSION=3
VCHORD_VERSION=0.5.3
context:postgres fingerprint_command:{ dpkg-query -W -f='${binary:Package}=${Version}\n' | LC_ALL=C sort; find /usr/lib/postgresql -type f -exec sha256su… (push) Successful in 55s
Build custom container images / build (map[base_image:python:3 build_args:PYTHON_VERSION=3
context:python-tools fingerprint_command:{ dpkg-query -W -f='${binary:Package}=${Version}\n' | LC_ALL=C sort; pip freeze | LC_ALL=C sort; }
name:python-tools oci_labels:org.opencontainers.ima… (push) Successful in 54s
Build custom container images / build (map[base_image:python:3.12-slim build_args:PYTHON_VERSION=3.12-slim
context:linkki-tiedotus fingerprint_command:{ dpkg-query -W -f='${binary:Package}=${Version}\n' | LC_ALL=C sort; pip freeze | LC_ALL=C sort; }
name:linkki-tiedotus oci_labels:… (push) Successful in 39s
122 lines
5.5 KiB
HTML
122 lines
5.5 KiB
HTML
<!doctype html>
|
|
<html lang="{{ effective_ui_language }}">
|
|
<head>
|
|
<meta charset="utf-8">
|
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
<title>{{ translations["app.title"] }}</title>
|
|
<link rel="icon" type="image/png" href="{{ static_asset_url('favicon.png') }}">
|
|
<link rel="stylesheet" href="{{ static_asset_url('app.css') }}">
|
|
</head>
|
|
<body>
|
|
<div class="layout">
|
|
<aside class="sidebar">
|
|
<div class="sidebar-actions">
|
|
<a class="sidebar-link {% if current_path == '/' %}active{% endif %}" href="/">{{ translations["nav.instructions"] }}</a>
|
|
<form method="post" action="/logout">
|
|
<button type="submit">{{ translations["nav.sign_out"] }}</button>
|
|
</form>
|
|
</div>
|
|
|
|
<form method="post" action="/language" class="language-form">
|
|
<input type="hidden" name="return_to" value="{{ current_path }}">
|
|
<label>
|
|
<span>{{ translations["nav.interface_language"] }}</span>
|
|
<select name="ui_language" onchange="this.form.submit()">
|
|
{% for option in language_options %}
|
|
<option value="{{ option.code }}" {% if option.code == ui_language %}selected{% endif %}>{{ translations[option.label_key] }}</option>
|
|
{% endfor %}
|
|
</select>
|
|
</label>
|
|
</form>
|
|
|
|
<form method="post" action="/announcements" class="create-form">
|
|
<input type="hidden" name="return_to" value="{{ current_path }}">
|
|
<input type="text" name="title" placeholder="{{ translations['create.placeholder'] }}" required class="{% if not permissions.save_announcements %}permission-disabled{% endif %}" {% if not permissions.save_announcements %}disabled title="{{ translations['create.permission_denied'] }}"{% endif %}>
|
|
<button type="submit" class="{% if not permissions.save_announcements %}permission-disabled{% endif %}" {% if not permissions.save_announcements %}disabled title="{{ translations['create.permission_denied'] }}"{% endif %}>{{ translations["create.button"] }}</button>
|
|
</form>
|
|
{% if create_error %}
|
|
<p class="error">{{ create_error }}</p>
|
|
{% endif %}
|
|
|
|
<nav class="announcement-list">
|
|
{% for item in announcements %}
|
|
<div class="announcement-entry">
|
|
<a class="announcement-item {% if broadcast and item.directory == broadcast.directory %}active{% endif %}" href="/announcements/{{ item.directory }}">
|
|
<span class="announcement-title-row">
|
|
{% if item.sidebar_has_pins %}
|
|
<span class="status-primary-marker status-{{ item.sidebar_pin_status }} status-primary-marker-pinned" aria-hidden="true"></span>
|
|
{% endif %}
|
|
{% if item.status_symbol %}
|
|
<span class="status-symbol status-{{ item.status }} {% if item.sidebar_links_incomplete %}status-symbol-incomplete{% endif %}">{{ item.status_symbol }}</span>
|
|
{% endif %}
|
|
<span class="announcement-title">{{ item.title }}</span>
|
|
</span>
|
|
<small>{{ item.target_summary }}</small>
|
|
{% for meta_line in item.sidebar_meta_lines %}
|
|
<small class="announcement-meta">{{ meta_line }}</small>
|
|
{% endfor %}
|
|
</a>
|
|
<div class="announcement-order-controls">
|
|
<form method="post" action="/announcements/{{ item.directory }}/move">
|
|
<input type="hidden" name="direction" value="up">
|
|
<input type="hidden" name="return_to" value="{{ current_path }}">
|
|
<button type="submit" class="{% if not permissions.save_announcements %}permission-disabled{% endif %}" {% if not item.can_move_up or not permissions.save_announcements %}disabled{% endif %}>↑</button>
|
|
</form>
|
|
<form method="post" action="/announcements/{{ item.directory }}/move">
|
|
<input type="hidden" name="direction" value="down">
|
|
<input type="hidden" name="return_to" value="{{ current_path }}">
|
|
<button type="submit" class="{% if not permissions.save_announcements %}permission-disabled{% endif %}" {% if not item.can_move_down or not permissions.save_announcements %}disabled{% endif %}>↓</button>
|
|
</form>
|
|
</div>
|
|
</div>
|
|
{% endfor %}
|
|
</nav>
|
|
</aside>
|
|
|
|
<main class="content">
|
|
{% block content %}{% endblock %}
|
|
</main>
|
|
</div>
|
|
<script>
|
|
(function () {
|
|
const key = "linkki.sidebar.scrollTop";
|
|
const sidebar = document.querySelector(".sidebar");
|
|
if (!sidebar) return;
|
|
|
|
const restore = () => {
|
|
const raw = window.sessionStorage.getItem(key);
|
|
if (!raw) return;
|
|
const scrollTop = Number(raw);
|
|
if (!Number.isFinite(scrollTop)) return;
|
|
sidebar.scrollTop = scrollTop;
|
|
window.requestAnimationFrame(() => {
|
|
sidebar.scrollTop = scrollTop;
|
|
});
|
|
};
|
|
|
|
const persist = () => {
|
|
window.sessionStorage.setItem(key, String(sidebar.scrollTop));
|
|
};
|
|
|
|
restore();
|
|
sidebar.addEventListener("scroll", persist, { passive: true });
|
|
window.addEventListener("beforeunload", persist);
|
|
|
|
document.addEventListener("click", (event) => {
|
|
const link = event.target.closest("a");
|
|
if (link && link.closest(".sidebar")) {
|
|
persist();
|
|
}
|
|
});
|
|
|
|
document.addEventListener("submit", (event) => {
|
|
const form = event.target;
|
|
if (form instanceof HTMLFormElement && form.closest(".sidebar")) {
|
|
persist();
|
|
}
|
|
});
|
|
})();
|
|
</script>
|
|
</body>
|
|
</html>
|