/** * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ (function() { "use strict"; // The chunk size of tailing the files, i.e., how many bytes will be shown // in the preview. var TAIL_CHUNK_SIZE = 32768; //This stores the current directory which is being browsed var current_directory = ""; function show_err_msg(msg) { $('#alert-panel-body').html(msg); $('#alert-panel').show(); } $(window).bind('hashchange', function () { $('#alert-panel').hide(); var dir = decodeURIComponent(window.location.hash.slice(1)); if(dir == "") { dir = "/"; } if(current_directory != dir) { browse_directory(dir); } }); function network_error_handler(url) { return function (jqxhr, text, err) { switch(jqxhr.status) { case 401: var msg = '

Authentication failed when trying to open ' + url + ': Unauthorized.

'; break; case 403: if(jqxhr.responseJSON !== undefined && jqxhr.responseJSON.RemoteException !== undefined) { var msg = '

' + jqxhr.responseJSON.RemoteException.message + "

"; break; } var msg = '

Permission denied when trying to open ' + url + ': ' + err + '

'; break; case 404: var msg = '

Path does not exist on HDFS or WebHDFS is disabled. Please check your path or enable WebHDFS

'; break; default: var msg = '

Failed to retrieve data from ' + url + ': ' + err + '

'; } show_err_msg(msg); }; } function append_path(prefix, s) { var l = prefix.length; var p = l > 0 && prefix[l - 1] == '/' ? prefix.substring(0, l - 1) : prefix; return p + '/' + s; } function get_response(data, type) { return data[type] !== undefined ? data[type] : null; } function get_response_err_msg(data) { return data.RemoteException !== undefined ? data.RemoteException.message : ""; } function delete_path(inode_name, absolute_file_path) { $('#delete-modal-title').text("Delete - " + inode_name); $('#delete-prompt').text("Are you sure you want to delete " + inode_name + " ?"); $('#delete-button').click(function() { // DELETE /webhdfs/v1/?op=DELETE&recursive= var url = '/webhdfs/v1' + encode_path(absolute_file_path) + '?op=DELETE' + '&recursive=true'; $.ajax(url, { type: 'DELETE' }).done(function(data) { browse_directory(current_directory); }).error(network_error_handler(url) ).complete(function() { $('#delete-modal').modal('hide'); $('#delete-button').button('reset'); }); }) $('#delete-modal').modal(); } /* This method loads the checkboxes on the permission info modal. It accepts * the octal permissions, eg. '644' or '755' and infers the checkboxes that * should be true and false */ function view_perm_details(e, filename, abs_path, perms) { $('.explorer-perm-links').popover('destroy'); e.popover({html: true, content: $('#explorer-popover-perm-info').html(), trigger: 'focus'}) .on('shown.bs.popover', function(e) { var popover = $(this), parent = popover.parent(); //Convert octal to binary permissions var bin_perms = parseInt(perms, 8).toString(2); bin_perms = bin_perms.length == 9 ? "0" + bin_perms : bin_perms; parent.find('#explorer-perm-cancel').on('click', function() { popover.popover('destroy'); }); parent.find('#explorer-set-perm-button').off().click(function() { set_permissions(abs_path); }); parent.find('input[type=checkbox]').each(function(idx, element) { var e = $(element); e.prop('checked', bin_perms.charAt(9 - e.attr('data-bit')) == '1'); }); }) .popover('show'); } // Use WebHDFS to set permissions on an absolute path function set_permissions(abs_path) { var p = 0; $.each($('.popover .explorer-popover-perm-body input:checked'), function(idx, e) { p |= 1 << (+$(e).attr('data-bit')); }); var permission_mask = p.toString(8); // PUT /webhdfs/v1/?op=SETPERMISSION&permission= var url = '/webhdfs/v1' + encode_path(abs_path) + '?op=SETPERMISSION' + '&permission=' + permission_mask; $.ajax(url, { type: 'PUT' }).done(function(data) { browse_directory(current_directory); }).error(network_error_handler(url)) .complete(function() { $('.explorer-perm-links').popover('destroy'); }); } function encode_path(abs_path) { abs_path = encodeURIComponent(abs_path); var re = /%2F/g; return abs_path.replace(re, '/'); } function view_file_details(path, abs_path) { function show_block_info(blocks) { var menus = $('#file-info-blockinfo-list'); menus.empty(); menus.data("blocks", blocks); menus.change(function() { var d = $(this).data('blocks')[$(this).val()]; if (d === undefined) { return; } dust.render('block-info', d, function(err, out) { $('#file-info-blockinfo-body').html(out); }); }); for (var i = 0; i < blocks.length; ++i) { var item = $(''); menus.append(item); } menus.change(); } abs_path = encode_path(abs_path); var url = '/webhdfs/v1' + abs_path + '?op=GET_BLOCK_LOCATIONS'; $.ajax({url: url, dataType: 'text'}).done(function(data_text) { var data = JSONParseBigNum(data_text); var d = get_response(data, "LocatedBlocks"); if (d === null) { show_err_msg(get_response_err_msg(data)); return; } $('#file-info-tail').hide(); $('#file-info-title').text("File information - " + path); var download_url = '/webhdfs/v1' + abs_path + '?op=OPEN'; $('#file-info-download').attr('href', download_url); $('#file-info-preview').click(function() { var offset = d.fileLength - TAIL_CHUNK_SIZE; var url = offset > 0 ? download_url + '&offset=' + offset : download_url; $.get(url, function(t) { $('#file-info-preview-body').val(t); $('#file-info-tail').show(); }, "text").error(network_error_handler(url)); }); if (d.fileLength > 0) { show_block_info(d.locatedBlocks); $('#file-info-blockinfo-panel').show(); } else { $('#file-info-blockinfo-panel').hide(); } $('#file-info').modal(); }).error(network_error_handler(url)); } /**Use X-editable to make fields editable with a nice UI. * elementType is the class of element(s) you want to make editable * op is the WebHDFS operation that will be triggered * parameter is (currently the 1) parameter which will be passed along with * the value entered by the user */ function makeEditable(elementType, op, parameter) { $(elementType).each(function(index, value) { $(this).editable({ url: function(params) { var inode_name = $(this).closest('tr').attr('inode-path'); var absolute_file_path = append_path(current_directory, inode_name); var url = '/webhdfs/v1' + encode_path(absolute_file_path) + '?op=' + op + '&' + parameter + '=' + encodeURIComponent(params.value); return $.ajax(url, { type: 'PUT', }) .error(network_error_handler(url)) .success(function() { browse_directory(current_directory); }); }, error: function(response, newValue) {return "";} }); }); } function func_size_render(data, type, row, meta) { if(type == 'display') { return dust.filters.fmt_bytes(data); } else return data; } // Change the format of date-time depending on how old the // the timestamp is. If older than 6 months, no need to be // show exact time. function func_time_render(data, type, row, meta) { if(type == 'display') { var cutoff = moment().subtract(6, 'months').unix() * 1000; if(data < cutoff) { return moment(Number(data)).format('MMM DD YYYY'); } else { return moment(Number(data)).format('MMM DD HH:mm'); } } return data; } function browse_directory(dir) { var HELPERS = { 'helper_date_tostring' : function (chunk, ctx, bodies, params) { var value = dust.helpers.tap(params.value, chunk, ctx); return chunk.write('' + moment(Number(value)).format('ddd MMM DD HH:mm:ss ZZ YYYY')); } }; var url = '/webhdfs/v1' + encode_path(dir) + '?op=LISTSTATUS'; $.get(url, function(data) { var d = get_response(data, "FileStatuses"); if (d === null) { show_err_msg(get_response_err_msg(data)); return; } current_directory = dir; $('#directory').val(dir); window.location.hash = dir; var base = dust.makeBase(HELPERS); dust.render('explorer', base.push(d), function(err, out) { $('#panel').html(out); $('.explorer-browse-links').click(function() { var type = $(this).attr('inode-type'); var path = $(this).closest('tr').attr('inode-path'); var abs_path = append_path(current_directory, path); if (type == 'DIRECTORY') { browse_directory(abs_path); } else { view_file_details(path, abs_path); } }); //Set the handler for changing permissions $('.explorer-perm-links').click(function() { var filename = $(this).closest('tr').attr('inode-path'); var abs_path = append_path(current_directory, filename); var perms = $(this).closest('tr').attr('data-permission'); view_perm_details($(this), filename, abs_path, perms); }); makeEditable('.explorer-owner-links', 'SETOWNER', 'owner'); makeEditable('.explorer-group-links', 'SETOWNER', 'group'); makeEditable('.explorer-replication-links', 'SETREPLICATION', 'replication'); $('.explorer-entry .glyphicon-trash').click(function() { var inode_name = $(this).closest('tr').attr('inode-path'); var absolute_file_path = append_path(current_directory, inode_name); delete_path(inode_name, absolute_file_path); }); $('#file-selector-all').click(function() { $('.file_selector').prop('checked', $('#file-selector-all')[0].checked ); }); //This needs to be last because it repaints the table $('#table-explorer').dataTable( { 'lengthMenu': [ [25, 50, 100, -1], [25, 50, 100, "All"] ], 'columns': [ { 'orderable' : false }, //select {'searchable': false }, //Permissions null, //Owner null, //Group { 'searchable': false, 'render': func_size_render}, //Size { 'searchable': false, 'render': func_time_render}, //Last Modified { 'searchable': false }, //Replication null, //Block Size null, //Name { 'orderable' : false } //Trash ], "deferRender": true }); }); }).error(network_error_handler(url)); } function init() { dust.loadSource(dust.compile($('#tmpl-explorer').html(), 'explorer')); dust.loadSource(dust.compile($('#tmpl-block-info').html(), 'block-info')); var b = function() { browse_directory($('#directory').val()); }; $('#btn-nav-directory').click(b); var dir = window.location.hash.slice(1); if(dir == "") { window.location.hash = "/"; } else { browse_directory(dir); } } $('#btn-create-directory').on('show.bs.modal', function(event) { $('#new_directory_pwd').text(current_directory); }); $('#btn-create-directory-send').click(function () { $(this).prop('disabled', true); $(this).button('complete'); var url = '/webhdfs/v1' + encode_path(append_path(current_directory, $('#new_directory').val())) + '?op=MKDIRS'; $.ajax(url, { type: 'PUT' } ).done(function(data) { browse_directory(current_directory); }).error(network_error_handler(url) ).complete(function() { $('#btn-create-directory').modal('hide'); $('#btn-create-directory-send').button('reset'); }); }) $('#modal-upload-file-button').click(function() { $(this).prop('disabled', true); $(this).button('complete'); var files = [] var numCompleted = 0 for(var i = 0; i < $('#modal-upload-file-input').prop('files').length; i++) { (function() { var file = $('#modal-upload-file-input').prop('files')[i]; var url = '/webhdfs/v1' + current_directory; url = encode_path(append_path(url, file.name)); url += '?op=CREATE&noredirect=true'; files.push( { file: file } ) files[i].request = $.ajax({ type: 'PUT', url: url, processData: false, crossDomain: true }); })() } for(var f in files) { (function() { var file = files[f]; file.request.done(function(data) { var url = data['Location']; $.ajax({ type: 'PUT', url: url, data: file.file, processData: false, crossDomain: true }).complete(function(data) { numCompleted++; if(numCompleted == files.length) { $('#modal-upload-file').modal('hide'); $('#modal-upload-file-button').button('reset'); browse_directory(current_directory); } }).error(function(jqXHR, textStatus, errorThrown) { numCompleted++; show_err_msg("Couldn't upload the file " + file.file.name + ". "+ errorThrown); }); }).error(function(jqXHR, textStatus, errorThrown) { numCompleted++; show_err_msg("Couldn't find datanode to write file. " + errorThrown); }); })(); } }); //Store the list of files which have been checked into session storage function store_selected_files(current_directory) { sessionStorage.setItem("source_directory", current_directory); var selected_files = $("input:checked.file_selector"); var selected_file_names = new Array(); selected_files.each(function(index) { selected_file_names[index] = $(this).closest('tr').attr('inode-path'); }) sessionStorage.setItem("selected_file_names", JSON.stringify(selected_file_names)); alert("Cut " + selected_file_names.length + " files/directories"); } //Retrieve the list of files from session storage and rename them to the current //directory function paste_selected_files() { var files = JSON.parse(sessionStorage.getItem("selected_file_names")); var source_directory = sessionStorage.getItem("source_directory"); $.each(files, function(index, value) { var url = "/webhdfs/v1" + encode_path(append_path(source_directory, value)) + '?op=RENAME&destination=' + encode_path(append_path(current_directory, value)); $.ajax({ type: 'PUT', url: url }).done(function(data) { if(index == files.length - 1) { browse_directory(current_directory); } }).error(function(jqXHR, textStatus, errorThrown) { show_err_msg("Couldn't move file " + value + ". " + errorThrown); }); }) } $('#explorer-cut').click(function() { store_selected_files(current_directory); }); $('#explorer-paste').click(function() { paste_selected_files(); }); init(); })();