add event manager

auto backup removed from setting. You can now schedule backups with
the event manager

Fixes #762

Signed-off-by: Nicola Murino <nicola.murino@gmail.com>
This commit is contained in:
Nicola Murino
2022-07-11 08:17:36 +02:00
parent e46051299f
commit 1b8f94c08f
54 changed files with 10238 additions and 869 deletions

View File

@@ -74,6 +74,22 @@
</li>
{{end}}
{{ if .LoggedAdmin.HasPermission "manage_event_rules"}}
<li class="nav-item {{if .IsEventManagerPage}}active{{end}}">
<a class="nav-link {{if not .IsEventManagerPage}}collapsed{{end}}" href="#" data-toggle="collapse" data-target="#collapseEventManager"
aria-expanded="true" aria-controls="collapseEventManager">
<i class="fas fa-calendar-alt"></i>
<span>Event Manager</span>
</a>
<div id="collapseEventManager" class="collapse {{if .IsEventManagerPage}}show{{end}}" aria-labelledby="headingEventManager" data-parent="#accordionSidebar">
<div class="bg-white py-2 collapse-inner rounded">
<a class="collapse-item {{if eq .CurrentURL .EventRulesURL}}active{{end}}" href="{{.EventRulesURL}}">{{.EventRulesTitle}}</a>
<a class="collapse-item {{if eq .CurrentURL .EventActionsURL}}active{{end}}" href="{{.EventActionsURL}}">{{.EventActionsTitle}}</a>
</div>
</div>
</li>
{{end}}
{{ if .LoggedAdmin.HasPermission "view_conns"}}
<li class="nav-item {{if eq .CurrentURL .ConnectionsURL}}active{{end}}">
<a class="nav-link" href="{{.ConnectionsURL}}">
@@ -142,6 +158,7 @@
<!-- Topbar Navbar -->
<ul class="navbar-nav ml-auto">
{{block "additionalnavitems" .}}{{end}}
<!-- Nav Item - User Information -->
<li class="nav-item dropdown no-arrow">

View File

