From 8b99a33e6ec2d351d866a09b64e4d3c60f6ad939 Mon Sep 17 00:00:00 2001 From: Aditya Toshniwal Date: Fri, 10 Jan 2020 12:53:32 +0530 Subject: [PATCH] 1) Ensure that path file name should not disappear when changing ext from the dropdown in file explorer dialog. Fixes #3812. 2) Fix column resizable issue in the file explorer dialog. Fixes #4827. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Used tablesorter's resizable widget. --- docs/en_US/release_notes_4_18.rst | 2 + web/package.json | 2 +- .../file_manager/static/css/file_manager.css | 4 +- .../misc/file_manager/static/js/utility.js | 455 ++++++++++-------- .../static/scss/_file_manager.scss | 41 +- web/pgadmin/static/scss/_pgadmin.style.scss | 21 +- web/yarn.lock | 8 +- 7 files changed, 299 insertions(+), 234 deletions(-) diff --git a/docs/en_US/release_notes_4_18.rst b/docs/en_US/release_notes_4_18.rst index 3b86ad193..8d4a9a568 100644 --- a/docs/en_US/release_notes_4_18.rst +++ b/docs/en_US/release_notes_4_18.rst @@ -19,4 +19,6 @@ Housekeeping Bug fixes ********* +| `Issue #3812 `_ - Ensure that path file name should not disappear when changing ext from the dropdown in file explorer dialog. +| `Issue #4827 `_ - Fix column resizable issue in the file explorer dialog. | `Issue #5074 `_ - Fix an issue where select, insert and update scripts on tables throwing an error. \ No newline at end of file diff --git a/web/package.json b/web/package.json index 022dad184..cb47fbfbd 100644 --- a/web/package.json +++ b/web/package.json @@ -91,7 +91,7 @@ "snapsvg": "^0.5.1", "spectrum-colorpicker": "^1.8.0", "split.js": "^1.5.10", - "tablesorter": "^2.31.1", + "tablesorter": "^2.31.2", "tempusdominus-bootstrap-4": "^5.1.2", "tempusdominus-core": "^5.0.3", "underscore": "^1.9.1", diff --git a/web/pgadmin/misc/file_manager/static/css/file_manager.css b/web/pgadmin/misc/file_manager/static/css/file_manager.css index 1fc5bd374..9bb060c03 100644 --- a/web/pgadmin/misc/file_manager/static/css/file_manager.css +++ b/web/pgadmin/misc/file_manager/static/css/file_manager.css @@ -14,10 +14,10 @@ .fileinfo #contents li .fm_file_rename, .fileinfo table#contents tr td:first-child input.fm_file_rename { display: none; - width: 80px; + width: 100%; margin: 0 auto; text-align: center; - height: 17px; + height: inherit; } .fileinfo > h1 { diff --git a/web/pgadmin/misc/file_manager/static/js/utility.js b/web/pgadmin/misc/file_manager/static/js/utility.js index 25aeaebe0..bd0e51cac 100644 --- a/web/pgadmin/misc/file_manager/static/js/utility.js +++ b/web/pgadmin/misc/file_manager/static/js/utility.js @@ -432,13 +432,207 @@ define([ return permission; }; + var getNoDataView = function(data) { + var lg = pgAdmin.FileUtils.lg; + var cap_no_folders = ['upload', 'create']; + data.Capabilities = cap_no_folders; + bindToolbar(data); + return `
${lg.could_not_retrieve_folder}
`; + }; + + var getGridView = function(data, capabilities) { + let ret_ele = '
    ', + no_data = _.isEmpty(data); + + if(!no_data) { + ret_ele += Object.keys(data).sort(function keyOrder(x, y) { + return pgAdmin.natural_sort(x.toLowerCase(), y.toLowerCase()); + }).map(function(key) { + let item_data = data[key], + props = item_data.Properties, + filename = _.escape(item_data.Filename), + icon_type = '', + cap_classes = ''; + + cap_classes = Object.keys(capabilities).map(function(cap) { + if (has_capability(item_data, capabilities[cap])) { + return 'cap_' + capabilities[cap]; + } + }).join(' '); + + item_data.Capabilities = capabilities; + bindToolbar(item_data); + + if (item_data.file_type == 'dir') { + icon_type = 'fa fa-folder-open fm_folder_grid'; + } else if (item_data.file_type == 'drive') { + icon_type = 'fa fa-hdd-o fm_drive'; + } else { + icon_type = 'fa fa-file-text-o fm_file_grid'; + } + + /* For the html ele */ + let item_ele = + `
  • +
    + `; + + if (item_data.Protected == 1) { + item_ele += ''; + } + + item_ele += '
    '; + + if (!has_capability(item_data, 'rename')) { + item_ele += `${filename}`; + } else { + item_ele += + `
    + + ${filename} +
    `; + } + if (props.Width && props.Width != '') { + item_ele += `${props.Width}x${props.Height}`; + } + if (props.Size && props.Size != '') { + item_ele += `${props.Size}`; + } + if (props['Date Created'] && props['Date Created'] != '') { + item_ele += `${props['Date Created']}`; + } + if (props['Date Modified'] && props['Date Modified'] != '') { + item_ele += `${props['Date Modified']}`; + } + item_ele += '
  • '; + + return item_ele; + }).join('\n'); + } + + ret_ele += '
'; + + if(no_data) { + ret_ele += getNoDataView(data); + } + return ret_ele; + }; + + var getListView = function(data, capabilities) { + let lg = pgAdmin.FileUtils.lg; + let no_data = _.isEmpty(data); + + /* file_listing_table class makes height 100%, because of which No folder message is not displayed + * file_listing_table_no_data will be removed when new folder is created + */ + let ret_ele = + ` + + + + + + + + `; + + if(!no_data) { + ret_ele += Object.keys(data).sort(function keyOrder(x, y) { + return pgAdmin.natural_sort(x.toLowerCase(), y.toLowerCase()); + }).map(function(key) { + let item_data = data[key], + props = item_data.Properties, + icon_type = '', + class_type = '', + cap_classes = ''; + + cap_classes = Object.keys(capabilities).map(function(cap) { + if (has_capability(item_data, capabilities[cap])) { + return 'cap_' + capabilities[cap]; + } + }).join(' '); + + item_data.Capabilities = capabilities; + bindToolbar(item_data); + + if (item_data.file_type == 'dir') { + class_type = 'tbl_folder'; + icon_type = 'fa fa-folder-open fm_folder_list'; + } else if (item_data.file_type == 'drive') { + class_type = 'tbl_drive'; + icon_type = 'fa fa-hdd-o'; + } else { + class_type = 'tbl_file'; + icon_type = 'fa fa-file-text-o'; + } + + /* For the html ele */ + let item_ele = + ` + '; + if (props.Size && props.Size != '') { + item_ele += ``; + } else { + item_ele += ''; + } + + if (props['Date Modified'] && props['Date Modified'] != '') { + item_ele += ``; + } else { + item_ele += ''; + } + + item_ele += ''; + + return item_ele; + }).join('\n'); + } + + ret_ele += + ` +
+ ${lg.name} + + ${lg.size} + + ${lg.modified} +
`; + + let data_protected = ''; + if (item_data.Protected == 1) { + data_protected = ''; + } + if (!has_capability(data[key], 'rename')) { + item_ele += + `${data_protected}; + ${_.escape(item_data.Filename)}`; + } else { + item_ele += + `
+ +
+
+ + ${data_protected} + ${_.escape(item_data.Filename)} +
+
+
`; + } + item_ele += '
${props.Size}${props['Date Modified']}
`; + + if(no_data) { + ret_ele += getNoDataView(data); + } + return ret_ele; + }; /* * Retrieves data for all items within the given folder and * creates a list view. */ - var getFolderInfo = function(path, file_type) { + var getFolderInfo = function(path, file_type, user_input) { $('.storage_dialog #uploader .input-path').prop('disabled', true); if (!file_type) { file_type = ''; @@ -446,6 +640,9 @@ define([ var capabilities = pgAdmin.FileUtils.data.Capabilities; // Update location for status, upload, & new folder functions. pgAdmin.FileUtils.setUploader(path); + if(user_input) { + $('.storage_dialog #uploader .input-path').val(path+user_input); + } // set default selected file type if (file_type === '') { @@ -489,7 +686,6 @@ define([ 'show_hidden': $('#show_hidden').prop('checked'), }; - var lg = pgAdmin.FileUtils.lg; $.ajax({ type: 'POST', data: JSON.stringify(post_data), @@ -513,184 +709,42 @@ define([ var $this, orig_value, newvalue; // generate HTML for files/folder and render into container - if (!_.isEmpty(data)) { - if ($('.fileinfo').data('view') == 'grid') { - result += '
    '; - Object.keys(data).sort(function keyOrder(x, y) { - return pgAdmin.natural_sort(x.toLowerCase(), y.toLowerCase()); - }).forEach(function(key) { - var props = (data[key]).Properties, - cap_classes = ''; - - Object.keys(capabilities).forEach(function(cap) { - if (has_capability(data[key], capabilities[cap])) { - cap_classes += 'cap_' + capabilities[cap]; - } - }); - - (data[key]).Capabilities = capabilities; - bindToolbar(data[key]); - - var class_type; - if ((data[key]).file_type == 'dir') { - class_type = 'fa fa-folder-open fm_folder_grid'; - } else if ((data[key]).file_type == 'drive') { - class_type = 'fa fa-hdd-o fm_drive'; - } else { - class_type = 'fa fa-file-text-o fm_file_grid'; - } - - var fm_filename = (data[key]).Filename; - if (fm_filename.length > 15) { - fm_filename = (data[key]).Filename.substr(0, 10) + '...'; - } - fm_filename = _.escape(fm_filename); - - var file_path_orig = _.escape((data[key]).Path); - - result += '
  • '; - if ((data[key]).Protected == 1) { - result += ''; - } - - result += '
    '; - if (!has_capability(data[key], 'rename')) { - result += '' + fm_filename + ''; - } else { - result += - '
    ' + - '' + fm_filename + - '
    '; - } - if (props.Width && props.Width != '') { - result += '' + - props.Width + 'x' + props.Height + ''; - } - if (props.Size && props.Size != '') { - result += '' + - props.Size + ''; - } - if (props['Date Created'] && props['Date Created'] != '') { - result += '' + - props['Date Created'] + ''; - } - if (props['Date Modified'] && props['Date Modified'] != '') { - result += '' + - props['Date Modified'] + ''; - } - result += '
  • '; - }); - - result += '
'; - } else { - result += ''; - result += ''; - result += ''; - result += ''; - result += ''; - - Object.keys(data).sort(function keyOrder(x, y) { - return pgAdmin.natural_sort(x.toLowerCase(), y.toLowerCase()); - }).forEach(function(key) { - var path = _.escape((data[key]).Path), - props = (data[key]).Properties, - cap_classes = '', - cap, class_type, icon_type; - - for (cap in capabilities) { - if (has_capability(data[key], capabilities[cap])) { - cap_classes += ' cap_' + capabilities[cap]; - } - } - - (data[key]).Capabilities = capabilities; - bindToolbar(data[key]); - - if ((data[key]).file_type == 'dir') { - class_type = 'tbl_folder'; - icon_type = 'fa fa-folder-open fm_folder_list'; - } else if ((data[key]).file_type == 'drive') { - class_type = 'tbl_drive'; - icon_type = 'fa fa-hdd-o'; - } else { - class_type = 'tbl_file'; - icon_type = 'fa fa-file-text-o'; - } - - result += ''; - - var fm_filename = (data[key]).Filename; - if (fm_filename.length > 48) { - fm_filename = (data[key]).Filename.substr(0, 48) + '...'; - } - fm_filename = _.escape(fm_filename); - - result += ''; - } else { - result += '
'+ - '
' + - '' + - data_protected + - '' + fm_filename + '' + - '
' + - '
'; - } - if (props.Size && props.Size != '') { - result += ''; - } else { - result += ''; - } - - if (props['Date Modified'] && props['Date Modified'] != '') { - result += ''; - } else { - result += ''; - } - - result += ''; - }); - - result += ''; - result += '
'; - result += '' + lg.name + '' + lg.size + '' + lg.modified + '
'; - - let data_protected = ''; - if ((data[key]).Protected == 1) { - data_protected = ''; - } - if (!has_capability(data[key], 'rename')) { - result += data_protected; - result += '' + - fm_filename + '' + - props.Size + '' + props['Date Modified'] + '
'; - } + if ($('.fileinfo').data('view') == 'grid') { + result += getGridView(data, capabilities); } else { - if ($('.fileinfo').data('view') == 'grid') { - result += '
    '; - } else { - /* file_listing_table class makes height 100%, because of which No folder message is not displayed - * file_listing_table_no_data will be removed when new folder is created - */ - result += ''; - result += '' + - '' + - '' + - '' + - ''; - result += '
    ' + lg.name + '' + lg.size + '' + lg.modified + '
    '; - } - result += '
    ' + lg.could_not_retrieve_folder + '
    '; - var cap_no_folders = ['upload', 'create']; - - data.Capabilities = cap_no_folders; - bindToolbar(data); + result += getListView(data, capabilities); } // Add the new markup to the DOM. $('.fileinfo .file_listing').html(result); - $('.fileinfo .file_listing #contents').tablesorter(); + + let $listing_table = $('.fileinfo .file_listing .file_listing_table'); + + $listing_table.tablesorter({ + widgets: [ 'resizable', 'stickyHeaders' ], + widgetOptions : { + stickyHeaders_attachTo:'.file_listing', + stickyHeaders_offset: 0, + resizable_widths: ['400px', '100px', '175px'], + }, + }); + + /* In order to fit our UI, some things need to be explicitly set + * as tablesorter resizable is creating trouble. + */ + $listing_table.on( 'resizableComplete', function() { + let wo = this.config.widgetOptions; + $.tablesorter.resizable.setWidth($listing_table.find('th[data-column="2"]'), wo.resizable_widths[2]); + }); + + $listing_table.on( 'tablesorter-ready', function() { + let wo = this.config.widgetOptions; + if($.tablesorter.storage($listing_table[0], 'tablesorter-table-resized-width') === '') { + $.tablesorter.resizable.setWidth($listing_table, $('.fileinfo .file_listing').width()); + } + $.tablesorter.resizable.setWidth($listing_table.find('th[data-column="2"]'), wo.resizable_widths[2]); + $listing_table.trigger('resizableUpdate'); + }); // rename file/folder $('.file_manager button.rename').off().on('click', function(e) { @@ -720,7 +774,7 @@ define([ } else if ($('.fileinfo').data('view') == 'list') { e.stopPropagation(); $this = $('.fileinfo').find( - 'table#contents tbody tr.selected td:first-child div' + 'table#contents tbody tr.selected td.tbl_file' ); orig_value = decodeURI($this.find('span.less_text').html()), newvalue = orig_value.substring(0, orig_value.lastIndexOf('.')); @@ -729,15 +783,15 @@ define([ newvalue = decodeURI(orig_value); } - $this.find('input').toggle().val(newvalue).trigger('focus'); - $this.find('span').toggle(); + $this.find('.fm_file_rename').toggle().val(newvalue).trigger('focus'); + $this.find('.fm_file_name').toggle(); // Rename folder/file on pressing enter key $('.file_manager').off().on('keyup', function(e) { if (e.keyCode == 13) { e.stopPropagation(); - $('.fileinfo table#contents tr.selected td div').find( - 'input' + $('.fileinfo table#contents tr.selected td.tbl_file').find( + 'fm_file_rename' ).trigger('blur'); } }); @@ -1153,8 +1207,15 @@ define([ $('.allowed_file_types select').on('change', function() { var selected_val = $(this).val(), - curr_path = $('.currentpath').val(); - getFolderInfo(curr_path, selected_val); + curr_path = $('.currentpath').val(), + user_input_file = null, + input_path = $('.storage_dialog #uploader .input-path').val(); + if (curr_path.endsWith('/')) { + user_input_file = input_path.substring(curr_path.lastIndexOf('/')+1); + } else { + user_input_file = input_path.substring(curr_path.lastIndexOf('\\')+1); + } + getFolderInfo(curr_path, selected_val, user_input_file); }); // If user have preference to show hidden files @@ -1191,7 +1252,6 @@ define([ // Switch to folder view $('.file_manager .fileinfo').on('click', function() { - $('.file_manager #uploader .input-path').val($('.currentpath').val()); enable_disable_btn(); }); @@ -1623,15 +1683,21 @@ define([ } else if ($('.fileinfo').data('view') == 'list') { // template to create new folder in table view folder_div = $( - '' + - '' + - '' + - '' + lg.new_folder + '' + - ''+ - ''+ - '' + - '' + - '' + ` + +
    + +
    +
    + + ${lg.new_folder} +
    +
    +
    + + + + ` ); $file_element_list = $(folder_div); @@ -1639,7 +1705,7 @@ define([ tableEl.removeClass('file_listing_table_no_data'); tableEl.find('tbody').prepend($file_element_list); - $file_element_list.find('td span.less_text').toggle(); + $file_element_list.find('td .fm_file_name').toggle(); $file_element_list.find('td input').toggle().val(lg.new_folder).select(); // rename folder/file on pressing enter key @@ -1656,7 +1722,8 @@ define([ var text_value = $file_element_list.find('td input').val(); path = $('.currentpath').val(); $file_element_list.find('td input').toggle(); - $file_element_list.find('td span.less_text').toggle().html(text_value); + $file_element_list.find('td .fm_file_name span.less_text').html(text_value); + $file_element_list.find('td .fm_file_name').toggle(); if (text_value === undefined) { text_value = lg.new_folder; } diff --git a/web/pgadmin/misc/file_manager/static/scss/_file_manager.scss b/web/pgadmin/misc/file_manager/static/scss/_file_manager.scss index 6bd173dc9..fc9019036 100644 --- a/web/pgadmin/misc/file_manager/static/scss/_file_manager.scss +++ b/web/pgadmin/misc/file_manager/static/scss/_file_manager.scss @@ -5,38 +5,25 @@ .file_listing { min-width: 100%; + position: relative; + overflow: auto; .file_listing_table_no_data { height: auto !important; } .file_listing_table { - height: 100%; - display: block; - padding: 0; - overflow-y: hidden !important; - } - - .file_listing_table thead { - display: table; - width: 100%; + table-layout: fixed; + & td, &th { + text-overflow: ellipsis; + white-space: nowrap; + } } .file_listing_table thead tr { - position: relative; - display: block; - width: 100%; border-bottom: $panel-border; } - .file_listing_table tbody { - display: block; - overflow: auto; - width: 100%; - /* 100% minus thead height */ - height: calc(100% - 30px); - } - .file_listing_table tbody tr { max-width: 100%; width: 100%; @@ -45,21 +32,20 @@ .file_listing_table tbody tr td:nth-child(1), .file_listing_table thead tr th:nth-child(1) { width: 400px; - min-width: 400px; + min-width: 100px; } .file_listing_table tbody tr td:nth-child(2), .file_listing_table thead tr th:nth-child(2) { width: 100px; min-width: 100px; - max-width: 100px; } .file_listing_table tbody tr td:nth-child(3), .file_listing_table thead tr th:nth-child(3) { - width: 100%; - min-width: 100%; - max-width: 100%; + width: 200px; + min-width: 200px; + max-width: 200px; } } @@ -83,13 +69,12 @@ display: block; } -.fileinfo table#contents tr td:first-child { +.fileinfo table#contents tr td { & span.less_text { - width: 100%; text-overflow: ellipsis; overflow: hidden; white-space: nowrap; - max-width: calc(400px - 30px); + display: block; } & .fa { diff --git a/web/pgadmin/static/scss/_pgadmin.style.scss b/web/pgadmin/static/scss/_pgadmin.style.scss index e6e4dabb9..32b7a08b7 100644 --- a/web/pgadmin/static/scss/_pgadmin.style.scss +++ b/web/pgadmin/static/scss/_pgadmin.style.scss @@ -652,12 +652,15 @@ fieldset.inline-fieldset > div { outline: none; } -input[type="checkbox"]:focus, -a:focus { - &.navbar-brand, &.dropdown-item { - outline: none !important; +input[type="checkbox"], +select, +a { + &:focus { + &.navbar-brand, &.dropdown-item { + outline: none !important; + } + outline: 2px solid $input-focus-border-color !important; } - outline: 2px solid $input-focus-border-color !important; } div.rolmembership { @@ -873,6 +876,14 @@ table.table-bottom-border { } } +table.table-right-border { + tr { + & td:last-of-type, & th:last-of-type { + border-right: $panel-border; + } + } +} + table.table-empty-rows{ thead th:last-of-type,tr:last-of-type{ & td, & th { diff --git a/web/yarn.lock b/web/yarn.lock index be76d7cd1..fe2822beb 100644 --- a/web/yarn.lock +++ b/web/yarn.lock @@ -8816,10 +8816,10 @@ table@^5.2.3: slice-ansi "^2.1.0" string-width "^3.0.0" -tablesorter@^2.31.1: - version "2.31.1" - resolved "https://registry.yarnpkg.com/tablesorter/-/tablesorter-2.31.1.tgz#19c550c555fad4d3814500bfd4373e8b4f972305" - integrity sha512-xXFYMu2vc73oKNWcf+hdId1Rvtl2W3zS9pJAX0BWjJcn6zmhquXCk+kuBQzKid/Iq/T8ERPlyAJ7q0Wkgym7Sg== +tablesorter@^2.31.2: + version "2.31.2" + resolved "https://registry.yarnpkg.com/tablesorter/-/tablesorter-2.31.2.tgz#3a37759c2b269688138b46f88454d61d1895b275" + integrity sha512-e4ubgJpHNXHWm8tR+eGt2BPAqoOCY+uOQ6PVDCgtklCtJO6VHmORmjZdtm9aYqSqWsUih72wB0nS95JiXZ2l2g== dependencies: jquery ">=1.2.6"