<!--
Copyright (C) 2024 Nicola Murino

This WebUI uses the KeenThemes Mega Bundle, a proprietary theme:

https://keenthemes.com/products/templates-mega-bundle

KeenThemes HTML/CSS/JS components are allowed for use only within the
SFTPGo product and restricted to be used in a resealable HTML template
that can compete with KeenThemes products anyhow.

This WebUI is allowed for use only within the SFTPGo product and
therefore cannot be used in derivative works/products without an
explicit grant from the SFTPGo Team (support@sftpgo.com).
-->
{{template "base" .}}

{{- define "extra_css"}}
<link href="{{.StaticURL}}/assets/plugins/custom/datatables/datatables.bundle.css" rel="stylesheet" type="text/css"/>
{{- end}}

{{- define "page_body"}}
{{- template "errmsg" ""}}
<div class="card shadow-sm">
    <div class="card-header bg-light">
        <h3 data-i18n="user.view_manage" class="card-title section-title">View and manage users</h3>
    </div>
    <div id="card_body" class="card-body">
        <div id="loader" class="align-items-center text-center my-10">
            <span class="spinner-border w-15px h-15px text-muted align-middle me-2"></span>
            <span data-i18n="general.loading" class="text-gray-700">Loading...</span>
        </div>
        <div id="card_content" class="d-none">
            <div class="d-flex flex-stack flex-wrap mb-5">
                <div class="d-flex align-items-center position-relative my-2">
                    <i class="ki-solid ki-magnifier fs-1 position-absolute ms-6"></i>
                    <input name="search" data-i18n="[placeholder]general.search" type="text" data-table-filter="search"
                        class="form-control rounded-1 w-250px ps-15 me-5" placeholder="Search" />
                    <button id="export_button" type="button" class="btn btn-light-primary ms-3" data-table-filter="export">
                        <span data-i18n="general.export" class="indicator-label">
                            Export
                        </span>
                        <span data-i18n="general.wait" class="indicator-progress">
                            Please wait...
                            <span class="spinner-border spinner-border-sm align-middle ms-2"></span>
                        </span>
                    </button>
                </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="checkColStatus" />
                            <label class="form-check-label" for="checkColStatus">
                                <span data-i18n="general.status" class="text-gray-800 fs-6">Status</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="checkColLastLogin" />
                            <label class="form-check-label" for="checkColLastLogin">
                                <span data-i18n="general.last_login" class="text-gray-800 fs-6">Last login</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="checkColEmail" />
                            <label class="form-check-label" for="checkColEmail">
                                <span data-i18n="general.email" class="text-gray-800 fs-6">Email</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="checkColStorage" />
                            <label class="form-check-label" for="checkColStorage">
                                <span data-i18n="storage.label" class="text-gray-800 fs-6">Storage</span>
                            </label>
                        </div>
                        <div class="menu-item px-3 py-2 form-check form-check-sm form-check-custom form-check-solid">
                            <input type="checkbox" class="form-check-input" value="" id="checkColRole" />
                            <label class="form-check-label" for="checkColRole">
                                <span data-i18n="general.role" class="text-gray-800 fs-6">Role</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="checkCol2FA" />
                            <label class="form-check-label" for="checkCol2FA">
                                <span data-i18n="title.two_factor_auth_short" class="text-gray-800 fs-6">2FA</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="checkColDiskQuota" />
                            <label class="form-check-label" for="checkColDiskQuota">
                                <span data-i18n="fs.quota_usage.disk" class="text-gray-800 fs-6">Disk quota</span>
                            </label>
                        </div>
                        <div class="menu-item px-3 py-2 form-check form-check-sm form-check-custom form-check-solid">
                            <input type="checkbox" class="form-check-input" value="" id="checkColTransferQuota" />
                            <label class="form-check-label" for="checkColTransferQuota">
                                <span data-i18n="fs.quota_usage.transfer" class="text-gray-800 fs-6">Transfer quota</span>
                            </label>
                        </div>
                        <div class="menu-item px-3 py-2 form-check form-check-sm form-check-custom form-check-solid">
                            <input type="checkbox" class="form-check-input" value="" id="checkColGroups" />
                            <label class="form-check-label" for="checkColGroups">
                                <span data-i18n="title.groups" class="text-gray-800 fs-6">Groups</span>
                            </label>
                        </div>
                    </div>
                    {{- if .LoggedUser.HasPermission "add_users"}}
                    <a href="{{.UserURL}}" 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>
                    <tr class="text-start text-muted fw-bold fs-6 gs-0">
                        <th data-i18n="login.username">Username</th>
                        <th data-i18n="general.status">Status</th>
                        <th data-i18n="general.last_login">Last login</th>
                        <th data-i18n="general.email">Email</th>
                        <th data-i18n="storage.label">Storage</th>
                        <th data-i18n="general.role">Role</th>
                        <th data-i18n="title.two_factor_auth_short">2FA</th>
                        <th data-i18n="fs.quota_usage.disk">Disk quota</th>
                        <th data-i18n="fs.quota_usage.transfer">Transfer quota</th>
                        <th data-i18n="title.groups">Groups</th>
                        <th class="min-w-100px"></th>
                    </tr>
                </thead>
                <tbody id="table_body" class="text-gray-800 fw-semibold"></tbody>
            </table>
        </div>
    </div>
