WIP new WebAdmin: roles page
Signed-off-by: Nicola Murino <nicola.murino@gmail.com>
This commit is contained in:
parent
0d387d9799
commit
3f479c5537
11 changed files with 405 additions and 274 deletions
|
@ -1787,6 +1787,8 @@ func (s *httpdServer) setupWebAdminRoutes() {
|
||||||
Post(webAdminEventRulePath+"/run/{name}", runOnDemandRule)
|
Post(webAdminEventRulePath+"/run/{name}", runOnDemandRule)
|
||||||
router.With(s.checkPerm(dataprovider.PermAdminManageRoles), s.refreshCookie).
|
router.With(s.checkPerm(dataprovider.PermAdminManageRoles), s.refreshCookie).
|
||||||
Get(webAdminRolesPath, s.handleWebGetRoles)
|
Get(webAdminRolesPath, s.handleWebGetRoles)
|
||||||
|
router.With(s.checkPerm(dataprovider.PermAdminManageRoles), compressor.Handler, s.refreshCookie).
|
||||||
|
Get(webAdminRolesPath+jsonAPISuffix, getAllRoles)
|
||||||
router.With(s.checkPerm(dataprovider.PermAdminManageRoles), s.refreshCookie).
|
router.With(s.checkPerm(dataprovider.PermAdminManageRoles), s.refreshCookie).
|
||||||
Get(webAdminRolePath, s.handleWebAddRoleGet)
|
Get(webAdminRolePath, s.handleWebAddRoleGet)
|
||||||
router.With(s.checkPerm(dataprovider.PermAdminManageRoles)).Post(webAdminRolePath, s.handleWebAddRolePost)
|
router.With(s.checkPerm(dataprovider.PermAdminManageRoles)).Post(webAdminRolePath, s.handleWebAddRolePost)
|
||||||
|
|
|
@ -102,8 +102,6 @@ const (
|
||||||
pageStatusTitle = "Status"
|
pageStatusTitle = "Status"
|
||||||
pageEventRulesTitle = "Event rules"
|
pageEventRulesTitle = "Event rules"
|
||||||
pageEventActionsTitle = "Event actions"
|
pageEventActionsTitle = "Event actions"
|
||||||
pageRolesTitle = "Roles"
|
|
||||||
pageChangePwdTitle = "Change password"
|
|
||||||
pageMaintenanceTitle = "Maintenance"
|
pageMaintenanceTitle = "Maintenance"
|
||||||
pageDefenderTitle = "Auto Blocklist"
|
pageDefenderTitle = "Auto Blocklist"
|
||||||
pageIPListsTitle = "IP Lists"
|
pageIPListsTitle = "IP Lists"
|
||||||
|
@ -169,11 +167,6 @@ type adminsPage struct {
|
||||||
Admins []dataprovider.Admin
|
Admins []dataprovider.Admin
|
||||||
}
|
}
|
||||||
|
|
||||||
type rolesPage struct {
|
|
||||||
basePage
|
|
||||||
Roles []dataprovider.Role
|
|
||||||
}
|
|
||||||
|
|
||||||
type eventRulesPage struct {
|
type eventRulesPage struct {
|
||||||
basePage
|
basePage
|
||||||
Rules []dataprovider.EventRule
|
Rules []dataprovider.EventRule
|
||||||
|
@ -518,7 +511,7 @@ func loadAdminTemplates(templatesPath string) {
|
||||||
filepath.Join(templatesPath, templateCommonDir, templateResetPassword),
|
filepath.Join(templatesPath, templateCommonDir, templateResetPassword),
|
||||||
}
|
}
|
||||||
rolesPaths := []string{
|
rolesPaths := []string{
|
||||||
filepath.Join(templatesPath, templateCommonDir, templateCommonCSS),
|
filepath.Join(templatesPath, templateCommonDir, templateCommonBase),
|
||||||
filepath.Join(templatesPath, templateAdminDir, templateBase),
|
filepath.Join(templatesPath, templateAdminDir, templateBase),
|
||||||
filepath.Join(templatesPath, templateAdminDir, templateRoles),
|
filepath.Join(templatesPath, templateAdminDir, templateRoles),
|
||||||
}
|
}
|
||||||
|
@ -839,7 +832,7 @@ func (s *httpdServer) renderProfilePage(w http.ResponseWriter, r *http.Request,
|
||||||
|
|
||||||
func (s *httpdServer) renderChangePasswordPage(w http.ResponseWriter, r *http.Request, err *util.I18nError) {
|
func (s *httpdServer) renderChangePasswordPage(w http.ResponseWriter, r *http.Request, err *util.I18nError) {
|
||||||
data := changePasswordPage{
|
data := changePasswordPage{
|
||||||
basePage: s.getBasePageData(pageChangePwdTitle, webChangeAdminPwdPath, r),
|
basePage: s.getBasePageData(util.I18nChangePwdTitle, webChangeAdminPwdPath, r),
|
||||||
Error: err,
|
Error: err,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3856,7 +3849,7 @@ func (s *httpdServer) handleWebUpdateEventRulePost(w http.ResponseWriter, r *htt
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *httpdServer) getWebRoles(w http.ResponseWriter, r *http.Request, limit int, minimal bool) ([]dataprovider.Role, error) {
|
func (s *httpdServer) getWebRoles(w http.ResponseWriter, r *http.Request, limit int, minimal bool) ([]dataprovider.Role, error) {
|
||||||
roles := make([]dataprovider.Role, 0, limit)
|
roles := make([]dataprovider.Role, 0, 10)
|
||||||
for {
|
for {
|
||||||
res, err := dataprovider.GetRoles(limit, len(roles), dataprovider.OrderASC, minimal)
|
res, err := dataprovider.GetRoles(limit, len(roles), dataprovider.OrderASC, minimal)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -3871,16 +3864,27 @@ func (s *httpdServer) getWebRoles(w http.ResponseWriter, r *http.Request, limit
|
||||||
return roles, nil
|
return roles, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func getAllRoles(w http.ResponseWriter, r *http.Request) {
|
||||||
|
r.Body = http.MaxBytesReader(w, r.Body, maxRequestSize)
|
||||||
|
roles := make([]dataprovider.Role, 0, 10)
|
||||||
|
for {
|
||||||
|
res, err := dataprovider.GetRoles(defaultQueryLimit, len(roles), dataprovider.OrderASC, false)
|
||||||
|
if err != nil {
|
||||||
|
sendAPIResponse(w, r, err, getI18NErrorString(err, util.I18nError500Message), http.StatusInternalServerError)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
roles = append(roles, res...)
|
||||||
|
if len(res) < defaultQueryLimit {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
render.JSON(w, r, roles)
|
||||||
|
}
|
||||||
|
|
||||||
func (s *httpdServer) handleWebGetRoles(w http.ResponseWriter, r *http.Request) {
|
func (s *httpdServer) handleWebGetRoles(w http.ResponseWriter, r *http.Request) {
|
||||||
r.Body = http.MaxBytesReader(w, r.Body, maxRequestSize)
|
r.Body = http.MaxBytesReader(w, r.Body, maxRequestSize)
|
||||||
roles, err := s.getWebRoles(w, r, 10, false)
|
data := s.getBasePageData(util.I18nRolesTitle, webAdminRolesPath, r)
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
data := rolesPage{
|
|
||||||
basePage: s.getBasePageData(pageRolesTitle, webAdminRolesPath, r),
|
|
||||||
Roles: roles,
|
|
||||||
}
|
|
||||||
renderAdminTemplate(w, templateRoles, data)
|
renderAdminTemplate(w, templateRoles, data)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -59,6 +59,7 @@ const (
|
||||||
I18nOAuth2Title = "title.oauth2_success"
|
I18nOAuth2Title = "title.oauth2_success"
|
||||||
I18nOAuth2ErrorTitle = "title.oauth2_error"
|
I18nOAuth2ErrorTitle = "title.oauth2_error"
|
||||||
I18nSessionsTitle = "title.connections"
|
I18nSessionsTitle = "title.connections"
|
||||||
|
I18nRolesTitle = "title.roles"
|
||||||
I18nErrorSetupInstallCode = "setup.install_code_mismatch"
|
I18nErrorSetupInstallCode = "setup.install_code_mismatch"
|
||||||
I18nInvalidAuth = "general.invalid_auth_request"
|
I18nInvalidAuth = "general.invalid_auth_request"
|
||||||
I18nError429Message = "general.error429"
|
I18nError429Message = "general.error429"
|
||||||
|
|
|
@ -221,7 +221,9 @@
|
||||||
"backup_ok": "Backup successfully restored",
|
"backup_ok": "Backup successfully restored",
|
||||||
"configs_saved": "Configurations has been successfully updated",
|
"configs_saved": "Configurations has been successfully updated",
|
||||||
"protocol": "Protocol",
|
"protocol": "Protocol",
|
||||||
"refresh": "Refresh"
|
"refresh": "Refresh",
|
||||||
|
"members": "Members",
|
||||||
|
"members_summary": "Users: {{users}}. Admins: {{admins}}"
|
||||||
},
|
},
|
||||||
"fs": {
|
"fs": {
|
||||||
"view_file": "View file \"{{- path}}\"",
|
"view_file": "View file \"{{- path}}\"",
|
||||||
|
@ -493,9 +495,7 @@
|
||||||
"template_no_user": "No valid user defined, unable to complete the requested action"
|
"template_no_user": "No valid user defined, unable to complete the requested action"
|
||||||
},
|
},
|
||||||
"group": {
|
"group": {
|
||||||
"view_manage": "View and manage groups",
|
"view_manage": "View and manage groups"
|
||||||
"members": "Members",
|
|
||||||
"members_summary": "Users: {{users}}. Admins: {{admins}}"
|
|
||||||
},
|
},
|
||||||
"virtual_folders": {
|
"virtual_folders": {
|
||||||
"view_manage": "View and manage virtual folders",
|
"view_manage": "View and manage virtual folders",
|
||||||
|
@ -686,5 +686,8 @@
|
||||||
"upload_info": "$t(connections.upload). Size: {{- size}}. Speed: {{- speed}}",
|
"upload_info": "$t(connections.upload). Size: {{- size}}. Speed: {{- speed}}",
|
||||||
"download_info": "$t(connections.download). Size: {{- size}}. Speed: {{- speed}}",
|
"download_info": "$t(connections.download). Size: {{- size}}. Speed: {{- speed}}",
|
||||||
"client": "Client: {{- val}}"
|
"client": "Client: {{- val}}"
|
||||||
|
},
|
||||||
|
"role": {
|
||||||
|
"view_manage": "View and manage roles"
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -221,7 +221,9 @@
|
||||||
"backup_ok": "Backup ripristinato correttamente",
|
"backup_ok": "Backup ripristinato correttamente",
|
||||||
"configs_saved": "Configurazioni aggiornate",
|
"configs_saved": "Configurazioni aggiornate",
|
||||||
"protocol": "Protocollo",
|
"protocol": "Protocollo",
|
||||||
"refresh": "Aggiorna"
|
"refresh": "Aggiorna",
|
||||||
|
"members": "Membri",
|
||||||
|
"members_summary": "Utenti: {{users}}. Amministratori: {{admins}}"
|
||||||
},
|
},
|
||||||
"fs": {
|
"fs": {
|
||||||
"view_file": "Visualizza file \"{{- path}}\"",
|
"view_file": "Visualizza file \"{{- path}}\"",
|
||||||
|
@ -493,9 +495,7 @@
|
||||||
"template_no_user": "Nessun utente valido definito. Impossibile completare l'azione richiesta"
|
"template_no_user": "Nessun utente valido definito. Impossibile completare l'azione richiesta"
|
||||||
},
|
},
|
||||||
"group": {
|
"group": {
|
||||||
"view_manage": "Visualizza e gestisci gruppi",
|
"view_manage": "Visualizza e gestisci gruppi"
|
||||||
"members": "Membri",
|
|
||||||
"members_summary": "Utenti: {{users}}. Amministratori: {{admins}}"
|
|
||||||
},
|
},
|
||||||
"virtual_folders": {
|
"virtual_folders": {
|
||||||
"view_manage": "Visualizza e gestisci cartelle virtuali",
|
"view_manage": "Visualizza e gestisci cartelle virtuali",
|
||||||
|
@ -686,5 +686,8 @@
|
||||||
"upload_info": "$t(connections.upload). Dimensione: {{- size}}. Velocità: {{- speed}}",
|
"upload_info": "$t(connections.upload). Dimensione: {{- size}}. Velocità: {{- speed}}",
|
||||||
"download_info": "$t(connections.download). Dimensione: {{- size}}. Velocità: {{- speed}}",
|
"download_info": "$t(connections.download). Dimensione: {{- size}}. Velocità: {{- speed}}",
|
||||||
"client": "Client: {{- val}}"
|
"client": "Client: {{- val}}"
|
||||||
|
},
|
||||||
|
"role": {
|
||||||
|
"view_manage": "Visualizza e gestisci ruoli"
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -191,30 +191,6 @@ explicit grant from the SFTPGo Team (support@sftpgo.com).
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{{- end}}
|
{{- end}}
|
||||||
{{- if .LoggedUser.HasPermission "manage_admins"}}
|
|
||||||
<div class="menu-item">
|
|
||||||
<a class="menu-link {{- if eq .CurrentURL .AdminsURL}} active{{- end}}" href="{{.AdminsURL}}">
|
|
||||||
<span class="menu-icon">
|
|
||||||
<i class="ki-solid ki-security-user fs-1"></i>
|
|
||||||
</span>
|
|
||||||
<span data-i18n="title.admins" class="menu-title">Admins</span>
|
|
||||||
</a>
|
|
||||||
</div>
|
|
||||||
{{- end}}
|
|
||||||
{{- if .LoggedUser.HasPermission "manage_roles"}}
|
|
||||||
<div class="menu-item">
|
|
||||||
<a class="menu-link {{- if eq .CurrentURL .RolesURL}} active{{- end}}" href="{{.RolesURL}}">
|
|
||||||
<span class="menu-icon">
|
|
||||||
<i class="ki-duotone ki-user-tick fs-1">
|
|
||||||
<span class="path1"></span>
|
|
||||||
<span class="path2"></span>
|
|
||||||
<span class="path3"></span>
|
|
||||||
</i>
|
|
||||||
</span>
|
|
||||||
<span data-i18n="title.roles" class="menu-title">Roles</span>
|
|
||||||
</a>
|
|
||||||
</div>
|
|
||||||
{{- end}}
|
|
||||||
{{- if or (.LoggedUser.HasPermission "manage_system") (.LoggedUser.HasPermission "view_status") (and .HasSearcher (.LoggedUser.HasPermission "view_events"))}}
|
{{- if or (.LoggedUser.HasPermission "manage_system") (.LoggedUser.HasPermission "view_status") (and .HasSearcher (.LoggedUser.HasPermission "view_events"))}}
|
||||||
<div data-kt-menu-trigger="click" class="menu-item menu-accordion {{- if .IsServerManagerPage}} here show{{- end}}">
|
<div data-kt-menu-trigger="click" class="menu-item menu-accordion {{- if .IsServerManagerPage}} here show{{- end}}">
|
||||||
<span class="menu-link">
|
<span class="menu-link">
|
||||||
|
@ -274,5 +250,29 @@ explicit grant from the SFTPGo Team (support@sftpgo.com).
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{{- end}}
|
{{- end}}
|
||||||
|
{{- if .LoggedUser.HasPermission "manage_admins"}}
|
||||||
|
<div class="menu-item">
|
||||||
|
<a class="menu-link {{- if eq .CurrentURL .AdminsURL}} active{{- end}}" href="{{.AdminsURL}}">
|
||||||
|
<span class="menu-icon">
|
||||||
|
<i class="ki-solid ki-security-user fs-1"></i>
|
||||||
|
</span>
|
||||||
|
<span data-i18n="title.admins" class="menu-title">Admins</span>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
{{- end}}
|
||||||
|
{{- if .LoggedUser.HasPermission "manage_roles"}}
|
||||||
|
<div class="menu-item">
|
||||||
|
<a class="menu-link {{- if eq .CurrentURL .RolesURL}} active{{- end}}" href="{{.RolesURL}}">
|
||||||
|
<span class="menu-icon">
|
||||||
|
<i class="ki-duotone ki-user-tick fs-1">
|
||||||
|
<span class="path1"></span>
|
||||||
|
<span class="path2"></span>
|
||||||
|
<span class="path3"></span>
|
||||||
|
</i>
|
||||||
|
</span>
|
||||||
|
<span data-i18n="title.roles" class="menu-title">Roles</span>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
{{- end}}
|
||||||
|
|
||||||
{{- end}}
|
{{- end}}
|
|
@ -286,19 +286,19 @@ explicit grant from the SFTPGo Team (support@sftpgo.com).
|
||||||
//{{- if .LoggedUser.HasPermission "manage_folders"}}
|
//{{- if .LoggedUser.HasPermission "manage_folders"}}
|
||||||
numActions++;
|
numActions++;
|
||||||
actions+=`<div class="menu-item px-3">
|
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>
|
<a data-i18n="general.edit" href="#" class="menu-link px-3" data-table-action="edit_row">Edit</a>
|
||||||
</div>`
|
</div>`
|
||||||
//{{- end}}
|
//{{- end}}
|
||||||
//{{- if .LoggedUser.HasPermission "manage_system"}}
|
//{{- if .LoggedUser.HasPermission "manage_system"}}
|
||||||
numActions++;
|
numActions++;
|
||||||
actions+=`<div class="menu-item px-3">
|
actions+=`<div class="menu-item px-3">
|
||||||
<a data-i18n="general.template" href="#" class="menu-link px-3" data-share-table-action="template_row">Template</a>
|
<a data-i18n="general.template" href="#" class="menu-link px-3" data-table-action="template_row">Template</a>
|
||||||
</div>`
|
</div>`
|
||||||
//{{- end}}
|
//{{- end}}
|
||||||
//{{- if .LoggedUser.HasPermission "manage_folders"}}
|
//{{- if .LoggedUser.HasPermission "manage_folders"}}
|
||||||
numActions++;
|
numActions++;
|
||||||
actions+=`<div class="menu-item px-3">
|
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>
|
<a data-i18n="general.delete" href="#" class="menu-link text-danger px-3" data-table-action="delete_row">Delete</a>
|
||||||
</div>`
|
</div>`
|
||||||
//{{- end}}
|
//{{- end}}
|
||||||
if (numActions > 0){
|
if (numActions > 0){
|
||||||
|
@ -377,7 +377,7 @@ explicit grant from the SFTPGo Team (support@sftpgo.com).
|
||||||
}
|
}
|
||||||
|
|
||||||
function handleRowActions() {
|
function handleRowActions() {
|
||||||
const editButtons = document.querySelectorAll('[data-share-table-action="edit_row"]');
|
const editButtons = document.querySelectorAll('[data-table-action="edit_row"]');
|
||||||
editButtons.forEach(d => {
|
editButtons.forEach(d => {
|
||||||
let el = $(d);
|
let el = $(d);
|
||||||
el.off("click");
|
el.off("click");
|
||||||
|
@ -388,7 +388,7 @@ explicit grant from the SFTPGo Team (support@sftpgo.com).
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
const templateButtons = document.querySelectorAll('[data-share-table-action="template_row"]');
|
const templateButtons = document.querySelectorAll('[data-table-action="template_row"]');
|
||||||
templateButtons.forEach(d => {
|
templateButtons.forEach(d => {
|
||||||
let el = $(d);
|
let el = $(d);
|
||||||
el.off("click");
|
el.off("click");
|
||||||
|
@ -399,7 +399,7 @@ explicit grant from the SFTPGo Team (support@sftpgo.com).
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
const deleteButtons = document.querySelectorAll('[data-share-table-action="delete_row"]');
|
const deleteButtons = document.querySelectorAll('[data-table-action="delete_row"]');
|
||||||
deleteButtons.forEach(d => {
|
deleteButtons.forEach(d => {
|
||||||
let el = $(d);
|
let el = $(d);
|
||||||
el.off("click");
|
el.off("click");
|
||||||
|
|
|
@ -47,7 +47,7 @@ explicit grant from the SFTPGo Team (support@sftpgo.com).
|
||||||
<div class="menu-item px-3 py-2 form-check form-check-sm form-check-custom form-check-solid">
|
<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="checkColMembers" />
|
<input type="checkbox" class="form-check-input" value="" id="checkColMembers" />
|
||||||
<label class="form-check-label" for="checkColMembers">
|
<label class="form-check-label" for="checkColMembers">
|
||||||
<span data-i18n="group.members" class="text-gray-800 fs-6">Members</span>
|
<span data-i18n="general.members" class="text-gray-800 fs-6">Members</span>
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
<div class="menu-item px-3 py-2 form-check form-check-sm form-check-custom form-check-solid">
|
<div class="menu-item px-3 py-2 form-check form-check-sm form-check-custom form-check-solid">
|
||||||
|
@ -70,7 +70,7 @@ explicit grant from the SFTPGo Team (support@sftpgo.com).
|
||||||
<thead>
|
<thead>
|
||||||
<tr class="text-start text-muted fw-bold fs-6 gs-0">
|
<tr class="text-start text-muted fw-bold fs-6 gs-0">
|
||||||
<th data-i18n="general.name">Name</th>
|
<th data-i18n="general.name">Name</th>
|
||||||
<th data-i18n="group.members">Members</th>
|
<th data-i18n="general.members">Members</th>
|
||||||
<th data-i18n="general.description">Description</th>
|
<th data-i18n="general.description">Description</th>
|
||||||
<th class="min-w-100px"></th>
|
<th class="min-w-100px"></th>
|
||||||
</tr>
|
</tr>
|
||||||
|
@ -84,7 +84,7 @@ explicit grant from the SFTPGo Team (support@sftpgo.com).
|
||||||
{{- define "extra_js"}}
|
{{- define "extra_js"}}
|
||||||
<script {{- if .CSPNonce}} nonce="{{.CSPNonce}}"{{- end}} src="{{.StaticURL}}/assets/plugins/custom/datatables/datatables.bundle.js"></script>
|
<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}}>
|
<script type="text/javascript" {{- if .CSPNonce}} nonce="{{.CSPNonce}}"{{- end}}>
|
||||||
function deleteAction(username) {
|
function deleteAction(name) {
|
||||||
ModalAlert.fire({
|
ModalAlert.fire({
|
||||||
text: $.t('general.delete_confirm_generic'),
|
text: $.t('general.delete_confirm_generic'),
|
||||||
icon: "warning",
|
icon: "warning",
|
||||||
|
@ -98,7 +98,7 @@ explicit grant from the SFTPGo Team (support@sftpgo.com).
|
||||||
if (result.isConfirmed){
|
if (result.isConfirmed){
|
||||||
$('#loading_message').text("");
|
$('#loading_message').text("");
|
||||||
KTApp.showPageLoading();
|
KTApp.showPageLoading();
|
||||||
let path = '{{.GroupURL}}' + "/" + encodeURIComponent(username);
|
let path = '{{.GroupURL}}' + "/" + encodeURIComponent(name);
|
||||||
|
|
||||||
axios.delete(path, {
|
axios.delete(path, {
|
||||||
timeout: 15000,
|
timeout: 15000,
|
||||||
|
@ -191,7 +191,7 @@ explicit grant from the SFTPGo Team (support@sftpgo.com).
|
||||||
if (row.admins){
|
if (row.admins){
|
||||||
admins = row.admins.length;
|
admins = row.admins.length;
|
||||||
}
|
}
|
||||||
return $.t('group.members_summary', {users: users, admins: admins});
|
return $.t('general.members_summary', {users: users, admins: admins});
|
||||||
}
|
}
|
||||||
return "";
|
return "";
|
||||||
}
|
}
|
||||||
|
@ -227,11 +227,11 @@ explicit grant from the SFTPGo Team (support@sftpgo.com).
|
||||||
//{{- if .LoggedUser.HasPermission "manage_groups"}}
|
//{{- if .LoggedUser.HasPermission "manage_groups"}}
|
||||||
numActions++;
|
numActions++;
|
||||||
actions+=`<div class="menu-item px-3">
|
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>
|
<a data-i18n="general.edit" href="#" class="menu-link px-3" data-table-action="edit_row">Edit</a>
|
||||||
</div>`
|
</div>`
|
||||||
numActions++;
|
numActions++;
|
||||||
actions+=`<div class="menu-item px-3">
|
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>
|
<a data-i18n="general.delete" href="#" class="menu-link text-danger px-3" data-table-action="delete_row">Delete</a>
|
||||||
</div>`
|
</div>`
|
||||||
//{{- end}}
|
//{{- end}}
|
||||||
if (numActions > 0){
|
if (numActions > 0){
|
||||||
|
@ -308,7 +308,7 @@ explicit grant from the SFTPGo Team (support@sftpgo.com).
|
||||||
}
|
}
|
||||||
|
|
||||||
function handleRowActions() {
|
function handleRowActions() {
|
||||||
const editButtons = document.querySelectorAll('[data-share-table-action="edit_row"]');
|
const editButtons = document.querySelectorAll('[data-table-action="edit_row"]');
|
||||||
editButtons.forEach(d => {
|
editButtons.forEach(d => {
|
||||||
let el = $(d);
|
let el = $(d);
|
||||||
el.off("click");
|
el.off("click");
|
||||||
|
@ -319,7 +319,7 @@ explicit grant from the SFTPGo Team (support@sftpgo.com).
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
const deleteButtons = document.querySelectorAll('[data-share-table-action="delete_row"]');
|
const deleteButtons = document.querySelectorAll('[data-table-action="delete_row"]');
|
||||||
deleteButtons.forEach(d => {
|
deleteButtons.forEach(d => {
|
||||||
let el = $(d);
|
let el = $(d);
|
||||||
el.off("click");
|
el.off("click");
|
||||||
|
|
|
@ -1,229 +1,347 @@
|
||||||
<!--
|
<!--
|
||||||
Copyright (C) 2019 Nicola Murino
|
Copyright (C) 2024 Nicola Murino
|
||||||
|
|
||||||
This program is free software: you can redistribute it and/or modify
|
This WebUI uses the KeenThemes Mega Bundle, a proprietary theme:
|
||||||
it under the terms of the GNU Affero General Public License as published
|
|
||||||
by the Free Software Foundation, version 3.
|
|
||||||
|
|
||||||
This program is distributed in the hope that it will be useful,
|
https://keenthemes.com/products/templates-mega-bundle
|
||||||
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.
|
|
||||||
|
|
||||||
You should have received a copy of the GNU Affero General Public License
|
KeenThemes HTML/CSS/JS components are allowed for use only within the
|
||||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
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" .}}
|
{{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"}}
|
{{- define "page_body"}}
|
||||||
<link href="{{.StaticURL}}/vendor/datatables/dataTables.bootstrap4.min.css" rel="stylesheet">
|
{{- template "errmsg" ""}}
|
||||||
<link href="{{.StaticURL}}/vendor/datatables/buttons.bootstrap4.min.css" rel="stylesheet">
|
<div class="card shadow-sm">
|
||||||
<link href="{{.StaticURL}}/vendor/datatables/fixedHeader.bootstrap4.min.css" rel="stylesheet">
|
<div class="card-header bg-light">
|
||||||
<link href="{{.StaticURL}}/vendor/datatables/responsive.bootstrap4.min.css" rel="stylesheet">
|
<h3 data-i18n="role.view_manage" class="card-title section-title">View and manage roles</h3>
|
||||||
<link href="{{.StaticURL}}/vendor/datatables/select.bootstrap4.min.css" rel="stylesheet">
|
|
||||||
{{end}}
|
|
||||||
|
|
||||||
{{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>
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
<script type="text/javascript">
|
|
||||||
function dismissErrorMsg(){
|
|
||||||
$('#errorMsg').hide();
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<div class="card shadow mb-4">
|
|
||||||
<div class="card-header py-3">
|
|
||||||
<h6 class="m-0 font-weight-bold text-primary">View and manage roles</h6>
|
|
||||||
</div>
|
</div>
|
||||||
<div class="card-body">
|
<div id="card_body" class="card-body">
|
||||||
<div class="table-responsive">
|
<div id="loader" class="align-items-center text-center my-10">
|
||||||
<table class="table table-hover nowrap" id="dataTable" width="100%" cellspacing="0">
|
<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>
|
||||||
|
|
||||||
|
<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="checkColMembers" />
|
||||||
|
<label class="form-check-label" for="checkColMembers">
|
||||||
|
<span data-i18n="general.members" class="text-gray-800 fs-6">Members</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_roles"}}
|
||||||
|
<a href="{{.RoleURL}}" 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>
|
||||||
|
|
||||||
|
<table id="dataTable" class="table align-middle table-row-dashed fs-6 gy-5">
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr class="text-start text-muted fw-bold fs-6 gs-0">
|
||||||
<th>Name</th>
|
<th data-i18n="general.name">Name</th>
|
||||||
<th>Description</th>
|
<th data-i18n="general.members">Members</th>
|
||||||
<th>Members</th>
|
<th data-i18n="general.description">Description</th>
|
||||||
|
<th class="min-w-100px"></th>
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody id="table_body" class="text-gray-800 fw-semibold"></tbody>
|
||||||
{{range .Roles}}
|
|
||||||
<tr>
|
|
||||||
<td>{{.Name}}</td>
|
|
||||||
<td>{{.Description}}</td>
|
|
||||||
<td>{{.GetMembersAsString}}</td>
|
|
||||||
</tr>
|
|
||||||
{{end}}
|
|
||||||
</tbody>
|
|
||||||
</table>
|
</table>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{{end}}
|
{{- end}}
|
||||||
|
|
||||||
{{define "dialog"}}
|
{{- define "extra_js"}}
|
||||||
<div class="modal fade" id="deleteModal" tabindex="-1" role="dialog" aria-labelledby="deleteModalLabel"
|
<script {{- if .CSPNonce}} nonce="{{.CSPNonce}}"{{- end}} src="{{.StaticURL}}/assets/plugins/custom/datatables/datatables.bundle.js"></script>
|
||||||
aria-hidden="true">
|
<script type="text/javascript" {{- if .CSPNonce}} nonce="{{.CSPNonce}}"{{- end}}>
|
||||||
<div class="modal-dialog" role="document">
|
function deleteAction(name) {
|
||||||
<div class="modal-content">
|
ModalAlert.fire({
|
||||||
<div class="modal-header">
|
text: $.t('general.delete_confirm_generic'),
|
||||||
<h5 class="modal-title" id="deleteModalLabel">
|
icon: "warning",
|
||||||
Confirmation required
|
confirmButtonText: $.t('general.delete_confirm_btn'),
|
||||||
</h5>
|
cancelButtonText: $.t('general.cancel'),
|
||||||
<button class="close" type="button" data-dismiss="modal" aria-label="Close">
|
customClass: {
|
||||||
<span aria-hidden="true">×</span>
|
confirmButton: "btn btn-danger",
|
||||||
</button>
|
cancelButton: 'btn btn-secondary'
|
||||||
</div>
|
}
|
||||||
<div class="modal-body">Do you want to delete the selected role? It is not possible to remove a role that does contain administrators</div>
|
}).then((result) => {
|
||||||
<div class="modal-footer">
|
if (result.isConfirmed){
|
||||||
<button class="btn btn-secondary" type="button" data-dismiss="modal">
|
$('#loading_message').text("");
|
||||||
Cancel
|
KTApp.showPageLoading();
|
||||||
</button>
|
let path = '{{.RoleURL}}' + "/" + encodeURIComponent(name);
|
||||||
<a class="btn btn-warning" href="#" onclick="deleteAction()">
|
|
||||||
Delete
|
|
||||||
</a>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
{{end}}
|
|
||||||
|
|
||||||
{{define "extra_js"}}
|
axios.delete(path, {
|
||||||
<script src="{{.StaticURL}}/vendor/datatables/jquery.dataTables.min.js"></script>
|
timeout: 15000,
|
||||||
<script src="{{.StaticURL}}/vendor/datatables/dataTables.bootstrap4.min.js"></script>
|
headers: {
|
||||||
<script src="{{.StaticURL}}/vendor/datatables/dataTables.buttons.min.js"></script>
|
'X-CSRF-TOKEN': '{{.CSRFToken}}'
|
||||||
<script src="{{.StaticURL}}/vendor/datatables/buttons.bootstrap4.min.js"></script>
|
},
|
||||||
<script src="{{.StaticURL}}/vendor/datatables/buttons.colVis.min.js"></script>
|
validateStatus: function (status) {
|
||||||
<script src="{{.StaticURL}}/vendor/datatables/dataTables.fixedHeader.min.js"></script>
|
return status == 200;
|
||||||
<script src="{{.StaticURL}}/vendor/datatables/dataTables.responsive.min.js"></script>
|
}
|
||||||
<script src="{{.StaticURL}}/vendor/datatables/responsive.bootstrap4.min.js"></script>
|
}).then(function(response){
|
||||||
<script src="{{.StaticURL}}/vendor/datatables/dataTables.select.min.js"></script>
|
location.reload();
|
||||||
<script src="{{.StaticURL}}/vendor/datatables/ellipsis.js"></script>
|
}).catch(function(error){
|
||||||
<script type="text/javascript">
|
KTApp.hidePageLoading();
|
||||||
|
let errorMessage;
|
||||||
function deleteAction() {
|
if (error && error.response) {
|
||||||
let table = $('#dataTable').DataTable();
|
switch (error.response.status) {
|
||||||
table.button('delete:name').enable(false);
|
case 403:
|
||||||
let roleName = table.row({ selected: true }).data()[0];
|
errorMessage = "general.delete_error_403";
|
||||||
let path = '{{.RoleURL}}' + "/" + fixedEncodeURIComponent(roleName);
|
break;
|
||||||
$('#deleteModal').modal('hide');
|
case 404:
|
||||||
$('#errorMsg').hide();
|
errorMessage = "general.delete_error_404";
|
||||||
|
break;
|
||||||
$.ajax({
|
|
||||||
url: path,
|
|
||||||
type: 'DELETE',
|
|
||||||
dataType: 'json',
|
|
||||||
headers: {'X-CSRF-TOKEN' : '{{.CSRFToken}}'},
|
|
||||||
timeout: 15000,
|
|
||||||
success: function (result) {
|
|
||||||
window.location.href = '{{.RolesURL}}';
|
|
||||||
},
|
|
||||||
error: function ($xhr, textStatus, errorThrown) {
|
|
||||||
var txt = "Unable to delete the selected role";
|
|
||||||
if ($xhr) {
|
|
||||||
var json = $xhr.responseJSON;
|
|
||||||
if (json) {
|
|
||||||
if (json.message){
|
|
||||||
txt += ": " + json.message;
|
|
||||||
} else {
|
|
||||||
txt += ": " + json.error;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
if (!errorMessage){
|
||||||
$('#errorTxt').text(txt);
|
errorMessage = "general.delete_error_generic";
|
||||||
$('#errorMsg').show();
|
}
|
||||||
|
ModalAlert.fire({
|
||||||
|
text: $.t(errorMessage),
|
||||||
|
icon: "warning",
|
||||||
|
confirmButtonText: $.t('general.ok'),
|
||||||
|
customClass: {
|
||||||
|
confirmButton: "btn btn-primary"
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
$(document).ready(function () {
|
var datatable = function(){
|
||||||
$.fn.dataTable.ext.buttons.add = {
|
var dt;
|
||||||
text: '<i class="fas fa-plus"></i>',
|
|
||||||
name: 'add',
|
|
||||||
titleAttr: "Add",
|
|
||||||
action: function (e, dt, node, config) {
|
|
||||||
window.location.href = '{{.RoleURL}}';
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
$.fn.dataTable.ext.buttons.edit = {
|
var initDatatable = function () {
|
||||||
text: '<i class="fas fa-pen"></i>',
|
$('#errorMsg').addClass("d-none");
|
||||||
name: 'edit',
|
dt = $('#dataTable').DataTable({
|
||||||
titleAttr: "Edit",
|
ajax: {
|
||||||
action: function (e, dt, node, config) {
|
url: "{{.RolesURL}}/json",
|
||||||
let roleName = table.row({ selected: true }).data()[0];
|
dataSrc: "",
|
||||||
let path = '{{.RoleURL}}' + "/" + fixedEncodeURIComponent(roleName);
|
error: function ($xhr, textStatus, errorThrown) {
|
||||||
window.location.href = path;
|
$(".dataTables_processing").hide();
|
||||||
},
|
let txt = "";
|
||||||
enabled: false
|
if ($xhr) {
|
||||||
};
|
let json = $xhr.responseJSON;
|
||||||
|
if (json) {
|
||||||
|
if (json.message){
|
||||||
|
txt = json.message;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!txt){
|
||||||
|
txt = "general.error500";
|
||||||
|
}
|
||||||
|
setI18NData($('#errorTxt'), txt);
|
||||||
|
$('#errorMsg').removeClass("d-none");
|
||||||
|
}
|
||||||
|
},
|
||||||
|
columns: [
|
||||||
|
{
|
||||||
|
data: "name",
|
||||||
|
render: function(data, type, row) {
|
||||||
|
if (type === 'display') {
|
||||||
|
return escapeHTML(data);
|
||||||
|
}
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
data: "users",
|
||||||
|
defaultContent: "",
|
||||||
|
searchable: false,
|
||||||
|
orderable: false,
|
||||||
|
render: function(data, type, row) {
|
||||||
|
if (type === 'display') {
|
||||||
|
let users = 0;
|
||||||
|
if (row.users){
|
||||||
|
users = row.users.length;
|
||||||
|
}
|
||||||
|
let admins = 0;
|
||||||
|
if (row.admins){
|
||||||
|
admins = row.admins.length;
|
||||||
|
}
|
||||||
|
return $.t('general.members_summary', {users: users, admins: admins});
|
||||||
|
}
|
||||||
|
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">`;
|
||||||
|
|
||||||
$.fn.dataTable.ext.buttons.delete = {
|
//{{- if .LoggedUser.HasPermission "manage_roles"}}
|
||||||
text: '<i class="fas fa-trash"></i>',
|
numActions++;
|
||||||
name: 'delete',
|
actions+=`<div class="menu-item px-3">
|
||||||
titleAttr: "Delete",
|
<a data-i18n="general.edit" href="#" class="menu-link px-3" data-table-action="edit_row">Edit</a>
|
||||||
action: function (e, dt, node, config) {
|
</div>`
|
||||||
$('#deleteModal').modal('show');
|
numActions++;
|
||||||
},
|
actions+=`<div class="menu-item px-3">
|
||||||
enabled: false
|
<a data-i18n="general.delete" href="#" class="menu-link text-danger px-3" data-table-action="delete_row">Delete</a>
|
||||||
};
|
</div>`
|
||||||
|
//{{- end}}
|
||||||
var table = $('#dataTable').DataTable({
|
if (numActions > 0){
|
||||||
"select": {
|
actions+=`</div>`;
|
||||||
"style": "single",
|
return actions;
|
||||||
"blurable": true
|
}
|
||||||
},
|
}
|
||||||
"stateSave": true,
|
return "";
|
||||||
"stateDuration": 0,
|
}
|
||||||
"buttons": [
|
},
|
||||||
{
|
],
|
||||||
"text": "Column visibility",
|
deferRender: true,
|
||||||
"extend": "colvis",
|
stateSave: true,
|
||||||
"columns": ":not(.noVis)"
|
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();
|
||||||
}
|
}
|
||||||
],
|
});
|
||||||
"columnDefs": [
|
|
||||||
{
|
|
||||||
"targets": [0],
|
|
||||||
"className": "noVis"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"targets": [2],
|
|
||||||
"render": $.fn.dataTable.render.ellipsis(100, true)
|
|
||||||
},
|
|
||||||
],
|
|
||||||
"scrollX": false,
|
|
||||||
"scrollY": false,
|
|
||||||
"responsive": true,
|
|
||||||
"language": {
|
|
||||||
"emptyTable": "No role defined"
|
|
||||||
},
|
|
||||||
"order": [[0, 'asc']]
|
|
||||||
});
|
|
||||||
|
|
||||||
new $.fn.dataTable.FixedHeader( table );
|
dt.on('draw', drawAction);
|
||||||
|
dt.on('column-reorder', function(e, settings, details){
|
||||||
|
drawAction();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
{{if .LoggedAdmin.HasPermission "manage_roles"}}
|
function drawAction() {
|
||||||
table.button().add(0,'delete');
|
KTMenu.createInstances();
|
||||||
table.button().add(0,'edit');
|
handleRowActions();
|
||||||
table.button().add(0,'add');
|
$('#table_body').localize();
|
||||||
|
}
|
||||||
|
|
||||||
table.buttons().container().appendTo('.col-md-6:eq(0)', table.table().container());
|
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');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
table.on('select deselect', function () {
|
var handleDatatableActions = function () {
|
||||||
var selectedRows = table.rows({ selected: true }).count();
|
const filterSearch = $(document.querySelector('[data-table-filter="search"]'));
|
||||||
table.button('delete:name').enable(selectedRows == 1);
|
filterSearch.off("keyup");
|
||||||
table.button('edit:name').enable(selectedRows == 1);
|
filterSearch.on('keyup', function (e) {
|
||||||
});
|
dt.rows().deselect();
|
||||||
{{end}}
|
dt.search(e.target.value, true, false).draw();
|
||||||
|
});
|
||||||
|
handleColVisibilityCheckbox($('#checkColMembers'), 1);
|
||||||
|
handleColVisibilityCheckbox($('#checkColDesc'), 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleRowActions() {
|
||||||
|
const editButtons = document.querySelectorAll('[data-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('{{.RoleURL}}' + "/" + encodeURIComponent(rowData['name']));
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
const deleteButtons = document.querySelectorAll('[data-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>
|
</script>
|
||||||
{{end}}
|
{{- end}}
|
|
@ -471,25 +471,25 @@ explicit grant from the SFTPGo Team (support@sftpgo.com).
|
||||||
//{{- if .LoggedUser.HasPermission "edit_users"}}
|
//{{- if .LoggedUser.HasPermission "edit_users"}}
|
||||||
numActions++;
|
numActions++;
|
||||||
actions+=`<div class="menu-item px-3">
|
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>
|
<a data-i18n="general.edit" href="#" class="menu-link px-3" data-table-action="edit_row">Edit</a>
|
||||||
</div>`
|
</div>`
|
||||||
//{{- end}}
|
//{{- end}}
|
||||||
//{{- if .LoggedUser.HasPermission "manage_system"}}
|
//{{- if .LoggedUser.HasPermission "manage_system"}}
|
||||||
numActions++;
|
numActions++;
|
||||||
actions+=`<div class="menu-item px-3">
|
actions+=`<div class="menu-item px-3">
|
||||||
<a data-i18n="general.template" href="#" class="menu-link px-3" data-share-table-action="template_row">Template</a>
|
<a data-i18n="general.template" href="#" class="menu-link px-3" data-table-action="template_row">Template</a>
|
||||||
</div>`
|
</div>`
|
||||||
//{{- end}}
|
//{{- end}}
|
||||||
//{{- if .LoggedUser.HasPermission "quota_scans"}}
|
//{{- if .LoggedUser.HasPermission "quota_scans"}}
|
||||||
numActions++;
|
numActions++;
|
||||||
actions+=`<div class="menu-item px-3">
|
actions+=`<div class="menu-item px-3">
|
||||||
<a data-i18n="general.quota_scan" href="#" class="menu-link px-3" data-share-table-action="quota_scan_row">Quota scan</a>
|
<a data-i18n="general.quota_scan" href="#" class="menu-link px-3" data-table-action="quota_scan_row">Quota scan</a>
|
||||||
</div>`
|
</div>`
|
||||||
//{{- end}}
|
//{{- end}}
|
||||||
//{{- if .LoggedUser.HasPermission "del_users"}}
|
//{{- if .LoggedUser.HasPermission "del_users"}}
|
||||||
numActions++;
|
numActions++;
|
||||||
actions+=`<div class="menu-item px-3">
|
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>
|
<a data-i18n="general.delete" href="#" class="menu-link text-danger px-3" data-table-action="delete_row">Delete</a>
|
||||||
</div>`
|
</div>`
|
||||||
//{{- end}}
|
//{{- end}}
|
||||||
if (numActions > 0){
|
if (numActions > 0){
|
||||||
|
@ -573,7 +573,7 @@ explicit grant from the SFTPGo Team (support@sftpgo.com).
|
||||||
}
|
}
|
||||||
|
|
||||||
function handleRowActions() {
|
function handleRowActions() {
|
||||||
const editButtons = document.querySelectorAll('[data-share-table-action="edit_row"]');
|
const editButtons = document.querySelectorAll('[data-table-action="edit_row"]');
|
||||||
editButtons.forEach(d => {
|
editButtons.forEach(d => {
|
||||||
let el = $(d);
|
let el = $(d);
|
||||||
el.off("click");
|
el.off("click");
|
||||||
|
@ -584,7 +584,7 @@ explicit grant from the SFTPGo Team (support@sftpgo.com).
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
const templateButtons = document.querySelectorAll('[data-share-table-action="template_row"]');
|
const templateButtons = document.querySelectorAll('[data-table-action="template_row"]');
|
||||||
templateButtons.forEach(d => {
|
templateButtons.forEach(d => {
|
||||||
let el = $(d);
|
let el = $(d);
|
||||||
el.off("click");
|
el.off("click");
|
||||||
|
@ -595,7 +595,7 @@ explicit grant from the SFTPGo Team (support@sftpgo.com).
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
const quotaScanButtons = document.querySelectorAll('[data-share-table-action="quota_scan_row"]');
|
const quotaScanButtons = document.querySelectorAll('[data-table-action="quota_scan_row"]');
|
||||||
quotaScanButtons.forEach(d => {
|
quotaScanButtons.forEach(d => {
|
||||||
let el = $(d);
|
let el = $(d);
|
||||||
el.off("click");
|
el.off("click");
|
||||||
|
@ -606,7 +606,7 @@ explicit grant from the SFTPGo Team (support@sftpgo.com).
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
const deleteButtons = document.querySelectorAll('[data-share-table-action="delete_row"]');
|
const deleteButtons = document.querySelectorAll('[data-table-action="delete_row"]');
|
||||||
deleteButtons.forEach(d => {
|
deleteButtons.forEach(d => {
|
||||||
let el = $(d);
|
let el = $(d);
|
||||||
el.off("click");
|
el.off("click");
|
||||||
|
|
|
@ -336,7 +336,7 @@ explicit grant from the SFTPGo Team (support@sftpgo.com).
|
||||||
if (type === 'display') {
|
if (type === 'display') {
|
||||||
return `<div class="d-flex justify-content-end">
|
return `<div class="d-flex justify-content-end">
|
||||||
<div class="ms-2">
|
<div class="ms-2">
|
||||||
<a href="#" class="btn btn-sm btn-icon btn-light btn-active-light-primary" data-share-table-action="show_link">
|
<a href="#" class="btn btn-sm btn-icon btn-light btn-active-light-primary" data-table-action="show_link">
|
||||||
<i class="ki-duotone ki-fasten fs-5 m-0">
|
<i class="ki-duotone ki-fasten fs-5 m-0">
|
||||||
<span class="path1"></span>
|
<span class="path1"></span>
|
||||||
<span class="path2"></span>
|
<span class="path2"></span>
|
||||||
|
@ -355,10 +355,10 @@ explicit grant from the SFTPGo Team (support@sftpgo.com).
|
||||||
</button>
|
</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-150px py-4" data-kt-menu="true">
|
<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-150px py-4" data-kt-menu="true">
|
||||||
<div class="menu-item px-3">
|
<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>
|
<a data-i18n="general.edit" href="#" class="menu-link px-3" data-table-action="edit_row">Edit</a>
|
||||||
</div>
|
</div>
|
||||||
<div class="menu-item px-3">
|
<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>
|
<a data-i18n="general.delete" href="#" class="menu-link text-danger px-3" data-table-action="delete_row">Delete</a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -415,7 +415,7 @@ explicit grant from the SFTPGo Team (support@sftpgo.com).
|
||||||
}
|
}
|
||||||
|
|
||||||
function handleRowActions() {
|
function handleRowActions() {
|
||||||
const editButtons = document.querySelectorAll('[data-share-table-action="edit_row"]');
|
const editButtons = document.querySelectorAll('[data-table-action="edit_row"]');
|
||||||
|
|
||||||
editButtons.forEach(d => {
|
editButtons.forEach(d => {
|
||||||
let el = $(d);
|
let el = $(d);
|
||||||
|
@ -427,7 +427,7 @@ explicit grant from the SFTPGo Team (support@sftpgo.com).
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
const deleteButtons = document.querySelectorAll('[data-share-table-action="delete_row"]');
|
const deleteButtons = document.querySelectorAll('[data-table-action="delete_row"]');
|
||||||
|
|
||||||
deleteButtons.forEach(d => {
|
deleteButtons.forEach(d => {
|
||||||
let el = $(d);
|
let el = $(d);
|
||||||
|
@ -439,7 +439,7 @@ explicit grant from the SFTPGo Team (support@sftpgo.com).
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
const showLinkButtons = document.querySelectorAll('[data-share-table-action="show_link"]');
|
const showLinkButtons = document.querySelectorAll('[data-table-action="show_link"]');
|
||||||
|
|
||||||
showLinkButtons.forEach(d => {
|
showLinkButtons.forEach(d => {
|
||||||
let el = $(d);
|
let el = $(d);
|
||||||
|
|
Loading…
Reference in a new issue