@@ -0,0 +1,511 @@
{{template "base" .}}
{{define "title"}}{{.Title}}{{end}}
{{define "extra_css"}}
<link href="{{.StaticURL}}/vendor/bootstrap-select/css/bootstrap-select.min.css" rel="stylesheet">
{{end}}
{{define "additionalnavitems"}}
<li class="nav-item dropdown no-arrow mx-1">
<a class="nav-link dropdown-toggle" href="#" id="editorDropdown" role="button"
data-toggle="modal" data-target="#infoModal">
<i class="fas fa-info fa-fw"></i>
</a>
</li>
<div class="topbar-divider d-none d-sm-block"></div>
{{end}}
{{define "page_body"}}
<div class="card shadow mb-4">
<div class="card-header py-3">
<h6 class="m-0 font-weight-bold text-primary">{{.Title}}</h6>
</div>
<div class="card-body">
{{if .Error}}
<div class="card mb-4 border-left-warning">
<div class="card-body text-form-error">{{.Error}}</div>
</div>
{{end}}
<form id="eventaction_form" action="{{.CurrentURL}}" method="POST" autocomplete="off">
<div class="form-group row">
<label for="idName" class="col-sm-2 col-form-label">Name</label>
<div class="col-sm-10">
<input type="text" class="form-control" id="idName" name="name" placeholder=""
value="{{.Action.Name}}" maxlength="255" autocomplete="nope" required {{if eq .Mode 2}}readonly{{end}}>
</div>
</div>
<div class="form-group row">
<label for="idDescription" class="col-sm-2 col-form-label">Description</label>
<div class="col-sm-10">
<input type="text" class="form-control" id="idDescription" name="description" placeholder=""
value="{{.Action.Description}}" maxlength="255" aria-describedby="descriptionHelpBlock">
<small id="descriptionHelpBlock" class="form-text text-muted">
Optional description
</small>
</div>
</div>
<div class="form-group row">
<label for="idType" class="col-sm-2 col-form-label">Type</label>
<div class="col-sm-10">
<select class="form-control selectpicker" id="idType" name="type" onchange="onTypeChanged(this.value)">
{{- range .ActionTypes}}
<option value="{{.Value}}" {{if eq $.Action.Type .Value }}selected{{end}}>{{.Name}}</option>
{{- end}}
</select>
</div>
</div>
<div class="form-group row action-type action-http">
<label for="idHTTPEndpoint" class="col-sm-2 col-form-label">Endpoint</label>
<div class="col-sm-10">
<input type="text" class="form-control" id="idHTTPEndpoint" name="http_endpoint" placeholder=""
aria-describedby="HTTPEndpointHelpBlock" value="{{.Action.Options.HTTPConfig.Endpoint}}">
<small id="HTTPEndpointHelpBlock" class="form-text text-muted">
Endpoint URL, i.e https://host:port/path
</small>
</div>
</div>
<div class="form-group row action-type action-http">
<label for="idHTTPUsername" class="col-sm-2 col-form-label">Username</label>
<div class="col-sm-3">
<input type="text" class="form-control" id="idHTTPUsername" name="http_username" placeholder=""
aria-describedby="usernameHelpBlock" value="{{.Action.Options.HTTPConfig.Username}}" maxlength="255">
<small id="httpBodyHelpBlock" class="form-text text-muted">
Placeholders are supported
</small>
</div>
<div class="col-sm-2"></div>
<label for="idHTTPPassword" class="col-sm-2 col-form-label">Password</label>
<div class="col-sm-3">
<input type="password" class="form-control" id="idHTTPPassword" name="http_password" placeholder=""
value="{{if .Action.Options.HTTPConfig.Password.IsEncrypted}}{{.RedactedSecret}}{{else}}{{.Action.Options.HTTPConfig.Password.GetPayload}}{{end}}">
</div>
</div>
<div class="card bg-light mb-3 action-type action-http">
<div class="card-header">
<b>HTTP headers</b>
</div>
<div class="card-body">
<h6 class="card-title mb-4">Placeholders are supported in header values.</h6>
<div class="form-group row">
<div class="col-md-12 form_field_http_headers_outer">
{{range $idx, $val := .Action.Options.HTTPConfig.Headers}}
<div class="row form_field_http_headers_outer_row">
<div class="form-group col-md-5">
<input type="text" class="form-control" id="idHTTPHeaderKey{{$idx}}" name="http_header_key{{$idx}}" placeholder="Enter key" value="{{$val.Key}}">
</div>
<div class="form-group col-md-5">
<input type="text" class="form-control" id="idHTTPHeaderVal{{$idx}}" name="http_header_val{{$idx}}" placeholder="Enter value" value="{{$val.Value}}">
</div>
<div class="form-group col-md-1"></div>
<div class="form-group col-md-1">
<button class="btn btn-circle btn-danger remove_http_header_btn_frm_field">
<i class="fas fa-trash"></i>
</button>
</div>
</div>
{{else}}
<div class="row form_field_http_headers_outer_row">
<div class="form-group col-md-5">
<input type="text" class="form-control" id="idHTTPHeaderKey0" name="http_header_key0" placeholder="Enter key" value="">
</div>
<div class="form-group col-md-5">
<input type="text" class="form-control" id="idHTTPHeaderVal0" name="http_header_val0" placeholder="Enter value" value="">
</div>
<div class="form-group col-md-1"></div>
<div class="form-group col-md-1">
<button class="btn btn-circle btn-danger remove_http_header_btn_frm_field">
<i class="fas fa-trash"></i>
</button>
</div>
</div>
{{end}}
</div>
</div>
<div class="row mx-1">
<button type="button" class="btn btn-secondary add_new_header_field_btn">
<i class="fas fa-plus"></i> Add new header
</button>
</div>
</div>
</div>
<div class="card bg-light mb-3 action-type action-http">
<div class="card-header">
<b>Query parameters</b>
</div>
<div class="card-body">
<h6 class="card-title mb-4">Placeholders are supported in query values.</h6>
<div class="form-group row">
<div class="col-md-12 form_field_http_query_outer">
{{range $idx, $val := .Action.Options.HTTPConfig.QueryParameters}}
<div class="row form_field_http_query_outer_row">
<div class="form-group col-md-5">
<input type="text" class="form-control" id="idHTTPQueryKey{{$idx}}" name="http_query_key{{$idx}}" placeholder="Enter key" value="{{$val.Key}}">
</div>
<div class="form-group col-md-5">
<input type="text" class="form-control" id="idHTTPQueryVal{{$idx}}" name="http_query_val{{$idx}}" placeholder="Enter value" value="{{$val.Value}}">
</div>
<div class="form-group col-md-1"></div>
<div class="form-group col-md-1">
<button class="btn btn-circle btn-danger remove_http_query_btn_frm_field">
<i class="fas fa-trash"></i>
</button>
</div>
</div>
{{else}}
<div class="row form_field_http_query_outer_row">
<div class="form-group col-md-5">
<input type="text" class="form-control" id="idHTTPQueryKey0" name="http_query_key0" placeholder="Enter key" value="">
</div>
<div class="form-group col-md-5">
<input type="text" class="form-control" id="idHTTPQueryVal0" name="http_query_val0" placeholder="Enter value" value="">
</div>
<div class="form-group col-md-1"></div>
<div class="form-group col-md-1">
<button class="btn btn-circle btn-danger remove_http_query_btn_frm_field">
<i class="fas fa-trash"></i>
</button>
</div>
</div>
{{end}}
</div>
</div>
<div class="row mx-1">
<button type="button" class="btn btn-secondary add_new_query_field_btn">
<i class="fas fa-plus"></i> Add new parameter
</button>
</div>
</div>
</div>
<div class="form-group row action-type action-http">
<label for="idHTTPMethod" class="col-sm-2 col-form-label">Method</label>
<div class="col-sm-10">
<select class="form-control selectpicker" id="idHTTPMethod" name="http_method">
{{- range .HTTPMethods}}
<option value="{{.}}" {{if eq $.Action.Options.HTTPConfig.Method . }}selected{{end}}>{{.}}</option>
{{- end}}
</select>
</div>
</div>
<div class="form-group row action-type action-http">
<label for="idHTTPBody" class="col-sm-2 col-form-label">Body</label>
<div class="col-sm-10">
<textarea class="form-control" id="idHTTPBody" name="http_body" rows="4" placeholder=""
aria-describedby="httpBodyHelpBlock">{{.Action.Options.HTTPConfig.Body}}</textarea>
<small id="httpBodyHelpBlock" class="form-text text-muted">
Placeholders are supported
</small>
</div>
</div>
<div class="form-group row action-type action-http">
<label for="idHTTPTimeout" class="col-sm-2 col-form-label">Timeout</label>
<div class="col-sm-10">
<input type="number" min="1" max="120" class="form-control" id="idHTTPTimeout" name="http_timeout" placeholder=""
value="{{.Action.Options.HTTPConfig.Timeout}}">
</div>
</div>
<div class="form-group action-type action-http">
<div class="form-check">
<input type="checkbox" class="form-check-input" id="idHTTPSkipTLSVerify" name="http_skip_tls_verify"
{{if .Action.Options.HTTPConfig.SkipTLSVerify}}checked{{end}}>
<label for="idHTTPSkipTLSVerify" class="form-check-label">Skip TLS verify</label>
</div>
</div>
<div class="form-group row action-type action-cmd">
<label for="idCmdPath" class="col-sm-2 col-form-label">Command</label>
<div class="col-sm-10">
<input type="text" class="form-control" id="idCmdPath" name="cmd_path" placeholder=""
aria-describedby="CmdPathHelpBlock" value="{{.Action.Options.CmdConfig.Cmd}}">
<small id="CmdPathHelpBlock" class="form-text text-muted">
Absolute path of the command to execute
</small>
</div>
</div>
<div class="form-group row action-type action-cmd">
<label for="idCmdTimeout" class="col-sm-2 col-form-label">Timeout</label>
<div class="col-sm-10">
<input type="number" min="1" max="120" class="form-control" id="idCmdTimeout" name="cmd_timeout" placeholder=""
value="{{.Action.Options.CmdConfig.Timeout}}">
</div>
</div>
<div class="card bg-light mb-3 action-type action-cmd">
<div class="card-header">
<b>Environment variables</b>
</div>
<div class="card-body">
<h6 class="card-title mb-4">Placeholders are supported in values.</h6>
<div class="form-group row">
<div class="col-md-12 form_field_cmd_env_outer">
{{range $idx, $val := .Action.Options.CmdConfig.EnvVars}}
<div class="row form_field_cmd_env_outer_row">
<div class="form-group col-md-5">
<input type="text" class="form-control" id="idCMDEnvKey{{$idx}}" name="cmd_env_key{{$idx}}" placeholder="Enter key" value="{{$val.Key}}">
</div>
<div class="form-group col-md-5">
<input type="text" class="form-control" id="idCMDEnvVal{{$idx}}" name="cmd_env_val{{$idx}}" placeholder="Enter value" value="{{$val.Value}}">
</div>
<div class="form-group col-md-1"></div>
<div class="form-group col-md-1">
<button class="btn btn-circle btn-danger remove_cmd_env_btn_frm_field">
<i class="fas fa-trash"></i>
</button>
</div>
</div>
{{else}}
<div class="row form_field_cmd_env_outer_row">
<div class="form-group col-md-5">
<input type="text" class="form-control" id="idCMDEnvKey0" name="cmd_env_key0" placeholder="Enter key" value="">
</div>
<div class="form-group col-md-5">
<input type="text" class="form-control" id="idCMDEnvVal0" name="cmd_env_val0" placeholder="Enter value" value="">
</div>
<div class="form-group col-md-1"></div>
<div class="form-group col-md-1">
<button class="btn btn-circle btn-danger remove_cmd_env_btn_frm_field">
<i class="fas fa-trash"></i>
</button>
</div>
</div>
{{end}}
</div>
</div>
<div class="row mx-1">
<button type="button" class="btn btn-secondary add_new_cmd_env_field_btn">
<i class="fas fa-plus"></i> Add environment variable
</button>
</div>
</div>
</div>
<div class="form-group row action-type action-smtp">
<label for="idEmailRecipients" class="col-sm-2 col-form-label">Email recipients</label>
<div class="col-sm-10">
<textarea class="form-control" id="idEmailRecipients" name="email_recipients" rows="2" placeholder=""
aria-describedby="smtpRecipientsHelpBlock">{{.Action.Options.EmailConfig.GetRecipientsAsString}}</textarea>
<small id="smtpRecipientsHelpBlock" class="form-text text-muted">
Comma separated email recipients
</small>
</div>
</div>
<div class="form-group row action-type action-smtp">
<label for="idEmailSubject" class="col-sm-2 col-form-label">Email subject</label>
<div class="col-sm-10">
<input type="text" class="form-control" id="idEmailSubject" name="email_subject" placeholder=""
value="{{.Action.Options.EmailConfig.Subject}}" maxlength="255" aria-describedby="emailSubjectHelpBlock">
<small id="emailSubjectHelpBlock" class="form-text text-muted">
Placeholders are supported
</small>
</div>
</div>
<div class="form-group row action-type action-smtp">
<label for="idEmailBody" class="col-sm-2 col-form-label">Email body</label>
<div class="col-sm-10">
<textarea class="form-control" id="idEmailBody" name="email_body" rows="4" placeholder=""
aria-describedby="smtpBodyHelpBlock">{{.Action.Options.EmailConfig.Body}}</textarea>
<small id="smtpBodyHelpBlock" class="form-text text-muted">
Placeholders are supported
</small>
</div>
</div>
<input type="hidden" name="_form_token" value="{{.CSRFToken}}">
<div class="col-sm-12 text-right px-0">
<button type="submit" class="btn btn-primary mt-3 ml-3 px-5" name="form_action" value="submit">Submit</button>
</div>
</form>
</div>
</div>
{{end}}
{{define "dialog"}}
<div class="modal fade" id="infoModal" tabindex="-1" role="dialog" aria-labelledby="infoModalLabel"
aria-hidden="true">
<div class="modal-dialog" role="document">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="infoModalLabel">
Supported placeholders
</h5>
<button class="close" type="button" data-dismiss="modal" aria-label="Close">
<span aria-hidden="true">&times;</span>
</button>
</div>
<div class="modal-body">
<p>
<span class="shortcut"><b>{{`{{Name}}`}}</b></span> => Username, folder name, or admin username for provider actions.
</p>
<p>
<span class="shortcut"><b>{{`{{Event}}`}}</b></span> => Event name, for example "upload", "download" for filesystem events or "add", "update" for provider events.
</p>
<p>
<span class="shortcut"><b>{{`{{Status}}`}}</b></span> => Status for "upload", "download" and "ssh_cmd" events. 1 means no error, 2 means a generic error occurred, 3 means quota exceeded error.
</p>
<p>
<span class="shortcut"><b>{{`{{VirtualPath}}`}}</b></span> => Path seen by SFTPGo users, for example "/adir/afile.txt".
</p>
<p>
<span class="shortcut"><b>{{`{{FsPath}}`}}</b></span> => Full filesystem path, for example "/user/homedir/adir/afile.txt" or "C:/data/user/homedir/adir/afile.txt" on Windows.
</p>
<p>
<span class="shortcut"><b>{{`{{ObjectName}}`}}</b></span> => File/directory name, for example "afile.txt" or provider object name.
</p>
<p>
<span class="shortcut"><b>{{`{{ObjectType}}`}}</b></span> => Object type for provider events: "user", "group", "admin", etc.
</p>
<p>
<span class="shortcut"><b>{{`{{VirtualTargetPath}}`}}</b></span> => Virtual target path for renames.
</p>
<p>
<span class="shortcut"><b>{{`{{FsTargetPath}}`}}</b></span> => Full filesystem target path for renames.
</p>
<p>
<span class="shortcut"><b>{{`{{FileSize}}`}}</b></span> => File size.
</p>
<p>
<span class="shortcut"><b>{{`{{Protocol}}`}}</b></span> => Protocol, for example "SFTP", "FTP".
</p>
<p>
<span class="shortcut"><b>{{`{{IP}}`}}</b></span> => Client IP address.
</p>
<p>
<span class="shortcut"><b>{{`{{Timestamp}}`}}</b></span> => Event timestamp as nanoseconds since epoch.
</p>
<p>
<span class="shortcut"><b>{{`{{ObjectData}}`}}</b></span> => Provider object data serialized as JSON with sensitive fields removed.
</p>
</div>
<div class="modal-footer">
<button class="btn btn-primary" type="button" data-dismiss="modal">OK</button>
</div>
</div>
</div>
</div>
{{end}}
{{define "extra_js"}}
<script src="{{.StaticURL}}/vendor/bootstrap-select/js/bootstrap-select.min.js"></script>
<script type="text/javascript">
$("body").on("click", ".add_new_header_field_btn", function () {
var index = $(".form_field_http_headers_outer").find(".form_field_http_headers_outer_row").length;
while (document.getElementById("idHTTPHeaderKey"+index) != null){
index++;
}
$(".form_field_http_headers_outer").append(`
<div class="row form_field_http_headers_outer_row">
<div class="form-group col-md-5">
<input type="text" class="form-control" id="idHTTPHeaderKey${index}" name="http_header_key${index}" placeholder="Enter key" value="">
</div>
<div class="form-group col-md-5">
<input type="text" class="form-control" id="idHTTPHeaderVal${index}" name="http_header_val${index}" placeholder="Enter value" value="">
</div>
<div class="form-group col-md-1"></div>
<div class="form-group col-md-1">
<button class="btn btn-circle btn-danger remove_http_header_btn_frm_field">
<i class="fas fa-trash"></i>
</button>
</div>
</div>
`);
});
$("body").on("click", ".remove_http_header_btn_frm_field", function () {
$(this).closest(".form_field_http_headers_outer_row").remove();
});
$("body").on("click", ".add_new_query_field_btn", function () {
var index = $(".form_field_http_query_outer").find(".form_field_http_query_outer_row").length;
while (document.getElementById("idHTTPQueryKey"+index) != null){
index++;
}
$(".form_field_http_query_outer").append(`
<div class="row form_field_http_query_outer_row">
<div class="form-group col-md-5">
<input type="text" class="form-control" id="idHTTPQueryKey${index}" name="http_query_key${index}" placeholder="Enter key" value="">
</div>
<div class="form-group col-md-5">
<input type="text" class="form-control" id="idHTTPQueryVal${index}" name="http_query_val${index}" placeholder="Enter value" value="">
</div>
<div class="form-group col-md-1"></div>
<div class="form-group col-md-1">
<button class="btn btn-circle btn-danger remove_http_query_btn_frm_field">
<i class="fas fa-trash"></i>
</button>
</div>
</div>
`);
});
$("body").on("click", ".remove_http_query_btn_frm_field", function () {
$(this).closest(".form_field_http_query_outer_row").remove();
});
$("body").on("click", ".add_new_cmd_env_field_btn", function () {
var index = $(".form_field_cmd_env_outer").find(".form_field_cmd_env_outer_row").length;
while (document.getElementById("idCMDEnvKey"+index) != null){
index++;
}
$(".form_field_cmd_env_outer").append(`
<div class="row form_field_cmd_env_outer_row">
<div class="form-group col-md-5">
<input type="text" class="form-control" id="idCMDEnvKey${index}" name="cmd_env_key${index}" placeholder="Enter key" value="">
</div>
<div class="form-group col-md-5">
<input type="text" class="form-control" id="idCMDEnvVal${index}" name="cmd_env_val${index}" placeholder="Enter value" value="">
</div>
<div class="form-group col-md-1"></div>
<div class="form-group col-md-1">
<button class="btn btn-circle btn-danger remove_cmd_env_btn_frm_field">
<i class="fas fa-trash"></i>
</button>
</div>
</div>
`);
});
$("body").on("click", ".remove_cmd_env_btn_frm_field", function () {
$(this).closest(".form_field_cmd_env_outer_row").remove();
});
function onTypeChanged(val){
$('.action-type').hide();
switch (val) {
case '1':
case 1:
$('.action-http').show();
break;
case '2':
case 2:
$('.action-cmd').show();
break;
case '3':
case 3:
$('.action-smtp').show();
break;
}
}
$(document).ready(function () {
onTypeChanged('{{.Action.Type}}');
});
</script>
{{end}}

