Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -96,3 +96,6 @@ ENV/

# Rope project settings
.ropeproject

#vscode settings
.vscode/
11 changes: 0 additions & 11 deletions TEKDB/TEKDB/static/admin/css/itk_base.css
Original file line number Diff line number Diff line change
Expand Up @@ -68,19 +68,8 @@ span.select2-results {
background-color: var(--body-bg);
}

/* EXPORT/IMPORT MODULES */

div#export-module a,
button#import-button {
margin: 1rem;
}

button.btn-info#export-info,
button.btn-info#import-info {
margin: -0.5rem 0 1rem 1rem;
float: right;
}

a.btn-primary#export-button {
color: var(--bs-white);
}
304 changes: 203 additions & 101 deletions TEKDB/TEKDB/static/admin/js/admin_index.js
Original file line number Diff line number Diff line change
@@ -1,108 +1,210 @@
$(function() {
$('button#import-button').click(function(e) {
e.preventDefault();
if (
window.confirm(
"This process will remove all data and files from your current " +
"database and replace it with data from the provided zip file. " +
"\n\n" +
"This process CANNOT be undone. \n\n" +
"It is recommended that you use the button above to export " +
"your current database status so that you may restore to your " +
"present state, AND that you COORDINATE WITH IT professionals " +
"prior to attempting this. \n\n" +
"You will need to log in again using administrator credentials " +
"as defined by the new data: your current account will cease to " +
"exist.\n\n" +
"Are you sure you understand and are prepared to take this risk?"
)
) {
form = $('#import-database-form');
$.ajax({
url: '/import_database/',
data: new FormData(form[0]),
type: "POST",
processData: false,
contentType: false,
success: function(data, status) {
if (data.hasOwnProperty('status_code') && data.hasOwnProperty('status_message')) {
if (data.status_code == 200) {
window.alert(data.status_message + "\n\nYou may now be logged out.");
window.location.reload();
} else {
window.alert("Error Code: " + data.status_code + "\n\n" + data.status_message);
}
} else {
window.alert("Unexpected error occurred: " + status);
}
},
error: function(xhr, desc, err) {
window.alert("Unexpected error occurred: " + err);
}
})
}
const importText = `
This process will remove all data and files from your current
database and replace it with data from the provided zip file.
</br><br>
This process CANNOT be undone. </br><br>
It is recommended that you use the 'Export to .zip' button above to export
your current database status so that you may restore to your
present state, AND that you COORDINATE WITH IT professionals
prior to attempting this. </br><br>
You will need to log in again using administrator credentials
as defined by the new data: your current account will cease to
exist.</br><br>
Are you sure you understand and are prepared to take this risk?
`;

const importInfoText = `The Import Database Tool is designed to support restoring your Traditional Knowledge Database back to a prior state. This is best used in conjunction with the 'Export Database' tool above. </br><br>
To use the Import Database tool is simple: select a properly formatted zip file to upload and then click 'Import'. All of your data will be reverted back to the state it was when that zipfile was created. </br><br>
This is VERY DANGEROUS! For this to work, all of the data currently
in your database, including your users, your records, and your page
contents will be removed, and then replaced. Also, any files
associated with your media records (images, audio, video, PDFs,
etc...) will be overwritten by any files of the same name included
in the zipped up backup file that you import.</br><br>
If you wish to use this, it is recommended that you work with IT
professionals prior to undertaking this task. While IT cannot create
one of these import files for a prior state, they can help you
securely preserve any new zipped export files, and should be able
to create a system backup of your database to restore in the event
of complications while using this tool. </br><br>
Note that a zipped backup file is easily created for your current
state with the 'Export to .zip' button above, but cannot be
created by hand. If you have not created any of these files, you
should not use this tool.`;

const exportInfoText = `The Export Database Tool is designed to support saving the current
state of your Traditional Knowledge Database. This is best
used in conjunction with the 'Import Database' tool below. </br><br>
To use the Export Database tool is simple: just press 'Export to
.zip' button and all of your data and media files will be collected
into a single zipfile and downloaded to your current computer.</br></br>
This is VERY DANGEROUS! While keeping regular backups
of your database is crucial to preventing data loss from future
incidents, the exported file will contain ALL of your data in an
unencrypted format. These exported files
must be kept in a secure place and only shared via secure channels:
NOT BY EMAIL.</br></br>
Please work with IT professionals to design
a safe and secure practice for regular backups, which may or may
not involve this tool.`;

$(function () {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So much cleaner!

const unexpectedError = (statusCode, statusMessage = "") => {
return `<p class='text-danger'>Unexpected error occurred: ${statusCode}</p><p>${statusMessage}</p>`;
};

const showNextStepsSection = () => {
$("#modalNextSteps").removeClass("hidden");
};

const showModalFooter = () => {
$(".modal-footer").show();
};

const hideModalFooter = () => {
$(".modal-footer").hide();
};

const resetModal = () => {
$("#continueImport").html("Close");
$("#closeModalButton").prop("disabled", false);
$("#continueImport").click(function () {
$("#exportImportModal").modal("hide");
});
};

$('button#export-info').click(function(e) {
window.alert(
"Export Database Tool:\n\n" +

"The Export Database Tool is designed to support saving the current " +
"state of your Traditional Knowledge Database. This is best " +
"used in conjunction with the 'Import Database' tool below. \n\n" +

"To use the Export Database tool is simple: just press 'Export to " +
".zip' button and all of your data and media files will be collected " +
"into a single zipfile and downloaded to your current computer." +
"\n\n" +

"This is VERY DANGEROUS! While keeping regular backups " +
"of your database is crucial to preventing data loss from future " +
"incidents, the exported file will contain ALL of your data in an " +
"unencrypted format. These exported files " +
"must be kept in a secure place and only shared via secure channels: " +
"NOT BY EMAIL.\n\n" +

"Please work with IT professionals to design " +
"a safe and secure practice for regular backups, which may or may " +
"not involve this tool."
);
})

$('button#import-info').click(function(e) {
window.alert(
"Import Database Tool:\n\n" +

"The Import Database Tool is designed to support restoring your " +
"Traditional Knowledge Database back to a prior state. This is best " +
"used in conjunction with the 'Export Database' tool above. \n\n" +

"To use the Import Database tool is simple: select a properly formatted " +
"zip file to upload and then click 'Import'. All of your data will " +
"be reverted back to the state it was when that zipfile was created." +
"\n\n" +

"This is VERY DANGEROUS! For this to work, all of the data currently " +
"in your database, including your users, your records, and your page " +
"contents will be removed, and then replaced. Also, any files " +
"associated with your media records (images, audio, video, PDFs, " +
"etc...) will be overwritten by any files of the same name included " +
"in the zipped up backup file that you import.\n\n" +

"If you wish to use this, it is recommended that you work with IT " +
"professionals prior to undertaking this task. While IT cannot create " +
"one of these import files for a prior state, they can help you " +
"securely preserve any new zipped export files, and should be able " +
"to create a system backup of your database to restore in the event " +
"of complications while using this tool. \n\n" +

"Note that a zipped backup file is easily created for your current " +
"state with the 'Export to .zip' button above, but cannot be " +
"created by hand. If you have not created any of these files, you " +
"should not use this tool."
const resetExportButton = (iframe, clearInterval) => {
$("#export-button").prop("disabled", false);
$("#export-button").html("Export to .zip");
iframe.remove();
clearInterval();
};

$("button#import-button").click(function (e) {
e.preventDefault();
showModalFooter();

$("#modalTitle").text("Import Database");
$("#modalBody").html(importText);

$("#continueImport").click(function () {
// prevent closing the modal after verifying import
$("#exportImportModal").modal({
backdrop: "static",
keyboard: false,
});

// add spinner to the button
// change button text to "Importing..."
$("#continueImport").html(
`<span class="spinner-border spinner-border-sm" role="status" aria-hidden="true"></span> Importing...`
);
})
$("#continueImport").prop("disabled", true);
$("#closeModalButton").prop("disabled", true);

const form = $("#import-database-form");
$.ajax({
url: "/import_database/",
data: new FormData(form[0]),
type: "POST",
processData: false,
contentType: false,
success: function (data, status) {
if (
data.hasOwnProperty("status_code") &&
data.hasOwnProperty("status_message")
) {
showNextStepsSection();
$("#continueImport").prop("disabled", false);

if (data.status_code == 200) {
$("#modalNextSteps").html(
`<p class='text-success'>Import successful. You may now be logged out.</p>`
);
$("#continueImport").html("Log out");
$("#continueImport").click(function () {
location.reload(true);
});
} else {
$("#modalNextSteps").html(
`<p class='text-danger'>Import failed with error code ${data.status_code}.</p><p class='text-danger'>${data.status_message}</p>`
);
resetModal();
}
} else {
$("#modalNextSteps").html(unexpectedError(status));
resetModal();
}
},
error: function (xhr) {
$("#modalNextSteps").html(
unexpectedError(xhr.status, xhr.statusText)
);
resetModal();
},
});
});
});

$("button#import-info").click(function (e) {
hideModalFooter();
$("#modalTitle").text("Import Database Tool");
$("#modalBody").html(importInfoText);
});

$("button#export-info").click(function (e) {
hideModalFooter();
$("#modalTitle").text("Export Database Tool");
$("#modalBody").html(exportInfoText);
});

$("button#export-button").click(function (e) {
e.preventDefault();

// remove error message if present
$("#exportStatus").addClass("hidden");

// disable button and show spinner
$("#export-button").prop("disabled", true);
$("#export-button").html(
`<span class="spinner-border spinner-border-sm" role="status" aria-hidden="true"></span> Exporting...`
);

// set cookie to pending
document.cookie = "export_status=pending";

// create hidden iframe to trigger download
const iframe = $("<iframe/>")
.hide()
.attr("src", "/export_database/")
.appendTo("body");

// poll cookie for export_status
const checkStatus = setInterval(function () {
const cookies = document.cookie.split(";").map((c) => c.trim());
const exportStatus = cookies.filter((c) =>
c.startsWith("export_status=")
);

if (exportStatus && exportStatus.includes("export_status=done")) {
resetExportButton(iframe, clearInterval(checkStatus));
} else if (exportStatus && exportStatus.includes("export_status=error")) {
$("#exportStatus").removeClass("hidden");
resetExportButton(iframe, clearInterval(checkStatus));
}
}, 1000);
});

// Clear modal content on close; reset to default state
$("#exportImportModal").on("hidden.bs.modal", function () {
$("#modalTitle").text("");
$("#modalBody").html("");
$("#modalNextSteps").html("");
$("#modalNextSteps").addClass("hidden");
$("#continueImport").html("Continue");
$("#continueImport").off("click");
});
});
30 changes: 24 additions & 6 deletions TEKDB/TEKDB/templates/admin/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -25,20 +25,21 @@

{% if user.is_superuser %}
<div class="module" id="export-module">
<h2>{% translate 'Export database' %}<button class='btn btn-info' id='export-info'>Info</button></h2>
<a href="/export_database/" id='export-button' class="btn btn-primary">
<h2>{% translate 'Export database' %}<button class='btn btn-info' id='export-info' data-bs-toggle="modal" data-bs-target="#exportImportModal">Info</button></h2>
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I love Bootstrap modals, and we already installed Bootstrap to the admin view, so this solution is perfect.

I expect my love of Bootstrap is dated (Twitter built it long before their name change, which is already old news -- I assume newer frameworks are en vogue ATM). But replacing Bootstrap with a modern framework here should be informed by and performed in conjunction with an often-suggested rebuild of the front-end.

<button id='export-button' class="mx-3 btn btn-primary">
Export to .zip
</a>
</button>
<div id="exportStatus" class="text-danger m-3 hidden">There was an error during export. Please try again.</div>
</div>

<hr />

<div class="module" id="import-module">
<h2>{% translate 'Import database' %}<button class='btn btn-info' id='import-info'>Info</button></h2>
<h2>{% translate 'Import database' %}<button class='btn btn-info' id='import-info' data-bs-toggle="modal" data-bs-target="#exportImportModal">Info</button></h2>
<form method="post" enctype="multipart/form-data" id='import-database-form'>
{% csrf_token %}
<input type="file" name="import_file" id='import_database_file_field'>
<button type="submit" id='import-button' class='btn btn-success'>Import</button>
<input type="file" name="import_file" id='import_database_file_field' class="mx-3">
<button data-bs-toggle="modal" data-bs-target="#exportImportModal" type="submit" id='import-button' class='m-3 btn btn-success'>Import</button>
</form>
</div>

Expand Down Expand Up @@ -74,5 +75,22 @@ <h3>{% translate 'My actions' %}</h3>
</ul>
{% endif %}
</div>

<div class="modal fade" id="exportImportModal" tabindex="-1" aria-labelledby="modalTitle" aria-hidden="true" role="dialog" data-bs-backdrop="static" data-bs-keyboard="false">
<div class="modal-dialog modal-dialog-centered modal-dialog-scrollable">
<div class="modal-content">
<div class="modal-header">
<h1 class="modal-title fs-5 text-dark" id="modalTitle"></h1>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close" id="closeModalButton"></button>
</div>
<div id="modalBody" class="modal-body">
</div>
<div id="modalNextSteps" class="pt-3 m-3 border rounded hidden"></div>
<div class="modal-footer">
<button type="button" id="continueImport" class="btn btn-primary">Continue</button>
</div>
</div>
</div>
</div>
</div>
{% endblock %}
Loading