61e6cc6985
Signed-off-by: Nicola Murino <nicola.murino@gmail.com>
334 lines
No EOL
14 KiB
HTML
334 lines
No EOL
14 KiB
HTML
<!--
|
|
Copyright (C) 2023 Nicola Murino
|
|
|
|
This WebUI uses the KeenThemes Mega Bundle, a proprietary theme:
|
|
|
|
https://keenthemes.com/products/templates-mega-bundle
|
|
|
|
KeenThemes HTML/CSS/JS components are allowed for use only within the
|
|
SFTPGo product and restricted to be used in a resealable HTML template
|
|
that can compete with KeenThemes products anyhow.
|
|
|
|
This WebUI is allowed for use only within the SFTPGo product and
|
|
therefore cannot be used in derivative works/products without an
|
|
explicit grant from the SFTPGo Team (support@sftpgo.com).
|
|
-->
|
|
{{template "base" .}}
|
|
|
|
{{define "title"}}{{.Title}}{{end}}
|
|
|
|
{{- define "extra_css"}}
|
|
<link href="{{.StaticURL}}/assets/plugins/custom/datatables/datatables.bundle.css" rel="stylesheet" type="text/css"/>
|
|
{{- end}}
|
|
|
|
|
|
{{define "page_body"}}
|
|
|
|
<div class="card shadow-sm">
|
|
<div class="card-header bg-light">
|
|
<h3 class="card-title text-primary">View and manage shares</h3>
|
|
</div>
|
|
<div id="card_body" class="card-body">
|
|
{{- template "errmsg" ""}}
|
|
<div id="loader" class="align-items-center text-center my-10">
|
|
<span class="spinner-border w-15px h-15px text-muted align-middle me-2"></span>
|
|
<span class="text-gray-600">Loading...</span>
|
|
</div>
|
|
<div id="card_content" class="d-none">
|
|
<div class="d-flex flex-stack mb-5">
|
|
<div class="d-flex align-items-center position-relative my-1">
|
|
<i class="ki-duotone ki-magnifier fs-1 position-absolute ms-6">
|
|
<span class="path1"></span>
|
|
<span class="path2"></span>
|
|
</i>
|
|
<input type="text" data-share-table-filter="search"
|
|
class="form-control form-control-solid w-200px ps-15" placeholder="Search" />
|
|
</div>
|
|
|
|
<div class="d-flex justify-content-end" data-share-table-toolbar="base">
|
|
<a href="{{.ShareURL}}" class="btn btn-primary">
|
|
<i class="ki-duotone ki-plus fs-2"></i>
|
|
Add
|
|
</a>
|
|
</div>
|
|
</div>
|
|
|
|
<table id="dataTable" class="table align-middle table-row-dashed fs-6 gy-5">
|
|
<thead>
|
|
<tr class="text-start text-muted fw-bold fs-6 gs-0">
|
|
<th>Name</th>
|
|
<th>Scope</th>
|
|
<th>Info</th>
|
|
<th class="min-w-100px"></th>
|
|
</tr>
|
|
</thead>
|
|
<tbody class="text-gray-800 fw-semibold"></tbody>
|
|
</table>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
{{- end}}
|
|
|
|
{{- define "modals"}}
|
|
<div class="modal fade" id="link_modal" tabindex="-1">
|
|
<div class="modal-dialog" role="document">
|
|
<div class="modal-content">
|
|
<div class="modal-header border-0">
|
|
<h3 class="modal-title">
|
|
Share access links
|
|
</h3>
|
|
<div class="btn btn-icon btn-sm btn-active-color-primary" data-bs-dismiss="modal" aria-label="Close">
|
|
<i class="ki-duotone ki-cross fs-1"><span class="path1"></span><span class="path2"></span></i>
|
|
</div>
|
|
</div>
|
|
<div class="modal-body fs-5">
|
|
<div id="readShare">
|
|
<p>You can download the shared contents, as single zip file, using this <a id="readLink" href="#" target="_blank">link</a>.</p>
|
|
<p>If the share consists of a single directory you can browse and download files using this <a id="readBrowseLink" href="#" target="_blank">page</a>.</p>
|
|
<p>If the share consists of a single file you can download it uncompressed using this <a id="readUncompressedLink" href="#" target="_blank">link</a>.</p>
|
|
</div>
|
|
<div id="writeShare">
|
|
<p>You can upload one or more files to the shared directory using this <a id="writePageLink" href="#" target="_blank">page</a></p>
|
|
</div>
|
|
<div id="expiredShare">
|
|
This share is no longer accessible because it has expired
|
|
</div>
|
|
</div>
|
|
<div class="modal-footer border-0">
|
|
<button class="btn btn-primary" type="button" data-bs-dismiss="modal">
|
|
OK
|
|
</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
{{end}}
|
|
|
|
{{define "extra_js"}}
|
|
<script src="{{.StaticURL}}/assets/plugins/custom/datatables/datatables.bundle.js"></script>
|
|
<script type="text/javascript" {{- if .CSPNonce}} nonce="{{.CSPNonce}}"{{- end}}>
|
|
|
|
function deleteAction(shareID) {
|
|
let errDivEl = $('#errorMsg');
|
|
let errTxtEl = $('#errorTxt');
|
|
errDivEl.addClass("d-none");
|
|
|
|
ModalAlert.fire({
|
|
text: `Do you want to delete the selected share? This action is irreversible`,
|
|
icon: "warning",
|
|
confirmButtonText: "Delete",
|
|
cancelButtonText: 'Cancel',
|
|
customClass: {
|
|
confirmButton: "btn btn-danger",
|
|
cancelButton: 'btn btn-secondary'
|
|
}
|
|
}).then((result) => {
|
|
if (result.isConfirmed){
|
|
$('#loading_message').text("");
|
|
KTApp.showPageLoading();
|
|
let path = '{{.ShareURL}}' + "/" + fixedEncodeURIComponent(shareID);
|
|
|
|
axios.delete(path, {
|
|
timeout: 15000,
|
|
headers: {
|
|
'X-CSRF-TOKEN': '{{.CSRFToken}}'
|
|
},
|
|
validateStatus: function (status) {
|
|
return status == 200;
|
|
}
|
|
}).then(function(response){
|
|
location.reload();
|
|
}).catch(function(error){
|
|
KTApp.hidePageLoading();
|
|
let errorMessage = `Unable to delete the selected share`;
|
|
if (error && error.response) {
|
|
if (error.response.data.message) {
|
|
errorMessage = error.response.data.message;
|
|
}
|
|
if (error.response.data.error) {
|
|
errorMessage += ": " + error.response.data.error;
|
|
}
|
|
}
|
|
errTxtEl.text(errorMessage);
|
|
errDivEl.removeClass("d-none");
|
|
});
|
|
}
|
|
});
|
|
}
|
|
|
|
function editAction(shareID) {
|
|
window.location.replace('{{.ShareURL}}' + "/" + fixedEncodeURIComponent(shareID));
|
|
}
|
|
|
|
function showShareLink(shareID, shareScope, isExpired) {
|
|
if (isExpired == "1") {
|
|
$('#expiredShare').show();
|
|
$('#writeShare').hide();
|
|
$('#readShare').hide();
|
|
} else {
|
|
let shareURL = '{{.BasePublicSharesURL}}' + "/" + fixedEncodeURIComponent(shareID);
|
|
if (shareScope == 'Read') {
|
|
$('#expiredShare').hide();
|
|
$('#writeShare').hide();
|
|
$('#readShare').show();
|
|
$('#readLink').attr("href", shareURL);
|
|
$('#readLink').attr("title", shareURL);
|
|
$('#readUncompressedLink').attr("href", shareURL + "?compress=false");
|
|
$('#readUncompressedLink').attr("title", shareURL + "?compress=false");
|
|
$('#readBrowseLink').attr("href", shareURL + "/browse");
|
|
$('#readBrowseLink').attr("title", shareURL + "/browse");
|
|
} else {
|
|
$('#expiredShare').hide();
|
|
$('#writeShare').show();
|
|
$('#readShare').hide();
|
|
$('#writePageLink').attr("href", shareURL + "/upload");
|
|
$('#writePageLink').attr("title", shareURL + "/upload");
|
|
}
|
|
}
|
|
$('#link_modal').modal('show');
|
|
}
|
|
|
|
const tableData = [];
|
|
{{- range .Shares}}
|
|
tableData.push(['{{.Name}}','{{.GetScopeAsString}}','{{.GetInfoString}}','{{.ShareID}}','{{- if .IsExpired}}1{{- else}}0{{- end}}']);
|
|
{{- end}}
|
|
|
|
var sharesDatatable = function(){
|
|
var dt;
|
|
|
|
var initDatatable = function () {
|
|
dt = $('#dataTable').DataTable({
|
|
data: tableData,
|
|
columnDefs: [
|
|
{
|
|
targets: 3,
|
|
searchable: false,
|
|
orderable: false,
|
|
className: 'text-end',
|
|
render: function (data, type, row) {
|
|
if (type === 'display') {
|
|
return `<div class="d-flex justify-content-end">
|
|
<div class="ms-2">
|
|
<a href="#" class="btn btn-sm btn-icon btn-light btn-active-light-primary" data-share-table-action="show_link">
|
|
<i class="ki-duotone ki-fasten fs-5 m-0">
|
|
<span class="path1"></span>
|
|
<span class="path2"></span>
|
|
</i>
|
|
</a>
|
|
</div>
|
|
<div class="ms-2">
|
|
<button type="button" class="btn btn-sm btn-icon btn-light btn-active-light-primary"
|
|
data-kt-menu-trigger="click" data-kt-menu-placement="bottom-end">
|
|
<i class="ki-duotone ki-dots-square fs-5 m-0">
|
|
<span class="path1"></span>
|
|
<span class="path2"></span>
|
|
<span class="path3"></span>
|
|
<span class="path4"></span>
|
|
</i>
|
|
</button>
|
|
<div class="menu menu-sub menu-sub-dropdown menu-column menu-rounded menu-gray-600 menu-state-bg-light-primary fw-semibold fs-7 w-150px py-4" data-kt-menu="true">
|
|
<div class="menu-item px-3">
|
|
<a href="#" class="menu-link px-3" data-share-table-action="edit_row">Edit</a>
|
|
</div>
|
|
<div class="menu-item px-3">
|
|
<a href="#" class="menu-link text-danger px-3" data-share-table-action="delete_row">Delete</a>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>`;
|
|
}
|
|
return "";
|
|
}
|
|
},
|
|
{
|
|
targets: [0, 2],
|
|
render: function (data, type, row) {
|
|
if (type === 'display') {
|
|
return escapeHTML(data);
|
|
}
|
|
return data
|
|
}
|
|
}
|
|
],
|
|
deferRender: true,
|
|
stateSave: true,
|
|
stateDuration: 0,
|
|
stateLoadParams: function (settings, data) {
|
|
if (data.search.search){
|
|
const filterSearch = document.querySelector('[data-share-table-filter="search"]');
|
|
filterSearch.value = data.search.search;
|
|
}
|
|
},
|
|
language: {
|
|
emptyTable: "No share defined"
|
|
},
|
|
order: [[1, 'asc']],
|
|
initComplete: function(settings, json) {
|
|
$('#loader').addClass("d-none");
|
|
$('#card_content').removeClass("d-none");
|
|
let api = $.fn.dataTable.Api(settings);
|
|
api.columns.adjust().draw();
|
|
KTMenu.createInstances();
|
|
handleRowActions();
|
|
}
|
|
});
|
|
|
|
dt.on('draw', function () {
|
|
KTMenu.createInstances();
|
|
handleRowActions();
|
|
});
|
|
}
|
|
|
|
var handleSearchDatatable = function () {
|
|
const filterSearch = document.querySelector('[data-share-table-filter="search"]');
|
|
filterSearch.addEventListener('keyup', function (e) {
|
|
dt.rows().deselect();
|
|
dt.search(e.target.value, true, false).draw();
|
|
});
|
|
}
|
|
|
|
function handleRowActions() {
|
|
const editButtons = document.querySelectorAll('[data-share-table-action="edit_row"]');
|
|
|
|
editButtons.forEach(d => {
|
|
d.addEventListener("click", function(e){
|
|
e.preventDefault();
|
|
const parent = e.target.closest('tr');
|
|
editAction(dt.row(parent).data()[3]);
|
|
});
|
|
});
|
|
|
|
const deleteButtons = document.querySelectorAll('[data-share-table-action="delete_row"]');
|
|
|
|
deleteButtons.forEach(d => {
|
|
d.addEventListener("click", function(e){
|
|
e.preventDefault();
|
|
const parent = e.target.closest('tr');
|
|
deleteAction(dt.row(parent).data()[3]);
|
|
});
|
|
});
|
|
|
|
const showLinkButtons = document.querySelectorAll('[data-share-table-action="show_link"]');
|
|
|
|
showLinkButtons.forEach(d => {
|
|
d.addEventListener("click", function(e){
|
|
e.preventDefault();
|
|
let rowData = dt.row(e.target.closest('tr')).data();
|
|
showShareLink(rowData[3], rowData[1], rowData[4]);
|
|
});
|
|
});
|
|
}
|
|
|
|
return {
|
|
init: function () {
|
|
initDatatable();
|
|
handleSearchDatatable();
|
|
}
|
|
}
|
|
}();
|
|
|
|
KTUtil.onDOMContentLoaded(function () {
|
|
sharesDatatable.init();
|
|
});
|
|
</script>
|
|
{{end}} |