View File

@@ -0,0 +1,206 @@
{{template "base" .}}
{{define "title"}}{{.Title}}{{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">
{{end}}
{{define "page_body"}}
<div id="errorMsg" class="card mb-4 border-left-warning" style="display: none;">
<div id="errorTxt" class="card-body text-form-error"></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 event actions</h6>
</div>
<div class="card-body">
<div class="table-responsive">
<table class="table table-hover nowrap" id="dataTable" width="100%" cellspacing="0">
<thead>
<tr>
<th>Name</th>
<th>Description</th>
<th>Type</th>
<th>Rules</th>
</tr>
</thead>
<tbody>
{{range .Actions}}
<tr>
<td>{{.Name}}</td>
<td>{{.Description}}</td>
<td>{{.GetTypeAsString}}</td>
<td>{{.GetRulesAsString}}</td>
</tr>
{{end}}
</tbody>
</table>
</div>
</div>
</div>
{{end}}
{{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">&times;</span>
</button>
</div>
<div class="modal-body">Do you want to delete the selected event action? A referenced action cannot be removed</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 type="text/javascript">
function deleteAction() {
var table = $('#dataTable').DataTable();
table.button('delete:name').enable(false);
var name = table.row({ selected: true }).data()[0];
var path = '{{.EventActionURL}}' + "/" + fixedEncodeURIComponent(name);
$('#deleteModal').modal('hide');
$.ajax({
url: path,
type: 'DELETE',
dataType: 'json',
headers: {'X-CSRF-TOKEN' : '{{.CSRFToken}}'},
timeout: 15000,
success: function (result) {
window.location.href = '{{.EventActionsURL}}';
},
error: function ($xhr, textStatus, errorThrown) {
var txt = "Unable to delete the selected action";
if ($xhr) {
var json = $xhr.responseJSON;
if (json) {
if (json.message){
txt += ": " + json.message;
} else {
txt += ": " + json.error;
}
}
}
$('#errorTxt').text(txt);
$('#errorMsg').show();
setTimeout(function () {
$('#errorMsg').hide();
}, 5000);
}
});
}
$(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 = '{{.EventActionURL}}';
}
};
$.fn.dataTable.ext.buttons.edit = {
text: '<i class="fas fa-pen"></i>',
name: 'edit',
titleAttr: "Edit",
action: function (e, dt, node, config) {
var name = table.row({ selected: true }).data()[0];
var path = '{{.EventActionURL}}' + "/" + fixedEncodeURIComponent(name);
window.location.href = path;
},
enabled: false
};
$.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
};
var table = $('#dataTable').DataTable({
"select": {
"style": "single",
"blurable": true
},
"stateSave": true,
"stateDuration": 0,
"buttons": [
{
"text": "Column visibility",
"extend": "colvis",
"columns": ":not(.noVis)"
}
],
"columnDefs": [
{
"targets": [0],
"className": "noVis"
},
{
"targets": [3],
"render": $.fn.dataTable.render.ellipsis(100, true)
},
],
"scrollX": false,
"scrollY": false,
"responsive": true,
"language": {
"emptyTable": "No event actions defined"
},
"order": [[0, 'asc']]
});
new $.fn.dataTable.FixedHeader( table );
table.button().add(0,'delete');
table.button().add(0,'edit');
table.button().add(0,'add');
table.buttons().container().appendTo('.col-md-6:eq(0)', table.table().container());
table.on('select deselect', function () {
var selectedRows = table.rows({ selected: true }).count();
table.button('delete:name').enable(selectedRows == 1);
table.button('edit:name').enable(selectedRows == 1);
});
});
</script>
{{end}}

