mirror of
https://github.com/retspen/webvirtcloud
synced 2026-11-08 11:16:47 +00:00
Settings > Users was a bold title, an unlabelled "+" and a table of names with a trash icon. The owner chose this layout from three rendered options: one list of owners, each with what it may do (change settings, delete the VM, change console settings, or power and console only), Edit rights and Remove, an empty state, and a labelled "Add owner". - Remove and Edit rights come back to the VM's Users tab: the return address carried #users unencoded, so it was lost, and Edit rights went to the user's account page. Both accounts views now go to a ?next page of this site. - The add-owner list offers only users who are not owners yet and not administrators, who can use every VM anyway; a superuser owner also took the only place when a VM may have one owner. - The unused del_owner view and URL are removed: the list uses the accounts page, which asks before removing an owner. XML: - An XML edit not saved yet is kept: the page no longer reloads itself when the VM's state changes, and the browser asks before leaving it. A state change lost the edit. - The button that opens the review dialog reads "Review changes"; it did not save, as "Change" suggested. Closing the dialog gives focus back to the editor.
99 lines
4 KiB
JavaScript
99 lines
4 KiB
JavaScript
// The VM page's XML editor. Review changes shows the lines the edit changes
|
|
// in the VM's definition (instances.views.change_xml), and Save in that dialog
|
|
// defines it. A refusal shows in the dialog, and the edit stays in the editor.
|
|
// An edit not saved yet keeps the page from reloading itself on a VM state
|
|
// change (window.xmlEditUnsaved), and the browser asks before leaving it.
|
|
(function () {
|
|
var form = document.getElementById("xml-form");
|
|
if (!form) {
|
|
return; // the editor exists only for superusers
|
|
}
|
|
var editor = ace.edit("editor");
|
|
editor.getSession().setMode("ace/mode/xml");
|
|
var base = editor.getValue(); // the definition the editor opened with
|
|
var dialog = document.getElementById("xml-review");
|
|
var modal = bootstrap.Modal.getOrCreateInstance(dialog);
|
|
var error = document.getElementById("xml-review-error");
|
|
var same = document.getElementById("xml-review-same");
|
|
var diff = document.getElementById("xml-review-diff");
|
|
var save = document.getElementById("xml-review-save");
|
|
var reviewed = ""; // Save defines the XML the dialog shows, not later edits
|
|
var clean = base; // the XML the page has: the opened definition, or the one Save just defined
|
|
|
|
window.xmlEditUnsaved = function () {
|
|
return editor.getValue() !== clean;
|
|
};
|
|
window.addEventListener("beforeunload", function (event) {
|
|
if (window.xmlEditUnsaved()) {
|
|
event.preventDefault();
|
|
}
|
|
});
|
|
dialog.addEventListener("hidden.bs.modal", function () {
|
|
editor.focus();
|
|
});
|
|
|
|
// The answer: diff, saved, or error (detail when the VM is busy or moved).
|
|
// Any other failure, such as a redirect, keeps the edit and says so.
|
|
function post(xml, confirm) {
|
|
var data = new FormData(form);
|
|
data.set("inst_xml", xml);
|
|
data.set("base", base);
|
|
if (confirm) {
|
|
data.set("confirm", "1");
|
|
}
|
|
return fetch(form.action, {
|
|
method: "POST", body: data, redirect: "manual", headers: {"X-Requested-With": "XMLHttpRequest"},
|
|
}).then(function (response) {
|
|
return response.json();
|
|
}).then(function (answer) {
|
|
return {diff: answer.diff, saved: answer.saved, error: answer.error || answer.detail};
|
|
}).catch(function () {
|
|
return {error: dialog.dataset.failed};
|
|
});
|
|
}
|
|
|
|
function show(answer) {
|
|
error.textContent = answer.error || "";
|
|
error.classList.toggle("d-none", !answer.error);
|
|
var lines = answer.diff || [];
|
|
same.classList.toggle("d-none", Boolean(answer.error) || lines.length > 0);
|
|
save.classList.toggle("d-none", lines.length === 0);
|
|
save.disabled = false;
|
|
diff.replaceChildren();
|
|
// skip the two file header lines; + added, - removed, @@ where
|
|
lines.slice(2).forEach(function (line) {
|
|
var row = document.createElement("div");
|
|
row.textContent = line;
|
|
row.className = {"+": "text-success", "-": "text-danger", "@": "text-body-secondary"}[line[0]] || "";
|
|
diff.appendChild(row);
|
|
});
|
|
diff.classList.toggle("d-none", lines.length === 0);
|
|
}
|
|
|
|
form.addEventListener("submit", function (event) {
|
|
event.preventDefault();
|
|
var xml = editor.getValue();
|
|
// the last answer shown is the one Save sends, whatever order answers come in
|
|
post(xml, false).then(function (answer) {
|
|
reviewed = xml;
|
|
show(answer);
|
|
modal.show();
|
|
});
|
|
});
|
|
|
|
save.addEventListener("click", function () {
|
|
save.disabled = true;
|
|
post(reviewed, true).then(function (answer) {
|
|
if (answer.saved) {
|
|
clean = reviewed;
|
|
location.hash = "xmledit";
|
|
location.reload();
|
|
} else {
|
|
// the diff stays, and Save can be tried again
|
|
error.textContent = answer.error;
|
|
error.classList.remove("d-none");
|
|
save.disabled = false;
|
|
}
|
|
});
|
|
});
|
|
})();
|