/*** * Here is our tree to browse a specific backup * The tree is first initialized with the 'root' part of the backup. * JSON example: * [ * { * "name": "/", * "parent": "", * "type": "d" * } * ] * This JSON is then parsed into another one to initialize our tree. * Each 'directory' is expandable. * A new JSON is returned for each one of them on-demand. * JSON output: * [ * { * "name": "etc", * "parent": "/", * "type": "d" * }, * { * "name": "home", * "parent": "/", * "type": "d" * } * ] */ $( document ).ready(function() { var fixNeeded = false; var treeCollapsed = function() { var btn = $("#btn-expand-collapse-tree"); btn.data('collapsed', true); btn.html(' {{ _("Expand tree")|escape }}'); }; $('[data-toggle="tooltip"]').tooltip(); $("#tree").fancytree({ checkbox: true, selectMode: 3, extensions: ["glyph", "table", "gridnav", "filter"], glyph: { preset: "bootstrap3", map: { doc: "glyphicon-file", docOpen: "glyphicon-file", checkbox: "glyphicon-unchecked", checkboxSelected: "glyphicon-check", checkboxUnknown: "glyphicon-share", dragHelper: "glyphicon-play", dropMarker: "glyphicon-arrow-right", error: "glyphicon-warning-sign", expanderClosed: "glyphicon-plus-sign", expanderLazy: "glyphicon-plus-sign", // expanderLazy: "glyphicon-expand", expanderOpen: "glyphicon-minus-sign", // expanderOpen: "glyphicon-collapse-down", folder: "glyphicon-folder-close", folderOpen: "glyphicon-folder-open", loading: "glyphicon-refresh fancytree-helper-spin" } }, persist: { expandLazy: false, overrideSource: false, store: "cookie", cookie: { path: '{{ url_for("api.client_tree", name=cname, backup=nbackup, server=server) }}', }, }, source: function() { {% if edit and edit.found -%} url = '{{ url_for("api.client_tree", name=cname, backup=nbackup, server=server, root=edit.roots, recursive=True, selected=True) }}'; {% else -%} url = '{{ url_for("api.client_tree", name=cname, backup=nbackup, server=server) }}'; {% endif -%} return $.getJSON(url, function(data) { $("#waiting-container").hide(); $("#tree-container").show(); return data; }) .fail(buiFail); }, lazyLoad: function(event, data) { fixNeeded = true; var node = data.node; p = node.key; if (p !== "/") p += '/'; p = encodeURIComponent(p); data.result = $.getJSON('{{ url_for("api.client_tree", name=cname, backup=nbackup, server=server) }}?root='+p); }, loadChildren: function(event, data) { // This is a hack to select all children of a selected node after // lazy loading. // The 'fixNeeded' flag is here to ensure we apply the fix only after // a lazy loading. if (fixNeeded) { data.node.fixSelection3AfterClick(); } }, init: function() { $('#tree').floatThead({ position: 'auto', autoReflow: true, top: $('.navbar').height(), }); }, scrollParent: $(window), renderColumns: function(event, data) { var node = data.node; $tdList = $(node.tr).find(">td"); $tdList.eq(1).text(node.data.mode); $tdList.eq(2).text(node.data.uid); $tdList.eq(3).text(node.data.gid); $tdList.eq(4).text(node.data.size); $tdList.eq(5).html(''+moment(node.data.date).tz(TIMEZONE).format({{ g.date_format|tojson }})+''); }, select: function(event, data) { toggleRestorationForms(data.tree); }, collapse: function(event, data) { if (!data.node.data.parent) { treeCollapsed(); } }, expand: function(event, data) { var btn = $("#btn-expand-collapse-tree"); if (btn.data('collapsed')) { btn.data('collapsed', false); btn.html(' {{ _("Collapse tree")|escape }}'); } } }); var tree = $("#tree").fancytree("getTree"); var toggleRestorationForms = function(my_tree) { var s = my_tree.getSelectedNodes(); if (s.length > 0) { $("#restore-form").show(); $("#server-initiated-form").show(); v = []; $.each(s, function(i, n) { v.push({key: n.key, folder: n.folder}); }); r = {restore:v}; $("input[name=list]").val(JSON.stringify(r)); $("input[name=list-sc]").val(JSON.stringify(r)); } else { $("#restore-form").hide(); $("#server-initiated-form").hide(); $("input[name=list]").val(''); $("input[name=list-sc]").val(''); } }; $('#tree').on('fancytreeinit', function() { toggleRestorationForms(tree); }); $("input[name=search-tree]").keyup(function(e){ var n, leavesOnly = $("#leavesOnly").is(":checked"), match = $(this).val(); if(e && e.which === $.ui.keyCode.ESCAPE || $.trim(match) === "") { $("#btnResetSearch").click(); return; } if($("#regex").is(":checked")) { // Pass function to perform match n = tree.filterNodes(function(node) { return new RegExp(match, "i").test(node.title); }, leavesOnly); } else { // Pass a string to perform case insensitive matching n = tree.filterNodes(match, leavesOnly); } $("#btnResetSearch").attr("disabled", false); $("span#matches").text("(" + n + " matches)"); }); $("#btnResetSearch").click(function(e){ $("input[name=search-tree]").val(""); $("span#matches").text(""); tree.clearFilter(); $("#btnResetSearch").attr("disabled", true); }).attr("disabled", true); $("input#hideMode").change(function(e){ tree.options.filter.mode = $(this).is(":checked") ? "hide" : "dimm"; tree.clearFilter(); $("input[name=search-tree]").keyup(); e.stopPropagation(); }); $("input#leavesOnly").change(function(e){ tree.clearFilter(); $("input[name=search-tree]").keyup(); e.stopPropagation(); }); $("input#regex").change(function(e){ tree.clearFilter(); $("input[name=search-tree]").keyup(); e.stopPropagation(); }); {% if config.WITH_CELERY -%} var _check_task_schedule = undefined; $('#cancel-running-restore').on('click', function(e) { var task_id = $(this).data('task_id'); var url = '{{ url_for("api.task_status", task_type="restore", task_id="", server=server) }}'+task_id; if (!task_id) { return; } if (_check_task_schedule) { clearTimeout(_check_task_schedule); _check_task_schedule = undefined; } $.ajax({ url: url, type: 'DELETE', }); }); {% endif -%} $("#form-restore").on('submit', function(e) { var $preparingFileModal = $("#restore-modal"); $preparingFileModal.modal('toggle'); {% if config.WITH_CELERY -%} var check_task = function(task_id) { $.getJSON('{{ url_for("api.task_status", task_type="restore", task_id="", server=server) }}'+task_id) .done(function(data) { if (data.state != 'SUCCESS') { _check_task_schedule = setTimeout(function() { check_task(task_id); }, 2000); } else { $.fileDownload(data.location, { successCallback: function (url) { $preparingFileModal.modal('hide'); }, failCallback: function (responseHtml, url) { $preparingFileModal.modal('hide'); if (responseHtml == 'encrypted') { msg = '{{ _("The backup seems encrypted, please provide the encryption key in the \'Download options\' form.")|escape }}'; } else { msg = responseHtml; } $("#error-response").empty().text(msg); $("#error-modal").modal('toggle'); }, }); } }) .fail(function(xhr, stat, err) { $preparingFileModal.modal('hide'); if (xhr.status != 502) { buiFail(xhr, stat, err); } if ('responseJSON' in xhr && 'message' in xhr.responseJSON) { resp = xhr.responseJSON.message; } else if ('responseText' in xhr) { resp = xhr.responseText; } else { return false; } if (resp == 'encrypted') { msg = '{{ _("The backup seems encrypted, please provide the encryption key in the \'Download options\' form.")|escape }}'; } else { msg = resp; } $("#error-response").empty().text(msg); $("#error-modal").modal('toggle'); }); }; $.post($(this).prop('action'), $(this).serialize()) .done(function(data) { check_task(data.id); $('#cancel-running-restore').data('task_id', data.id); }) .fail(function(xhr, stat, err) { $preparingFileModal.modal('hide'); buiFail(xhr, stat, err); }); {% else -%} $.fileDownload($(this).prop('action'), { successCallback: function (url) { $preparingFileModal.modal('hide'); }, failCallback: function (responseHtml, url) { $preparingFileModal.modal('hide'); if (responseHtml == 'encrypted') { msg = '{{ _("The backup seems encrypted, please provide the encryption key in the \'Download options\' form.")|escape }}'; } else { msg = responseHtml; } $("#error-response").empty().text(msg); $("#error-modal").modal('toggle'); }, httpMethod: "POST", data: $(this).serialize() }); {% endif -%} e.preventDefault(); return false; }); $("#form-server-initiated").on('submit', function(e) { e.preventDefault(); var form = $(this); var url = "{{ url_for('api.server_restore', name=cname, backup=nbackup, server=server) }}"; $.ajax({ url: url, headers: { 'X-From-UI': true }, type: 'PUT', data: form.serialize(), dataType: 'text json', }) .fail(buiFail) .done(function(data) { notifAll(data); }); return false; }); $("#btn-load-all-tree").on('click', function() { var btn = $("#btn-load-all-tree"); btn.prop('disabled', true); btn.html(' {{ _("Loading") }}'); var load_all_tree = function(url) { $.get(url) .fail(buiFail) .done(function(data) { tree.reload(data); btn.html(' {{ _("Nodes loaded") }}'); $('#'+btn.attr('aria-describedby')).remove(); btn.blur(); }); }; {% if config.WITH_CELERY -%} var url = "{{ url_for('api.task_client_tree_all', name=cname, backup=nbackup, server=server) }}"; var _task_status_schedule = undefined; var task_status = function(task_id) { $.getJSON('{{ url_for("api.task_status", task_type="browse", task_id="", server=server) }}'+task_id) .fail(buiFail) .done(function(data) { if (data.state != 'SUCCESS') { _task_status_schedule = setTimeout(function() { task_status(task_id); }, 2000); } else { load_all_tree(data.location); } }); }; $.post(url) .fail(buiFail) .done(function(data) { task_status(data.id); }); {% else -%} var url = "{{ url_for('api.client_tree_all', name=cname, backup=nbackup, server=server) }}"; load_all_tree(url); {% endif -%} }); $("#btn-expand-collapse-tree").on('click', function() { var btn = $("#btn-expand-collapse-tree"); var collapsed = btn.data('collapsed'); tree.getRootNode().visit(function(node) { if (collapsed) { if (node.lazy && !node.children && node.title != "/") { return 'skip'; } } node.setExpanded(collapsed); }); if (!collapsed) { treeCollapsed(); } }); $("#btn-clear").on('click', function() { tree.visit(function(node){ node.setSelected(false); }); return false; }); $(".dropdown-menu label, .dropdown-menu input, .dropdown-menu li").click(function(e) { e.stopPropagation(); }); {% if encrypted -%} $("#perform").attr("disabled", "disabled"); $("#pass").on('change', function() { if ($(this).val().length > 0) { $("#perform").removeAttr("disabled"); $("#notice").hide(); } else { $("#perform").attr("disabled", "disabled"); $("#notice").show(); } }); {% endif -%} {% if edit -%} $('#btn-cancel-restore').on('click', function(e) { {% if edit.orig_client -%} url = '{{ url_for("api.is_server_restore", name=edit.to, server=server) }}'; {% else -%} url = '{{ url_for("api.is_server_restore", name=cname, server=server) }}'; {% endif -%} $.ajax({ url: url, headers: { 'X-From-UI': true }, type: 'DELETE' }).done(function(data) { notifAll(data); if (data[0] == 0) { $('#btn-cancel-restore').hide(); } }).fail(buiFail); }); {% endif -%} }); var app = angular.module('MainApp', ['ngSanitize']); app.controller('BrowseCtrl', function($scope, $http) { $scope.sc_restore = {}; $scope.load_all = false; {% if edit and edit.orig_client -%} $scope.sc_restore.to = '{{ edit.to }}'; {% endif -%} $http.get('{{ url_for("api.clients_all", server=server) }}', { headers: { 'X-From-UI': true } }) .then(function(response) { $scope.sc_restore.clients = response.data; }, function(response) { notifAll(response.data); }); $http.get('{{ url_for("api.setting_options", server=server) }}', { headers: { 'X-From-UI': true } }) .then(function(response) { $scope.sc_restore.enabled = response.data.server_can_restore; $scope.load_all = response.data.batch_list_supported; }, function(response) { notifAll(response.data); }); });