WIP new WebAdmin: folders page
Signed-off-by: Nicola Murino <nicola.murino@gmail.com>
This commit is contained in:
parent
1a0f734a9c
commit
0722c4369b
9 changed files with 551 additions and 365 deletions
|
@ -1682,7 +1682,7 @@ func (s *httpdServer) setupWebAdminRoutes() {
|
|||
|
||||
router.With(s.checkPerm(dataprovider.PermAdminViewUsers), s.refreshCookie).
|
||||
Get(webUsersPath, s.handleGetWebUsers)
|
||||
router.With(s.checkPerm(dataprovider.PermAdminViewUsers), s.refreshCookie).
|
||||
router.With(s.checkPerm(dataprovider.PermAdminViewUsers), compressor.Handler, s.refreshCookie).
|
||||
Get(webUsersPath+"/json", getAllUsers)
|
||||
router.With(s.checkPerm(dataprovider.PermAdminAddUsers), s.refreshCookie).
|
||||
Get(webUserPath, s.handleWebAddUserGet)
|
||||
|
@ -1693,7 +1693,7 @@ func (s *httpdServer) setupWebAdminRoutes() {
|
|||
s.handleWebUpdateUserPost)
|
||||
router.With(s.checkPerm(dataprovider.PermAdminManageGroups), s.refreshCookie).
|
||||
Get(webGroupsPath, s.handleWebGetGroups)
|
||||
router.With(s.checkPerm(dataprovider.PermAdminManageGroups), s.refreshCookie).
|
||||
router.With(s.checkPerm(dataprovider.PermAdminManageGroups), compressor.Handler, s.refreshCookie).
|
||||
Get(webGroupsPath+"/json", getAllGroups)
|
||||
router.With(s.checkPerm(dataprovider.PermAdminManageGroups), s.refreshCookie).
|
||||
Get(webGroupPath, s.handleWebAddGroupGet)
|
||||
|
@ -1708,6 +1708,8 @@ func (s *httpdServer) setupWebAdminRoutes() {
|
|||
Get(webConnectionsPath, s.handleWebGetConnections)
|
||||
router.With(s.checkPerm(dataprovider.PermAdminManageFolders), s.refreshCookie).
|
||||
Get(webFoldersPath, s.handleWebGetFolders)
|
||||
router.With(s.checkPerm(dataprovider.PermAdminManageFolders), compressor.Handler, s.refreshCookie).
|
||||
Get(webFoldersPath+"/json", getAllFolders)
|
||||
router.With(s.checkPerm(dataprovider.PermAdminManageFolders), s.refreshCookie).
|
||||
Get(webFolderPath, s.handleWebAddFolderGet)
|
||||
router.With(s.checkPerm(dataprovider.PermAdminManageFolders)).Post(webFolderPath, s.handleWebAddFolderPost)
|
||||
|
|
|
@ -172,11 +172,6 @@ type adminsPage struct {
|
|||
Admins []dataprovider.Admin
|
||||
}
|
||||
|
||||
type foldersPage struct {
|
||||
basePage
|
||||
Folders []vfs.BaseVirtualFolder
|
||||
}
|
||||
|
||||
type rolesPage struct {
|
||||
basePage
|
||||
Roles []dataprovider.Role
|
||||
|
@ -428,7 +423,7 @@ func loadAdminTemplates(templatesPath string) {
|
|||
filepath.Join(templatesPath, templateAdminDir, templateMessage),
|
||||
}
|
||||
foldersPaths := []string{
|
||||
filepath.Join(templatesPath, templateCommonDir, templateCommonCSS),
|
||||
filepath.Join(templatesPath, templateCommonDir, templateCommonBase),
|
||||
filepath.Join(templatesPath, templateAdminDir, templateBase),
|
||||
filepath.Join(templatesPath, templateAdminDir, templateFolders),
|
||||
}
|
||||
|
@ -3444,7 +3439,7 @@ func (s *httpdServer) handleWebUpdateFolderPost(w http.ResponseWriter, r *http.R
|
|||
}
|
||||
|
||||
func (s *httpdServer) getWebVirtualFolders(w http.ResponseWriter, r *http.Request, limit int, minimal bool) ([]vfs.BaseVirtualFolder, error) {
|
||||
folders := make([]vfs.BaseVirtualFolder, 0, limit)
|
||||
folders := make([]vfs.BaseVirtualFolder, 0, 50)
|
||||
for {
|
||||
f, err := dataprovider.GetFolders(limit, len(folders), dataprovider.OrderASC, minimal)
|
||||
if err != nil {
|
||||
|
@ -3459,25 +3454,27 @@ func (s *httpdServer) getWebVirtualFolders(w http.ResponseWriter, r *http.Reques
|
|||
return folders, nil
|
||||
}
|
||||
|
||||
func (s *httpdServer) handleWebGetFolders(w http.ResponseWriter, r *http.Request) {
|
||||
func getAllFolders(w http.ResponseWriter, r *http.Request) {
|
||||
r.Body = http.MaxBytesReader(w, r.Body, maxRequestSize)
|
||||
limit := defaultQueryLimit
|
||||
if _, ok := r.URL.Query()["qlimit"]; ok {
|
||||
var err error
|
||||
limit, err = strconv.Atoi(r.URL.Query().Get("qlimit"))
|
||||
if err != nil {
|
||||
limit = defaultQueryLimit
|
||||
}
|
||||
}
|
||||
folders, err := s.getWebVirtualFolders(w, r, limit, false)
|
||||
folders := make([]vfs.BaseVirtualFolder, 0, 50)
|
||||
for {
|
||||
f, err := dataprovider.GetFolders(defaultQueryLimit, len(folders), dataprovider.OrderASC, false)
|
||||
if err != nil {
|
||||
sendAPIResponse(w, r, err, getI18NErrorString(err, util.I18nError500Message), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
data := foldersPage{
|
||||
basePage: s.getBasePageData(util.I18nFoldersTitle, webFoldersPath, r),
|
||||
Folders: folders,
|
||||
folders = append(folders, f...)
|
||||
if len(f) < defaultQueryLimit {
|
||||
break
|
||||
}
|
||||
}
|
||||
render.JSON(w, r, folders)
|
||||
}
|
||||
|
||||
func (s *httpdServer) handleWebGetFolders(w http.ResponseWriter, r *http.Request) {
|
||||
r.Body = http.MaxBytesReader(w, r.Body, maxRequestSize)
|
||||
|
||||
data := s.getBasePageData(util.I18nFoldersTitle, webFoldersPath, r)
|
||||
renderAdminTemplate(w, templateFolders, data)
|
||||
}
|
||||
|
||||
|
|
|
@ -206,7 +206,8 @@
|
|||
"zero_no_limit_help": "0 means no limit",
|
||||
"global_settings": "Global settings",
|
||||
"mandatory_encryption": "Mandatory encryption",
|
||||
"name_invalid": "The specified username is not valid, the following characters are allowed: a-zA-Z0-9-_.~"
|
||||
"name_invalid": "The specified username is not valid, the following characters are allowed: a-zA-Z0-9-_.~",
|
||||
"associations": "Associations"
|
||||
},
|
||||
"fs": {
|
||||
"view_file": "View file \"{{- path}}\"",
|
||||
|
@ -474,10 +475,12 @@
|
|||
"members_summary": "Users: {{users}}. Admins: {{admins}}"
|
||||
},
|
||||
"virtual_folders": {
|
||||
"view_manage": "View and manage virtual folders",
|
||||
"mount_path": "mount path, i.e. /vfolder",
|
||||
"quota_size": "Quota size",
|
||||
"quota_size_help": "0 means no limit. You can use MB/GB/TB suffix",
|
||||
"quota_files": "Quota files"
|
||||
"quota_files": "Quota files",
|
||||
"associations_summary": "Users: {{users}}. Groups: {{groups}}"
|
||||
},
|
||||
"storage": {
|
||||
"title": "File system",
|
||||
|
|
|
@ -206,7 +206,8 @@
|
|||
"zero_no_limit_help": "0 significa nessun limite",
|
||||
"global_settings": "Impostazioni globali",
|
||||
"mandatory_encryption": "Crittografia obbligatoria",
|
||||
"name_invalid": "Il nome specificato non è valido, sono consentiti i seguenti caratteri: a-zA-Z0-9-_.~"
|
||||
"name_invalid": "Il nome specificato non è valido, sono consentiti i seguenti caratteri: a-zA-Z0-9-_.~",
|
||||
"associations": "Associazioni"
|
||||
},
|
||||
"fs": {
|
||||
"view_file": "Visualizza file \"{{- path}}\"",
|
||||
|
@ -474,10 +475,12 @@
|
|||
"members_summary": "Utenti: {{users}}. Amministratori: {{admins}}"
|
||||
},
|
||||
"virtual_folders": {
|
||||
"view_manage": "Visualizza e gestisci cartelle virtuali",
|
||||
"mount_path": "percorso, es. /vfolder",
|
||||
"quota_size": "Quota (dimensione)",
|
||||
"quota_size_help": "0 significa nessun limite. E' possibile utilizzare il suffisso MB/GB/TB",
|
||||
"quota_files": "Quota (numero file)"
|
||||
"quota_files": "Quota (numero file)",
|
||||
"associations_summary": "Utenti: {{users}}. Gruppi: {{groups}}"
|
||||
},
|
||||
"storage": {
|
||||
"title": "File system",
|
||||
|
|
|
@ -1,361 +1,409 @@
|
|||
<!--
|
||||
Copyright (C) 2019 Nicola Murino
|
||||
Copyright (C) 2024 Nicola Murino
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Affero General Public License as published
|
||||
by the Free Software Foundation, version 3.
|
||||
This WebUI uses the KeenThemes Mega Bundle, a proprietary theme:
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Affero General Public License for more details.
|
||||
https://keenthemes.com/products/templates-mega-bundle
|
||||
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
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 "extra_css"}}
|
||||
<link href="{{.StaticURL}}/vendor/datatables/dataTables.bootstrap4.min.css" rel="stylesheet">
|
||||
<link href="{{.StaticURL}}/vendor/datatables/buttons.bootstrap4.min.css" rel="stylesheet">
|
||||
<link href="{{.StaticURL}}/vendor/datatables/fixedHeader.bootstrap4.min.css" rel="stylesheet">
|
||||
<link href="{{.StaticURL}}/vendor/datatables/responsive.bootstrap4.min.css" rel="stylesheet">
|
||||
<link href="{{.StaticURL}}/vendor/datatables/select.bootstrap4.min.css" rel="stylesheet">
|
||||
<link href="{{.StaticURL}}/vendor/datatables/colReorder.bootstrap4.min.css" rel="stylesheet">
|
||||
{{end}}
|
||||
{{- define "page_body"}}
|
||||
{{- template "errmsg" ""}}
|
||||
<div class="card shadow-sm">
|
||||
<div class="card-header bg-light">
|
||||
<h3 data-i18n="virtual_folders.view_manage" class="card-title section-title">View and manage folders</h3>
|
||||
</div>
|
||||
<div id="card_body" class="card-body">
|
||||
<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 data-i18n="general.loading" class="text-gray-700">Loading...</span>
|
||||
</div>
|
||||
<div id="card_content" class="d-none">
|
||||
<div class="d-flex flex-stack flex-wrap mb-5">
|
||||
<div class="d-flex align-items-center position-relative my-2">
|
||||
<i class="ki-solid ki-magnifier fs-1 position-absolute ms-6"></i>
|
||||
<input name="search" data-i18n="[placeholder]general.search" type="text" data-table-filter="search"
|
||||
class="form-control rounded-1 w-250px ps-15 me-5" placeholder="Search" />
|
||||
</div>
|
||||
|
||||
{{define "page_body"}}
|
||||
<div id="errorMsg" class="alert alert-warning fade show" style="display: none;" role="alert">
|
||||
<span id="errorTxt"></span>
|
||||
<button type="button" class="close" aria-label="Close" onclick="dismissErrorMsg();">
|
||||
<span aria-hidden="true">×</span>
|
||||
<div class="d-flex justify-content-end my-2" data-table-toolbar="base">
|
||||
<button type="button" class="btn btn-light-primary rotate" data-kt-menu-trigger="click" data-kt-menu-placement="bottom" data-kt-menu-permanent="true">
|
||||
<span data-i18n="general.colvis">Column visibility</span>
|
||||
<i class="ki-duotone ki-down fs-3 rotate-180 ms-3 me-0"></i>
|
||||
</button>
|
||||
<div class="menu menu-sub menu-sub-dropdown menu-column menu-rounded menu-gray-800 menu-state-bg-light-primary fw-semibold w-auto min-w-200 mw-300px py-4" data-kt-menu="true">
|
||||
<div class="menu-item px-3 py-2 form-check form-check-sm form-check-custom form-check-solid">
|
||||
<input type="checkbox" class="form-check-input" value="" id="checkColStorage" />
|
||||
<label class="form-check-label" for="checkColStorage">
|
||||
<span data-i18n="storage.label" class="text-gray-800 fs-6">Storage</span>
|
||||
</label>
|
||||
</div>
|
||||
<div class="menu-item px-3 py-2 form-check form-check-sm form-check-custom form-check-solid">
|
||||
<input type="checkbox" class="form-check-input" value="" id="checkColQuota" />
|
||||
<label class="form-check-label" for="checkColQuota">
|
||||
<span data-i18n="fs.quota_usage.disk" class="text-gray-800 fs-6">Disk quota</span>
|
||||
</label>
|
||||
</div>
|
||||
<div class="menu-item px-3 py-2 form-check form-check-sm form-check-custom form-check-solid">
|
||||
<input type="checkbox" class="form-check-input" value="" id="checkColAssociations" />
|
||||
<label class="form-check-label" for="checkColAssociations">
|
||||
<span data-i18n="general.associations" class="text-gray-800 fs-6">Associations</span>
|
||||
</label>
|
||||
</div>
|
||||
<div class="menu-item px-3 py-2 form-check form-check-sm form-check-custom form-check-solid">
|
||||
<input type="checkbox" class="form-check-input" value="" id="checkColDesc" />
|
||||
<label class="form-check-label" for="checkColDesc">
|
||||
<span data-i18n="general.description" class="text-gray-800 fs-6">Description</span>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
{{- if .LoggedUser.HasPermission "manage_folders"}}
|
||||
<a href="{{.FolderURL}}" class="btn btn-primary ms-5">
|
||||
<i class="ki-duotone ki-plus fs-2"></i>
|
||||
<span data-i18n="general.add">Add</span>
|
||||
</a>
|
||||
{{- end}}
|
||||
</div>
|
||||
</div>
|
||||
<script type="text/javascript">
|
||||
function dismissErrorMsg(){
|
||||
$('#errorMsg').hide();
|
||||
}
|
||||
</script>
|
||||
|
||||
<div id="successMsg" class="card mb-4 border-left-success" style="display: none;">
|
||||
<div id="successTxt" class="card-body"></div>
|
||||
</div>
|
||||
|
||||
<div class="card shadow mb-4">
|
||||
<div class="card-header py-3">
|
||||
<h6 class="m-0 font-weight-bold text-primary">View and manage folders</h6>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div class="table-responsive">
|
||||
<table class="table table-hover nowrap" id="dataTable" width="100%" cellspacing="0">
|
||||
<table id="dataTable" class="table align-middle table-row-dashed fs-6 gy-5">
|
||||
<thead>
|
||||
<tr>
|
||||
<th></th>
|
||||
<th>Name</th>
|
||||
<th>Storage</th>
|
||||
<th>Description</th>
|
||||
<th>Associated users</th>
|
||||
<th>Associated groups</th>
|
||||
<th>Quota</th>
|
||||
<tr class="text-start text-muted fw-bold fs-6 gs-0">
|
||||
<th data-i18n="general.name">Name</th>
|
||||
<th data-i18n="storage.label">Storage</th>
|
||||
<th data-i18n="fs.quota_usage.disk">Disk quota</th>
|
||||
<th data-i18n="general.associations">Associations</th>
|
||||
<th data-i18n="general.description">Description</th>
|
||||
<th class="min-w-100px"></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{{range .Folders}}
|
||||
<tr>
|
||||
<td>{{.GetLastQuotaUpdateAsString}}</td>
|
||||
<td>{{.Name}}</td>
|
||||
<td>{{.GetStorageDescrition}}</td>
|
||||
<td>{{.Description}}</td>
|
||||
<td>{{.GetUsersAsString}}</td>
|
||||
<td>{{.GetGroupsAsString}}</td>
|
||||
<td>{{.GetQuotaSummary}}</td>
|
||||
</tr>
|
||||
{{end}}
|
||||
|
||||
</tbody>
|
||||
<tbody id="table_body" class="text-gray-800 fw-semibold"></tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{{end}}
|
||||
{{- end}}
|
||||
{{- define "extra_js"}}
|
||||
<script {{- if .CSPNonce}} nonce="{{.CSPNonce}}"{{- end}} src="{{.StaticURL}}/assets/plugins/custom/datatables/datatables.bundle.js"></script>
|
||||
<script type="text/javascript" {{- if .CSPNonce}} nonce="{{.CSPNonce}}"{{- end}}>
|
||||
function deleteAction(name) {
|
||||
ModalAlert.fire({
|
||||
text: $.t('general.delete_confirm_generic'),
|
||||
icon: "warning",
|
||||
confirmButtonText: $.t('general.delete_confirm_btn'),
|
||||
cancelButtonText: $.t('general.cancel'),
|
||||
customClass: {
|
||||
confirmButton: "btn btn-danger",
|
||||
cancelButton: 'btn btn-secondary'
|
||||
}
|
||||
}).then((result) => {
|
||||
if (result.isConfirmed){
|
||||
$('#loading_message').text("");
|
||||
KTApp.showPageLoading();
|
||||
let path = '{{.FolderURL}}' + "/" + encodeURIComponent(name);
|
||||
|
||||
{{define "dialog"}}
|
||||
<div class="modal fade" id="deleteModal" tabindex="-1" role="dialog" aria-labelledby="deleteModalLabel"
|
||||
aria-hidden="true">
|
||||
<div class="modal-dialog" role="document">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h5 class="modal-title" id="deleteModalLabel">
|
||||
Confirmation required
|
||||
</h5>
|
||||
<button class="close" type="button" data-dismiss="modal" aria-label="Close">
|
||||
<span aria-hidden="true">×</span>
|
||||
</button>
|
||||
</div>
|
||||
<div class="modal-body">Do you want to delete the selected virtual folder and any users mapping?</div>
|
||||
<div class="modal-footer">
|
||||
<button class="btn btn-secondary" type="button" data-dismiss="modal">
|
||||
Cancel
|
||||
</button>
|
||||
<a class="btn btn-warning" href="#" onclick="deleteAction()">
|
||||
Delete
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{{end}}
|
||||
|
||||
{{define "extra_js"}}
|
||||
<script src="{{.StaticURL}}/vendor/datatables/jquery.dataTables.min.js"></script>
|
||||
<script src="{{.StaticURL}}/vendor/datatables/dataTables.bootstrap4.min.js"></script>
|
||||
<script src="{{.StaticURL}}/vendor/datatables/dataTables.buttons.min.js"></script>
|
||||
<script src="{{.StaticURL}}/vendor/datatables/buttons.bootstrap4.min.js"></script>
|
||||
<script src="{{.StaticURL}}/vendor/datatables/buttons.colVis.min.js"></script>
|
||||
<script src="{{.StaticURL}}/vendor/datatables/dataTables.fixedHeader.min.js"></script>
|
||||
<script src="{{.StaticURL}}/vendor/datatables/dataTables.responsive.min.js"></script>
|
||||
<script src="{{.StaticURL}}/vendor/datatables/responsive.bootstrap4.min.js"></script>
|
||||
<script src="{{.StaticURL}}/vendor/datatables/dataTables.select.min.js"></script>
|
||||
<script src="{{.StaticURL}}/vendor/datatables/ellipsis.js"></script>
|
||||
<script src="{{.StaticURL}}/vendor/datatables/dataTables.colReorder.min.js"></script>
|
||||
<script type="text/javascript">
|
||||
|
||||
function deleteAction() {
|
||||
let table = $('#dataTable').DataTable();
|
||||
table.button('delete:name').enable(false);
|
||||
let folderName = table.row({ selected: true }).data()[1];
|
||||
let path = '{{.FolderURL}}' + "/" + fixedEncodeURIComponent(folderName);
|
||||
$('#deleteModal').modal('hide');
|
||||
$('#errorMsg').hide();
|
||||
|
||||
$.ajax({
|
||||
url: path,
|
||||
type: 'DELETE',
|
||||
dataType: 'json',
|
||||
headers: {'X-CSRF-TOKEN' : '{{.CSRFToken}}'},
|
||||
axios.delete(path, {
|
||||
timeout: 15000,
|
||||
success: function (result) {
|
||||
window.location.href = '{{.FoldersURL}}';
|
||||
headers: {
|
||||
'X-CSRF-TOKEN': '{{.CSRFToken}}'
|
||||
},
|
||||
error: function ($xhr, textStatus, errorThrown) {
|
||||
var txt = "Unable to delete the selected folder";
|
||||
if ($xhr) {
|
||||
var json = $xhr.responseJSON;
|
||||
if (json) {
|
||||
if (json.message){
|
||||
txt += ": " + json.message;
|
||||
} else {
|
||||
txt += ": " + json.error;
|
||||
validateStatus: function (status) {
|
||||
return status == 200;
|
||||
}
|
||||
}).then(function(response){
|
||||
location.reload();
|
||||
}).catch(function(error){
|
||||
KTApp.hidePageLoading();
|
||||
let errorMessage;
|
||||
if (error && error.response) {
|
||||
switch (error.response.status) {
|
||||
case 403:
|
||||
errorMessage = "general.delete_error_403";
|
||||
break;
|
||||
case 404:
|
||||
errorMessage = "general.delete_error_404";
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!errorMessage){
|
||||
errorMessage = "general.delete_error_generic";
|
||||
}
|
||||
$('#errorTxt').text(txt);
|
||||
$('#errorMsg').show();
|
||||
ModalAlert.fire({
|
||||
text: $.t(errorMessage),
|
||||
icon: "warning",
|
||||
confirmButtonText: $.t('general.ok'),
|
||||
customClass: {
|
||||
confirmButton: "btn btn-primary"
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
$(document).ready(function () {
|
||||
$.fn.dataTable.ext.buttons.add = {
|
||||
text: '<i class="fas fa-plus"></i>',
|
||||
name: 'add',
|
||||
titleAttr: "Add",
|
||||
action: function (e, dt, node, config) {
|
||||
window.location.href = '{{.FolderURL}}';
|
||||
}
|
||||
};
|
||||
var datatable = function(){
|
||||
var dt;
|
||||
|
||||
$.fn.dataTable.ext.buttons.edit = {
|
||||
text: '<i class="fas fa-pen"></i>',
|
||||
name: 'edit',
|
||||
titleAttr: "Edit",
|
||||
action: function (e, dt, node, config) {
|
||||
var folderName = table.row({ selected: true }).data()[1];
|
||||
var path = '{{.FolderURL}}' + "/" + fixedEncodeURIComponent(folderName);
|
||||
window.location.href = path;
|
||||
},
|
||||
enabled: false
|
||||
};
|
||||
|
||||
$.fn.dataTable.ext.buttons.template = {
|
||||
text: '<i class="fas fa-clone"></i>',
|
||||
name: 'template',
|
||||
titleAttr: "Template",
|
||||
action: function (e, dt, node, config) {
|
||||
var selectedRows = table.rows({ selected: true }).count();
|
||||
if (selectedRows == 1){
|
||||
var folderName = table.row({ selected: true }).data()[1];
|
||||
var path = '{{.FolderTemplateURL}}' + "?from=" + encodeURIComponent(folderName);
|
||||
window.location.href = path;
|
||||
} else {
|
||||
window.location.href = '{{.FolderTemplateURL}}';
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
$.fn.dataTable.ext.buttons.delete = {
|
||||
text: '<i class="fas fa-trash"></i>',
|
||||
name: 'delete',
|
||||
titleAttr: "Delete",
|
||||
action: function (e, dt, node, config) {
|
||||
$('#deleteModal').modal('show');
|
||||
},
|
||||
enabled: false
|
||||
};
|
||||
|
||||
$.fn.dataTable.ext.buttons.quota_scan = {
|
||||
text: '<i class="fas fa-redo-alt"></i>',
|
||||
name: 'quota_scan',
|
||||
titleAttr: 'Quota Scan',
|
||||
action: function (e, dt, node, config) {
|
||||
dt.button('quota_scan:name').enable(false);
|
||||
let folderName = dt.row({ selected: true }).data()[1];
|
||||
let path = '{{.FolderQuotaScanURL}}'+ "/" + fixedEncodeURIComponent(folderName);
|
||||
$.ajax({
|
||||
url: path,
|
||||
type: 'POST',
|
||||
headers: {'X-CSRF-TOKEN' : '{{.CSRFToken}}'},
|
||||
timeout: 15000,
|
||||
success: function (result) {
|
||||
//dt.button('quota_scan:name').enable(true);
|
||||
$('#successTxt').text("Quota scan started for the selected folder. Please reload the folders page to check when the scan ends");
|
||||
$('#successMsg').show();
|
||||
setTimeout(function () {
|
||||
$('#successMsg').hide();
|
||||
}, 15000);
|
||||
},
|
||||
var initDatatable = function () {
|
||||
$('#errorMsg').addClass("d-none");
|
||||
dt = $('#dataTable').DataTable({
|
||||
ajax: {
|
||||
url: "{{.FoldersURL}}/json",
|
||||
dataSrc: "",
|
||||
error: function ($xhr, textStatus, errorThrown) {
|
||||
dt.button('quota_scan:name').enable(true);
|
||||
var txt = "Unable to update quota for the selected folder";
|
||||
$(".dataTables_processing").hide();
|
||||
let txt = "";
|
||||
if ($xhr) {
|
||||
var json = $xhr.responseJSON;
|
||||
let json = $xhr.responseJSON;
|
||||
if (json) {
|
||||
if (json.message){
|
||||
txt += ": " + json.message;
|
||||
} else if (json.error) {
|
||||
txt += ": " + json.error;
|
||||
txt = json.message;
|
||||
}
|
||||
}
|
||||
}
|
||||
$('#errorTxt').text(txt);
|
||||
$('#errorMsg').show();
|
||||
if (!txt){
|
||||
txt = "general.error500";
|
||||
}
|
||||
});
|
||||
},
|
||||
enabled: false
|
||||
};
|
||||
|
||||
let dateFn = $.fn.dataTable.render.datetime();
|
||||
let table = $('#dataTable').DataTable({
|
||||
"select": {
|
||||
"style": "single",
|
||||
"blurable": true
|
||||
},
|
||||
"colReorder": {
|
||||
"enable": true,
|
||||
"fixedColumnsLeft": 2
|
||||
},
|
||||
"stateSave": true,
|
||||
"stateDuration": 0,
|
||||
"buttons": [
|
||||
{
|
||||
"text": "Column visibility",
|
||||
"extend": "colvis",
|
||||
"columns": ":not(.noVis)"
|
||||
setI18NData($('#errorTxt'), txt);
|
||||
$('#errorMsg').removeClass("d-none");
|
||||
}
|
||||
],
|
||||
"columnDefs": [
|
||||
{
|
||||
"targets": [0],
|
||||
"visible": false,
|
||||
"className": "noVis"
|
||||
},
|
||||
columns: [
|
||||
{
|
||||
"targets": [1],
|
||||
"className": "noVis"
|
||||
},
|
||||
{
|
||||
"targets": [2],
|
||||
"render": $.fn.dataTable.render.ellipsis(50, true)
|
||||
},
|
||||
{
|
||||
"targets": [3],
|
||||
"visible": false
|
||||
},
|
||||
{
|
||||
"targets": [4],
|
||||
"render": $.fn.dataTable.render.ellipsis(40, true)
|
||||
},
|
||||
{
|
||||
"targets": [5],
|
||||
"visible": false,
|
||||
"render": $.fn.dataTable.render.ellipsis(40, true)
|
||||
},
|
||||
{
|
||||
"targets": [6],
|
||||
"visible": false,
|
||||
"render": function ( data, type, row, meta ) {
|
||||
if (type !== 'display') {
|
||||
data: "name",
|
||||
render: function(data, type, row) {
|
||||
if (type === 'display') {
|
||||
return escapeHTML(data);
|
||||
}
|
||||
return data;
|
||||
}
|
||||
if (row[0] !== ""){
|
||||
let formattedDate = dateFn(row[0], type);
|
||||
data = `${data}. Updated at: ${formattedDate}`;
|
||||
}
|
||||
let ellipsisFn = $.fn.dataTable.render.ellipsis(60, true);
|
||||
return ellipsisFn(data, type);
|
||||
}
|
||||
}
|
||||
],
|
||||
"scrollX": false,
|
||||
"scrollY": false,
|
||||
"responsive": true,
|
||||
"language": {
|
||||
"emptyTable": "No folder defined"
|
||||
},
|
||||
"order": [[1, 'asc']]
|
||||
{
|
||||
data: "filesystem.provider",
|
||||
defaultContent: "",
|
||||
render: function(data, type, row) {
|
||||
if (type === 'display') {
|
||||
switch (data){
|
||||
case 1:
|
||||
return $.t('storage.s3');
|
||||
case 2:
|
||||
return $.t('storage.gcs');
|
||||
case 3:
|
||||
return $.t('storage.azblob');
|
||||
case 4:
|
||||
return $.t('storage.encrypted');
|
||||
case 5:
|
||||
return $.t('storage.sftp');
|
||||
case 6:
|
||||
return $.t('storage.http');
|
||||
default:
|
||||
return $.t('storage.local');
|
||||
}
|
||||
}
|
||||
return data;
|
||||
}
|
||||
},
|
||||
{
|
||||
data: "used_quota_size",
|
||||
visible: false,
|
||||
searchable: false,
|
||||
orderable: false,
|
||||
render: function(data, type, row) {
|
||||
if (type === 'display') {
|
||||
let val = "";
|
||||
if (row.used_quota_size) {
|
||||
let usage = fileSizeIEC(row.used_quota_size);
|
||||
val += $.t('fs.quota_usage.size', {val: usage})+". ";
|
||||
}
|
||||
if (row.used_quota_files){
|
||||
val += $.t('fs.quota_usage.files', {val: row.used_quota_files});
|
||||
}
|
||||
return val
|
||||
}
|
||||
return data;
|
||||
}
|
||||
},
|
||||
{
|
||||
data: "users",
|
||||
defaultContent: "",
|
||||
visible: false,
|
||||
searchable: false,
|
||||
orderable: false,
|
||||
render: function(data, type, row) {
|
||||
if (type === 'display') {
|
||||
let users = 0;
|
||||
if (row.users){
|
||||
users = row.users.length;
|
||||
}
|
||||
let groups = 0;
|
||||
if (row.groups){
|
||||
groups = row.groups.length;
|
||||
}
|
||||
return $.t('virtual_folders.associations_summary', {users: users, groups: groups});
|
||||
}
|
||||
return "";
|
||||
}
|
||||
},
|
||||
{
|
||||
data: "description",
|
||||
visible: false,
|
||||
defaultContent: "",
|
||||
render: function(data, type, row) {
|
||||
if (type === 'display') {
|
||||
if (data){
|
||||
return escapeHTML(data);
|
||||
}
|
||||
return ""
|
||||
}
|
||||
return data;
|
||||
}
|
||||
},
|
||||
{
|
||||
data: "id",
|
||||
searchable: false,
|
||||
orderable: false,
|
||||
className: 'text-end',
|
||||
render: function (data, type, row) {
|
||||
if (type === 'display') {
|
||||
let numActions = 0;
|
||||
let actions = `<button class="btn btn-light btn-active-light-primary btn-flex btn-center btn-sm rotate" data-kt-menu-trigger="click" data-kt-menu-placement="bottom-end">
|
||||
<span data-i18n="general.actions" class="fs-6">Actions</span>
|
||||
<i class="ki-duotone ki-down fs-5 ms-1 rotate-180"></i>
|
||||
</button>
|
||||
<div class="menu menu-sub menu-sub-dropdown menu-column menu-rounded menu-gray-700 menu-state-bg-light-primary fw-semibold fs-6 w-200px py-4" data-kt-menu="true">`;
|
||||
|
||||
//{{- if .LoggedUser.HasPermission "manage_folders"}}
|
||||
numActions++;
|
||||
actions+=`<div class="menu-item px-3">
|
||||
<a data-i18n="general.edit" href="#" class="menu-link px-3" data-share-table-action="edit_row">Edit</a>
|
||||
</div>`
|
||||
numActions++;
|
||||
actions+=`<div class="menu-item px-3">
|
||||
<a data-i18n="general.delete" href="#" class="menu-link text-danger px-3" data-share-table-action="delete_row">Delete</a>
|
||||
</div>`
|
||||
//{{- end}}
|
||||
if (numActions > 0){
|
||||
actions+=`</div>`;
|
||||
return actions;
|
||||
}
|
||||
}
|
||||
return "";
|
||||
}
|
||||
},
|
||||
],
|
||||
deferRender: true,
|
||||
stateSave: true,
|
||||
stateDuration: 0,
|
||||
colReorder: {
|
||||
enable: true,
|
||||
fixedColumnsLeft: 1
|
||||
},
|
||||
stateLoadParams: function (settings, data) {
|
||||
if (data.search.search){
|
||||
const filterSearch = document.querySelector('[data-table-filter="search"]');
|
||||
filterSearch.value = data.search.search;
|
||||
}
|
||||
},
|
||||
language: {
|
||||
info: $.t('datatable.info'),
|
||||
infoEmpty: $.t('datatable.info_empty'),
|
||||
infoFiltered: $.t('datatable.info_filtered'),
|
||||
loadingRecords: "",
|
||||
processing: $.t('datatable.processing'),
|
||||
zeroRecords: "",
|
||||
emptyTable: $.t('datatable.no_records')
|
||||
},
|
||||
order: [[0, 'asc']],
|
||||
initComplete: function(settings, json) {
|
||||
$('#loader').addClass("d-none");
|
||||
$('#card_content').removeClass("d-none");
|
||||
let api = $.fn.dataTable.Api(settings);
|
||||
api.columns.adjust().draw("page");
|
||||
drawAction();
|
||||
}
|
||||
});
|
||||
|
||||
new $.fn.dataTable.FixedHeader( table );
|
||||
dt.on('draw', drawAction);
|
||||
dt.on('column-reorder', function(e, settings, details){
|
||||
drawAction();
|
||||
});
|
||||
}
|
||||
|
||||
{{if .LoggedAdmin.HasPermission "quota_scans"}}
|
||||
table.button().add(0,'quota_scan');
|
||||
{{end}}
|
||||
function drawAction() {
|
||||
KTMenu.createInstances();
|
||||
handleRowActions();
|
||||
$('#table_body').localize();
|
||||
}
|
||||
|
||||
{{if .LoggedAdmin.HasPermission "del_users"}}
|
||||
table.button().add(0,'delete');
|
||||
{{end}}
|
||||
function handleColVisibilityCheckbox(el, index) {
|
||||
el.off("change");
|
||||
el.prop('checked', dt.column(index).visible());
|
||||
el.on("change", function(e){
|
||||
dt.column(index).visible($(this).is(':checked'));
|
||||
dt.draw('page');
|
||||
});
|
||||
}
|
||||
|
||||
{{if .LoggedAdmin.HasPermission "add_users"}}
|
||||
table.button().add(0,'template');
|
||||
{{end}}
|
||||
var handleDatatableActions = function () {
|
||||
const filterSearch = $(document.querySelector('[data-table-filter="search"]'));
|
||||
filterSearch.off("keyup");
|
||||
filterSearch.on('keyup', function (e) {
|
||||
dt.rows().deselect();
|
||||
dt.search(e.target.value, true, false).draw();
|
||||
});
|
||||
handleColVisibilityCheckbox($('#checkColStorage'), 1);
|
||||
handleColVisibilityCheckbox($('#checkColQuota'), 2);
|
||||
handleColVisibilityCheckbox($('#checkColAssociations'), 3);
|
||||
handleColVisibilityCheckbox($('#checkColDesc'), 4);
|
||||
}
|
||||
|
||||
{{if .LoggedAdmin.HasPermission "edit_users"}}
|
||||
table.button().add(0,'edit');
|
||||
{{end}}
|
||||
|
||||
{{if .LoggedAdmin.HasPermission "add_users"}}
|
||||
table.button().add(0,'add');
|
||||
{{end}}
|
||||
|
||||
table.buttons().container().appendTo('.col-md-6:eq(0)', table.table().container());
|
||||
|
||||
table.on('select deselect', function () {
|
||||
var selectedRows = table.rows({ selected: true }).count();
|
||||
{{if .LoggedAdmin.HasPermission "del_users"}}
|
||||
table.button('delete:name').enable(selectedRows == 1);
|
||||
{{end}}
|
||||
{{if .LoggedAdmin.HasPermission "edit_users"}}
|
||||
table.button('edit:name').enable(selectedRows == 1);
|
||||
{{end}}
|
||||
{{if .LoggedAdmin.HasPermission "quota_scans"}}
|
||||
table.button('quota_scan:name').enable(selectedRows == 1);
|
||||
{{end}}
|
||||
function handleRowActions() {
|
||||
const editButtons = document.querySelectorAll('[data-share-table-action="edit_row"]');
|
||||
editButtons.forEach(d => {
|
||||
let el = $(d);
|
||||
el.off("click");
|
||||
el.on("click", function(e){
|
||||
e.preventDefault();
|
||||
let rowData = dt.row(e.target.closest('tr')).data();
|
||||
window.location.replace('{{.FolderURL}}' + "/" + encodeURIComponent(rowData['name']));
|
||||
});
|
||||
});
|
||||
|
||||
const deleteButtons = document.querySelectorAll('[data-share-table-action="delete_row"]');
|
||||
deleteButtons.forEach(d => {
|
||||
let el = $(d);
|
||||
el.off("click");
|
||||
el.on("click", function(e){
|
||||
e.preventDefault();
|
||||
const parent = e.target.closest('tr');
|
||||
deleteAction(dt.row(parent).data()['name']);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
return {
|
||||
init: function () {
|
||||
initDatatable();
|
||||
handleDatatableActions();
|
||||
}
|
||||
}
|
||||
}();
|
||||
|
||||
$(document).on("i18nshow", function(){
|
||||
datatable.init();
|
||||
});
|
||||
</script>
|
||||
{{end}}
|
||||
{{- end}}
|
|
@ -126,7 +126,14 @@ explicit grant from the SFTPGo Team (support@sftpgo.com).
|
|||
if (!errorMessage){
|
||||
errorMessage = "general.delete_error_generic";
|
||||
}
|
||||
showToast(2, errorMessage);
|
||||
ModalAlert.fire({
|
||||
text: $.t(errorMessage),
|
||||
icon: "warning",
|
||||
confirmButtonText: $.t('general.ok'),
|
||||
customClass: {
|
||||
confirmButton: "btn btn-primary"
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
});
|
||||
|
|
|
@ -175,7 +175,14 @@ explicit grant from the SFTPGo Team (support@sftpgo.com).
|
|||
if (!errorMessage){
|
||||
errorMessage = "general.delete_error_generic";
|
||||
}
|
||||
showToast(2, errorMessage);
|
||||
ModalAlert.fire({
|
||||
text: $.t(errorMessage),
|
||||
icon: "warning",
|
||||
confirmButtonText: $.t('general.ok'),
|
||||
customClass: {
|
||||
confirmButton: "btn btn-primary"
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
});
|
||||
|
@ -209,7 +216,14 @@ explicit grant from the SFTPGo Team (support@sftpgo.com).
|
|||
if (!errorMessage) {
|
||||
errorMessage = "general.quota_scan_error";
|
||||
}
|
||||
showToast(2, errorMessage);
|
||||
ModalAlert.fire({
|
||||
text: $.t(errorMessage),
|
||||
icon: "warning",
|
||||
confirmButtonText: $.t('general.ok'),
|
||||
customClass: {
|
||||
confirmButton: "btn btn-primary"
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -364,7 +378,7 @@ explicit grant from the SFTPGo Team (support@sftpgo.com).
|
|||
if (!used){
|
||||
used = 0;
|
||||
}
|
||||
let usage = fileSizeIEC(used)+"/"+fileSizeIEC(row.quota_size)
|
||||
let usage = fileSizeIEC(used)+"/"+fileSizeIEC(row.quota_size);
|
||||
val += $.t('fs.quota_usage.size', {val: usage})+". ";
|
||||
} else if (row.used_quota_size && row.used_quota_size > 0) {
|
||||
val += $.t('fs.quota_usage.size', {val: fileSizeIEC(row.used_quota_size)})+". ";
|
||||
|
|
|
@ -1224,7 +1224,14 @@ explicit grant from the SFTPGo Team (support@sftpgo.com).
|
|||
}
|
||||
items = checkMoveCopyItems(items)
|
||||
if (items.length == 0){
|
||||
showToast(2, "fs.invalid_name");
|
||||
ModalAlert.fire({
|
||||
text: $.t('fs.invalid_name'),
|
||||
icon: "warning",
|
||||
confirmButtonText: $.t('general.ok'),
|
||||
customClass: {
|
||||
confirmButton: "btn btn-primary"
|
||||
}
|
||||
});
|
||||
return;
|
||||
}
|
||||
keepAlive();
|
||||
|
@ -1296,7 +1303,14 @@ explicit grant from the SFTPGo Team (support@sftpgo.com).
|
|||
if (!errorMessage){
|
||||
errorMessage = "fs.copy.err_generic";
|
||||
}
|
||||
showToast(2, errorMessage);
|
||||
ModalAlert.fire({
|
||||
text: $.t(errorMessage),
|
||||
icon: "warning",
|
||||
confirmButtonText: $.t('general.ok'),
|
||||
customClass: {
|
||||
confirmButton: "btn btn-primary"
|
||||
}
|
||||
});
|
||||
copyItem();
|
||||
});
|
||||
}
|
||||
|
@ -1315,10 +1329,24 @@ explicit grant from the SFTPGo Team (support@sftpgo.com).
|
|||
}).then((result)=>{
|
||||
if (result.error) {
|
||||
hasError = true;
|
||||
showToast(2, "fs.copy.err_generic");
|
||||
ModalAlert.fire({
|
||||
text: $.t("fs.copy.err_generic"),
|
||||
icon: "warning",
|
||||
confirmButtonText: $.t('general.ok'),
|
||||
customClass: {
|
||||
confirmButton: "btn btn-primary"
|
||||
}
|
||||
});
|
||||
} else if (result.data.length > 0){
|
||||
hasError = true;
|
||||
showToast(2, "fs.copy.err_exists");
|
||||
ModalAlert.fire({
|
||||
text: $.t("fs.copy.err_exists"),
|
||||
icon: "warning",
|
||||
confirmButtonText: $.t('general.ok'),
|
||||
customClass: {
|
||||
confirmButton: "btn btn-primary"
|
||||
}
|
||||
});
|
||||
}
|
||||
copyItem();
|
||||
});
|
||||
|
@ -1332,7 +1360,14 @@ explicit grant from the SFTPGo Team (support@sftpgo.com).
|
|||
}
|
||||
items = checkMoveCopyItems(items)
|
||||
if (items.length == 0){
|
||||
showToast(2, "fs.invalid_name");
|
||||
ModalAlert.fire({
|
||||
text: $.t("fs.invalid_name"),
|
||||
icon: "warning",
|
||||
confirmButtonText: $.t('general.ok'),
|
||||
customClass: {
|
||||
confirmButton: "btn btn-primary"
|
||||
}
|
||||
});
|
||||
return;
|
||||
}
|
||||
keepAlive();
|
||||
|
@ -1404,7 +1439,14 @@ explicit grant from the SFTPGo Team (support@sftpgo.com).
|
|||
if (!errorMessage){
|
||||
errorMessage = "fs.move.err_generic";
|
||||
}
|
||||
showToast(2, errorMessage);
|
||||
ModalAlert.fire({
|
||||
text: $.t(errorMessage),
|
||||
icon: "warning",
|
||||
confirmButtonText: $.t('general.ok'),
|
||||
customClass: {
|
||||
confirmButton: "btn btn-primary"
|
||||
}
|
||||
});
|
||||
moveItem();
|
||||
});
|
||||
}
|
||||
|
@ -1423,10 +1465,24 @@ explicit grant from the SFTPGo Team (support@sftpgo.com).
|
|||
}).then((result)=>{
|
||||
if (result.error) {
|
||||
hasError = true;
|
||||
showToast(2, "fs.move.err_generic");
|
||||
ModalAlert.fire({
|
||||
text: $.t("fs.move.err_generic"),
|
||||
icon: "warning",
|
||||
confirmButtonText: $.t('general.ok'),
|
||||
customClass: {
|
||||
confirmButton: "btn btn-primary"
|
||||
}
|
||||
});
|
||||
} else if (result.data.length > 0){
|
||||
hasError = true;
|
||||
showToast(2, "fs.move.err_exists");
|
||||
ModalAlert.fire({
|
||||
text: $.t("fs.move.err_exists"),
|
||||
icon: "warning",
|
||||
confirmButtonText: $.t('general.ok'),
|
||||
customClass: {
|
||||
confirmButton: "btn btn-primary"
|
||||
}
|
||||
});
|
||||
}
|
||||
moveItem();
|
||||
});
|
||||
|
@ -1493,7 +1549,14 @@ explicit grant from the SFTPGo Team (support@sftpgo.com).
|
|||
if (!errorMessage){
|
||||
errorMessage = "fs.delete.err_generic";
|
||||
}
|
||||
showToast(2, errorMessage, {name: itemName});
|
||||
ModalAlert.fire({
|
||||
text: $.t(errorMessage, {name: itemName}),
|
||||
icon: "warning",
|
||||
confirmButtonText: $.t('general.ok'),
|
||||
customClass: {
|
||||
confirmButton: "btn btn-primary"
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
});
|
||||
|
@ -1523,15 +1586,36 @@ explicit grant from the SFTPGo Team (support@sftpgo.com).
|
|||
let oldName = getNameFromMeta(meta);
|
||||
let newName = $('#rename_new_name').val();
|
||||
if (!newName){
|
||||
showToast(2, "general.name_required");
|
||||
ModalAlert.fire({
|
||||
text: $.t('general.name_required'),
|
||||
icon: "warning",
|
||||
confirmButtonText: $.t('general.ok'),
|
||||
customClass: {
|
||||
confirmButton: "btn btn-primary"
|
||||
}
|
||||
});
|
||||
return;
|
||||
}
|
||||
if (newName == oldName){
|
||||
showToast(2, "general.name_different");
|
||||
ModalAlert.fire({
|
||||
text: $.t('general.name_different'),
|
||||
icon: "warning",
|
||||
confirmButtonText: $.t('general.ok'),
|
||||
customClass: {
|
||||
confirmButton: "btn btn-primary"
|
||||
}
|
||||
});
|
||||
return;
|
||||
}
|
||||
if (newName.includes("/")){
|
||||
showToast(2, "fs.invalid_name");
|
||||
ModalAlert.fire({
|
||||
text: $.t('fs.invalid_name'),
|
||||
icon: "warning",
|
||||
confirmButtonText: $.t('general.ok'),
|
||||
customClass: {
|
||||
confirmButton: "btn btn-primary"
|
||||
}
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -1567,7 +1651,14 @@ explicit grant from the SFTPGo Team (support@sftpgo.com).
|
|||
if (!errorMessage) {
|
||||
errorMessage = "fs.rename.err_generic";
|
||||
}
|
||||
showToast(2, errorMessage, { name: oldName });
|
||||
ModalAlert.fire({
|
||||
text: $.t(errorMessage, {name: oldName}),
|
||||
icon: "warning",
|
||||
confirmButtonText: $.t('general.ok'),
|
||||
customClass: {
|
||||
confirmButton: "btn btn-primary"
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -1578,12 +1669,26 @@ explicit grant from the SFTPGo Team (support@sftpgo.com).
|
|||
}).then((result)=>{
|
||||
if (result.error) {
|
||||
KTApp.hidePageLoading();
|
||||
showToast(2, "fs.rename.err_generic", { name: oldName });
|
||||
ModalAlert.fire({
|
||||
text: $.t('fs.rename.err_generic', { name: oldName }),
|
||||
icon: "warning",
|
||||
confirmButtonText: $.t('general.ok'),
|
||||
customClass: {
|
||||
confirmButton: "btn btn-primary"
|
||||
}
|
||||
});
|
||||
return;
|
||||
}
|
||||
if (result.data.length > 0){
|
||||
KTApp.hidePageLoading();
|
||||
showToast(2, "fs.rename.err_exists", { name: oldName });
|
||||
ModalAlert.fire({
|
||||
text: $.t('fs.rename.err_exists', { name: oldName }),
|
||||
icon: "warning",
|
||||
confirmButtonText: $.t('general.ok'),
|
||||
customClass: {
|
||||
confirmButton: "btn btn-primary"
|
||||
}
|
||||
});
|
||||
return;
|
||||
}
|
||||
executeRename();
|
||||
|
|
|
@ -205,7 +205,14 @@ explicit grant from the SFTPGo Team (support@sftpgo.com).
|
|||
if (!errorMessage){
|
||||
errorMessage = "general.delete_error_generic";
|
||||
}
|
||||
showToast(2, errorMessage);
|
||||
ModalAlert.fire({
|
||||
text: $.t(errorMessage),
|
||||
icon: "warning",
|
||||
confirmButtonText: $.t('general.ok'),
|
||||
customClass: {
|
||||
confirmButton: "btn btn-primary"
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
});
|
||||
|
|
Loading…
Reference in a new issue