</div>
{{- end}}
{{- 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}}/vendor/papaparse/papaparse.min.js"></script>
<script {{- if .CSPNonce}} nonce="{{.CSPNonce}}"{{- end}} src="{{.StaticURL}}/vendor/file-saver/FileSaver.min.js"></script>
<script type="text/javascript" {{- if .CSPNonce}} nonce="{{.CSPNonce}}"{{- end}}>
    function deleteAction(username) {
        ModalAlert.fire({
            text: $.t('general.delete_confirm_generic'),
            icon: "warning",
            confirmButtonText: $.t('general.delete_confirm_btn'),
            cancelButtonText: $.t('general.cancel'),
            customClass: {
                confirmButton: "btn btn-danger",
                cancelButton: 'btn btn-secondary'
            }
        }).then((result) => {
            if (result.isConfirmed){
                clearLoading();
                KTApp.showPageLoading();
                let path = '{{.UserURL}}' + "/" + encodeURIComponent(username);

                axios.delete(path, {
                    timeout: 15000,
                    headers: {
                        'X-CSRF-TOKEN': '{{.CSRFToken}}'
                    },
                    validateStatus: function (status) {
                        return status == 200;
                    }
                }).then(function(response){
                    location.reload();
                }).catch(function(error){
                    KTApp.hidePageLoading();
                    let errorMessage;
                    if (error && error.response) {
                        switch (error.response.status) {
                            case 403:
                                errorMessage = "general.delete_error_403";
                                break;
                            case 404:
                                errorMessage = "general.delete_error_404";
                                break;
                        }
                    }
                    if (!errorMessage){
                        errorMessage = "general.delete_error_generic";
                    }
                    ModalAlert.fire({
                        text: $.t(errorMessage),
                        icon: "warning",
                        confirmButtonText: $.t('general.ok'),
                        customClass: {
                            confirmButton: "btn btn-primary"
                        }
                    });
                });
            }
        });
    }

    function disableSeconFactorAction(username) {
        ModalAlert.fire({
            text: $.t('2fa.disable_confirm'),
            icon: "warning",
            confirmButtonText: $.t('general.disable_confirm_btn'),
            cancelButtonText: $.t('general.cancel'),
            customClass: {
                confirmButton: "btn btn-danger",
                cancelButton: 'btn btn-secondary'
            }
        }).then((result) => {
            if (result.isConfirmed){
                clearLoading();
                KTApp.showPageLoading();
                let path = '{{.UserURL}}' + "/" + encodeURIComponent(username)+"/2fa/disable";

                axios.put(path, null, {
                    timeout: 15000,
                    headers: {
                        'X-CSRF-TOKEN': '{{.CSRFToken}}'
                    },
                    validateStatus: function (status) {
                        return status == 200;
                    }
                }).then(function(response){
                    location.reload();
                }).catch(function(error){
                    KTApp.hidePageLoading();
                    ModalAlert.fire({
                        text: $.t('2fa.save_err'),
                        icon: "warning",
                        confirmButtonText: $.t('general.ok'),
                        customClass: {
                            confirmButton: "btn btn-primary"
                        }
                    });
                });
            }
        });
    }

    function quotaScanAction(username) {
        clearLoading();
        KTApp.showPageLoading();
        let path = '{{.QuotaScanURL}}' + "/" + encodeURIComponent(username);
        axios.post(path, null, {
            timeout: 15000,
            headers: {
                'X-CSRF-TOKEN': '{{.CSRFToken}}'
            },
            validateStatus: function (status) {
                return status == 202;
            }
        }).then(function (response) {
            KTApp.hidePageLoading();
            showToast(1, 'general.quota_scan_started');
        }).catch(function (error) {
            KTApp.hidePageLoading();
            let errorMessage;
            if (error && error.response) {
                switch (error.response.status) {
                    case 409:
                        errorMessage = "general.quota_scan_conflit";
                        break;
                }
            }
            if (!errorMessage) {
                errorMessage = "general.quota_scan_error";
            }
            ModalAlert.fire({
                text: $.t(errorMessage),
                icon: "warning",
                confirmButtonText: $.t('general.ok'),
                customClass: {
                    confirmButton: "btn btn-primary"
                }
            });
        });
    }

    var datatable = function(){
        var dt;

        var initDatatable = function () {
            $('#errorMsg').addClass("d-none");
            dt = $('#dataTable').DataTable({
                ajax: {
                    url: "{{.UsersURL}}/json",
                    dataSrc: "",
                    error: function ($xhr, textStatus, errorThrown) {
                        $(".dt-processing").hide();
                        $('#loader').addClass("d-none");
                        let txt = "";
                        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: "username",
                        render: function(data, type, row) {
                            if (type === 'display') {
                                return escapeHTML(data);
                            }
                            return data;
                        }
                    },
                    {
                        data: "status",
                        render: function(data, type, row) {
                            let result = data;
                            if (row.expiration_date){
                                if (row.expiration_date < Date.now()){
                                    result = -1;
                                }
                            }
                            if (type === 'display') {
                                switch (result){
                                    case 1:
                                        return $.t('general.active');
                                    case -1:
                                        return $.t('general.expired');
                                    default:
                                        return $.t('general.inactive');
                                }
                            }
                            return result;
                        }
                    },
                    {
                        data: "last_login",
                        defaultContent: "",
                        render: function(data, type, row) {
                            if (type === 'display') {
                                if (data > 0){
                                    return $.t('general.datetime', {
                                        val: parseInt(data, 10),
                                        formatParams: {
                                            val: { year: 'numeric', month: 'numeric', day: 'numeric', hour: 'numeric', minute: 'numeric' },
                                        }
                                    });
                                }
                                return ""
                            }
                            return data;
                        }
                    },
                    {
                        data: "email",
                        visible: false,
                        defaultContent: "",
                        render: function(data, type, row) {
                            if (type === 'display') {
                                if (data){
                                    return escapeHTML(data);
                                }
                                return "";
                            }
                            return data;
                        }
                    },
                    {
                        data: "filesystem.provider",
                        visible: false,
                        defaultContent: "",
                        render: function(data, type, row) {
                            if (type === 'display') {
                                switch (data){
                                    case 1:
                                        return $.t('storage.s3');
                                    case 2:
                                        return $.t('storage.gcs');
                                    case 3:
                                        return $.t('storage.azblob');
                                    case 4:
                                        return $.t('storage.encrypted');
                                    case 5:
                                        return $.t('storage.sftp');
                                    case 6:
                                        return $.t('storage.http');
                                    default:
                                        return $.t('storage.local');
                                }
                            }
                            return data;
                        }
                    },
                    {
                        data: "role",
                        visible: false,
                        defaultContent: "",
                        render: function(data, type, row) {
                            if (type === 'display') {
                                if (data){
                                    return escapeHTML(data);
                                }
                                return ""
                            }
                            return data;
                        }
                    },
                    {
                        data: "filters.totp_config",
                        visible: false,
                        defaultContent: "",
                        render: function(data, type, row) {
                            let protocols = "";
                            if (data && data.enabled){
                                protocols = data.protocols.join(', ');
                            }
                            if (type === 'display') {
                                if (protocols){
                                    return escapeHTML(protocols);
                                }
                            }
                            return protocols;
                        }
                    },
                    {
                        data: "quota_files",
                        visible: false,
                        searchable: false,
                        orderable: false,
                        defaultContent: "",
                        render: function(data, type, row) {
                            if (type === 'display') {
                                let val = "";
                                if (row.quota_size > 0) {
                                    let used = row.used_quota_size;
                                    if (!used){
                                        used = 0;
                                    }
                                    let usage = fileSizeIEC(used)+"/"+fileSizeIEC(row.quota_size);
                                    val += $.t('fs.quota_usage.size', {val: usage})+". ";
                                } else if (row.used_quota_size && row.used_quota_size > 0) {
                                    val += $.t('fs.quota_usage.size', {val: fileSizeIEC(row.used_quota_size)})+". ";
                                }
                                if (row.quota_files > 0){
                                    let used = row.used_quota_files;
                                    if (!used){
                                        used = 0;
                                    }
                                    let usage = used+"/"+row.quota_files;
                                    val += $.t('fs.quota_usage.files', {val: usage});
                                } else if (row.used_quota_files && row.used_quota_files > 0) {
                                    val += $.t('fs.quota_usage.files', {val: row.used_quota_files});
                                }
                                return val
                            }
                            return "";
                        }
                    },
                    {
                        data: "total_data_transfer",
                        visible: false,
                        searchable: false,
                        orderable: false,
                        defaultContent: "",
                        render: function(data, type, row) {
                            if (type === 'display') {
                                let val = "";
                                if (row.total_data_transfer > 0) {
                                    let used;
                                    if (row.used_download_data_transfer){
                                        used+=row.used_download_data_transfer;
                                    }
                                    if (row.used_upload_data_transfer){
                                        used+=row.used_upload_data_transfer;
                                    }
                                    let usage = fileSizeIEC(used)+"/"+fileSizeIEC(row.total_data_transfer*1048576);
                                    val = $.t('fs.quota_usage.total', {val: usage});
                                } else {
                                    if (row.upload_data_transfer > 0) {
                                        let used = row.used_upload_data_transfer;
                                        if (!used){
                                            used = 0;
                                        }
                                        let usage = fileSizeIEC(used)+"/"+fileSizeIEC(row.upload_data_transfer*1048576);
                                        val = $.t('fs.quota_usage.uploads', {val: usage})+". ";
                                    }
                                    if (row.download_data_transfer > 0) {
                                        let used = row.used_download_data_transfer;
                                        if (!used){
                                            used = 0;
                                        }
                                        let usage = fileSizeIEC(used)+"/"+fileSizeIEC(row.download_data_transfer*1048576);
                                        if (val) {
                                            val += ". ";
                                        }
                                        val += $.t('fs.quota_usage.downloads', {val: usage});
                                    }
                                }
                                return val;
                            }
                            return "";
                        }
                    },
                    {
                        data: "groups",
                        visible: false,
                        defaultContent: [],
                        render: function(data, type, row) {
                            if (!data){
                                return "";
                            }
                            let groups = [];
                            let result = "";
                            for (i = 0; i < data.length; i++){
                                groups.push(data[i].name);
                            }
                            result = groups.join(", ");
                            if (type === 'display') {
                                return escapeHTML(result);
                            }
                            return result;
                        }
                    },
                    {
                        data: "id",
                        searchable: false,
                        orderable: false,
                        className: 'text-end',
                        render: function (data, type, row) {
                            if (type === 'display') {
                                let numActions = 0;
                                let actions = `<button class="btn btn-light btn-active-light-primary btn-flex btn-center btn-sm rotate" data-kt-menu-trigger="click" data-kt-menu-placement="bottom-end">
                                                    <span data-i18n="general.actions" class="fs-6">Actions</span>
										            <i class="ki-duotone ki-down fs-5 ms-1 rotate-180"></i>
                                                </button>
                                                <div class="menu menu-sub menu-sub-dropdown menu-column menu-rounded menu-gray-700 menu-state-bg-light-primary fw-semibold fs-6 w-200px py-4" data-kt-menu="true">`;

                                //{{- if .LoggedUser.HasPermission "edit_users"}}
                                numActions++;
                                actions+=`<div class="menu-item px-3">
										      <a data-i18n="general.edit" href="#" class="menu-link px-3" data-table-action="edit_row">Edit</a>
										  </div>`;
                                //{{- end}}
                                //{{- if .LoggedUser.HasPermissions "add_users" "edit_users"}}
                                numActions++;
                                actions+=`<div class="menu-item px-3">
										      <a data-i18n="general.template" href="#" class="menu-link px-3" data-table-action="template_row">Template</a>
										  </div>`;
                                //{{- end}}
                                //{{- if .LoggedUser.HasPermission "quota_scans"}}
                                numActions++;
                                actions+=`<div class="menu-item px-3">
										      <a data-i18n="general.quota_scan" href="#" class="menu-link px-3" data-table-action="quota_scan_row">Quota scan</a>
										  </div>`;
                                //{{- end}}
                                //{{- if .LoggedUser.HasPermission "disable_mfa"}}
                                if (row.filters.totp_config && row.filters.totp_config.enabled){
                                    numActions++;
                                    actions+=`<div class="menu-item px-3">
                                                <a data-i18n="2fa.disable_msg" href="#" class="menu-link text-danger px-3" data-table-action="disable_2fa_row">Disable 2FA</a>
										      </div>`;
                                }
                                //{{- end}}
                                //{{- if .LoggedUser.HasPermission "del_users"}}
                                numActions++;
                                actions+=`<div class="menu-item px-3">
                                             <a data-i18n="general.delete" href="#" class="menu-link text-danger px-3" data-table-action="delete_row">Delete</a>
										  </div>`;
                                //{{- end}}
                                if (numActions > 0){
                                    actions+=`</div>`;
                                    return actions;
                                }
                            }
                            return "";
                        }
                    },
                ],
                deferRender: true,
                stateSave: true,
                stateDuration: 0,
                colReorder: {
                    enable: true,
                    fixedColumnsLeft: 1
                },
                stateLoadParams: function (settings, data) {
                        if (data.search.search){
                            const filterSearch = document.querySelector('[data-table-filter="search"]');
                            filterSearch.value = data.search.search;
                        }
                    },
                language: {
                    info: $.t('datatable.info'),
                    infoEmpty: $.t('datatable.info_empty'),
                    infoFiltered: $.t('datatable.info_filtered'),
                    loadingRecords: "",
                    processing: $.t('datatable.processing'),
                    zeroRecords: "",
                    emptyTable: $.t('datatable.no_records')
                },
                order: [[0, 'asc']],
                initComplete: function(settings, json) {
                    $('#loader').addClass("d-none");
                    $('#card_content').removeClass("d-none");
                    let api = $.fn.dataTable.Api(settings);
                    api.columns.adjust().draw("page");
                    drawAction();
                }
            });

            dt.on('draw', drawAction);
            dt.on('column-reorder', function(e, settings, details){
                drawAction();
            });
        }

        function drawAction() {
            KTMenu.createInstances();
            handleRowActions();
            $('#table_body').localize();
        }

        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');
            });
        }

        var handleDatatableActions = function () {
            const filterSearch = $(document.querySelector('[data-table-filter="search"]'));
            filterSearch.off("keyup");
            filterSearch.on('keyup', function (e) {
                dt.rows().deselect();
                dt.search(e.target.value).draw();
            });
            handleColVisibilityCheckbox($('#checkColStatus'), 1);
            handleColVisibilityCheckbox($('#checkColLastLogin'), 2);
            handleColVisibilityCheckbox($('#checkColEmail'), 3);
            handleColVisibilityCheckbox($('#checkColStorage'), 4);
            handleColVisibilityCheckbox($('#checkColRole'), 5);
            handleColVisibilityCheckbox($('#checkCol2FA'), 6);
            handleColVisibilityCheckbox($('#checkColDiskQuota'), 7);
            handleColVisibilityCheckbox($('#checkColTransferQuota'), 8);
            handleColVisibilityCheckbox($('#checkColGroups'), 9);

            const exportButton = $(document.querySelector('[data-table-filter="export"]'));
            exportButton.on('click', function(e){
                e.preventDefault();
                this.blur();
                this.setAttribute('data-kt-indicator', 'on');
			    this.disabled = true;

                let data = [];
                dt.rows({ search: 'applied' }).every(function (rowIdx, tableLoop, rowLoop){
                    let line = {};
                    let rowData = dt.row(rowIdx).data();
                    let filters = rowData["filters"];

                    line["Username"] = rowData["username"];
                    let status = "Inactive";
                    if (rowData["status"] == 1){
                        status = "Active";
                    } else if (rowData["status"] == -1){
                        status = "Expired";
                    }
                    line["Status"] = status;
                    if (rowData["has_password"]){
                        line["Password"] = "[redacted]";
                    } else {
                        line["Password"] = "";
                    }
                    if (rowData["created_at"]) {
                        line["Created At"] = moment(rowData["created_at"]).format("YYYY-MM-DD HH:mm");
                    } else {
                        line["Created At"] = "";
                    }
                    if (rowData["updated_at"]) {
                        line["Updated At"] = moment(rowData["updated_at"]).format("YYYY-MM-DD HH:mm");
                    } else {
                        line["Updated At"] = "";
                    }
                    if (rowData["last_login"]) {
                        line["Last Login"] = moment(rowData["last_login"]).format("YYYY-MM-DD HH:mm");
                    } else {
                        line["Last Login"] = "";
                    }
                    if (rowData["expiration_date"]) {
                        line["Expiration Date"] = moment(rowData["expiration_date"]).format("YYYY-MM-DD HH:mm");
                    } else {
                        line["Expiration Date"] = "";
                    }
                    if (rowData["last_password_change"]){
                        line["Last password change"] = moment(rowData["last_password_change"]).format("YYYY-MM-DD HH:mm");
                    } else {
                        line["Last password change"] = "";
                    }
                    if (rowData["email"]){
                        line["Email"] = rowData["email"];
                    } else {
                        line["Email"] = "";
                    }
                    let fsProvider = "Local storage: "+rowData["home_dir"];
                    switch (rowData["filesystem"]["provider"]){
                        case 1:
                            fsProvider = "S3 Compatible";
                            break;
                        case 2:
                            fsProvider = "Google Cloud Storage";
                            break;
                        case 3:
                            fsProvider = "Azure Blob";
                            break;
                        case 4:
                            fsProvider = "Local storage encrypted: "+rowData["home_dir"];
                            break;
                        case 5:
                            fsProvider = "SFTP";
                            break;
                        case 6:
                            fsProvider = "HTTP";
                            break;
                    }
                    line["Filesystem"] = fsProvider;
                    let virtualFolders = [];
                    let rowFolders = rowData["virtual_folders"];
                    if (rowFolders){
                        for (i = 0; i < rowFolders.length; i++){
                            virtualFolders.push(`${rowFolders[i].virtual_path} (${rowFolders[i].name})`);
                        }
                    }
                    line["Virtual Folders"] = virtualFolders.join(", ");

                    let permissions = "";
                    for (let key in rowData["permissions"]){
                        let values = rowData["permissions"][key];
                        if (values){
                            permissions+=`${key}: [${values.join(", ")}]; `;
                        } else {
                            permissions+=`${key}: []; `;
                        }
                    }
                    line["Permissions"] = permissions;

                    if (rowData["max_sessions"] > 0){
                        line["Max sessions"] = rowData["max_sessions"];
                    } else {
                        line["Max sessions"] = ""
                    }
                    if (rowData["quota_size"] > 0){
                        let used = rowData["used_quota_size"];
                        if (!used){
                            used = 0;
                        }
                        line["Quota size"] = fileSizeIEC(used)+"/"+fileSizeIEC(rowData["quota_size"]);
                    } else if (rowData["used_quota_size"] && rowData["used_quota_size"] > 0){
                        let used = rowData["used_quota_size"];
                        if (!used){
                            used = 0;
                        }
                        line["Quota size"] = fileSizeIEC(used);
                    } else {
                        line["Quota size"] = "";
                    }

                    if (rowData["quota_files"] > 0){
                        let used = rowData["used_quota_files"];
                        if (!used){
                            used = 0;
                        }
                        line["Quota files"] = used+"/"+rowData["quota_files"];
                    } else if (rowData["used_quota_files"] && rowData["used_quota_files"] > 0){
                        let used = rowData["used_quota_files"];
                        if (!used){
                            used = 0;
                        }
                        line["Quota files"] = used;
                    } else {
                        line["Quota files"] = "";
                    }

                    if (rowData["total_data_transfer"] > 0) {
                        let used;
                        if (rowData["used_download_data_transfer"]){
                            used+=rowData["used_download_data_transfer"];
                        }
                        if (rowData["used_upload_data_transfer"]){
                            used+=rowData["used_upload_data_transfer"];
                        }
                        line["Data transfer"] = fileSizeIEC(used) + "/" + fileSizeIEC(rowData["total_data_transfer"] * 1048576);
                    } else {
                        let val = "";
                        if (rowData["upload_data_transfer"] > 0) {
                            let used = rowData["used_upload_data_transfer"];
                            if (!used){
                                used = 0;
                            }
                            val = "Upload: "+fileSizeIEC(used)+"/"+fileSizeIEC(rowData["upload_data_transfer"] * 1048576);
                        }
                        if (rowData["download_data_transfer"] > 0) {
                            let used = rowData["used_download_data_transfer"];
                            if (!used){
                                used = 0;
                            }
                            if (val){
                                val += ". ";
                            }
                            val += "Download: "+fileSizeIEC(used)+"/"+fileSizeIEC(rowData["download_data_transfer"] * 1048576);
                        }
                        line["Data transfer"] = val;
                    }

                    if (filters["allowed_ip"]){
                        line["Allowed IPs/Networks"] = filters["allowed_ip"].join(", ");
                    } else {
                        line["Allowed IPs/Networks"] = "";
                    }
                    if (filters["denied_ip"]){
                        line["Denied IPs/Networks"] = filters["denied_ip"].join(", ");
                    } else {
                        line["Denied IPs/Networks"] = "";
                    }
                    if (filters["denied_protocols"]){
                        line["Denied protocols"] = filters["denied_protocols"].join(", ");
                    } else {
                        line["Denied protocols"] = "";
                    }
                    if (filters["denied_login_methods"]){
                        line["Denied login methods"] = filters["denied_login_methods"].join(", ");
                    } else {
                        line["Denied login methods"] = "";
                    }
                    if (filters["max_upload_file_size"]){
                        line["Max upload size"] = fileSizeIEC(filters["max_upload_file_size"]);
                    } else {
                        line["Max upload size"] = "";
                    }

                    let totpConfig = filters["totp_config"];
                    let totpProtocols = "";
                    if (totpConfig && totpConfig["enabled"]){
                        totpProtocols = totpConfig["protocols"].join(", ");
                        line["Two-factor auth"] = totpProtocols;
                    } else {
                        line["Two-factor auth"] = "Disabled";
                    }

                    let webClientPermissions = filters["web_client"];
                    if (webClientPermissions){
                        line["WebClient permissions"] = webClientPermissions.join(", ");
                    } else {
                        line["WebClient permissions"] = "";
                    }

                    let groups = [];
                    let rowGroups = rowData["groups"];
                    if (rowGroups){
                        for (i = 0; i < rowGroups.length; i++ ){
                            groups.push(rowGroups[i].name);
                        }
                    }
                    line["Groups"] = groups.join(", ");
                    if (rowData["role"]){
                        line["Role"] = rowData["role"];
                    } else {
                        line["Role"] = "";
                    }
                    if (rowData["public_keys"]){
                        line["Public keys"] = rowData["public_keys"].length;
                    } else {
                        line["Public keys"] = "0";
                    }
                    if (filters["tls_certs"]){
                        line["TLS certificates"] = filters["tls_certs"].length;
                    } else {
                        line["TLS certificates"] = "0";
                    }
                    if (rowData["description"]){
                        line["Description"] = rowData["description"];
                    } else {
                        line["Description"] = "";
                    }
                    if (rowData["additional_info"]){
                        line["Additional info"] = rowData["additional_info"];
                    } else {
                        line["Additional info"] = "";
                    }

                    data.push(line);
                });

                let csv = Papa.unparse(data, {
                    quotes: false,
	                quoteChar: '"',
	                escapeChar: '"',
	                delimiter: ",",
	                header: true,
	                newline: "\r\n",
	                skipEmptyLines: false,
	                columns: null,
                    escapeFormulae: true
                });
                let blob = new Blob([csv], {type: "text/csv"});
                let ts = moment().format("YYYY_MM_DD_HH_mm_ss");
                saveAs(blob, `users_${ts}.csv`);

                this.removeAttribute('data-kt-indicator');
                this.disabled = false;
            });
        }

        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('{{.UserURL}}' + "/" + encodeURIComponent(rowData['username']));
                });
            });

            const templateButtons = document.querySelectorAll('[data-table-action="template_row"]');
            templateButtons.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('{{.UserTemplateURL}}' + "?from=" + encodeURIComponent(rowData['username']));
                });
            });

            const quotaScanButtons = document.querySelectorAll('[data-table-action="quota_scan_row"]');
            quotaScanButtons.forEach(d => {
                let el = $(d);
                el.off("click");
                el.on("click", function(e){
                    e.preventDefault();
                    let rowData = dt.row(e.target.closest('tr')).data();
                    quotaScanAction(rowData['username']);
                });
            });

            const diable2FAButtons = document.querySelectorAll('[data-table-action="disable_2fa_row"]');
            diable2FAButtons.forEach(d => {
                let el = $(d);
                el.off("click");
                el.on("click", function(e){
                    e.preventDefault();
                    let rowData = dt.row(e.target.closest('tr')).data();
                    disableSeconFactorAction(rowData['username']);
                });
            });

            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()['username']);
                });
            });
        }

        return {
            init: function () {
                initDatatable();
                handleDatatableActions();
            }
        }
    }();

    $(document).on("i18nshow", function(){
        datatable.init();
    });
</script>
{{- end}}