pictures in the blog
This commit is contained in:
@@ -11,14 +11,12 @@ admin-title = Admin
|
|||||||
admin-dashboard = Dashboard
|
admin-dashboard = Dashboard
|
||||||
admin-blog = Blog
|
admin-blog = Blog
|
||||||
admin-audio = Audio
|
admin-audio = Audio
|
||||||
admin-images = Images
|
|
||||||
admin-about = About
|
admin-about = About
|
||||||
admin-exit = Exit
|
admin-exit = Exit
|
||||||
view-site = View site
|
view-site = View site
|
||||||
admin-blog-desc = create and update blog articles.
|
admin-blog-desc = create and update blog articles.
|
||||||
admin-about-desc = edit the public about page content.
|
admin-about-desc = edit the public about page content.
|
||||||
admin-audio-desc = upload songs, then group them into albums.
|
admin-audio-desc = upload songs, then group them into albums.
|
||||||
admin-images-desc = upload images for covers and articles.
|
|
||||||
logout = Log out
|
logout = Log out
|
||||||
settings = Settings
|
settings = Settings
|
||||||
settings-language = Language
|
settings-language = Language
|
||||||
@@ -96,6 +94,11 @@ featured-image-id = Featured image id
|
|||||||
image-file = Image file
|
image-file = Image file
|
||||||
uploaded-image-id = Uploaded image id
|
uploaded-image-id = Uploaded image id
|
||||||
url = URL
|
url = URL
|
||||||
|
upload-featured-image = Upload image
|
||||||
|
image-upload-help = Upload an image here to use it as the article image.
|
||||||
|
image-uploading = Uploading...
|
||||||
|
image-uploaded = Image uploaded and selected.
|
||||||
|
image-upload-error = Image upload failed.
|
||||||
admin-blog-articles = Blog articles
|
admin-blog-articles = Blog articles
|
||||||
admin-blog-index-desc = Create, edit, and remove blog posts.
|
admin-blog-index-desc = Create, edit, and remove blog posts.
|
||||||
admin-blog-create-desc = Create a blog post for the public site.
|
admin-blog-create-desc = Create a blog post for the public site.
|
||||||
@@ -103,7 +106,6 @@ admin-no-articles = No articles yet.
|
|||||||
admin-create-first-post = Create the first blog post.
|
admin-create-first-post = Create the first blog post.
|
||||||
edit-article = Edit article
|
edit-article = Edit article
|
||||||
create-article = Create article
|
create-article = Create article
|
||||||
admin-images-upload-desc = Upload images for blog posts and audio covers.
|
|
||||||
edit-about = Edit About
|
edit-about = Edit About
|
||||||
update-about-page = Update the public about page.
|
update-about-page = Update the public about page.
|
||||||
view-page = View page
|
view-page = View page
|
||||||
|
|||||||
@@ -11,14 +11,12 @@ admin-title = Administrácia
|
|||||||
admin-dashboard = Prehľad
|
admin-dashboard = Prehľad
|
||||||
admin-blog = Blog
|
admin-blog = Blog
|
||||||
admin-audio = Hudba
|
admin-audio = Hudba
|
||||||
admin-images = Obrázky
|
|
||||||
admin-about = O mne
|
admin-about = O mne
|
||||||
admin-exit = Späť na web
|
admin-exit = Späť na web
|
||||||
view-site = Zobraziť web
|
view-site = Zobraziť web
|
||||||
admin-blog-desc = vytvoriť a upravovať blogové články.
|
admin-blog-desc = vytvoriť a upravovať blogové články.
|
||||||
admin-about-desc = upraviť obsah verejnej stránky o mne.
|
admin-about-desc = upraviť obsah verejnej stránky o mne.
|
||||||
admin-audio-desc = nahrať skladby a potom ich zoskupiť do albumov.
|
admin-audio-desc = nahrať skladby a potom ich zoskupiť do albumov.
|
||||||
admin-images-desc = nahrať obrázky pre obaly a články.
|
|
||||||
logout = Odhlásiť sa
|
logout = Odhlásiť sa
|
||||||
settings = Nastavenia
|
settings = Nastavenia
|
||||||
settings-language = Jazyk
|
settings-language = Jazyk
|
||||||
@@ -96,6 +94,11 @@ featured-image-id = ID hlavného obrázka
|
|||||||
image-file = Súbor obrázka
|
image-file = Súbor obrázka
|
||||||
uploaded-image-id = ID nahratého obrázka
|
uploaded-image-id = ID nahratého obrázka
|
||||||
url = URL
|
url = URL
|
||||||
|
upload-featured-image = Nahrať obrázok
|
||||||
|
image-upload-help = Tu nahraj obrázok, ktorý sa použije ako obrázok článku.
|
||||||
|
image-uploading = Nahrávam...
|
||||||
|
image-uploaded = Obrázok je nahratý a vybraný.
|
||||||
|
image-upload-error = Nahratie obrázka zlyhalo.
|
||||||
admin-blog-articles = Blogové články
|
admin-blog-articles = Blogové články
|
||||||
admin-blog-index-desc = Vytvárať, upravovať a odstraňovať blogové články.
|
admin-blog-index-desc = Vytvárať, upravovať a odstraňovať blogové články.
|
||||||
admin-blog-create-desc = Vytvoriť blogový článok pre verejný web.
|
admin-blog-create-desc = Vytvoriť blogový článok pre verejný web.
|
||||||
@@ -103,7 +106,6 @@ admin-no-articles = Zatiaľ žiadne články.
|
|||||||
admin-create-first-post = Vytvor prvý blogový článok.
|
admin-create-first-post = Vytvor prvý blogový článok.
|
||||||
edit-article = Upraviť článok
|
edit-article = Upraviť článok
|
||||||
create-article = Vytvoriť článok
|
create-article = Vytvoriť článok
|
||||||
admin-images-upload-desc = Nahrať obrázky pre blogové články a obaly albumov.
|
|
||||||
edit-about = Upraviť O mne
|
edit-about = Upraviť O mne
|
||||||
update-about-page = Upraviť verejnú stránku O mne.
|
update-about-page = Upraviť verejnú stránku O mne.
|
||||||
view-page = Zobraziť stránku
|
view-page = Zobraziť stránku
|
||||||
|
|||||||
@@ -71,7 +71,6 @@
|
|||||||
<li><a href="/admin/dashboard" data-nav="/admin/dashboard">{{ t(key="admin-dashboard", lang=lang | default(value='sk')) }}</a></li>
|
<li><a href="/admin/dashboard" data-nav="/admin/dashboard">{{ t(key="admin-dashboard", lang=lang | default(value='sk')) }}</a></li>
|
||||||
<li><a href="/admin/blog/articles" data-nav="/admin/blog">{{ t(key="admin-blog", lang=lang | default(value='sk')) }}</a></li>
|
<li><a href="/admin/blog/articles" data-nav="/admin/blog">{{ t(key="admin-blog", lang=lang | default(value='sk')) }}</a></li>
|
||||||
<li><a href="/admin/audio/albums" data-nav="/admin/audio">{{ t(key="admin-audio", lang=lang | default(value='sk')) }}</a></li>
|
<li><a href="/admin/audio/albums" data-nav="/admin/audio">{{ t(key="admin-audio", lang=lang | default(value='sk')) }}</a></li>
|
||||||
<li><a href="/admin/images" data-nav="/admin/images">{{ t(key="admin-images", lang=lang | default(value='sk')) }}</a></li>
|
|
||||||
<li><a href="/admin/about" data-nav="/admin/about">{{ t(key="admin-about", lang=lang | default(value='sk')) }}</a></li>
|
<li><a href="/admin/about" data-nav="/admin/about">{{ t(key="admin-about", lang=lang | default(value='sk')) }}</a></li>
|
||||||
<li><a href="/" class="t-blue">{{ t(key="admin-exit", lang=lang | default(value='sk')) }}</a></li>
|
<li><a href="/" class="t-blue">{{ t(key="admin-exit", lang=lang | default(value='sk')) }}</a></li>
|
||||||
<li>
|
<li>
|
||||||
@@ -93,7 +92,6 @@
|
|||||||
<li><a href="/admin/dashboard">{{ t(key="admin-dashboard", lang=lang | default(value='sk')) }}</a></li>
|
<li><a href="/admin/dashboard">{{ t(key="admin-dashboard", lang=lang | default(value='sk')) }}</a></li>
|
||||||
<li><a href="/admin/blog/articles">{{ t(key="admin-blog", lang=lang | default(value='sk')) }}</a></li>
|
<li><a href="/admin/blog/articles">{{ t(key="admin-blog", lang=lang | default(value='sk')) }}</a></li>
|
||||||
<li><a href="/admin/audio/albums">{{ t(key="admin-audio", lang=lang | default(value='sk')) }}</a></li>
|
<li><a href="/admin/audio/albums">{{ t(key="admin-audio", lang=lang | default(value='sk')) }}</a></li>
|
||||||
<li><a href="/admin/images">{{ t(key="admin-images", lang=lang | default(value='sk')) }}</a></li>
|
|
||||||
<li><a href="/admin/about">{{ t(key="admin-about", lang=lang | default(value='sk')) }}</a></li>
|
<li><a href="/admin/about">{{ t(key="admin-about", lang=lang | default(value='sk')) }}</a></li>
|
||||||
<li><a href="/" class="t-blue">{{ t(key="admin-exit", lang=lang | default(value='sk')) }}</a></li>
|
<li><a href="/" class="t-blue">{{ t(key="admin-exit", lang=lang | default(value='sk')) }}</a></li>
|
||||||
<li>
|
<li>
|
||||||
|
|||||||
@@ -20,12 +20,56 @@
|
|||||||
</label>
|
</label>
|
||||||
<label>
|
<label>
|
||||||
{{ t(key="featured-image-id", lang=lang | default(value='sk')) }}
|
{{ t(key="featured-image-id", lang=lang | default(value='sk')) }}
|
||||||
<input type="text" name="featured_image_id" value="{% if article.featured_image_id %}{{ article.featured_image_id }}{% endif %}">
|
<input type="text" name="featured_image_id" data-blog-image-id value="{% if article.featured_image_id %}{{ article.featured_image_id }}{% endif %}">
|
||||||
</label>
|
</label>
|
||||||
|
<div>
|
||||||
|
<input type="file" accept="image/jpeg,image/png,image/webp,image/gif" data-blog-image-file class="file-input file-input-bordered">
|
||||||
|
<button type="button" class="btn btn-outline btn-sm" data-blog-image-upload data-uploading="{{ t(key="image-uploading", lang=lang | default(value='sk')) }}" data-ready="{{ t(key="upload-featured-image", lang=lang | default(value='sk')) }}">{{ t(key="upload-featured-image", lang=lang | default(value='sk')) }}</button>
|
||||||
|
<p class="text-sm opacity-70" data-blog-image-status>{{ t(key="image-upload-help", lang=lang | default(value='sk')) }}</p>
|
||||||
|
<img data-blog-image-preview alt="" class="mt-2 max-h-48 rounded border border-base-300 object-cover" {% if article.featured_image_id %}src="/images/{{ article.featured_image_id }}"{% endif %} style="{% if not article.featured_image_id %}display: none;{% endif %}">
|
||||||
|
</div>
|
||||||
<label>
|
<label>
|
||||||
<input type="checkbox" name="published" {% if article.published %}checked{% endif %}>
|
<input type="checkbox" name="published" {% if article.published %}checked{% endif %}>
|
||||||
{{ t(key="published", lang=lang | default(value='sk')) }}
|
{{ t(key="published", lang=lang | default(value='sk')) }}
|
||||||
</label>
|
</label>
|
||||||
<button type="submit">{{ t(key="save", lang=lang | default(value='sk')) }}</button>
|
<button type="submit">{{ t(key="save", lang=lang | default(value='sk')) }}</button>
|
||||||
</form>
|
</form>
|
||||||
|
<script>
|
||||||
|
(function () {
|
||||||
|
const fileInput = document.querySelector('[data-blog-image-file]');
|
||||||
|
const idInput = document.querySelector('[data-blog-image-id]');
|
||||||
|
const uploadButton = document.querySelector('[data-blog-image-upload]');
|
||||||
|
const status = document.querySelector('[data-blog-image-status]');
|
||||||
|
const preview = document.querySelector('[data-blog-image-preview]');
|
||||||
|
if (!fileInput || !idInput || !uploadButton || !status || !preview) return;
|
||||||
|
|
||||||
|
function setPreview(filename) {
|
||||||
|
if (!filename) return;
|
||||||
|
preview.src = '/images/' + filename;
|
||||||
|
preview.style.display = '';
|
||||||
|
}
|
||||||
|
|
||||||
|
uploadButton.addEventListener('click', async function () {
|
||||||
|
const file = fileInput.files && fileInput.files[0];
|
||||||
|
if (!file) return;
|
||||||
|
uploadButton.disabled = true;
|
||||||
|
uploadButton.textContent = uploadButton.dataset.uploading;
|
||||||
|
const formData = new FormData();
|
||||||
|
formData.append('file', file);
|
||||||
|
try {
|
||||||
|
const response = await fetch('/images/upload', { method: 'POST', body: formData });
|
||||||
|
if (!response.ok) throw new Error('upload failed');
|
||||||
|
const result = await response.json();
|
||||||
|
idInput.value = result.filename;
|
||||||
|
setPreview(result.filename);
|
||||||
|
status.textContent = '{{ t(key="image-uploaded", lang=lang | default(value='sk')) }}';
|
||||||
|
} catch (_error) {
|
||||||
|
status.textContent = '{{ t(key="image-upload-error", lang=lang | default(value='sk')) }}';
|
||||||
|
} finally {
|
||||||
|
uploadButton.disabled = false;
|
||||||
|
uploadButton.textContent = uploadButton.dataset.ready;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
})();
|
||||||
|
</script>
|
||||||
{% endblock content %}
|
{% endblock content %}
|
||||||
|
|||||||
@@ -32,7 +32,13 @@
|
|||||||
|
|
||||||
<div class="form-control">
|
<div class="form-control">
|
||||||
<label class="label"><span class="label-text">{{ t(key="featured-image-id", lang=lang | default(value='sk')) }}</span></label>
|
<label class="label"><span class="label-text">{{ t(key="featured-image-id", lang=lang | default(value='sk')) }}</span></label>
|
||||||
<input type="text" name="featured_image_id" class="input input-bordered w-full">
|
<div class="flex flex-wrap gap-2">
|
||||||
|
<input type="text" name="featured_image_id" data-blog-image-id class="input input-bordered min-w-0 flex-1">
|
||||||
|
<input type="file" accept="image/jpeg,image/png,image/webp,image/gif" data-blog-image-file class="file-input file-input-bordered min-w-0 flex-1">
|
||||||
|
<button type="button" class="btn btn-outline btn-sm" data-blog-image-upload data-uploading="{{ t(key="image-uploading", lang=lang | default(value='sk')) }}" data-ready="{{ t(key="upload-featured-image", lang=lang | default(value='sk')) }}">{{ t(key="upload-featured-image", lang=lang | default(value='sk')) }}</button>
|
||||||
|
</div>
|
||||||
|
<p class="text-sm opacity-70" data-blog-image-status>{{ t(key="image-upload-help", lang=lang | default(value='sk')) }}</p>
|
||||||
|
<img data-blog-image-preview alt="" class="mt-2 hidden max-h-48 rounded border border-base-300 object-cover">
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<label class="label cursor-pointer justify-start gap-2">
|
<label class="label cursor-pointer justify-start gap-2">
|
||||||
@@ -48,4 +54,42 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<script>
|
||||||
|
(function () {
|
||||||
|
const fileInput = document.querySelector('[data-blog-image-file]');
|
||||||
|
const idInput = document.querySelector('[data-blog-image-id]');
|
||||||
|
const uploadButton = document.querySelector('[data-blog-image-upload]');
|
||||||
|
const status = document.querySelector('[data-blog-image-status]');
|
||||||
|
const preview = document.querySelector('[data-blog-image-preview]');
|
||||||
|
if (!fileInput || !idInput || !uploadButton || !status || !preview) return;
|
||||||
|
|
||||||
|
function setPreview(filename) {
|
||||||
|
if (!filename) return;
|
||||||
|
preview.src = '/images/' + filename;
|
||||||
|
preview.classList.remove('hidden');
|
||||||
|
}
|
||||||
|
|
||||||
|
uploadButton.addEventListener('click', async function () {
|
||||||
|
const file = fileInput.files && fileInput.files[0];
|
||||||
|
if (!file) return;
|
||||||
|
uploadButton.disabled = true;
|
||||||
|
uploadButton.textContent = uploadButton.dataset.uploading;
|
||||||
|
const formData = new FormData();
|
||||||
|
formData.append('file', file);
|
||||||
|
try {
|
||||||
|
const response = await fetch('/images/upload', { method: 'POST', body: formData });
|
||||||
|
if (!response.ok) throw new Error('upload failed');
|
||||||
|
const result = await response.json();
|
||||||
|
idInput.value = result.filename;
|
||||||
|
setPreview(result.filename);
|
||||||
|
status.textContent = '{{ t(key="image-uploaded", lang=lang | default(value='sk')) }}';
|
||||||
|
} catch (_error) {
|
||||||
|
status.textContent = '{{ t(key="image-upload-error", lang=lang | default(value='sk')) }}';
|
||||||
|
} finally {
|
||||||
|
uploadButton.disabled = false;
|
||||||
|
uploadButton.textContent = uploadButton.dataset.ready;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
})();
|
||||||
|
</script>
|
||||||
{% endblock content %}
|
{% endblock content %}
|
||||||
|
|||||||
@@ -1,38 +0,0 @@
|
|||||||
{% extends "admin/base.html" %}
|
|
||||||
|
|
||||||
{% block title %}{{ t(key="admin-images", lang=lang | default(value='sk')) }}{% endblock title %}
|
|
||||||
|
|
||||||
{% block content %}
|
|
||||||
<div class="space-y-2">
|
|
||||||
<div class="flex flex-wrap items-center justify-between gap-3">
|
|
||||||
<div>
|
|
||||||
<h1 class="text-2xl font-bold">{{ t(key="admin-images", lang=lang | default(value='sk')) }}</h1>
|
|
||||||
<p class="text-sm opacity-70">{{ t(key="admin-images-upload-desc", lang=lang | default(value='sk')) }}</p>
|
|
||||||
</div>
|
|
||||||
<a href="/admin/dashboard" class="btn btn-ghost btn-sm">{{ t(key="back-to-dashboard", lang=lang | default(value='sk')) }}</a>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="card border border-base-300 bg-base-100 shadow-sm">
|
|
||||||
<div class="card-body">
|
|
||||||
{% if uploaded %}
|
|
||||||
<div class="alert mb-4">
|
|
||||||
<div>
|
|
||||||
<p class="font-medium">{{ t(key="uploaded-image-id", lang=lang | default(value='sk')) }}: {{ uploaded }}</p>
|
|
||||||
<p class="text-sm opacity-70">{{ t(key="url", lang=lang | default(value='sk')) }}: {{ uploaded_url }}</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
{% endif %}
|
|
||||||
|
|
||||||
<form method="post" action="/admin/images/upload" enctype="multipart/form-data" class="space-y-2">
|
|
||||||
<div class="form-control">
|
|
||||||
<label class="label"><span class="label-text">{{ t(key="image-file", lang=lang | default(value='sk')) }}</span></label>
|
|
||||||
<input type="file" name="file" accept="image/jpeg,image/png,image/webp,image/gif" required class="file-input file-input-bordered w-full">
|
|
||||||
</div>
|
|
||||||
<div class="flex flex-wrap gap-2 pt-2">
|
|
||||||
<button type="submit" class="btn btn-neutral btn-sm">{{ t(key="upload", lang=lang | default(value='sk')) }}</button>
|
|
||||||
</div>
|
|
||||||
</form>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
{% endblock content %}
|
|
||||||
@@ -56,19 +56,5 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</article>
|
</article>
|
||||||
|
|
||||||
<article class="card">
|
|
||||||
<div class="term-head">
|
|
||||||
<span class="term-head-name">/admin/images</span>
|
|
||||||
<span class="term-head-meta term-tag is-green">{{ t(key="admin-images", lang=lang | default(value='sk')) }}</span>
|
|
||||||
</div>
|
|
||||||
<div class="card-body">
|
|
||||||
<h2 class="card-title text-base">{{ t(key="admin-images", lang=lang | default(value='sk')) }}</h2>
|
|
||||||
<p class="text-sm opacity-70">{{ t(key="admin-images-desc", lang=lang | default(value='sk')) }}</p>
|
|
||||||
<div class="pt-2">
|
|
||||||
<a href="/admin/images" class="btn btn-primary btn-sm">[ {{ t(key="open", lang=lang | default(value='sk')) }} → ]</a>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</article>
|
|
||||||
</div>
|
</div>
|
||||||
{% endblock content %}
|
{% endblock content %}
|
||||||
|
|||||||
@@ -24,6 +24,9 @@
|
|||||||
<span class="term-head-meta term-tag">{{ t(key="post", lang=lang | default(value='sk')) }}</span>
|
<span class="term-head-meta term-tag">{{ t(key="post", lang=lang | default(value='sk')) }}</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="card-body">
|
<div class="card-body">
|
||||||
|
{% if article.featured_image_id %}
|
||||||
|
<img src="/images/{{ article.featured_image_id }}" alt="" class="mb-3 max-h-64 w-full rounded object-cover">
|
||||||
|
{% endif %}
|
||||||
<h2 class="card-title text-base">
|
<h2 class="card-title text-base">
|
||||||
<a href="/blog/{{ article.slug }}">{{ article.title }}</a>
|
<a href="/blog/{{ article.slug }}">{{ article.title }}</a>
|
||||||
</h2>
|
</h2>
|
||||||
@@ -59,6 +62,9 @@
|
|||||||
<span class="term-head-meta term-tag">{{ t(key="post", lang=lang | default(value='sk')) }}</span>
|
<span class="term-head-meta term-tag">{{ t(key="post", lang=lang | default(value='sk')) }}</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="card-body">
|
<div class="card-body">
|
||||||
|
{% if article.featured_image_id %}
|
||||||
|
<img src="/images/{{ article.featured_image_id }}" alt="" class="mb-3 max-h-64 w-full rounded object-cover">
|
||||||
|
{% endif %}
|
||||||
<h2 class="card-title text-base">
|
<h2 class="card-title text-base">
|
||||||
<a href="/blog/{{ article.slug }}">{{ article.title }}</a>
|
<a href="/blog/{{ article.slug }}">{{ article.title }}</a>
|
||||||
</h2>
|
</h2>
|
||||||
|
|||||||
@@ -21,6 +21,9 @@
|
|||||||
<span class="term-head-meta term-tag is-blue">{{ t(key="readonly", lang=lang | default(value='sk')) }}</span>
|
<span class="term-head-meta term-tag is-blue">{{ t(key="readonly", lang=lang | default(value='sk')) }}</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="card-body">
|
<div class="card-body">
|
||||||
|
{% if article.featured_image_id %}
|
||||||
|
<img src="/images/{{ article.featured_image_id }}" alt="" class="mb-4 max-h-[28rem] w-full rounded object-cover">
|
||||||
|
{% endif %}
|
||||||
{% if article.excerpt %}
|
{% if article.excerpt %}
|
||||||
<p class="term-prose t-yellow"># {{ article.excerpt }}</p>
|
<p class="term-prose t-yellow"># {{ article.excerpt }}</p>
|
||||||
<div class="border-t border-base-300 pt-4"></div>
|
<div class="border-t border-base-300 pt-4"></div>
|
||||||
@@ -45,6 +48,9 @@
|
|||||||
<span class="term-head-meta term-tag is-blue">{{ t(key="readonly", lang=lang | default(value='sk')) }}</span>
|
<span class="term-head-meta term-tag is-blue">{{ t(key="readonly", lang=lang | default(value='sk')) }}</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="card-body">
|
<div class="card-body">
|
||||||
|
{% if article.featured_image_id %}
|
||||||
|
<img src="/images/{{ article.featured_image_id }}" alt="" class="mb-4 max-h-[28rem] w-full rounded object-cover">
|
||||||
|
{% endif %}
|
||||||
{% if article.excerpt %}
|
{% if article.excerpt %}
|
||||||
<p class="term-prose t-yellow"># {{ article.excerpt }}</p>
|
<p class="term-prose t-yellow"># {{ article.excerpt }}</p>
|
||||||
<div class="border-t border-base-300 pt-4"></div>
|
<div class="border-t border-base-300 pt-4"></div>
|
||||||
|
|||||||
@@ -24,7 +24,6 @@ use sea_orm::{
|
|||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use serde_json::json;
|
use serde_json::json;
|
||||||
use std::{
|
use std::{
|
||||||
collections::HashMap,
|
|
||||||
path::{Path as StdPath, PathBuf},
|
path::{Path as StdPath, PathBuf},
|
||||||
str::FromStr,
|
str::FromStr,
|
||||||
};
|
};
|
||||||
@@ -436,36 +435,6 @@ async fn image_upload(auth: auth::JWT, State(ctx): State<AppContext>, multipart:
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
#[debug_handler]
|
|
||||||
async fn admin_images(
|
|
||||||
auth: auth::JWT,
|
|
||||||
jar: CookieJar,
|
|
||||||
ViewEngine(v): ViewEngine<TeraView>,
|
|
||||||
Query(query): Query<HashMap<String, String>>,
|
|
||||||
State(ctx): State<AppContext>,
|
|
||||||
) -> Result<Response> {
|
|
||||||
admin::current_admin(auth, &ctx).await?;
|
|
||||||
let uploaded = query.get("uploaded");
|
|
||||||
format::view(
|
|
||||||
&v,
|
|
||||||
"admin/images/index.html",
|
|
||||||
json!({
|
|
||||||
"uploaded": uploaded,
|
|
||||||
"uploaded_url": uploaded.map(|filename| format!("/images/{filename}")),
|
|
||||||
"lang": current_lang(&jar),
|
|
||||||
}),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[debug_handler]
|
|
||||||
async fn admin_image_upload(auth: auth::JWT, State(ctx): State<AppContext>, multipart: Multipart) -> Result<Response> {
|
|
||||||
admin::current_admin(auth, &ctx).await?;
|
|
||||||
let data = read_multipart_file(multipart, IMAGE_MAX_BYTES).await?;
|
|
||||||
let extension = detect_image_extension(&data)?;
|
|
||||||
let filename = store_upload(&ctx, IMAGE_STORAGE_DIR, extension, data).await?;
|
|
||||||
format::redirect(&format!("/admin/images?uploaded={filename}"))
|
|
||||||
}
|
|
||||||
|
|
||||||
#[debug_handler]
|
#[debug_handler]
|
||||||
async fn image_serve(Path(filename): Path<String>, State(ctx): State<AppContext>) -> Result<Response> {
|
async fn image_serve(Path(filename): Path<String>, State(ctx): State<AppContext>) -> Result<Response> {
|
||||||
let filename = safe_filename(&filename)?;
|
let filename = safe_filename(&filename)?;
|
||||||
@@ -1079,8 +1048,6 @@ pub fn routes() -> Routes {
|
|||||||
.add("/audio/albums/{slug}/tracks", get(public_album_tracks))
|
.add("/audio/albums/{slug}/tracks", get(public_album_tracks))
|
||||||
.add("/audio/tracks", get(public_tracks))
|
.add("/audio/tracks", get(public_tracks))
|
||||||
.add("/audio/tracks/{id}/stream", get(track_stream))
|
.add("/audio/tracks/{id}/stream", get(track_stream))
|
||||||
.add("/admin/images", get(admin_images))
|
|
||||||
.add("/admin/images/upload", post(admin_image_upload).layer(DefaultBodyLimit::max(IMAGE_MAX_BYTES + 1024 * 1024)))
|
|
||||||
.add("/admin/audio/albums", get(admin_albums))
|
.add("/admin/audio/albums", get(admin_albums))
|
||||||
.add("/admin/audio/albums/create", get(admin_album_new))
|
.add("/admin/audio/albums/create", get(admin_album_new))
|
||||||
.add("/admin/audio/albums/create", post(admin_album_create).layer(DefaultBodyLimit::max(IMAGE_MAX_BYTES + 1024 * 1024)))
|
.add("/admin/audio/albums/create", post(admin_album_create).layer(DefaultBodyLimit::max(IMAGE_MAX_BYTES + 1024 * 1024)))
|
||||||
|
|||||||
Reference in New Issue
Block a user