View File

@@ -0,0 +1,541 @@
{{template "base" .}}
{{define "title"}}{{.Title}}{{end}}
{{define "extra_css"}}
<link href="{{.StaticURL}}/vendor/bootstrap-select/css/bootstrap-select.min.css" rel="stylesheet">
{{end}}
{{define "page_body"}}
<div class="card shadow mb-4">
<div class="card-header py-3">
<h6 class="m-0 font-weight-bold text-primary">{{.Title}}</h6>
</div>
<div class="card-body">
{{if .Error}}
<div class="card mb-4 border-left-warning">
<div class="card-body text-form-error">{{.Error}}</div>
</div>
{{end}}
<form id="eventrule_form" action="{{.CurrentURL}}" method="POST" autocomplete="off">
<div class="form-group row">
<label for="idName" class="col-sm-2 col-form-label">Name</label>
<div class="col-sm-10">
<input type="text" class="form-control" id="idName" name="name" placeholder=""
value="{{.Rule.Name}}" maxlength="255" autocomplete="nope" required {{if eq .Mode 2}}readonly{{end}}>
</div>
</div>
<div class="form-group row">
<label for="idDescription" class="col-sm-2 col-form-label">Description</label>
<div class="col-sm-10">
<input type="text" class="form-control" id="idDescription" name="description" placeholder=""
value="{{.Rule.Description}}" maxlength="255" aria-describedby="descriptionHelpBlock">
<small id="descriptionHelpBlock" class="form-text text-muted">
Optional description
</small>
</div>
</div>
<div class="form-group row">
<label for="idTrigger" class="col-sm-2 col-form-label">Trigger</label>
<div class="col-sm-10">
<select class="form-control selectpicker" id="idTrigger" name="trigger" onchange="onTriggerChanged(this.value)">
{{- range .TriggerTypes}}
<option value="{{.Value}}" {{if eq $.Rule.Trigger .Value }}selected{{end}}>{{.Name}}</option>
{{- end}}
</select>
</div>
</div>
<div class="form-group row trigger trigger-fs">
<label for="idFsEvents" class="col-sm-2 col-form-label">Fs events</label>
<div class="col-sm-10">
<select class="form-control selectpicker" id="idFsEvents" name="fs_events" multiple>
{{- range $event := .FsEvents}}
<option value="{{$event}}" {{- range $.Rule.Conditions.FsEvents }}{{- if eq . $event}}selected{{- end}}{{- end}}>{{$event}}</option>
{{- end}}
</select>
</div>
</div>
<div class="form-group row trigger trigger-provider">
<label for="idProviderEvents" class="col-sm-2 col-form-label">Provider events</label>
<div class="col-sm-10">
<select class="form-control selectpicker" id="idProviderEvents" name="provider_events" multiple>
{{- range $event := .ProviderEvents}}
<option value="{{$event}}" {{- range $.Rule.Conditions.ProviderEvents }}{{- if eq . $event}}selected{{- end}}{{- end}}>{{$event}}</option>
{{- end}}
</select>
</div>
</div>
<div class="card bg-light mb-3 trigger trigger-schedule">
<div class="card-header">
<b>Schedules</b>
</div>
<div class="card-body">
<h6 class="card-title mb-4">Hours: 0-23. Day of week: 0-6 (Sun-Sat). Day of month: 1-31. Month: 1-12. Asterisk (*) indicates a match for all the values of the field. e.g. every day of week, every day of month and so on. More <a href="https://pkg.go.dev/github.com/robfig/cron/v3#hdr-CRON_Expression_Format" target="_blank">info</a>.</h6>
<div class="form-group row">
<div class="col-md-12 form_field_schedules_outer">
{{range $idx, $val := .Rule.Conditions.Schedules}}
<div class="row form_field_schedules_outer_row">
<div class="form-group col-md-3">
<input type="text" class="form-control" id="idScheduleHour{{$idx}}" name="schedule_hour{{$idx}}" placeholder="Hours" value="{{$val.Hours}}">
</div>
<div class="form-group col-md-3">
<input type="text" class="form-control" id="idScheduleDayOfWeek{{$idx}}" name="schedule_day_of_week{{$idx}}" placeholder="Day of week" value="{{$val.DayOfWeek}}">
</div>
<div class="form-group col-md-3">
<input type="text" class="form-control" id="idScheduleDayOfMonth{{$idx}}" name="schedule_day_of_month{{$idx}}" placeholder="Day of month" value="{{$val.DayOfMonth}}">
</div>
<div class="form-group col-md-2">
<input type="text" class="form-control" id="idScheduleMonth{{$idx}}" name="schedule_month{{$idx}}" placeholder="Month" value="{{$val.Month}}">
</div>
<div class="form-group col-md-1">
<button class="btn btn-circle btn-danger remove_schedule_btn_frm_field">
<i class="fas fa-trash"></i>
</button>
</div>
</div>
{{else}}
<div class="row form_field_schedules_outer_row">
<div class="form-group col-md-3">
<input type="text" class="form-control" id="idScheduleHour0" name="schedule_hour0" placeholder="Hours" value="">
</div>
<div class="form-group col-md-3">
<input type="text" class="form-control" id="idScheduleDayOfWeek0" name="schedule_day_of_week0" placeholder="Day of week" value="">
</div>
<div class="form-group col-md-3">
<input type="text" class="form-control" id="idScheduleDayOfMonth0" name="schedule_day_of_month0" placeholder="Day of month" value="">
</div>
<div class="form-group col-md-2">
<input type="text" class="form-control" id="idScheduleMonth0" name="schedule_month0" placeholder="Month" value="">
</div>
<div class="form-group col-md-1">
<button class="btn btn-circle btn-danger remove_schedule_btn_frm_field">
<i class="fas fa-trash"></i>
</button>
</div>
</div>
{{end}}
</div>
</div>
<div class="row mx-1">
<button type="button" class="btn btn-secondary add_new_schedule_field_btn">
<i class="fas fa-plus"></i> Add new schedule
</button>
</div>
</div>
</div>
{{if .IsShared}}
<div class="form-group trigger trigger-schedule">
<div class="form-check">
<input type="checkbox" class="form-check-input" id="idConcurrentExecution" name="concurrent_execution"
{{if .Rule.Conditions.Options.ConcurrentExecution}}checked{{end}}>
<label for="idConcurrentExecution" class="form-check-label">Allow concurrent execution from multiple instances</label>
</div>
</div>
{{end}}
<div class="form-group row trigger trigger-fs">
<label for="idFsProtocols" class="col-sm-2 col-form-label">Protocol filters</label>
<div class="col-sm-10">
<select class="form-control selectpicker" id="idFsProtocols" name="fs_protocols" aria-describedby="fsProtocolsHelpBlock" multiple>
{{- range $p := .Protocols}}
<option value="{{$p}}" {{- range $.Rule.Conditions.Options.Protocols }}{{- if eq . $p}}selected{{- end}}{{- end}}>{{$p}}</option>
{{- end}}
</select>
<small id="fsProtocolsHelpBlock" class="form-text text-muted">
No selection means any protocol will trigger events
</small>
</div>
</div>
<div class="form-group row trigger trigger-provider">
<label for="idProviderObjects" class="col-sm-2 col-form-label">Object filters</label>
<div class="col-sm-10">
<select class="form-control selectpicker" id="idProviderObjects" name="provider_objects" aria-describedby="providerObjectsHelpBlock" multiple>
{{- range $p := .ProviderObjects}}
<option value="{{$p}}" {{- range $.Rule.Conditions.Options.ProviderObjects }}{{- if eq . $p}}selected{{- end}}{{- end}}>{{$p}}</option>
{{- end}}
</select>
<small id="providerObjectsHelpBlock" class="form-text text-muted">
No selection means any provider object will trigger events
</small>
</div>
</div>
<div class="card bg-light mb-3">
<div class="card-header">
<b>Name filters</b>
</div>
<div class="card-body">
<h6 class="card-title mb-4">Shell-like pattern filters for usernames, folder names. For example "user*"" will match names starting with "user"</h6>
<div class="form-group row">
<div class="col-md-12 form_field_names_outer">
{{range $idx, $val := .Rule.Conditions.Options.Names}}
<div class="row form_field_names_outer_row">
<div class="form-group col-md-8">
<input type="text" class="form-control" id="idNamePattern{{$idx}}" name="name_pattern{{$idx}}" placeholder="" value="{{$val.Pattern}}" maxlength="255">
</div>
<div class="form-group col-md-3">
<select class="form-control selectpicker" id="idNamePatternType{{$idx}}" name="type_name_pattern{{$idx}}">
<option value=""></option>
<option value="inverse" {{if $val.InverseMatch}}selected{{end}}>Inverse match</option>
</select>
</div>
<div class="form-group col-md-1">
<button class="btn btn-circle btn-danger remove_name_pattern_btn_frm_field">
<i class="fas fa-trash"></i>
</button>
</div>
</div>
{{else}}
<div class="row form_field_names_outer_row">
<div class="form-group col-md-8">
<input type="text" class="form-control" id="idNamePattern0" name="name_pattern0" placeholder="" value="" maxlength="255">
</div>
<div class="form-group col-md-3">
<select class="form-control selectpicker" id="idNamePatternType0" name="type_name_pattern0">
<option value=""></option>
<option value="inverse">Inverse match</option>
</select>
</div>
<div class="form-group col-md-1">
<button class="btn btn-circle btn-danger remove_name_pattern_btn_frm_field">
<i class="fas fa-trash"></i>
</button>
</div>
</div>
{{end}}
</div>
</div>
<div class="row mx-1">
<button type="button" class="btn btn-secondary add_new_name_pattern_field_btn">
<i class="fas fa-plus"></i> Add new filter
</button>
</div>
</div>
</div>
<div class="card bg-light mb-3 trigger trigger-fs">
<div class="card-header">
<b>Path filters</b>
</div>
<div class="card-body">
<h6 class="card-title mb-4">Shell-like pattern filters for filesystem events. For example "/adir/*.txt"" will match paths in the "/adir" directory ending with ".txt"</h6>
<div class="form-group row">
<div class="col-md-12 form_field_fs_paths_outer">
{{range $idx, $val := .Rule.Conditions.Options.FsPaths}}
<div class="row form_field_fs_paths_outer_row">
<div class="form-group col-md-8">
<input type="text" class="form-control" id="idFsPathPattern{{$idx}}" name="fs_path_pattern{{$idx}}" placeholder="" value="{{$val.Pattern}}" maxlength="255">
</div>
<div class="form-group col-md-3">
<select class="form-control selectpicker" id="idFsPathPatternType{{$idx}}" name="type_fs_path_pattern{{$idx}}">
<option value=""></option>
<option value="inverse" {{if $val.InverseMatch}}selected{{end}}>Inverse match</option>
</select>
</div>
<div class="form-group col-md-1">
<button class="btn btn-circle btn-danger remove_fs_path_pattern_btn_frm_field">
<i class="fas fa-trash"></i>
</button>
</div>
</div>
{{else}}
<div class="row form_field_fs_paths_outer_row">
<div class="form-group col-md-8">
<input type="text" class="form-control" id="idFsPathPattern0" name="fs_path_pattern0" placeholder="" value="" maxlength="255">
</div>
<div class="form-group col-md-3">
<select class="form-control selectpicker" id="idFsPathPatternType0" name="type_fs_path_pattern0">
<option value=""></option>
<option value="inverse">Inverse match</option>
</select>
</div>
<div class="form-group col-md-1">
<button class="btn btn-circle btn-danger remove_fs_path_pattern_btn_frm_field">
<i class="fas fa-trash"></i>
</button>
</div>
</div>
{{end}}
</div>
</div>
<div class="row mx-1">
<button type="button" class="btn btn-secondary add_new_fs_path_pattern_field_btn">
<i class="fas fa-plus"></i> Add new filter
</button>
</div>
</div>
</div>
<div class="card bg-light mb-3 trigger trigger-fs">
<div class="card-header">
<b>File size limits. 0 means no limit</b>
</div>
<div class="card-body">
<div class="form-group row">
<label for="idFsMinSize" class="col-sm-2 col-form-label">Min size</label>
<div class="col-sm-3">
<input type="number" class="form-control" id="idFsMinSize" name="fs_min_size" placeholder=""
value="{{.Rule.Conditions.Options.MinFileSize}}" min="0">
</div>
<div class="col-sm-2"></div>
<label for="idFsMaxSize" class="col-sm-2 col-form-label">Max size</label>
<div class="col-sm-3">
<input type="number" class="form-control" id="idFsMaxSize" name="fs_max_size" placeholder=""
value="{{.Rule.Conditions.Options.MaxFileSize}}" min="0">
</div>
</div>
</div>
</div>
<div class="card bg-light mb-3">
<div class="card-header">
<b>Actions</b>
</div>
<div class="card-body">
<h6 class="card-title mb-4">One or more actions to execute. The "Execute sync" options is only supported for upload events</h6>
<div class="form-group row">
<div class="col-md-12 form_field_action_outer">
{{range $idx, $val := .Rule.Actions}}
<div class="row form_field_action_outer_row">
<div class="form-group col-md-5">
<select class="form-control selectpicker" data-live-search="true" id="idActionName{{$idx}}" name="action_name{{$idx}}">
<option value=""></option>
{{range $.Actions}}
<option value="{{.Name}}" {{if eq $val.Name .Name}}selected{{end}}>{{.Name}}</option>
{{end}}
</select>
</div>
<div class="form-group col-md-5">
<select class="form-control selectpicker" id="idActionOptions{{$idx}}" name="action_options{{$idx}}" multiple>
<option value="2" {{if $val.Options.StopOnFailure}}selected{{end}}>Stop on failure</option>
<option value="3" {{if $val.Options.ExecuteSync}}selected{{end}}>Execute sync</option>
<option value="1" {{if $val.Options.IsFailureAction}}selected{{end}}>Is failure action</option>
</select>
</div>
<div class="col-sm-1">
<input type="hidden" name="action_order{{$idx}}" value="{{$idx}}">
</div>
<div class="form-group col-md-1">
<button class="btn btn-circle btn-danger remove_action_btn_frm_field">
<i class="fas fa-trash"></i>
</button>
</div>
</div>
{{else}}
<div class="row form_field_action_outer_row">
<div class="form-group col-md-5">
<select class="form-control selectpicker" data-live-search="true" id="idActionName0" name="action_name0">
<option value=""></option>
{{range $.Actions}}
<option value="{{.Name}}">{{.Name}}</option>
{{end}}
</select>
</div>
<div class="form-group col-md-5">
<select class="form-control selectpicker" id="idActionOptions0" name="action_options0" multiple>
<option value="1">Is failure action</option>
<option value="2">Stop on failure</option>
<option value="3">Execute sync</option>
</select>
</div>
<div class="col-sm-1">
<input type="hidden" name="action_order0" value="0">
</div>
<div class="form-group col-md-1">
<button class="btn btn-circle btn-danger remove_action_btn_frm_field">
<i class="fas fa-trash"></i>
</button>
</div>
</div>
{{end}}
</div>
</div>
<div class="row mx-1">
<button type="button" class="btn btn-secondary add_new_action_field_btn">
<i class="fas fa-plus"></i> Add new action
</button>
</div>
</div>
</div>
<input type="hidden" name="_form_token" value="{{.CSRFToken}}">
<div class="col-sm-12 text-right px-0">
<button type="submit" class="btn btn-primary mt-3 ml-3 px-5" name="form_action" value="submit">Submit</button>
</div>
</form>
</div>
</div>
{{end}}
{{define "extra_js"}}
<script src="{{.StaticURL}}/vendor/bootstrap-select/js/bootstrap-select.min.js"></script>
<script type="text/javascript">
$("body").on("click", ".add_new_schedule_field_btn", function () {
var index = $(".form_field_schedules_outer").find(".form_field_schedules_outer_row").length;
while (document.getElementById("idScheduleHour"+index) != null){
index++;
}
$(".form_field_schedules_outer").append(`
<div class="row form_field_schedules_outer_row">
<div class="form-group col-md-3">
<input type="text" class="form-control" id="idScheduleHour${index}" name="schedule_hour${index}" placeholder="Hours" value="">
</div>
<div class="form-group col-md-3">
<input type="text" class="form-control" id="idScheduleDayOfWeek${index}" name="schedule_day_of_week${index}" placeholder="Day of week" value="">
</div>
<div class="form-group col-md-3">
<input type="text" class="form-control" id="idScheduleDayOfMonth${index}" name="schedule_day_of_month${index}" placeholder="Day of month" value="">
</div>
<div class="form-group col-md-2">
<input type="text" class="form-control" id="idScheduleMonth${index}" name="schedule_month${index}" placeholder="Month" value="">
</div>
<div class="form-group col-md-1">
<button class="btn btn-circle btn-danger remove_schedule_btn_frm_field">
<i class="fas fa-trash"></i>
</button>
</div>
</div>
`);
});
$("body").on("click", ".remove_schedule_btn_frm_field", function () {
$(this).closest(".form_field_schedules_outer_row").remove();
});
$("body").on("click", ".add_new_name_pattern_field_btn", function () {
var index = $(".form_field_names_outer").find(".form_field_names_outer_row").length;
while (document.getElementById("idNamePattern"+index) != null){
index++;
}
$(".form_field_names_outer").append(`
<div class="row form_field_names_outer_row">
<div class="form-group col-md-8">
<input type="text" class="form-control" id="idNamePattern${index}" name="name_pattern${index}" placeholder="" value="" maxlength="255">
</div>
<div class="form-group col-md-3">
<select class="form-control" id="idNamePatternType${index}" name="type_name_pattern${index}">
<option value=""></option>
<option value="inverse">Inverse match</option>
</select>
</div>
<div class="form-group col-md-1">
<button class="btn btn-circle btn-danger remove_name_pattern_btn_frm_field">
<i class="fas fa-trash"></i>
</button>
</div>
</div>
`);
$("#idNamePatternType"+index).selectpicker();
});
$("body").on("click", ".remove_name_pattern_btn_frm_field", function () {
$(this).closest(".form_field_names_outer_row").remove();
});
$("body").on("click", ".add_new_fs_path_pattern_field_btn", function () {
var index = $(".form_field_fs_paths_outer").find("form_field_fs_paths_outer_row").length;
while (document.getElementById("idFsPathPattern"+index) != null){
index++;
}
$(".form_field_fs_paths_outer").append(`
<div class="row form_field_fs_paths_outer_row">
<div class="form-group col-md-8">
<input type="text" class="form-control" id="idFsPathPattern${index}" name="fs_path_pattern${index}" placeholder="" value="" maxlength="255">
</div>
<div class="form-group col-md-3">
<select class="form-control" id="idFsPathPatternType${index}" name="type_fs_path_pattern${index}">
<option value=""></option>
<option value="inverse">Inverse match</option>
</select>
</div>
<div class="form-group col-md-1">
<button class="btn btn-circle btn-danger remove_fs_path_pattern_btn_frm_field">
<i class="fas fa-trash"></i>
</button>
</div>
</div>
`);
$("#idFsPathPatternType"+index).selectpicker();
});
$("body").on("click", ".remove_fs_path_pattern_btn_frm_field", function () {
$(this).closest(".form_field_fs_paths_outer_row").remove();
});
$("body").on("click", ".add_new_action_field_btn", function () {
var index = $(".form_field_action_outer").find("form_field_action_outer_row").length;
while (document.getElementById("idActionName"+index) != null){
index++;
}
$(".form_field_action_outer").append(`
<div class="row form_field_action_outer_row">
<div class="form-group col-md-5">
<select class="form-control" id="idActionName${index}" name="action_name${index}">
<option value=""></option>
</select>
</div>
<div class="form-group col-md-5">
<select class="form-control" id="idActionOptions${index}" name="action_options${index}" multiple>
<option value="1">Is failure action</option>
<option value="2">Stop on failure</option>
<option value="3">Execute sync</option>
</select>
</div>
<div class="col-sm-1">
<input type="hidden" name="action_order${index}" value="${index}">
</div>
<div class="form-group col-md-1">
<button class="btn btn-circle btn-danger remove_action_btn_frm_field">
<i class="fas fa-trash"></i>
</button>
</div>
</div>
`);
{{- range .Actions}}
$("#idActionName"+index).append($('<option>').val('{{.Name}}').text('{{.Name}}'));
{{- end}}
console.log("index "+index);
$("#idActionName"+index).selectpicker({'liveSearch': true});
$("#idActionOptions"+index).selectpicker();
});
$("body").on("click", ".remove_action_btn_frm_field", function () {
$(this).closest(".form_field_action_outer_row").remove();
});
function onTriggerChanged(val){
$('.trigger').hide();
switch (val) {
case '1':
case 1:
$('.trigger-fs').show();
break;
case '2':
case 2:
$('.trigger-provider').show();
break;
case '3':
case 3:
$('.trigger-schedule').show();
break;
default:
console.log(`unsupported event trigger type: ${val}`);
}
}
$(document).ready(function () {
onTriggerChanged('{{.Rule.Trigger}}');
});
</script>
{{end}}

