Files
penracourses/templates/rad/media_cleanup.html
T
2026-06-08 10:57:01 +01:00

188 lines
8.1 KiB
HTML

{% extends 'base.html' %}
{% block css %}
{{ block.super }}
<style>
.media-cleanup-loading.htmx-indicator {
display: none;
}
.media-cleanup-form.htmx-request .media-cleanup-loading.htmx-indicator {
display: flex;
}
.media-cleanup-form.htmx-request button,
.media-cleanup-form.htmx-request input,
.media-cleanup-form.htmx-request textarea {
pointer-events: none;
opacity: 0.7;
}
.media-cleanup-modal-spinner {
width: 2.5rem;
height: 2.5rem;
}
</style>
{% endblock %}
{% block content %}
<div class="container-fluid py-4">
<div class="d-flex flex-wrap justify-content-between align-items-start gap-3 mb-4">
<div>
<h2 class="mb-1">Unused Media Cleanup</h2>
<p class="text-muted mb-0">
Run django-unused-media from the web UI to preview or remove orphaned files.
</p>
</div>
<a href="{% url 'logs_view' %}" class="btn btn-outline-secondary btn-sm">
<i class="bi bi-journal-text me-1"></i> View logs
</a>
</div>
<div class="card border-0 shadow-sm mb-4">
<div class="card-body">
<form
id="media-cleanup-form"
method="post"
hx-post="{% url 'media_cleanup' %}"
hx-target="#media-cleanup-result"
hx-swap="innerHTML"
hx-indicator="#media-cleanup-loading"
class="row g-3 media-cleanup-form"
>
{% csrf_token %}
<div class="col-12 col-lg-4">
<label for="id_minimum_file_age" class="form-label">Minimum file age (seconds)</label>
<input
id="id_minimum_file_age"
type="number"
min="0"
step="1"
name="minimum_file_age"
class="form-control"
value="{{ minimum_file_age|default:60 }}"
>
<div class="form-text">Skip very recent files to avoid race conditions with active uploads.</div>
</div>
<div class="col-12 col-lg-8">
<label for="id_exclude" class="form-label">Exclude masks (one per line)</label>
<textarea
id="id_exclude"
name="exclude"
rows="4"
class="form-control"
placeholder="example: thumbnails/*"
>{{ exclude }}</textarea>
<div class="form-text">Uses django-unused-media wildcard matching.</div>
</div>
<div class="col-12">
<div class="form-check">
<input
id="id_remove_empty_dirs"
type="checkbox"
class="form-check-input"
name="remove_empty_dirs"
{% if remove_empty_dirs %}checked{% endif %}
>
<label for="id_remove_empty_dirs" class="form-check-label">
Remove empty directories after cleanup
</label>
</div>
</div>
<div class="col-12 d-flex flex-wrap gap-2">
<button
type="submit"
name="action"
value="preview"
class="btn btn-primary"
data-loading-title="Previewing unused files"
data-loading-message="Scanning media storage for orphaned files."
>
<i class="bi bi-search me-1"></i> Preview unused files
</button>
<button
type="submit"
name="action"
value="cleanup"
class="btn btn-danger"
hx-confirm="Delete unused media files now? This cannot be undone."
data-loading-title="Deleting unused files"
data-loading-message="Removing orphaned media and recalculating disk usage."
>
<i class="bi bi-trash me-1"></i> Delete unused files
</button>
</div>
<div id="media-cleanup-loading" class="col-12 media-cleanup-loading htmx-indicator align-items-center gap-2 text-info">
<div class="spinner-border spinner-border-sm" role="status" aria-hidden="true"></div>
<span>Cleanup in progress. This can take a while for large media directories.</span>
</div>
</form>
</div>
</div>
<div id="media-cleanup-result">
{% include 'rad/partials/_media_cleanup_result.html' %}
</div>
</div>
<div class="modal fade" id="media-cleanup-loading-modal" tabindex="-1" aria-labelledby="media-cleanup-loading-title" aria-hidden="true" data-bs-backdrop="static" data-bs-keyboard="false">
<div class="modal-dialog modal-dialog-centered modal-sm">
<div class="modal-content border-0 shadow">
<div class="modal-body text-center py-4">
<div class="spinner-border text-info media-cleanup-modal-spinner mb-3" role="status" aria-hidden="true"></div>
<h5 id="media-cleanup-loading-title" class="mb-1">Working...</h5>
<p id="media-cleanup-loading-message" class="text-muted mb-0 small">Please wait while this operation completes.</p>
</div>
</div>
</div>
</div>
<script>
(function () {
var form = document.getElementById('media-cleanup-form');
var modalEl = document.getElementById('media-cleanup-loading-modal');
if (!form || !modalEl || typeof bootstrap === 'undefined') {
return;
}
var titleEl = document.getElementById('media-cleanup-loading-title');
var messageEl = document.getElementById('media-cleanup-loading-message');
var loadingModal = bootstrap.Modal.getOrCreateInstance(modalEl);
var activeButton = null;
form.addEventListener('click', function (event) {
var btn = event.target.closest('button[type="submit"]');
if (!btn || !form.contains(btn)) {
return;
}
activeButton = btn;
});
document.body.addEventListener('htmx:beforeRequest', function (event) {
if (event.target !== form) {
return;
}
var title = activeButton ? activeButton.getAttribute('data-loading-title') : null;
var message = activeButton ? activeButton.getAttribute('data-loading-message') : null;
titleEl.textContent = title || 'Working...';
messageEl.textContent = message || 'Please wait while this operation completes.';
loadingModal.show();
});
function hideLoadingModal(event) {
if (event.target !== form) {
return;
}
loadingModal.hide();
activeButton = null;
}
document.body.addEventListener('htmx:afterRequest', hideLoadingModal);
document.body.addEventListener('htmx:responseError', hideLoadingModal);
document.body.addEventListener('htmx:sendError', hideLoadingModal);
})();
</script>
{% endblock %}