Hi Dave,
Please find updated patch below for direct file navigation.
I have covered all of above mentioned case.
--
*Harshal Dhumal*
*Software Engineer*
EnterpriseDB India: http://www.enterprisedb.com
The Enterprise PostgreSQL Company
On Fri, Nov 25, 2016 at 4:09 PM, Dave Page <[email protected]> wrote:
> Hi
>
> On Thu, Nov 24, 2016 at 10:25 AM, Harshal Dhumal
> <[email protected]> wrote:
> > Hi Dave,
> >
> > Please find updated attached patch for RM1911 (V2)
> >
> > --
> > Harshal Dhumal
> > Software Engineer
> >
> > EnterpriseDB India: http://www.enterprisedb.com
> > The Enterprise PostgreSQL Company
> >
> > On Mon, Nov 21, 2016 at 7:48 PM, Dave Page <[email protected]> wrote:
> >>
> >> Hi
> >>
> >> On Sat, Nov 19, 2016 at 5:57 PM, Harshal Dhumal
> >> <[email protected]> wrote:
> >> > Hi,
> >> >
> >> > PFA for RM1911
> >> >
> >> > Feature added: Allow user add file/folder path directly in input
> field.
> >> > Other improvements like clean up after file manager is closed.
> >>
> >> I did a quick check on Mac, in server mode and immediately hit problems:
> >>
> >> - [Screenshot 1]: I typed in "/Foo2" to navigate into the Foo2
> >> directory. a) it didn't navigate as expected, and b) it gave me a very
> >> non-user friendly error message.
> >>
> > Fixed.
> >>
> >> - [Screenshot 2]: I typed in "foo2.sql". It added the leading /
> >> automatically, then gave the error dpagefoo2.sql does not exist.
> >>
> > Fixed.
> >>
> >> Then on Windows in desktop mode:
> >>
> >> - [Screenshot 3]: I typed in "C:\Users\dpage" (which does exist) and
> >> got the error shown.
> >>
> > Fixed.
> >>
> >> - [Screenshot 4]: I typed in "\\172.16.253.235\data" (which does
> >> exist) and got the error shown.
> >
> > Fixed.
>
> - If I type in a path and hit return, it's properly opened. If I hit
> the Refresh button, it reloads a different location - in my case, for
> /Users/dpage it alwoys goes back to /Users. Oddly, it doesn't do that
> in /usr/bin.
>
> - The issue above is also seen with /Test_Folder (when running in
> server mode). It refreshes back to /
>
> - If I navigate to /abc/123/, then upload a file called emails.txt, it
> gets saved as /abc/123emails.txt.
>
> - On Windows (in server mode), if I try to navigate to
> \\vmware-host\Shared Folders\dpage (which exists and is accessible
> from the machine/user), it changes the name to "/\\vmware-host\Shared
> Folders\dpage" and gives an error that "dpage" doesn't exist.
>
> - After many of these errors, the OK button is enabled, and clicking
> it gives an error: "Invalid mode ('rb') or filename"
>
> - On Windows (in desktop mode), I'm stuck in the root of the C drive.
> I cannot click the Up directory button to see the available drives.
>
> - On Windows (in desktop mode), if I type "C:\" into the location bar,
> it navigates to the folder, but changes the path to "C:\/"
>
> - On windows (in desktop mode), if I type "\\172.16.253.235\data" into
> the location bar, I get "'' file does not exist"
>
> Please test thoroughly on Windows and *nix, in both server and desktop
> mode before resubmitting.
>
> Thanks.
>
> --
> Dave Page
> Blog: http://pgsnake.blogspot.com
> Twitter: @pgsnake
>
> EnterpriseDB UK: http://www.enterprisedb.com
> The Enterprise PostgreSQL Company
>
diff --git a/web/pgadmin/misc/file_manager/__init__.py b/web/pgadmin/misc/file_manager/__init__.py
index 4d8f2c5..0054010 100644
--- a/web/pgadmin/misc/file_manager/__init__.py
+++ b/web/pgadmin/misc/file_manager/__init__.py
@@ -16,6 +16,7 @@ import string
import sys
import time
from sys import platform as _platform
+import config
import simplejson as json
from flask import render_template, Response, session, request as req, url_for
@@ -246,12 +247,12 @@ class Filemanager(object):
{
'Error': gettext('No permission to operate on \
specified path.'),
- 'Code': -1
+ 'Code': 0
}
)
self.dir = get_storage_directory()
- if (self.dir is not None and isinstance(self.dir, list)):
+ if self.dir is not None and isinstance(self.dir, list):
self.dir = ""
@staticmethod
@@ -391,11 +392,11 @@ class Filemanager(object):
bitmask >>= 1
if (drive_name != '' and drive_name is not None and
drive_name in drives):
- return "{0}{1}".format(drive_name, ':/')
+ return "{0}{1}".format(drive_name, ':')
else:
return drives # return drives if no argument is passed
except Exception:
- return ['C:/']
+ return ['C:']
else:
return '/'
@@ -408,12 +409,26 @@ class Filemanager(object):
path = unquote(path)
if hasattr(str, 'decode'):
path = unquote(path).encode('utf-8').decode('utf-8')
+
+ try:
+ Filemanager.check_access_permission(dir, path)
+ except Exception as e:
+ err_msg = "Error: {0}".format(str(e))
+ files = {
+ 'Code': 0,
+ 'Error': err_msg
+ }
+ return files
+
+ orig_path = "{0}{1}".format(dir, path)
+
files = {}
- if (_platform == "win32" and path == '/') and dir is None:
+ if (_platform == "win32" and (path == '/' or path == '\\'))\
+ and dir is None:
drives = Filemanager._get_drives()
for drive in drives:
protected = 0
- path = file_name = "{0}:/".format(drive)
+ path = file_name = "{0}:".format(drive)
try:
drive_size = getDriveSize(path)
drive_size_in_units = sizeof_fmt(drive_size)
@@ -452,7 +467,7 @@ class Filemanager(object):
system_path = os.path.join(os.path.join(orig_path, f))
# continue if file/folder is hidden
- if (is_folder_hidden(system_path) or f.startswith('.')):
+ if is_folder_hidden(system_path) or f.startswith('.'):
continue
if hasattr(str, 'decode'):
@@ -502,10 +517,55 @@ class Filemanager(object):
err_msg = "Error: {0}".format(e)
files = {
'Code': 0,
- 'err_msg': err_msg
+ 'Error': err_msg
}
return files
+ @staticmethod
+ def check_access_permission(dir, path):
+ if dir is None:
+ dir = ""
+ orig_path = Filemanager.get_abs_path(dir, path)
+
+ # This translates path with relative path notations like ./ and ../ to
+ # absolute path.
+ orig_path = os.path.abspath(orig_path)
+
+ # Do not allow user to access outside his storage dir in server mode.
+ if config.SERVER_MODE is True and not orig_path.startswith(dir):
+ raise Exception(
+ gettext("Access denied ({})".format(orig_path)))
+ return True
+
+ @staticmethod
+ def get_abs_path(dir, path):
+
+ if path.startswith('\\\\'):
+ return path
+
+ if path == '/' or path == '\\':
+ if _platform == 'win32':
+ if dir.endswith('\\'):
+ return dir
+ else:
+ return dir + '\\'
+ else:
+ if dir.endswith('/'):
+ return dir
+ else:
+ return dir + '/'
+
+ if dir.endswith('/') or dir.endswith('\\'):
+ if path.startswith('/') or path.startswith('\\'):
+ return "{}{}".format(dir[:-1], path)
+ else:
+ return "{}{}".format(dir, path)
+ else:
+ if path.startswith('/') or path.startswith('\\'):
+ return "{}{}".format(dir, path)
+ else:
+ return "{}/{}".format(dir, path)
+
def validate_request(self, capability):
"""
It validates the capability with the capabilities
@@ -525,13 +585,35 @@ class Filemanager(object):
if self.dir is None:
self.dir = ""
orig_path = u"{0}{1}".format(self.dir, path)
+
+ try:
+ Filemanager.check_access_permission(self.dir, path)
+ except Exception as e:
+ thefile = {
+ 'Filename': split_path(path)[-1],
+ 'FileType': '',
+ 'Path': path,
+ 'Error': gettext("Error: {0}".format(str(e))),
+ 'Code': 0,
+ 'Info': '',
+ 'Properties': {
+ 'Date Created': '',
+ 'Date Modified': '',
+ 'Width': '',
+ 'Height': '',
+ 'Size': ''
+ }
+ }
+ return thefile
+
user_dir = path
thefile = {
'Filename': split_path(orig_path)[-1],
- 'File Type': '',
+ 'FileType': '',
'Path': user_dir,
'Error': '',
- 'Code': 0,
+ 'Code': 1,
+ 'Info': '',
'Properties': {
'Date Created': '',
'Date Modified': '',
@@ -542,13 +624,16 @@ class Filemanager(object):
}
if not path_exists(orig_path):
- thefile['Error'] = gettext('File does not exist.')
- return (encode_json(thefile), None, 'application/json')
-
- if split_path(user_dir)[-1] == '/':
- thefile['File Type'] = 'Directory'
+ thefile['Error'] = gettext("'{}' file does not exist.".format(
+ split_path(orig_path)[-1]))
+ thefile['Code'] = -1
+ return thefile
+
+ if split_path(user_dir)[-1] == '/'\
+ or os.path.isfile(orig_path) is False:
+ thefile['FileType'] = 'Directory'
else:
- thefile['File Type'] = splitext(user_dir)
+ thefile['FileType'] = splitext(user_dir)
created = time.ctime(os.path.getctime(orig_path))
modified = time.ctime(os.path.getmtime(orig_path))
@@ -575,10 +660,21 @@ class Filemanager(object):
if not self.validate_request('rename'):
return {
'Error': gettext('Not allowed'),
- 'Code': 1
+ 'Code': 0
}
dir = self.dir if self.dir is not None else ''
+
+ try:
+ Filemanager.check_access_permission(dir, old)
+ Filemanager.check_access_permission(dir, new)
+ except Exception as e:
+ res = {
+ 'Error': gettext("Error: {0}".format(str(e))),
+ 'Code': 0
+ }
+ return res
+
# check if it's dir
if old[-1] == '/':
old = old[:-1]
@@ -607,8 +703,8 @@ class Filemanager(object):
code = 1
try:
os.rename(oldpath_sys, newpath_sys)
- code = 0
except Exception as e:
+ code = 0
error_msg = "{0} {1}".format(
gettext('There was an error renaming the file:'),
str(e))
@@ -631,23 +727,31 @@ class Filemanager(object):
if not self.validate_request('delete'):
return {
'Error': gettext('Not allowed'),
- 'Code': 1
+ 'Code': 0
}
dir = self.dir if self.dir is not None else ''
path = path.encode('utf-8').decode('utf-8') if hasattr(str, 'decode') else path
orig_path = u"{0}{1}".format(dir, path)
+ try:
+ Filemanager.check_access_permission(dir, path)
+ except Exception as e:
+ res = {
+ 'Error': gettext("Error: {0}".format(str(e))),
+ 'Code': 0
+ }
+ return res
+
err_msg = ''
code = 1
try:
if os.path.isdir(orig_path):
os.rmdir(orig_path)
- code = 0
else:
os.remove(orig_path)
- code = 0
except Exception as e:
+ code = 0
err_msg = "Error: {0}".format(e.strerror)
result = {
@@ -665,7 +769,7 @@ class Filemanager(object):
if not self.validate_request('upload'):
return {
'Error': gettext('Not allowed'),
- 'Code': 1
+ 'Code': 0
}
dir = self.dir if self.dir is not None else ''
@@ -684,10 +788,19 @@ class Filemanager(object):
with open(newName, 'wb') as f:
f.write(file_obj.read())
- code = 0
except Exception as e:
+ code = 0
err_msg = "Error: {0}".format(e.strerror)
+ try:
+ Filemanager.check_access_permission(dir, path)
+ except Exception as e:
+ res = {
+ 'Error': gettext("Error: {0}".format(str(e))),
+ 'Code': 0
+ }
+ return res
+
result = {
'Path': path,
'Name': newName,
@@ -711,13 +824,17 @@ class Filemanager(object):
path = unquote(path).encode('utf-8')
try:
orig_path = "{0}{1}".format(dir, path)
+ Filemanager.check_access_permission(dir, "{}{}".format(path, name))
+
newName = '{0}{1}'.format(orig_path, name)
- if os.path.exists(newName):
+ if not os.path.exists(newName):
code = 0
- else:
- code = 1
except Exception as e:
- err_msg = "Error: {0}".format(e.strerror)
+ code = 0
+ if hasattr(e, 'strerror'):
+ err_msg = "Error: {0}".format(e.strerror)
+ else:
+ err_msg = "Error: {0}".format(str(e))
result = {
'Path': path,
@@ -754,7 +871,7 @@ class Filemanager(object):
if not self.validate_request('create'):
return {
'Error': gettext('Not allowed'),
- 'Code': 1
+ 'Code': 0
}
dir = self.dir if self.dir is not None else ''
@@ -762,6 +879,16 @@ class Filemanager(object):
if hasattr(str, 'decode'):
newName = name.encode('utf-8')
+ try:
+ Filemanager.check_access_permission(dir, "{}{}".format(
+ path, newName))
+ except Exception as e:
+ res = {
+ 'Error': gettext("Error: {0}".format(str(e))),
+ 'Code': 0
+ }
+ return res
+
if dir != "":
if hasattr(str, 'decode'):
newPath = dir + '/' + path + newName.decode('utf-8') + '/'
@@ -778,15 +905,15 @@ class Filemanager(object):
if not path_exists(newPath):
try:
os.mkdir(newPath)
- code = 0
except Exception as e:
+ code = 0
err_msg = "Error: {0}".format(e.strerror)
else:
newPath, newName = self.getNewName(dir, path, newName)
try:
os.mkdir(newPath)
- code = 0
except Exception as e:
+ code = 0
err_msg = "Error: {0}".format(e.strerror)
result = {
@@ -805,15 +932,25 @@ class Filemanager(object):
if not self.validate_request('download'):
return {
'Error': gettext('Not allowed'),
- 'Code': 1
+ 'Code': 0
}
dir = self.dir if self.dir is not None else ''
+
if hasattr(str, 'decode'):
path = path.encode('utf-8')
orig_path = u"{0}{1}".format(dir, path.decode('utf-8'))
else:
orig_path = "{0}{1}".format(dir, path)
+
+ try:
+ Filemanager.check_access_permission(dir, "{}{}".format(
+ path, path))
+ except Exception as e:
+ resp = Response(gettext("Error: {0}".format(str(e))))
+ resp.headers['Content-Disposition'] = 'attachment; filename=' + name
+ return resp
+
name = path.split('/')[-1]
content = open(orig_path, 'rb')
resp = Response(content)
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 ea502ac..56d9c38 100755
--- a/web/pgadmin/misc/file_manager/static/css/file_manager.css
+++ b/web/pgadmin/misc/file_manager/static/css/file_manager.css
@@ -15,19 +15,17 @@
top: 35px;
}
-#uploader h1 {
+#uploader .input-path {
font-size: 14px;
margin: 0;
- margin-left: 5px;
padding: 0;
display: block;
float: left;
text-align: left;
- line-height:1.9em;
- max-width: 367px;
+ line-height:1.6em;
+ width: calc(100% - 72px);
text-overflow: ellipsis;
overflow: hidden;
- color: #999;
}
#uploader h1 b {
diff --git a/web/pgadmin/misc/file_manager/templates/file_manager/index.html b/web/pgadmin/misc/file_manager/templates/file_manager/index.html
index 197386f..992fe5d 100755
--- a/web/pgadmin/misc/file_manager/templates/file_manager/index.html
+++ b/web/pgadmin/misc/file_manager/templates/file_manager/index.html
@@ -5,13 +5,14 @@
</head>
<body>
<div class="file_manager">
- <form id="uploader" method="post" class='col-xs-12'>
+ <div id="uploader" class='col-xs-12'>
<div class="btn-group filemanager-path-group col-sm-7 col-xs-12" role="group">
<button name="home" type="button" value="Home" title="Home" class="fa fa-home btn home"><span></span>
</button>
<button name="level-up" type="button" title="Back" value="LevelUp" class="btn fa fa-level-up level-up"
disabled><span></span></button>
- <h1 title=''></h1>
+ <input class='input-path' title='' type="text"/>
+
</div>
<div class="btn-group filemanager-btn-group" role="group">
<div class="uploadresponse"></div>
@@ -29,7 +30,7 @@
<button class="ON fa fa-th btn grid" type="button" title="View as grid"><span></span></button>
<button type="button" class="btn fa fa-list list" title="View as Table"><span></span></button>
</div>
- </form>
+ </div>
<div class="fileinfo">
<span class="activity">
<img src="{{ url_for('browser.static', filename='css/aciTree/image/load-root.gif') }}">
diff --git a/web/pgadmin/misc/file_manager/templates/file_manager/js/file_manager.js b/web/pgadmin/misc/file_manager/templates/file_manager/js/file_manager.js
index 473a05f..6ec50e3 100644
--- a/web/pgadmin/misc/file_manager/templates/file_manager/js/file_manager.js
+++ b/web/pgadmin/misc/file_manager/templates/file_manager/js/file_manager.js
@@ -81,6 +81,7 @@ define([
return {
main: function(params) {
// Set title and button name
+ var self = this;
if (_.isUndefined(params['dialog_title'])) {
params['dialog_title'] = 'Storage manager';
}
@@ -96,6 +97,11 @@ define([
renderStoragePanel(params);
this.elements.dialog.style.minWidth = '630px';
this.show();
+ setTimeout(function() {
+ $($container.find('.file_manager')).on('enter-key', function() {
+ $($(self.elements.footer).find('.file_manager_ok')).trigger('click')
+ });
+ }, 200);
},
settings: {
label: undefined
@@ -133,19 +139,18 @@ define([
},
callback: function(closeEvent) {
if (closeEvent.button.text == "{{ _('Select') }}") {
- if($('.fileinfo').data('view') == 'grid') {
- sel_file = $('.fileinfo').find('#contents li.selected p span').attr('title');
- } else {
- sel_file = $('.fileinfo tbody tr.selected td p span').attr('title');
- }
- var newFile = $('.currentpath').val() + sel_file;
+ var newFile = $('.storage_dialog #uploader .input-path').val();
pgAdmin.Browser.Events.trigger('pgadmin-storage:finish_btn:storage_dialog', newFile);
+ var innerbody = $(this.elements.body).find('.storage_content')
+ $(innerbody).find('*').off();
+ innerbody.remove();
+ removeTransId(trans_id);
} else if (closeEvent.button.text == "{{ _('Cancel') }}") {
- if (removeTransId(trans_id)) {
- this.destroy();
- return;
- }
+ var innerbody = $(this.elements.body).find('.storage_content')
+ $(innerbody).find('*').off();
+ innerbody.remove();
+ removeTransId(trans_id);
}
},
build: function() {
@@ -195,6 +200,7 @@ define([
return {
main: function(params) {
// Set title and button name
+ var self = this;
if (_.isUndefined(params['dialog_title'])) {
params['dialog_title'] = 'Select file';
}
@@ -210,6 +216,11 @@ define([
renderStoragePanel(params);
this.elements.dialog.style.minWidth = '630px';
this.show();
+ setTimeout(function() {
+ $($container.find('.file_manager')).on('enter-key', function() {
+ $($(self.elements.footer).find('.file_manager_ok')).trigger('click')
+ });
+ }, 200);
},
settings: {
label: undefined
@@ -249,20 +260,18 @@ define([
},
callback: function(closeEvent) {
if (closeEvent.button.text == "{{ _('Select') }}") {
- if($('.fileinfo').data('view') == 'grid') {
- sel_file = $('.fileinfo').find('#contents li.selected p span').attr('title');
- } else {
- sel_file = $('.fileinfo tbody tr.selected td p span').attr('title');
- }
- var newFile = $('.currentpath').val() + sel_file;
+ var newFile = $('.storage_dialog #uploader .input-path').val();
pgAdmin.Browser.Events.trigger('pgadmin-storage:finish_btn:select_file', newFile);
+ var innerbody = $(this.elements.body).find('.storage_content')
+ $(innerbody).find('*').off();
+ innerbody.remove();
removeTransId(trans_id);
} else if (closeEvent.button.text == "{{ _('Cancel') }}") {
- if (removeTransId(trans_id)) {
- this.destroy();
- return;
- }
+ var innerbody = $(this.elements.body).find('.storage_content')
+ $(innerbody).find('*').off();
+ innerbody.remove();
+ removeTransId(trans_id);
}
},
build: function() {
@@ -311,6 +320,7 @@ define([
// Dialog property
return {
main: function(params) {
+ var self = this;
// Set title and button name
if (_.isUndefined(params['dialog_title'])) {
params['dialog_title'] = 'Select folder';
@@ -327,6 +337,11 @@ define([
renderStoragePanel(params);
this.elements.dialog.style.minWidth = '630px';
this.show();
+ setTimeout(function() {
+ $($container.find('.file_manager')).on('enter-key', function() {
+ $($(self.elements.footer).find('.file_manager_ok')).trigger('click')
+ });
+ }, 200);
},
settings: {
label: undefined
@@ -366,20 +381,18 @@ define([
},
callback: function(closeEvent) {
if (closeEvent.button.text == "{{ _('Select') }}") {
- if($('.fileinfo').data('view') == 'grid') {
- sel_file = $('.fileinfo').find('#contents li.selected p span').attr('title');
- } else {
- sel_file = $('.fileinfo tbody tr.selected td p span').attr('title');
- }
- var newFile = $('.currentpath').val() + sel_file;
+ var newFile = $('.storage_dialog #uploader .input-path').val();
pgAdmin.Browser.Events.trigger('pgadmin-storage:finish_btn:select_folder', newFile);
+ var innerbody = $(this.elements.body).find('.storage_content')
+ $(innerbody).find('*').off();
+ innerbody.remove();
removeTransId(trans_id);
} else if (closeEvent.button.text == "{{ _('Cancel') }}") {
- if (removeTransId(trans_id)) {
- this.destroy();
- return;
- }
+ var innerbody = $(this.elements.body).find('.storage_content')
+ $(innerbody).find('*').off();
+ innerbody.remove();
+ removeTransId(trans_id);
}
},
build: function() {
@@ -427,7 +440,8 @@ define([
// Dialog property
return {
main: function(params) {
- var trans_id;
+ var self = this,
+ trans_id;
// Set title and button name
if (_.isUndefined(params['dialog_title'])) {
params['dialog_title'] = 'Create file';
@@ -444,6 +458,11 @@ define([
renderStoragePanel(params);
this.elements.dialog.style.minWidth = '630px';
this.show();
+ setTimeout(function() {
+ $($container.find('.file_manager')).on('enter-key', function() {
+ $($(self.elements.footer).find('.file_manager_ok')).trigger('click')
+ });
+ }, 200);
},
settings: {
label: undefined
@@ -485,8 +504,7 @@ define([
$('.replace_file, .fm_dimmer').show();
$('.replace_file .btn_yes').click(function(self) {
$('.replace_file, .fm_dimmer').hide();
- var selected_item = $('.allowed_file_types .create_input input[type="text"]').val(),
- newFile = $('.currentpath').val() + selected_item;
+ var newFile = $('.storage_dialog #uploader .input-path').val()
pgAdmin.Browser.Events.trigger('pgadmin-storage:finish_btn:create_file', newFile);
$('.file_manager_create_cancel').trigger('click');
@@ -496,11 +514,13 @@ define([
});
},
is_file_exist: function() {
- var selected_item = $('.allowed_file_types .create_input input[type="text"]').val(),
+ var full_path = $('.storage_dialog #uploader .input-path').val(),
+ path = full_path.substr(0, full_path.lastIndexOf('/') + 1),
+ selected_item = full_path.substr(full_path.lastIndexOf('/') + 1),
is_exist = false;
var file_data = {
- 'path': $('.currentpath').val(),
+ 'path': path,
'name': selected_item,
'mode': 'is_file_exist'
};
@@ -514,7 +534,7 @@ define([
async: false,
success: function(resp) {
data = resp.data.result;
- if(data['Code'] === 0) {
+ if(data['Code'] === 1) {
is_exist = true;
} else {
is_exist = false;
@@ -525,22 +545,23 @@ define([
},
callback: function(closeEvent) {
if (closeEvent.button.text == "{{ _('Create') }}") {
- var selected_item = $('.allowed_file_types .create_input input[type="text"]').val();
- var newFile = $('.currentpath').val() + selected_item;
+ var newFile = $('.storage_dialog #uploader .input-path').val();
- if(!_.isUndefined(selected_item) && selected_item !== '' && this.is_file_exist()) {
+ if(!_.isUndefined(newFile) && newFile !== '' && this.is_file_exist()) {
this.replace_file();
closeEvent.cancel = true;
- }
- else {
+ } else {
pgAdmin.Browser.Events.trigger('pgadmin-storage:finish_btn:create_file', newFile);
+ var innerbody = $(this.elements.body).find('.storage_content')
+ $(innerbody).find('*').off();
+ innerbody.remove();
removeTransId(trans_id);
}
} else if (closeEvent.button.text == "{{ _('Cancel') }}") {
- if (removeTransId(trans_id)) {
- this.destroy();
- return;
- }
+ var innerbody = $(this.elements.body).find('.storage_content')
+ $(innerbody).find('*').off();
+ innerbody.remove();
+ removeTransId(trans_id);
}
},
build: function() {
diff --git a/web/pgadmin/misc/file_manager/templates/file_manager/js/utility.js b/web/pgadmin/misc/file_manager/templates/file_manager/js/utility.js
index d3fa870..2d345fe 100755
--- a/web/pgadmin/misc/file_manager/templates/file_manager/js/utility.js
+++ b/web/pgadmin/misc/file_manager/templates/file_manager/js/utility.js
@@ -161,34 +161,31 @@ var setUploader = function(path) {
path = decodeURI(path);
- var display_string = path,
- file_path = '';
-
- // split path
- var split_path = display_string.split('/');
- split_path = split_path.filter(function(e) {return e;});
-
- // set empty path if it is windows
- if (config.options.platform_type === "win32" && config.options.show_volumes) {
- file_path = "";
- } else if (split_path.length === 0) {
- file_path = '/';
+ if (config.options.platform_type === "win32" && config.options.show_volumes &&
+ (path.startsWith('\\') || path.startsWith('/')) && !path.startsWith('\\\\')) {
+ $('.storage_dialog #uploader .input-path').val('');
+ } else if (!(config.options.platform_type === "win32" && config.options.show_volumes) &&
+ (path == '' || (!path.startsWith('\\') && !path.startsWith('/')))) {
+ path = '/' + path;
+ $('.storage_dialog #uploader .input-path').val(path);
} else {
- file_path = '/';
+ $('.storage_dialog #uploader .input-path').val(path);
}
- Object.keys(split_path).forEach(function (i) {
- file_path += split_path[i] + '/';
- });
- $('.storage_dialog #uploader h1').html(file_path);
+ if( path.lastIndexOf('\\') == -1 && path.lastIndexOf('/') == -1) {
+ $('.currentpath').val(path);
+ } else if(path.lastIndexOf('/') > path.lastIndexOf('\\')) {
+ $('.currentpath').val(path.substr(0, path.lastIndexOf('/') + 1));
+ } else {
+ $('.currentpath').val(path.substr(0, path.lastIndexOf('\\') + 1));
+ }
- $('.currentpath').val(path);
if ($('.storage_dialog #uploader h1 span').length === 0) {
$('<span>'+lg.current_folder+'</span>').appendTo($('.storage_dialog #uploader h1'));
}
- $('.storage_dialog #uploader h1').attr('title', display_string);
- $('.storage_dialog #uploader h1').attr('data-path', display_string);
+ $('.storage_dialog #uploader .input-path').attr('title', path);
+ $('.storage_dialog #uploader .input-path').attr('data-path', path);
// create new folder
$('.create').unbind().click(function() {
@@ -287,7 +284,7 @@ var setUploader = function(path) {
var d = new Date(); // to prevent IE cache issues
$.getJSON(fileConnector + '?mode=addfolder&path=' + $('.currentpath').val() + '&name=' + foldername, function(resp) {
var result = resp.data.result;
- if (result.Code === 0) {
+ if (result.Code === 1) {
alertify.success(lg.successful_added_folder);
getFolderInfo(result.Parent);
} else {
@@ -381,7 +378,6 @@ var enable_disable_btn = function() {
$('.file_manager').find('button.download').prop('disabled', true);
$('.file_manager').find('button.rename').prop('disabled', true);
if ($grid_file.length > 0) {
- $('.create_input input[type="text"]').val('');
$('.file_manager_ok').addClass('disabled');
}
} else {
@@ -391,14 +387,13 @@ var enable_disable_btn = function() {
$('.file_manager').find('button.download').prop('disabled', true);
$('.file_manager').find('button.rename').prop('disabled', true);
if ($list_file.length > 0) {
- $('.create_input input[type="text"]').val('');
$('.file_manager_ok').addClass('disabled');
}
}
$('.delete_item').hide();
// clear address bar
- $('.file_manager #uploader h1').show();
+ $('.file_manager #uploader .input-path').show();
$('.file_manager #uploader .show_selected_file').remove();
};
@@ -435,7 +430,7 @@ var renameItem = function(data) {
async: false,
success: function(resp) {
var result = resp.data.result;
- if (result.Code === 0) {
+ if (result.Code === 1) {
var newPath = result['New Path'],
newName = result['New Name'],
title = $("#preview h1").attr("title");
@@ -499,7 +494,7 @@ var deleteItem = function(data) {
async: false,
success: function(resp) {
var result = resp.data.result;
- if (result.Code === 0) {
+ if (result.Code === 1) {
isDeleted = true;
if (isDeleted) {
alertify.success(lg.successful_delete);
@@ -547,11 +542,11 @@ var getDetailView = function(path) {
*/
var getFileInfo = function(file) {
// Update location for status, upload, & new folder functions.
- var currentpath = file.substr(0, file.lastIndexOf('/') + 1);
- setUploader(currentpath);
+ setUploader(file);
// Retrieve the data & populate the template.
- var d = new Date(); // to prevent IE cache issues
+ var d = new Date(), // to prevent IE cache issues
+ is_file_valid = false;
var post_data = {
'path': file,
'mode': 'getinfo'
@@ -566,8 +561,8 @@ var getFileInfo = function(file) {
async: false,
success: function(resp) {
var data = resp.data.result;
-
- if (data.Code === 0) {
+ if (data.Code === 1) {
+ $('.file_manager_ok').removeClass('disabled');
var properties = '';
if (
data.Properties.Size || parseInt(data.Properties.Size)==0
@@ -578,11 +573,28 @@ var getFileInfo = function(file) {
}
data.Capabilities = capabilities;
bindToolbar(data);
+ if (data.FileType == 'Directory') {
+ // Enable/Disable level up button
+ enab_dis_level_up();
+ $('.file_manager_ok').addClass('disabled');
+
+ $('.file_manager button.delete, .file_manager button.rename').attr('disabled', 'disabled');
+ $('.file_manager button.download').attr('disabled', 'disabled');
+
+ if (file.charAt(file.length - 1) != '/' && file.charAt(file.length - 1) != '\\') {
+ file += '/';
+ }
+ getFolderInfo(file);
+ } else {
+ is_file_valid = true;
+ }
} else {
+ $('.file_manager_ok').addClass('disabled');
alertify.error(data.Error);
}
}
});
+ return is_file_valid;
};
/*
@@ -590,6 +602,7 @@ var getFileInfo = function(file) {
* creates a list view.
*/
var getFolderInfo = function(path, file_type) {
+ $('.storage_dialog #uploader .input-path').prop('disabled', true);
if (!file_type) {
file_type = '';
}
@@ -639,16 +652,16 @@ var getFolderInfo = function(path, file_type) {
contentType: "application/json; charset=utf-8",
async: false,
success: function(resp) {
+ $('.storage_dialog #uploader .input-path').prop('disabled', false);
var result = '',
data = resp.data.result;
// hide activity indicator
$('.fileinfo').find('span.activity').hide();
if (data.Code === 0) {
- alertify.error(data.err_msg);
+ alertify.error(data.Error);
return;
}
-
// generate HTML for files/folder and render into container
if (!_.isEmpty(data)) {
if ($('.fileinfo').data('view') == 'grid') {
@@ -1002,18 +1015,14 @@ var getFolderInfo = function(path, file_type) {
var path = decodeURI($(this).find('span').attr('data-alt'));
- if (path.lastIndexOf("/") == path.length - 1) {
+ if (path.lastIndexOf("/") == path.length - 1 || path.lastIndexOf("\\") == path.length - 1) {
$('.file_manager_ok').addClass('disabled');
- var $create_input = $('.create_input input[type="text"]');
-
$('.file_manager button.delete, .file_manager button.rename').attr('disabled', 'disabled');
$('.file_manager button.download').attr('disabled', 'disabled');
getFolderInfo(path);
- if ($create_input.length != 0 && $create_input.val() != '') {
- $('.file_manager_ok').removeClass('disabled');
- }
+
} else {
getFileInfo(path);
}
@@ -1027,7 +1036,7 @@ var getFolderInfo = function(path, file_type) {
'.clip span.fm_lock_icon'
).attr('data-protected');
- if (path.lastIndexOf('/') == path.length - 1) {
+ if (path.lastIndexOf('/') == path.length - 1 || path.lastIndexOf('\\') == path.length - 1) {
if (
has_capability(data_cap, 'select_folder') &&
is_protected == undefined
@@ -1043,7 +1052,7 @@ var getFolderInfo = function(path, file_type) {
'disabled', 'disabled'
);
// set selected folder name in breadcrums
- $('.file_manager #uploader h1').hide();
+ $('.file_manager #uploader .input-path').hide();
$('.file_manager #uploader .show_selected_file').remove();
$('<span class="show_selected_file">'+path+'</span>').appendTo(
'.file_manager #uploader .filemanager-path-group'
@@ -1064,13 +1073,6 @@ var getFolderInfo = function(path, file_type) {
$('.file_manager #uploader .show_selected_file').remove();
}
- if (
- config.options.dialog_type == 'create_file' &&
- is_protected == undefined
- ) {
- $('.create_input input[type="text"]').val(decodeURI(file_name));
- $('.file_manager_ok, .file_manager_create').removeClass('disabled');
- }
getFileInfo(path);
}
});
@@ -1085,7 +1087,7 @@ var getFolderInfo = function(path, file_type) {
'i.tbl_lock_icon'
).attr('data-protected');
- if (path.lastIndexOf('/') == path.length - 1) {
+ if (path.lastIndexOf('/') == path.length - 1 || path.lastIndexOf('\\') == path.length - 1) {
if (has_capability(data_cap, 'select_folder') && is_protected == undefined) {
$(this).parent().find('tr.selected').removeClass('selected');
$('td:first-child', this).parent().addClass('selected');
@@ -1094,7 +1096,7 @@ var getFolderInfo = function(path, file_type) {
$('.file_manager button.delete, .file_manager button.rename').removeAttr('disabled');
// set selected folder name in breadcrums
- $('.file_manager #uploader h1').hide();
+ $('.file_manager #uploader .input-path').hide();
$('.file_manager #uploader .show_selected_file').remove();
$('<span class="show_selected_file">'+path+'</span>').appendTo(
'.file_manager #uploader .filemanager-path-group'
@@ -1104,33 +1106,25 @@ var getFolderInfo = function(path, file_type) {
if (has_capability(data_cap, 'select_file') && is_protected == undefined) {
$(this).parent().find('tr.selected').removeClass('selected');
$('td:first-child', this).parent().addClass('selected');
- $('.file_manager_ok').removeClass('disabled');
$('.file_manager button.delete, .file_manager button.download, .file_manager button.rename').removeAttr(
'disabled'
);
// set selected folder name in breadcrums
$('.file_manager #uploader .show_selected_file').remove();
}
- if (
- config.options.dialog_type == 'create_file' &&
- is_protected == undefined
- ) {
- $('.create_input input[type="text"]').val(file_name);
- $('.file_manager_ok, .file_manager_create').removeClass('disabled');
- }
+
getFileInfo(path);
}
});
$('.fileinfo table#contents tbody tr').on('dblclick', function(e) {
e.stopPropagation();
-
// Enable/Disable level up button
enab_dis_level_up();
var path = $('td:first-child', this).attr('title');
- if (path.lastIndexOf('/') == path.length - 1) {
+ if (path.lastIndexOf('/') == path.length - 1 || path.lastIndexOf('\\') == path.length - 1) {
$('.file_manager_ok').removeClass('disabled');
$('.file_manager button.download').attr('disabled', 'disabled');
$('.file_manager button.delete, .file_manager button.rename').attr('disabled', 'disabled');
@@ -1141,13 +1135,17 @@ var getFolderInfo = function(path, file_type) {
});
}
+ input_object.set_cap(data_cap);
+ },
+ error: function() {
+ $('.storage_dialog #uploader .input-path').prop('disabled', false);
}
});
};
// Enable/Disable level up button
var enab_dis_level_up = function() {
- $('.file_manager #uploader h1').show();
+ $('.file_manager #uploader .input-path').show();
$('.show_selected_file').remove();
setTimeout(function() {
@@ -1202,6 +1200,7 @@ var fileRoot = config.options.fileRoot,
* Get localized messages from file
* through culture var or from URL
*/
+
var lg = [],
enjs = '{{ url_for("file_manager.index") }}' + "en.js",
lgf = loadData(enjs);
@@ -1259,39 +1258,26 @@ if (
});
}
-if (config.options.dialog_type == 'create_file') {
- var create_file_html = '<div class="create_input">'+
- '<span>Filename:</span>'+
- '<input type="text" name="new_filename" class="fm_create_input form-control" />'+
- '</div>';
-
- $('.create_mode_dlg').find('.allowed_file_types').prepend(create_file_html);
- $('.create_input input[type="text"]').on('keypress, keydown', function() {
- var input_text_len = $(this).val().length;
- if (input_text_len > 0 ) {
- $('.file_manager_ok').removeClass('disabled');
- } else {
- $('.file_manager_ok').addClass('disabled');
- }
- });
-}
-
/*---------------------------------------------------------
Item Actions - Object events
---------------------------------------------------------*/
// switch to folder view
$('.file_manager .fileinfo').on('click', function(e) {
+ $('.file_manager #uploader .input-path').val($('.currentpath').val())
enable_disable_btn();
});
// refresh current directory
$('.file_manager .refresh').on('click', function(e) {
enable_disable_btn();
- var curr_path = $('.currentpath').val(),
- path = curr_path.substring(
- 0, curr_path.lastIndexOf("/")
- ) + "/";
+ var curr_path = $('.currentpath').val();
+ $('.file_manager #uploader .input-path').val(curr_path);
+ if(curr_path.endsWith("/")) {
+ var path = curr_path.substring(0, curr_path.lastIndexOf("/")) + "/";
+ } else {
+ var path = curr_path.substring(0, curr_path.lastIndexOf("\\")) + "\\";
+ }
getFolderInfo(path);
});
@@ -1344,12 +1330,20 @@ $('.file_manager .home').click(function() {
// Go one directory back
$(".file_manager .level-up").click(function() {
var b = $('.currentpath').val();
-
// Enable/Disable level up button
enab_dis_level_up();
+ if (b.endsWith('\\') || b.endsWith('/')) {
+ b = b.substring(0, b.length - 1)
+ }
+
if (b != fileRoot) {
- parent = b.substring(0, b.slice(0, -1).lastIndexOf("/")) + "/";
+ if(b.lastIndexOf('/') > b.lastIndexOf('\\')) {
+ var parent = b.substring(0, b.slice(0, -1).lastIndexOf("/")) + "/";
+ } else {
+ var parent = b.substring(0, b.slice(0, -1).lastIndexOf("\\")) + "\\";
+ }
+
var d = $(".fileinfo").data("view");
$(".fileinfo").data("view", d);
getFolderInfo(parent);
@@ -1375,12 +1369,103 @@ $('.file_manager .list').click(function() {
// Provide initial values for upload form, status, etc.
setUploader(fileRoot);
-$('#uploader').attr('action', fileConnector);
-
var data = {
'Capabilities': capabilities
};
+function InputObject() {
+ this.init= function(cap) {
+ var self = this,
+ check_obj = function(path, check) {
+
+ var path = decodeURI(path);
+
+ if (path.lastIndexOf('/') == path.length - 1 || path.lastIndexOf('\\') == path.length - 1) {
+ if (
+ has_capability(self.data_cap, 'select_folder')
+ ) {
+ $('.file_manager_ok').removeClass('disabled');
+ $('.file_manager button.delete, .file_manager button.rename').removeAttr(
+ 'disabled', 'disabled'
+ );
+ $('.file_manager button.download').attr(
+ 'disabled', 'disabled'
+ );
+ // set selected folder name in breadcrums
+ $('.file_manager #uploader .input-path').hide();
+ $('.file_manager #uploader .show_selected_file').remove();
+ $('<span class="show_selected_file">'+path+'</span>').appendTo(
+ '.file_manager #uploader .filemanager-path-group'
+ );
+ } else {
+ $('.file_manager_ok').addClass('disabled');
+ if(check) {
+ // Enable/Disable level up button
+ enab_dis_level_up();
+
+ $('.file_manager button.delete, .file_manager button.rename').attr('disabled', 'disabled');
+ $('.file_manager button.download').attr('disabled', 'disabled');
+ getFolderInfo(path);
+ }
+ }
+ } else {
+ if (
+ has_capability(self.data_cap, 'select_file')
+ ) {
+ $('.file_manager_ok').removeClass('disabled');
+ $('.file_manager button.delete, .file_manager button.download, .file_manager button.rename').removeAttr(
+ 'disabled'
+ );
+ // set selected folder name in breadcrums
+ $('.file_manager #uploader .show_selected_file').remove();
+ }
+
+ if(check) {
+ if (config.options.dialog_type == 'create_file') {
+ $('.file_manager').trigger('enter-key');
+ return;
+ }
+ if(config.options.dialog_type == 'select_file') {
+ var file_status = getFileInfo(path);
+ if (file_status) {
+ $('.file_manager').trigger('enter-key');
+ }
+ }
+ }
+ }
+ };
+
+ self.data_cap = cap;
+
+ $('.storage_dialog #uploader .input-path').keyup(function(e) {
+ if(e.keyCode == 13) {
+ e.stopPropagation();
+ var path = $(this).val();
+ if (path == '') {
+ path = '/';
+ } else if(config.options.platform_type != "win32" &&
+ path.startsWith('/') == false &&
+ path.startsWith('\\\\') == false) {
+ path = '/' + path;
+ $(this).val(path);
+ }
+ setTimeout(function() {
+ check_obj(path, true);
+ });
+
+ return;
+ }
+ check_obj($(this).val(), false);
+ });
+ }
+ this.set_cap = function(cap) {
+ this.data_cap = cap;
+ }
+}
+
+var input_object = new InputObject()
+input_object.init(data);
+
// Upload file
if (has_capability(data, 'upload')) {
Dropzone.autoDiscover = false;
@@ -1462,7 +1547,7 @@ if (has_capability(data, 'upload')) {
var data = response.data.result,
$this = $(file.previewTemplate);
- if (data.Code == 0) {
+ if (data.Code == 1) {
setTimeout(function() {
$this.find(".dz-upload").addClass("success");
}, 1000);
diff --git a/web/pgadmin/tools/sqleditor/__init__.py b/web/pgadmin/tools/sqleditor/__init__.py
index ae401cd..34bdfc1 100644
--- a/web/pgadmin/tools/sqleditor/__init__.py
+++ b/web/pgadmin/tools/sqleditor/__init__.py
@@ -8,8 +8,6 @@
##########################################################################
"""A blueprint module implementing the sqleditor frame."""
-MODULE_NAME = 'sqleditor'
-
import simplejson as json
import os
import pickle
@@ -26,7 +24,9 @@ from pgadmin.utils.ajax import make_json_response, bad_request, \
from pgadmin.utils.driver import get_driver
from pgadmin.utils.sqlautocomplete.autocomplete import SQLAutoComplete
-from config import PG_DEFAULT_DRIVER
+from config import PG_DEFAULT_DRIVER, SERVER_MODE
+
+MODULE_NAME = 'sqleditor'
# import unquote from urlib for python2.x and python3.x
try:
@@ -1274,6 +1274,16 @@ def save_file():
file_path.lstrip('/')
)
+ try:
+ # Do not allow user to access outside his storage dir in server mode.
+ if SERVER_MODE is True:
+ orig_path = os.path.abspath(file_path)
+ if not orig_path.startswith(storage_manager_path):
+ raise Exception(
+ gettext("Access denied ({})".format(orig_path)))
+ except Exception as e:
+ return internal_server_error(errormsg=str(e))
+
if hasattr(str, 'decode'):
file_content = file_data['file_content']
else:
@@ -1281,7 +1291,7 @@ def save_file():
# write to file
try:
- with open(file_path, 'wb') as output_file:
+ with open(file_path, 'wb+') as output_file:
if hasattr(str, 'decode'):
output_file.write(file_content.encode('utf-8'))
else:
--
Sent via pgadmin-hackers mailing list ([email protected])
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgadmin-hackers