View File

@@ -0,0 +1,206 @@
{{template "base" .}}
{{define "title"}}{{.Title}}{{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">
{{end}}
{{define "page_body"}}
<div id="errorMsg" class="card mb-4 border-left-warning" style="display: none;">
<div id="errorTxt" class="card-body text-form-error"></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 event rules</h6>
</div>
<div class="card-body">
<div class="table-responsive">
<table class="table table-hover nowrap" id="dataTable" width="100%" cellspacing="0">
<thead>
<tr>
<th>Name</th>
<th>Description</th>
<th>Trigger</th>
<th>Actions</th>
</tr>
</thead>
<tbody>
{{range .Rules}}
<tr>
<td>{{.Name}}</td>
<td>{{.Description}}</td>
<td>{{.GetTriggerAsString}}</td>
<td>{{.GetActionsAsString}}</td>
</tr>
{{end}}
</tbody>
</table>
</div>
</div>
</div>
{{end}}
{{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">&times;</span>
</button>
</div>
<div class="modal-body">Do you want to delete the selected rule?</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 type="text/javascript">
function deleteAction() {
var table = $('#dataTable').DataTable();
table.button('delete:name').enable(false);
var name = table.row({ selected: true }).data()[0];
var path = '{{.EventRuleURL}}' + "/" + fixedEncodeURIComponent(name);
$('#deleteModal').modal('hide');
$.ajax({
url: path,
type: 'DELETE',
dataType: 'json',
headers: {'X-CSRF-TOKEN' : '{{.CSRFToken}}'},
timeout: 15000,
success: function (result) {
window.location.href = '{{.EventRulesURL}}';
},
error: function ($xhr, textStatus, errorThrown) {
var txt = "Unable to delete the selected rule";
if ($xhr) {
var json = $xhr.responseJSON;
if (json) {
if (json.message){
txt += ": " + json.message;
} else {
txt += ": " + json.error;
}
}
}
$('#errorTxt').text(txt);
$('#errorMsg').show();
setTimeout(function () {
$('#errorMsg').hide();
}, 5000);
}
});
}
$(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 = '{{.EventRuleURL}}';
}
};
$.fn.dataTable.ext.buttons.edit = {
text: '<i class="fas fa-pen"></i>',
name: 'edit',
titleAttr: "Edit",
action: function (e, dt, node, config) {
var name = table.row({ selected: true }).data()[0];
var path = '{{.EventRuleURL}}' + "/" + fixedEncodeURIComponent(name);
window.location.href = path;
},
enabled: false
};
$.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
};
var table = $('#dataTable').DataTable({
"select": {
"style": "single",
"blurable": true
},
"stateSave": true,
"stateDuration": 0,
"buttons": [
{
"text": "Column visibility",
"extend": "colvis",
"columns": ":not(.noVis)"
}
],
"columnDefs": [
{
"targets": [0],
"className": "noVis"
},
{
"targets": [3],
"render": $.fn.dataTable.render.ellipsis(100, true)
},
],
"scrollX": false,
"scrollY": false,
"responsive": true,
"language": {
"emptyTable": "No event rules defined"
},
"order": [[0, 'asc']]
});
new $.fn.dataTable.FixedHeader( table );
table.button().add(0,'delete');
table.button().add(0,'edit');
table.button().add(0,'add');
table.buttons().container().appendTo('.col-md-6:eq(0)', table.table().container());
table.on('select deselect', function () {
var selectedRows = table.rows({ selected: true }).count();
table.button('delete:name').enable(selectedRows == 1);
table.button('edit:name').enable(selectedRows == 1);
});
});
</script>
{{end}}

View File

@@ -15,10 +15,6 @@
<div id="errorTxt" class="card-body text-form-error"></div>
</div>
<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 groups</h6>
@@ -88,7 +84,7 @@
<script src="{{.StaticURL}}/vendor/datatables/ellipsis.js"></script>
<script type="text/javascript">
function deleteAction() {
function deleteAction() {
var table = $('#dataTable').DataTable();
table.button('delete:name').enable(false);
var groupName = table.row({ selected: true }).data()[0];

View File

@@ -28,9 +28,9 @@
$("#idSubDirPermissions"+index).selectpicker();
});
$("body").on("click", ".remove_dirperms_btn_frm_field", function () {
$(this).closest(".form_field_dirperms_outer_row").remove();
});
$("body").on("click", ".remove_dirperms_btn_frm_field", function () {
$(this).closest(".form_field_dirperms_outer_row").remove();
});
$("body").on("click", ".add_new_vfolder_field_btn", function () {
var index = $(".form_field_vfolders_outer").find(".form_field_vfolder_outer_row").length;