mirror of
https://github.com/minetest/contentdb.git
synced 2025-01-03 19:57:29 +01:00
Add ability to add packages from collection page
This commit is contained in:
parent
57ba3e8700
commit
5b4f997f3d
@ -122,7 +122,7 @@ def create_edit(author=None, name=None):
|
|||||||
if collection:
|
if collection:
|
||||||
for item in collection.items:
|
for item in collection.items:
|
||||||
form.descriptions.append_entry(item.description)
|
form.descriptions.append_entry(item.description)
|
||||||
form.package_ids.append_entry(item.package.id)
|
form.package_ids.append_entry(item.package.get_id())
|
||||||
form.package_removed.append_entry("0")
|
form.package_removed.append_entry("0")
|
||||||
else:
|
else:
|
||||||
form.name = None
|
form.name = None
|
||||||
@ -177,14 +177,31 @@ def handle_create_edit(collection: Collection, form: CollectionForm,
|
|||||||
form.populate_obj(collection)
|
form.populate_obj(collection)
|
||||||
collection.name = name
|
collection.name = name
|
||||||
|
|
||||||
|
order = 1
|
||||||
for i, package_id in enumerate(form.package_ids):
|
for i, package_id in enumerate(form.package_ids):
|
||||||
item = next((x for x in collection.items if str(x.package.id) == package_id.data), None)
|
link = next((x for x in collection.items if str(x.package.get_id()) == package_id.data), None)
|
||||||
if item is None:
|
to_delete = form.package_removed[i].data == "1"
|
||||||
continue
|
if link is None:
|
||||||
|
if to_delete:
|
||||||
|
continue
|
||||||
|
|
||||||
item.description = form.descriptions[i].data
|
package = Package.get_by_key(package_id.data)
|
||||||
if form.package_removed[i].data == "1":
|
if package is None:
|
||||||
db.session.delete(item)
|
abort(400)
|
||||||
|
|
||||||
|
link = CollectionPackage()
|
||||||
|
link.package = package
|
||||||
|
link.collection = collection
|
||||||
|
link.description = form.descriptions[i].data
|
||||||
|
link.order = order
|
||||||
|
order += 1
|
||||||
|
db.session.add(link)
|
||||||
|
elif to_delete:
|
||||||
|
db.session.delete(link)
|
||||||
|
else:
|
||||||
|
link.description = form.descriptions[i].data
|
||||||
|
link.order = order
|
||||||
|
order += 1
|
||||||
|
|
||||||
add_audit_log(severity, current_user,
|
add_audit_log(severity, current_user,
|
||||||
f"Edited collection {collection.author.username}/{collection.name}",
|
f"Edited collection {collection.author.username}/{collection.name}",
|
||||||
|
@ -2,16 +2,150 @@
|
|||||||
// @license magnet:?xt=urn:btih:1f739d935676111cfff4b4693e3816e664797050&dn=gpl-3.0.txt GPL-v3-or-Later
|
// @license magnet:?xt=urn:btih:1f739d935676111cfff4b4693e3816e664797050&dn=gpl-3.0.txt GPL-v3-or-Later
|
||||||
|
|
||||||
|
|
||||||
function handleRemovePackage(card) {
|
function removePackage(card) {
|
||||||
if (!confirm(card.getAttribute("data-delete-confirm"))) {
|
const message = document.getElementById("confirm_delete").innerText.trim();
|
||||||
|
const title = card.querySelector("h5 a").innerText.trim();
|
||||||
|
if (!confirm(message.replace("{title}", title))) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
card.querySelector("input[name^=package_removed]").value = "1";
|
card.querySelector("input[name^=package_removed]").value = "1";
|
||||||
card.classList.add("d-none");
|
card.classList.add("d-none");
|
||||||
|
onPackageQueryUpdate();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function restorePackage(id) {
|
||||||
|
const idElement = document.querySelector(`[value='${id}']`);
|
||||||
|
if (!idElement) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const card = idElement.parentNode.parentNode;
|
||||||
|
card.classList.remove("d-none");
|
||||||
|
card.querySelector("input[name^=package_removed]").value = "0";
|
||||||
|
card.scrollIntoView();
|
||||||
|
onPackageQueryUpdate();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function getAddedPackages() {
|
||||||
|
const ids = document.querySelectorAll("#package_list > article:not(.d-none) input[name^=package_ids]");
|
||||||
|
return [...ids].map(x => x.value);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function escapeHtml(unsafe) {
|
||||||
|
return unsafe
|
||||||
|
.replace(/&/g, "&")
|
||||||
|
.replace(/</g, "<")
|
||||||
|
.replace(/>/g, ">")
|
||||||
|
.replace(/"/g, """)
|
||||||
|
.replace(/'/g, "'");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function addPackage(pkg) {
|
||||||
|
document.getElementById("add_package").value = "";
|
||||||
|
document.getElementById("add_package_results").innerHTML = "";
|
||||||
|
|
||||||
|
const id = `${pkg.author}/${pkg.name}`;
|
||||||
|
if (restorePackage(id)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const nextId = document.querySelectorAll("input[name^=package_ids-]").length;
|
||||||
|
const url = `/packages/${id}/`;
|
||||||
|
const temp = document.createElement("div");
|
||||||
|
temp.innerHTML = `
|
||||||
|
<article class="card my-3">
|
||||||
|
<div class="card-body">
|
||||||
|
<button class="btn btn-sm btn-danger remove-package float-right" type="button" aria-label="Remove">
|
||||||
|
<i class="fas fa-trash"></i>
|
||||||
|
</button>
|
||||||
|
<h5>
|
||||||
|
<a href="${escapeHtml(url)}" target="_blank">
|
||||||
|
${escapeHtml(pkg.title)} by ${escapeHtml(pkg.author)}
|
||||||
|
</a>
|
||||||
|
</h5>
|
||||||
|
<p class="text-muted">
|
||||||
|
${escapeHtml(pkg.short_description)}
|
||||||
|
</p>
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="descriptions-${nextId}">Short Description</label>
|
||||||
|
<input class="form-control" id="descriptions-${nextId}" maxlength="500" minlength="0"
|
||||||
|
name="descriptions-${nextId}" type="text" value="">
|
||||||
|
<small class="form-text text-muted">You can replace the description with your own</small>
|
||||||
|
</div>
|
||||||
|
<input id="package_ids-${nextId}" name="package_ids-${nextId}" type="hidden" value="${id}">
|
||||||
|
<input id="package_removed-${nextId}" name="package_removed-${nextId}" type="hidden" value="0">
|
||||||
|
</div>
|
||||||
|
</article>
|
||||||
|
`;
|
||||||
|
|
||||||
|
const newElement = temp.children[0];
|
||||||
|
document.getElementById("package_list").appendChild(newElement);
|
||||||
|
newElement.scrollIntoView();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function updateResults(packages) {
|
||||||
|
const results = document.getElementById("add_package_results");
|
||||||
|
results.innerHTML = "";
|
||||||
|
document.getElementById("add_package_empty").style.display = packages.length === 0 ? "block" : "none";
|
||||||
|
|
||||||
|
const alreadyAdded = getAddedPackages();
|
||||||
|
packages.filter(pkg => !alreadyAdded.includes(`${pkg.author}/${pkg.name}`)).slice(0, 5).forEach(pkg => {
|
||||||
|
const result = document.createElement("a");
|
||||||
|
result.classList.add("list-group-item");
|
||||||
|
result.classList.add("list-group-item-action");
|
||||||
|
result.innerText = `${pkg.title} by ${pkg.author}`;
|
||||||
|
result.addEventListener("click", () => addPackage(pkg));
|
||||||
|
results.appendChild(result);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
let currentRequestId;
|
||||||
|
|
||||||
|
async function fetchPackagesAndUpdateResults(query) {
|
||||||
|
const requestId = Math.random() * 1000000;
|
||||||
|
currentRequestId = requestId;
|
||||||
|
if (query === "") {
|
||||||
|
updateResults([]);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const url = new URL("/api/packages/", window.location.origin);
|
||||||
|
url.searchParams.set("q", query);
|
||||||
|
const resp = await fetch(url.toString());
|
||||||
|
if (!resp.ok) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const packages = await resp.json();
|
||||||
|
if (currentRequestId !== requestId) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
updateResults(packages);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
let timeoutHandle;
|
||||||
|
function onPackageQueryUpdate() {
|
||||||
|
const query = document.getElementById("add_package").value.trim();
|
||||||
|
if (timeoutHandle) {
|
||||||
|
clearTimeout(timeoutHandle);
|
||||||
|
}
|
||||||
|
timeoutHandle = setTimeout(
|
||||||
|
() => fetchPackagesAndUpdateResults(query).catch(console.error),
|
||||||
|
200);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
window.onload = () => {
|
window.onload = () => {
|
||||||
console.log("Loaded");
|
|
||||||
document.querySelectorAll(".remove-package").forEach(button => {
|
document.querySelectorAll(".remove-package").forEach(button => {
|
||||||
const card = button.parentNode.parentNode;
|
const card = button.parentNode.parentNode;
|
||||||
const field = card.querySelector("input[name^=package_removed]");
|
const field = card.querySelector("input[name^=package_removed]");
|
||||||
@ -20,7 +154,12 @@ window.onload = () => {
|
|||||||
if (field && field.value === "1") {
|
if (field && field.value === "1") {
|
||||||
card.classList.add("d-none");
|
card.classList.add("d-none");
|
||||||
} else {
|
} else {
|
||||||
button.addEventListener("click", () => handleRemovePackage(card));
|
button.addEventListener("click", () => removePackage(card));
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const addPackageQuery = document.getElementById("add_package");
|
||||||
|
addPackageQuery.value = "";
|
||||||
|
addPackageQuery.classList.remove("d-none");
|
||||||
|
addPackageQuery.addEventListener("input", onPackageQueryUpdate);
|
||||||
};
|
};
|
||||||
|
@ -9,7 +9,7 @@
|
|||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
{% block scriptextra %}
|
{% block scriptextra %}
|
||||||
<script src="/static/collection_editor.js"></script>
|
<script src="/static/collection_editor.js?v=2"></script>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
{% block content %}
|
{% block content %}
|
||||||
@ -34,36 +34,45 @@
|
|||||||
|
|
||||||
{% if collection and collection.items %}
|
{% if collection and collection.items %}
|
||||||
<h2>{{ _("Packages") }}</h2>
|
<h2>{{ _("Packages") }}</h2>
|
||||||
<p class="text-muted">
|
<div class="mb-5">
|
||||||
{{ _("To add or remove a package, go to the package's page and click 'Add to collection'") }}
|
<label for="add_package" class="sr-only">Add package</label>
|
||||||
</p>
|
<input id="add_package" type="search" class="form-control d-none" placeholder="Add package">
|
||||||
{% for item in collection.items %}
|
<p id="add_package_empty" class="mt-2" style="display: none;">
|
||||||
{% set package = item.package %}
|
<i>{{ _("No results") }}</i>
|
||||||
<article class="card my-3"
|
</p>
|
||||||
data-delete-confirm="{{ _('Are you sure you want to remove %(title)s?', title=package.title) }}">
|
<div id="add_package_results" class="list-group"></div>
|
||||||
<div class="card-body">
|
</div>
|
||||||
<button class="btn btn-sm btn-danger remove-package float-right"
|
<div id="package_list">
|
||||||
type="button" aria-label="{{ _('Remove') }}">
|
{% for item in collection.items %}
|
||||||
<i class="fas fa-trash"></i>
|
{% set package = item.package %}
|
||||||
</button>
|
<article class="card my-3">
|
||||||
<h5>
|
<div class="card-body">
|
||||||
<a href="{{ package.get_url('packages.view') }}" target="_blank">
|
<button class="btn btn-sm btn-danger remove-package float-right"
|
||||||
{{ _("%(title)s by %(author)s", title=package.title, author=package.author.display_name) }}
|
type="button" aria-label="{{ _('Remove') }}">
|
||||||
</a>
|
<i class="fas fa-trash"></i>
|
||||||
</h5>
|
</button>
|
||||||
<p class="text-muted">
|
<h5>
|
||||||
{{ package.short_desc }}
|
<a href="{{ package.get_url('packages.view') }}" target="_blank">
|
||||||
</p>
|
{{ _("%(title)s by %(author)s", title=package.title, author=package.author.display_name) }}
|
||||||
{{ render_field(form.descriptions[loop.index - 1], hint=_("You can replace the description with your own")) }}
|
</a>
|
||||||
{{ form.package_ids[loop.index - 1]() }}
|
</h5>
|
||||||
{{ form.package_removed[loop.index - 1]() }}
|
<p class="text-muted">
|
||||||
</div>
|
{{ package.short_desc }}
|
||||||
</article>
|
</p>
|
||||||
{% endfor %}
|
{{ render_field(form.descriptions[loop.index - 1], hint=_("You can replace the description with your own")) }}
|
||||||
|
{{ form.package_ids[loop.index - 1]() }}
|
||||||
|
{{ form.package_removed[loop.index - 1]() }}
|
||||||
|
</div>
|
||||||
|
</article>
|
||||||
|
{% endfor %}
|
||||||
|
</div>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
<div class="mt-5">
|
<div class="mt-5">
|
||||||
{{ render_submit_field(form.submit) }}
|
{{ render_submit_field(form.submit) }}
|
||||||
</div>
|
</div>
|
||||||
|
<span id="confirm_delete" class="d-none">
|
||||||
|
{{ _("Are you sure you want to remove {title}?") }}
|
||||||
|
</span>
|
||||||
</form>
|
</form>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
Loading…
Reference in New Issue
Block a user