Added support for the server side file manager, which will be useful in
selection, creation, upload/download files/directories resides on the server side. This will be useful for file selection/creation for different server side utilites like pg_dump, pg_dumpall, pg_restore.pull/3/head
parent
180630ce0e
commit
9cdd1f8098
|
@ -27,3 +27,4 @@ backgrid-filter 01b2b21 MIT https://github.com/wyuenho/backgrid
|
|||
backbone.paginator 2.0.3 MIT http://github.com/backbone-paginator/backbone.paginator
|
||||
backgrid-paginator 03632df MIT https://github.com/wyuenho/backgrid-paginator
|
||||
backgrid-select-all 1a00053 MIT https://github.com/wyuenho/backgrid-select-all
|
||||
dropzone 4e20bd4 MIT https://github.com/enyo/dropzone
|
||||
|
|
|
@ -226,6 +226,22 @@ UPGRADE_CHECK_ENABLED = True
|
|||
# Where should we get the data from?
|
||||
UPGRADE_CHECK_URL = 'http://www.pgadmin.org/versions.json'
|
||||
|
||||
##########################################################################
|
||||
# Storage Manager storage url config settings
|
||||
# If user sets STORAGE_DIR to empty it will show all volumes if platform
|
||||
# is Windows, '/' if it is Linux, Mac or any other unix type system.
|
||||
|
||||
# For example:
|
||||
# 1. STORAGE_DIR = get_drive("C") or get_drive() # return C:/ by default
|
||||
# where C can be any drive character such as "D", "E", "G" etc
|
||||
# 2. Set path manually like
|
||||
# STORAGE_DIR = "/path/to/directory/"
|
||||
##########################################################################
|
||||
STORAGE_DIR = os.path.join(
|
||||
os.path.realpath(os.path.expanduser('~/.pgadmin/')),
|
||||
'storage'
|
||||
)
|
||||
|
||||
##########################################################################
|
||||
# Local config settings
|
||||
##########################################################################
|
||||
|
|
|
@ -191,6 +191,9 @@ def create_app(app_name=config.APP_NAME):
|
|||
db.init_app(app)
|
||||
Mail(app)
|
||||
|
||||
import pgadmin.utils.storage as storage
|
||||
storage.init_app(app)
|
||||
|
||||
# Setup Flask-Security
|
||||
user_datastore = SQLAlchemyUserDatastore(db, User, Role)
|
||||
security = Security(app, user_datastore)
|
||||
|
|
|
@ -0,0 +1,845 @@
|
|||
##########################################################################
|
||||
#
|
||||
# pgAdmin 4 - PostgreSQL Tools
|
||||
#
|
||||
# Copyright (C) 2013 - 2016, The pgAdmin Development Team
|
||||
# This software is released under the PostgreSQL Licence
|
||||
#
|
||||
##########################################################################
|
||||
|
||||
"""Implements File Manager"""
|
||||
|
||||
from pgadmin.utils import PgAdminModule
|
||||
from flask.ext.babel import gettext
|
||||
from flask.ext.security import login_required
|
||||
from flask import render_template, Response, session, request as req, url_for
|
||||
from pgadmin.utils.ajax import make_json_response
|
||||
import random
|
||||
import os
|
||||
import os.path
|
||||
import time
|
||||
import simplejson as json
|
||||
import string
|
||||
from sys import platform as _platform
|
||||
from pgadmin.utils import get_storage_directory
|
||||
|
||||
|
||||
# Checks if platform is Windows
|
||||
if _platform == "win32":
|
||||
import ctypes
|
||||
file_root = ""
|
||||
|
||||
# uppercase supported in py2, ascii_uppercase supported in py3
|
||||
try:
|
||||
letters = string.uppercase
|
||||
except Exception:
|
||||
letters = string.ascii_uppercase
|
||||
|
||||
# import unquote from urlib for python2.x and python3.x
|
||||
try:
|
||||
from urllib import unquote
|
||||
except Exception as e:
|
||||
from urllib.parse import unquote
|
||||
|
||||
|
||||
MODULE_NAME = 'file_manager'
|
||||
global transid
|
||||
|
||||
path_exists = os.path.exists
|
||||
split_path = os.path.split
|
||||
encode_json = json.JSONEncoder().encode
|
||||
|
||||
|
||||
# utility functions
|
||||
# convert bytes type to human readable format
|
||||
def sizeof_fmt(num, suffix='B'):
|
||||
for unit in ['', 'K', 'M', 'G', 'T', 'P', 'E', 'Z']:
|
||||
if abs(num) < 1024.0:
|
||||
return "%3.1f %s%s" % (num, unit, suffix)
|
||||
num /= 1024.0
|
||||
return "%.1f %s%s" % (num, 'Y', suffix)
|
||||
|
||||
|
||||
# return size of file
|
||||
def getSize(path):
|
||||
st = os.stat(path)
|
||||
return st.st_size
|
||||
|
||||
|
||||
def getDriveSize(path):
|
||||
if _platform == "win32":
|
||||
free_bytes = ctypes.c_ulonglong(0)
|
||||
ctypes.windll.kernel32.GetDiskFreeSpaceExW(
|
||||
ctypes.c_wchar_p(path), None, None, ctypes.pointer(free_bytes))
|
||||
return free_bytes.value
|
||||
|
||||
|
||||
# split extension for files
|
||||
def splitext(path):
|
||||
for ext in ['.tar.gz', '.tar.bz2']:
|
||||
if path.endswith(ext):
|
||||
path, ext = path[:-len(ext)], path[-len(ext):]
|
||||
break
|
||||
else:
|
||||
path, ext = os.path.splitext(path)
|
||||
return ext[1:]
|
||||
|
||||
|
||||
# check if file is hidden in windows platform
|
||||
def is_folder_hidden(filepath):
|
||||
if _platform == "win32":
|
||||
try:
|
||||
attrs = ctypes.windll.kernel32.GetFileAttributesW(
|
||||
unicode(filepath))
|
||||
assert attrs != -1
|
||||
result = bool(attrs & 2)
|
||||
except (AttributeError, AssertionError):
|
||||
result = False
|
||||
return result
|
||||
return False
|
||||
|
||||
|
||||
class FileManagerModule(PgAdminModule):
|
||||
"""
|
||||
FileManager lists files and folders and does
|
||||
following operations:
|
||||
- File selection
|
||||
- Folder selection
|
||||
- Open file
|
||||
- Create File
|
||||
and also supports:
|
||||
- Rename file
|
||||
- Delete file
|
||||
- Upload file
|
||||
- Create folder
|
||||
"""
|
||||
|
||||
LABEL = gettext("Storage")
|
||||
|
||||
def get_own_javascripts(self):
|
||||
return [
|
||||
{
|
||||
'name': 'pgadmin.file_manager',
|
||||
'path': url_for('file_manager.index') + 'file_manager',
|
||||
'when': None
|
||||
},
|
||||
]
|
||||
|
||||
def get_own_stylesheets(self):
|
||||
return [
|
||||
url_for('static', filename='css/jquery.dropzone/dropzone.css'),
|
||||
url_for('file_manager.static', filename='css/file_manager.css')
|
||||
]
|
||||
|
||||
def get_own_menuitems(self):
|
||||
return {
|
||||
'file_items': []
|
||||
}
|
||||
|
||||
def get_file_size_preference(self):
|
||||
return self.file_upload_size
|
||||
|
||||
def register_preferences(self):
|
||||
# Register 'file upload size' preference
|
||||
self.file_upload_size = self.preference.register(
|
||||
'options', 'file_upload_size',
|
||||
gettext("Maximum file upload size(MB)"), 'integer', 50,
|
||||
category_label=gettext('Options')
|
||||
)
|
||||
|
||||
# Initialise the module
|
||||
blueprint = FileManagerModule(MODULE_NAME, __name__)
|
||||
|
||||
|
||||
@blueprint.route("/")
|
||||
@login_required
|
||||
def index():
|
||||
"""Render the preferences dialog."""
|
||||
return render_template(
|
||||
MODULE_NAME + "/index.html", _=gettext)
|
||||
|
||||
|
||||
@blueprint.route("/utility.js")
|
||||
@login_required
|
||||
def utility():
|
||||
"""render the required javascript"""
|
||||
return Response(response=render_template(
|
||||
"file_manager/js/utility.js", _=gettext),
|
||||
status=200,
|
||||
mimetype="application/javascript")
|
||||
|
||||
|
||||
@blueprint.route("/file_manager.js")
|
||||
@login_required
|
||||
def file_manager_js():
|
||||
"""render the required javascript"""
|
||||
return Response(response=render_template(
|
||||
"file_manager/js/file_manager.js", _=gettext),
|
||||
status=200,
|
||||
mimetype="application/javascript")
|
||||
|
||||
|
||||
@blueprint.route("/en.js")
|
||||
@login_required
|
||||
def language():
|
||||
"""render the required javascript"""
|
||||
return Response(response=render_template(
|
||||
"file_manager/js/languages/en.js", _=gettext),
|
||||
status=200,
|
||||
mimetype="application/javascript")
|
||||
|
||||
|
||||
@blueprint.route("/file_manager_config.js")
|
||||
@login_required
|
||||
def file_manager_config_js():
|
||||
"""render the required javascript"""
|
||||
return Response(response=render_template(
|
||||
"file_manager/js/file_manager_config.js", _=gettext),
|
||||
status=200,
|
||||
mimetype="application/javascript")
|
||||
|
||||
|
||||
@blueprint.route("/<int:trans_id>/file_manager_config.json")
|
||||
@login_required
|
||||
def file_manager_config(trans_id):
|
||||
"""render the required json"""
|
||||
# trans_id = Filemanager.create_new_transaction()
|
||||
data = Filemanager.get_trasaction_selection(trans_id)
|
||||
return Response(response=render_template(
|
||||
"file_manager/js/file_manager_config.json", _=gettext,
|
||||
data=data),
|
||||
status=200,
|
||||
mimetype="application/json")
|
||||
|
||||
|
||||
@blueprint.route("/get_trans_id", methods=["GET", "POST"])
|
||||
@login_required
|
||||
def get_trans_id():
|
||||
if len(req.data) != 0:
|
||||
configs = json.loads(req.data)
|
||||
trans_id = Filemanager.create_new_transaction(configs)
|
||||
global transid
|
||||
transid = trans_id
|
||||
return make_json_response(
|
||||
data={'fileTransId': transid, 'status': True}
|
||||
)
|
||||
|
||||
|
||||
@blueprint.route("/del_trans_id/<int:trans_id>", methods=["GET", "POST"])
|
||||
@login_required
|
||||
def delete_trans_id(trans_id):
|
||||
Filemanager.release_transaction(trans_id)
|
||||
return make_json_response(
|
||||
data={'status': True}
|
||||
)
|
||||
|
||||
|
||||
def __get_drives(drive_name=None):
|
||||
"""
|
||||
This is a generic function which returns the default path for storage
|
||||
manager dialog irrespective of any Platform type to list all
|
||||
files and directories.
|
||||
Platform windows:
|
||||
if no path is given, it will list volumes, else list directory
|
||||
Platform unix:
|
||||
it returns path to root directory if no path is specified.
|
||||
"""
|
||||
if _platform == "win32":
|
||||
try:
|
||||
drives = []
|
||||
bitmask = ctypes.windll.kernel32.GetLogicalDrives()
|
||||
for letter in letters:
|
||||
if bitmask & 1:
|
||||
drives.append(letter)
|
||||
bitmask >>= 1
|
||||
if (drive_name != '' and drive_name is not None and
|
||||
drive_name in drives):
|
||||
return "{0}{1}".format(drive_name, ':/')
|
||||
else:
|
||||
return drives # return drives if no argument is passed
|
||||
except Exception:
|
||||
return 'C:/'
|
||||
else:
|
||||
return '/'
|
||||
|
||||
|
||||
class Filemanager(object):
|
||||
"""FileManager Class."""
|
||||
def __init__(self, trans_id):
|
||||
self.trans_id = trans_id
|
||||
self.patherror = encode_json(
|
||||
{
|
||||
'Error': gettext('No permission to operate on specified path.'),
|
||||
'Code': -1
|
||||
}
|
||||
)
|
||||
self.dir = get_storage_directory()
|
||||
|
||||
if isinstance(self.dir, list):
|
||||
self.dir = ""
|
||||
|
||||
@staticmethod
|
||||
def create_new_transaction(params):
|
||||
"""
|
||||
It will also create a unique transaction id and
|
||||
store the information into session variable.
|
||||
Args:
|
||||
capabilities: Allow/Disallow user to perform
|
||||
selection, rename, delete etc.
|
||||
"""
|
||||
|
||||
# Define configs for dialog types
|
||||
# select file, select folder, create mode
|
||||
fm_type = params['dialog_type']
|
||||
storage_dir = get_storage_directory()
|
||||
|
||||
# It is used in utitlity js to decide to
|
||||
# show or hide select file type options
|
||||
show_volumes = True if (isinstance(storage_dir, list) or
|
||||
not storage_dir) else False
|
||||
supp_types = allow_upload_files = params['supported_types'] \
|
||||
if 'supported_types' in params else []
|
||||
if fm_type == 'select_file':
|
||||
capabilities = ['select_file', 'rename', 'upload', 'create']
|
||||
supp_types = supp_types
|
||||
files_only = True
|
||||
folders_only = False
|
||||
title = "Select File"
|
||||
elif fm_type == 'select_folder':
|
||||
capabilities = ['select_folder', 'rename', 'create']
|
||||
files_only = False
|
||||
folders_only = True
|
||||
title = "Select Folder"
|
||||
elif fm_type == 'create_file':
|
||||
capabilities = ['select_file', 'rename', 'create']
|
||||
supp_types = supp_types
|
||||
files_only = True
|
||||
folders_only = False
|
||||
title = "Create File"
|
||||
elif fm_type == 'storage_dialog':
|
||||
capabilities = ['select_folder', 'select_file', 'download',
|
||||
'rename', 'delete', 'upload', 'create']
|
||||
supp_types = supp_types
|
||||
files_only = True
|
||||
folders_only = False
|
||||
title = "Storage Manager"
|
||||
|
||||
# create configs using above configs
|
||||
configs = {
|
||||
"fileroot": "/",
|
||||
"dialog_type": fm_type,
|
||||
"title": title,
|
||||
"upload": {
|
||||
"multiple": True
|
||||
},
|
||||
"capabilities": capabilities,
|
||||
"security": {
|
||||
"uploadPolicy": "",
|
||||
"uploadRestrictions": allow_upload_files
|
||||
},
|
||||
"files_only": files_only,
|
||||
"folders_only": folders_only,
|
||||
"supported_types": supp_types,
|
||||
"platform_type": _platform,
|
||||
"show_volumes": show_volumes
|
||||
}
|
||||
|
||||
# Create a unique id for the transaction
|
||||
trans_id = str(random.randint(1, 9999999))
|
||||
|
||||
if 'fileManagerData' not in session:
|
||||
file_manager_data = dict()
|
||||
else:
|
||||
file_manager_data = session['fileManagerData']
|
||||
|
||||
file_upload_size = blueprint.get_file_size_preference().get()
|
||||
configs['upload']['fileSizeLimit'] = file_upload_size
|
||||
file_manager_data[trans_id] = configs
|
||||
session['fileManagerData'] = file_manager_data
|
||||
|
||||
return trans_id
|
||||
|
||||
@staticmethod
|
||||
def get_trasaction_selection(trans_id):
|
||||
"""
|
||||
This method returns the information of unique transaction
|
||||
id from the session variable.
|
||||
|
||||
Args:
|
||||
trans_id: unique transaction id
|
||||
"""
|
||||
file_manager_data = session['fileManagerData']
|
||||
|
||||
# Return from the function if transaction id not found
|
||||
if str(trans_id) in file_manager_data:
|
||||
return file_manager_data[str(trans_id)]
|
||||
|
||||
@staticmethod
|
||||
def release_transaction(trans_id):
|
||||
"""
|
||||
This method is to remove the information of unique transaction
|
||||
id from the session variable.
|
||||
|
||||
Args:
|
||||
trans_id: unique transaction id
|
||||
"""
|
||||
file_manager_data = session['fileManagerData']
|
||||
# Return from the function if transaction id not found
|
||||
if str(trans_id) not in file_manager_data:
|
||||
return make_json_response(data={'status': True})
|
||||
|
||||
# Remove the information of unique transaction id
|
||||
# from the session variable.
|
||||
file_manager_data.pop(str(trans_id), None)
|
||||
session['fileManagerData'] = file_manager_data
|
||||
|
||||
return make_json_response(data={'status': True})
|
||||
|
||||
@staticmethod
|
||||
def list_filesystem(dir, path, trans_data, file_type):
|
||||
"""
|
||||
It lists all file and folders within the given
|
||||
directory.
|
||||
"""
|
||||
files = {}
|
||||
if (_platform == "win32" and path == '/') and (not dir):
|
||||
drives = __get_drives()
|
||||
for drive in drives:
|
||||
protected = 0
|
||||
path = file_name = "{0}:/".format(drive)
|
||||
try:
|
||||
drive_size = getDriveSize(path)
|
||||
drive_size_in_units = sizeof_fmt(drive_size)
|
||||
except:
|
||||
drive_size = 0
|
||||
protected = 1 if drive_size == 0 else 0
|
||||
files[drive] = {
|
||||
"Filename": file_name,
|
||||
"Path": path,
|
||||
"file_type": 'drive',
|
||||
"Protected": protected,
|
||||
"Properties": {
|
||||
"Date Created": "",
|
||||
"Date Modified": "",
|
||||
"Size": drive_size_in_units
|
||||
}
|
||||
}
|
||||
return files
|
||||
|
||||
orig_path = "{0}{1}".format(dir, path)
|
||||
user_dir = path
|
||||
folders_only = trans_data['folders_only'] if 'folders_only' in \
|
||||
trans_data else ''
|
||||
files_only = trans_data['files_only'] if 'files_only' in \
|
||||
trans_data else ''
|
||||
supported_types = trans_data['supported_types'] \
|
||||
if 'supported_types' in trans_data else []
|
||||
|
||||
orig_path = unquote(orig_path)
|
||||
try:
|
||||
for f in sorted(os.listdir(orig_path)):
|
||||
protected = 0
|
||||
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('.')):
|
||||
continue
|
||||
|
||||
user_path = os.path.join(os.path.join(user_dir, f))
|
||||
created = time.ctime(os.path.getctime(system_path))
|
||||
modified = time.ctime(os.path.getmtime(system_path))
|
||||
file_extension = str(splitext(system_path))
|
||||
|
||||
# set protected to 1 if no write or read permission
|
||||
if(not os.access(system_path, os.R_OK) or
|
||||
not os.access(system_path, os.W_OK)):
|
||||
protected = 1
|
||||
|
||||
# list files only or folders only
|
||||
if os.path.isdir(system_path):
|
||||
if files_only == 'true':
|
||||
continue
|
||||
file_extension = str('dir')
|
||||
user_path = "{0}/".format(user_path)
|
||||
else:
|
||||
# filter files based on file_type
|
||||
if file_type is not None and file_type != "*":
|
||||
if folders_only or len(supported_types) > 0 and \
|
||||
file_extension not in supported_types or \
|
||||
file_type != file_extension:
|
||||
continue
|
||||
|
||||
# create a list of files and folders
|
||||
files[user_path] = {
|
||||
"Filename": f,
|
||||
"Path": user_path,
|
||||
"file_type": file_extension,
|
||||
"Protected": protected,
|
||||
"Properties": {
|
||||
"Date Created": created,
|
||||
"Date Modified": modified,
|
||||
"Size": sizeof_fmt(getSize(system_path))
|
||||
}
|
||||
}
|
||||
except Exception as e:
|
||||
if e.strerror == gettext('Permission denied'):
|
||||
err_msg = "Error: {0}".format(e.strerror)
|
||||
else:
|
||||
err_msg = "Error: {0}".format(e.strerror)
|
||||
files = {
|
||||
'Code': 0,
|
||||
'err_msg': err_msg
|
||||
}
|
||||
return files
|
||||
|
||||
def validate_request(self, capability):
|
||||
"""
|
||||
It validates the capability with the capabilities
|
||||
stored in the session
|
||||
"""
|
||||
trans_data = Filemanager.get_trasaction_selection(self.trans_id)
|
||||
return False if capability not in trans_data['capabilities'] else True
|
||||
|
||||
def getinfo(self, path=None, getsize=True, name=None, req=None):
|
||||
"""
|
||||
Returns a JSON object containing information
|
||||
about the given file.
|
||||
"""
|
||||
|
||||
path = unquote(path)
|
||||
orig_path = "{0}{1}".format(self.dir, path)
|
||||
user_dir = path
|
||||
thefile = {
|
||||
'Filename': split_path(orig_path)[-1],
|
||||
'File Type': '',
|
||||
'Path': user_dir,
|
||||
'Error': '',
|
||||
'Code': 0,
|
||||
'Properties': {
|
||||
'Date Created': '',
|
||||
'Date Modified': '',
|
||||
'Width': '',
|
||||
'Height': '',
|
||||
'Size': ''
|
||||
}
|
||||
}
|
||||
|
||||
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'
|
||||
else:
|
||||
thefile['File Type'] = splitext(user_dir)
|
||||
|
||||
created = time.ctime(os.path.getctime(orig_path))
|
||||
modified = time.ctime(os.path.getmtime(orig_path))
|
||||
|
||||
thefile['Properties']['Date Created'] = created
|
||||
thefile['Properties']['Date Modified'] = modified
|
||||
thefile['Properties']['Size'] = sizeof_fmt(getSize(orig_path))
|
||||
|
||||
return thefile
|
||||
|
||||
def getfolder(self, path=None, file_type="", name=None, req=None):
|
||||
"""
|
||||
Returns files and folders in give path
|
||||
"""
|
||||
trans_data = Filemanager.get_trasaction_selection(self.trans_id)
|
||||
dir = self.dir
|
||||
filelist = self.list_filesystem(dir, path, trans_data, file_type)
|
||||
return filelist
|
||||
|
||||
def rename(self, old=None, new=None, req=None):
|
||||
"""
|
||||
Rename file or folder
|
||||
"""
|
||||
if not self.validate_request('rename'):
|
||||
return {
|
||||
'Error': gettext('Not allowed'),
|
||||
'Code': 1
|
||||
}
|
||||
|
||||
dir = self.dir
|
||||
# check if it's dir
|
||||
if old[-1] == '/':
|
||||
old = old[:-1]
|
||||
|
||||
# extract filename
|
||||
oldname = split_path(old)[-1]
|
||||
path = str(old)
|
||||
path = split_path(path)[0] # extract path
|
||||
|
||||
if not path[-1] == '/':
|
||||
path += '/'
|
||||
|
||||
# newname = encode_urlpath(new)
|
||||
newname = new
|
||||
newpath = path + newname
|
||||
|
||||
# make system old path
|
||||
oldpath_sys = "{0}{1}".format(dir, old)
|
||||
newpath_sys = "{0}{1}".format(dir, newpath)
|
||||
|
||||
error_msg = gettext('Renamed Successfully.')
|
||||
code = 1
|
||||
try:
|
||||
os.rename(oldpath_sys, newpath_sys)
|
||||
code = 0
|
||||
except Exception as e:
|
||||
error_msg = "{0} - {1}".format(
|
||||
gettext('There was an error renaming the file.'),
|
||||
str(e))
|
||||
|
||||
result = {
|
||||
'Old Path': old,
|
||||
'Old Name': oldname,
|
||||
'New Path': newpath,
|
||||
'New Name': newname,
|
||||
'Error': error_msg,
|
||||
'Code': code
|
||||
}
|
||||
|
||||
return result
|
||||
|
||||
def delete(self, path=None, req=None):
|
||||
"""
|
||||
Delete file or folder
|
||||
"""
|
||||
if not self.validate_request('delete'):
|
||||
return {
|
||||
'Error': gettext('Not allowed'),
|
||||
'Code': 1
|
||||
}
|
||||
|
||||
dir = self.dir
|
||||
orig_path = "{0}{1}".format(dir, path)
|
||||
|
||||
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:
|
||||
err_msg = "Error: {0}".format(e.strerror)
|
||||
|
||||
result = {
|
||||
'Path': path,
|
||||
'Error': err_msg,
|
||||
'Code': code
|
||||
}
|
||||
|
||||
return result
|
||||
|
||||
def add(self, req=None):
|
||||
"""
|
||||
File upload functionality
|
||||
"""
|
||||
if not self.validate_request('upload'):
|
||||
return {
|
||||
'Error': gettext('Not allowed'),
|
||||
'Code': 1
|
||||
}
|
||||
|
||||
dir = self.dir
|
||||
err_msg = ''
|
||||
code = 1
|
||||
try:
|
||||
path = req.form.get('currentpath')
|
||||
orig_path = "{0}{1}".format(dir, path)
|
||||
thefile = req.files['newfile']
|
||||
newName = '{0}{1}'.format(orig_path, thefile.filename)
|
||||
|
||||
with open(newName, 'wb') as f:
|
||||
f.write(thefile.read())
|
||||
code = 0
|
||||
except Exception as e:
|
||||
err_msg = "Error: {0}".format(e.strerror)
|
||||
|
||||
result = {
|
||||
'Path': path,
|
||||
'Name': newName,
|
||||
'Error': err_msg,
|
||||
'Code': code
|
||||
}
|
||||
|
||||
return result
|
||||
|
||||
def is_file_exist(self, path, name, req=None):
|
||||
"""
|
||||
Checks whether given file exists or not
|
||||
"""
|
||||
dir = self.dir
|
||||
err_msg = ''
|
||||
code = 1
|
||||
try:
|
||||
orig_path = "{0}{1}".format(dir, path)
|
||||
newName = '{0}{1}'.format(orig_path, name)
|
||||
if os.path.exists(newName):
|
||||
code = 0
|
||||
else:
|
||||
code = 1
|
||||
except Exception as e:
|
||||
err_msg = "Error: {0}".format(e.strerror)
|
||||
|
||||
result = {
|
||||
'Path': path,
|
||||
'Name': newName,
|
||||
'Error': err_msg,
|
||||
'Code': code
|
||||
}
|
||||
|
||||
return result
|
||||
|
||||
def create_file(self, path, name, req=None):
|
||||
"""
|
||||
Create new file functionality
|
||||
"""
|
||||
if not self.validate_request('create'):
|
||||
return {
|
||||
'Error': gettext('Not allowed'),
|
||||
'Code': 1
|
||||
}
|
||||
|
||||
dir = self.dir
|
||||
err_msg = ''
|
||||
code = 1
|
||||
try:
|
||||
orig_path = "{0}{1}".format(dir, path)
|
||||
newName = '{0}{1}'.format(orig_path, name)
|
||||
if not os.path.exists(newName):
|
||||
open(newName, 'w')
|
||||
code = 0
|
||||
else:
|
||||
err_msg = gettext("Error: File already exists")
|
||||
except Exception as e:
|
||||
err_msg = "Error: {0}".format(e.strerror)
|
||||
|
||||
result = {
|
||||
'Path': path,
|
||||
'Name': newName,
|
||||
'Error': err_msg,
|
||||
'Code': code
|
||||
}
|
||||
|
||||
return result
|
||||
|
||||
@staticmethod
|
||||
def getNewName(dir, path, newName, count=1):
|
||||
"""
|
||||
Utility to provide new name for folder if file
|
||||
with same name already exists
|
||||
"""
|
||||
last_char = newName[-1]
|
||||
tnewPath = dir + '/' + path + newName + '_'+str(count)
|
||||
if last_char == 'r' and not path_exists(tnewPath):
|
||||
return tnewPath, newName
|
||||
else:
|
||||
last_char = int(tnewPath[-1]) + 1
|
||||
newPath = dir + '/' + path + newName + '_'+str(last_char)
|
||||
if path_exists(newPath):
|
||||
count += 1
|
||||
return Filemanager.getNewName(dir, path, newName, count)
|
||||
else:
|
||||
return newPath, newName
|
||||
|
||||
def addfolder(self, path, name):
|
||||
"""
|
||||
Functionality to create new folder
|
||||
"""
|
||||
if not self.validate_request('create'):
|
||||
return {
|
||||
'Error': gettext('Not allowed'),
|
||||
'Code': 1
|
||||
}
|
||||
|
||||
dir = self.dir
|
||||
newName = name
|
||||
if dir != "":
|
||||
newPath = dir + '/' + path + newName + '/'
|
||||
else:
|
||||
newPath = path + newName + '/'
|
||||
|
||||
err_msg = ''
|
||||
code = 1
|
||||
if not path_exists(newPath):
|
||||
try:
|
||||
os.mkdir(newPath)
|
||||
code = 0
|
||||
except Exception as e:
|
||||
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:
|
||||
err_msg = "Error: {0}".format(e.strerror)
|
||||
|
||||
result = {
|
||||
'Parent': path,
|
||||
'Name': newName,
|
||||
'Error': err_msg,
|
||||
'Code': code
|
||||
}
|
||||
|
||||
return result
|
||||
|
||||
def download(self, path=None, name=None, req=None):
|
||||
"""
|
||||
Functionality to download file
|
||||
"""
|
||||
if not self.validate_request('download'):
|
||||
return {
|
||||
'Error': gettext('Not allowed'),
|
||||
'Code': 1
|
||||
}
|
||||
|
||||
dir = self.dir
|
||||
orig_path = "{0}{1}".format(dir, path)
|
||||
name = path.split('/')[-1]
|
||||
content = open(orig_path, 'r')
|
||||
resp = Response(content)
|
||||
resp.headers['Content-Disposition'] = 'attachment; filename='+name
|
||||
return resp
|
||||
|
||||
|
||||
@blueprint.route("/filemanager/<int:trans_id>/", methods=["GET", "POST"])
|
||||
@login_required
|
||||
def file_manager(trans_id):
|
||||
"""
|
||||
It is the common function for every call which is made
|
||||
and takes function name from post request and calls it.
|
||||
It gets unique transaction id from post request and
|
||||
rotate it into Filemanager class.
|
||||
"""
|
||||
myFilemanager = Filemanager(trans_id)
|
||||
mode = ''
|
||||
kwargs = {}
|
||||
if req.method == 'POST':
|
||||
if req.files:
|
||||
mode = 'add'
|
||||
kwargs = {'req': req}
|
||||
else:
|
||||
kwargs = json.loads(req.data)
|
||||
kwargs['req'] = req
|
||||
mode = kwargs['mode']
|
||||
del kwargs['mode']
|
||||
elif req.method == 'GET':
|
||||
kwargs = {
|
||||
'path': req.args['path'],
|
||||
'name': req.args['name'] if 'name' in req.args else ''
|
||||
}
|
||||
mode = req.args['mode']
|
||||
|
||||
try:
|
||||
func = getattr(myFilemanager, mode)
|
||||
res = func(**kwargs)
|
||||
return make_json_response(data={'result': res, 'status': True})
|
||||
except Exception:
|
||||
return getattr(myFilemanager, mode)(**kwargs)
|
|
@ -0,0 +1,660 @@
|
|||
/*
|
||||
* CSS for Storage Manager Dialog
|
||||
*/
|
||||
|
||||
.file_manager #uploader {
|
||||
padding: 2px 4px;
|
||||
border-width: 1px;
|
||||
display: block;
|
||||
text-align: right;
|
||||
height: auto;
|
||||
min-height:30px;
|
||||
max-height: 80px;
|
||||
overflow: hidden;
|
||||
border-bottom: 1px;
|
||||
top: 35px;
|
||||
}
|
||||
|
||||
#uploader h1 {
|
||||
font-size: 14px;
|
||||
margin: 0;
|
||||
margin-left: 5px;
|
||||
padding: 0;
|
||||
display: block;
|
||||
float: left;
|
||||
text-align: left;
|
||||
line-height:1.9em;
|
||||
text-shadow:1px 1px 0px #ffffff;
|
||||
}
|
||||
|
||||
#uploader h1 b {
|
||||
font-weight: normal;
|
||||
}
|
||||
|
||||
.uploadresponse {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.fileinfo {
|
||||
min-width: 100px;
|
||||
overflow: auto;
|
||||
/* no margin or border allowed */
|
||||
}
|
||||
|
||||
.fileinfo #contents li.selected, .fileinfo #contents tbody tr.selected {
|
||||
background: #D9EDF7;
|
||||
}
|
||||
|
||||
.fileinfo #contents li .fm_file_rename,
|
||||
.fileinfo table#contents tr td:first-child input.fm_file_rename {
|
||||
display: none;
|
||||
width: 80px;
|
||||
margin: 0 auto;
|
||||
text-align: center;
|
||||
height: 17px;
|
||||
}
|
||||
|
||||
.fileinfo table#contents tr td p {
|
||||
display: inline-block;
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.fileinfo table#contents tr td p {
|
||||
font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
|
||||
}
|
||||
|
||||
.fileinfo > h1 {
|
||||
font-size: 16px;
|
||||
margin: 100px auto;
|
||||
}
|
||||
|
||||
#toolbar {
|
||||
display: block;
|
||||
clear: left;
|
||||
margin: 50px auto;
|
||||
}
|
||||
|
||||
.fm_folder {
|
||||
font-size: xx-large !important;
|
||||
color: rgb(255, 204, 0);
|
||||
}
|
||||
|
||||
.fm_drive {
|
||||
font-size: xx-large !important;
|
||||
color: darkgray;
|
||||
}
|
||||
|
||||
.fm_file {
|
||||
font-size: xx-large !important;
|
||||
}
|
||||
|
||||
.file_manager button {
|
||||
background-color: #C0C0C0;
|
||||
}
|
||||
|
||||
.file_manager h1 {
|
||||
font-size: medium;
|
||||
}
|
||||
|
||||
/** Input file Replacement */
|
||||
.file-input-container {
|
||||
margin:0;
|
||||
position:relative;
|
||||
top:0px;
|
||||
width:215px;
|
||||
height:32px;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
/** Firefox hack */
|
||||
@-moz-document url-prefix() {
|
||||
.file-input-container {
|
||||
top:11px;
|
||||
width:255px;
|
||||
}
|
||||
}
|
||||
|
||||
/** Opera hack */
|
||||
x:-o-prefocus, .file-input-container {top:16px;width:198px;}
|
||||
|
||||
.newfile {
|
||||
position: absolute;
|
||||
top:0;
|
||||
left: 3px;
|
||||
right:0;
|
||||
width: 152px;
|
||||
height:23px;
|
||||
opacity:0; filter: alpha(opacity=0);
|
||||
cursor: pointer;
|
||||
border:1px solid blue;
|
||||
}
|
||||
|
||||
.alt-fileinput {
|
||||
display: inline;
|
||||
wrap: no-wrap;
|
||||
}
|
||||
|
||||
.filepath {
|
||||
background-color: #F4F1ED;
|
||||
border: 1px solid #dcdcdc;
|
||||
margin: 0;
|
||||
padding: 0.1em 0.3em;
|
||||
line-height: 1.7em;
|
||||
-webkit-border-top-left-radius: 6px;
|
||||
-webkit-border-bottom-left-radius: 6px;
|
||||
-moz-border-radius-topleft: 6px;
|
||||
-moz-border-radius-bottomleft: 6px;
|
||||
border-top-left-radius: 6px;
|
||||
border-bottom-left-radius: 6px;
|
||||
}
|
||||
|
||||
@-moz-document url-prefix() {
|
||||
.filepath {
|
||||
padding:0.2em 0.3em;
|
||||
}
|
||||
}
|
||||
|
||||
/** Input file Replacement - end */
|
||||
.file_listing #contents.grid {
|
||||
padding: 25px;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
.file_listing #contents.grid li {
|
||||
display: block;
|
||||
float: left;
|
||||
width: 100px;
|
||||
min-height: 80px;
|
||||
text-align: center;
|
||||
overflow: hidden;
|
||||
margin-bottom: 10px;
|
||||
-webkit-border-radius: 2px;
|
||||
-moz-border-radius: 2px;
|
||||
border-radius: 2px;
|
||||
}
|
||||
|
||||
div.clip {
|
||||
width: 30px;
|
||||
height: 30px;
|
||||
margin: 10px auto;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.file_listing #contents.grid p {
|
||||
display: block;
|
||||
text-align: center;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.file_listing #contents.list {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.file_listing #contents.list th,
|
||||
.file_listing #contents.list td {
|
||||
text-align: left;
|
||||
padding: 6px;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.file_listing #contents.list thead {
|
||||
background: rgb(244,241,237); /* Old browsers */
|
||||
background: -moz-linear-gradient(top, rgba(244,241,237,1) 0%, rgba(214,212,209,1) 100%); /* FF3.6+ */
|
||||
background: -webkit-gradient(linear, left top, left bottom, color-stop(0%,rgba(244,241,237,1)), color-stop(100%,rgba(214,212,209,1))); /* Chrome,Safari4+ */
|
||||
background: -webkit-linear-gradient(top, rgba(244,241,237,1) 0%,rgba(214,212,209,1) 100%); /* Chrome10+,Safari5.1+ */
|
||||
background: -o-linear-gradient(top, rgba(244,241,237,1) 0%,rgba(214,212,209,1) 100%); /* Opera 11.10+ */
|
||||
background: -ms-linear-gradient(top, rgba(244,241,237,1) 0%,rgba(214,212,209,1) 100%); /* IE10+ */
|
||||
background: linear-gradient(to bottom, rgba(244,241,237,1) 0%,rgba(214,212,209,1) 100%); /* W3C */
|
||||
border-bottom: 1px solid #ccc;
|
||||
display: inline-block;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.file_listing #contents.list th {
|
||||
font-weight: bold;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.file_listing #contents.list th.tablesorter-headerAsc,
|
||||
.file_listing #contents.list th.tablesorter-headerDesc {
|
||||
background: rgb(214,212,209); /* Old browsers */
|
||||
background: -moz-linear-gradient(top, rgba(214,212,209,1) 0%, rgba(244,241,237,1) 100%); /* FF3.6+ */
|
||||
background: -webkit-gradient(linear, left top, left bottom, color-stop(0%,rgba(214,212,209,1)), color-stop(100%,rgba(244,241,237,1))); /* Chrome,Safari4+ */
|
||||
background: -webkit-linear-gradient(top, rgba(214,212,209,1) 0%,rgba(244,241,237,1) 100%); /* Chrome10+,Safari5.1+ */
|
||||
background: -o-linear-gradient(top, rgba(214,212,209,1) 0%,rgba(244,241,237,1) 100%); /* Opera 11.10+ */
|
||||
background: -ms-linear-gradient(top, rgba(214,212,209,1) 0%,rgba(244,241,237,1) 100%); /* IE10+ */
|
||||
background: linear-gradient(to bottom, rgba(214,212,209,1) 0%,rgba(244,241,237,1) 100%); /* W3C */
|
||||
}
|
||||
|
||||
.file_listing #contents.list td {
|
||||
border-bottom: 1px dotted #ccc;
|
||||
}
|
||||
|
||||
.file_listing #contents.list td:first-child {
|
||||
display: table-cell;
|
||||
padding-left: 0;
|
||||
width: 100%;
|
||||
padding-left: 22px;
|
||||
background-repeat: no-repeat;
|
||||
background-position: 3px center;
|
||||
}
|
||||
|
||||
.file_listing #contents.list td.tbl_folder:first-child:before {
|
||||
margin-right: 5px;
|
||||
color: rgb(255, 204, 0);
|
||||
}
|
||||
|
||||
.file_listing #contents.list td.tbl_file:first-child:before {
|
||||
margin-right: 5px;
|
||||
}
|
||||
|
||||
.file_listing #contents.list td.tbl_drive:first-child:before {
|
||||
color: darkgray;
|
||||
margin-right: 5px;
|
||||
}
|
||||
|
||||
.file_listing #contents.list tbody tr:hover {
|
||||
background-color: #eee;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.file_listing #contents.grid li:hover {
|
||||
border: 1px solid #E5E5E5;
|
||||
background-color: #F7F7F7;
|
||||
cursor: pointer;
|
||||
max-height: 78px;
|
||||
}
|
||||
|
||||
.meta {
|
||||
display: none;
|
||||
}
|
||||
|
||||
#activity {
|
||||
margin: 100px auto;
|
||||
}
|
||||
|
||||
button.grid span,
|
||||
button.list span {
|
||||
width: 20px;
|
||||
background-repeat: no-repeat;
|
||||
background-position: center center;
|
||||
}
|
||||
|
||||
.file_listing #contents li {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
#dropzone-container h2 {
|
||||
font-size: 20px;
|
||||
margin-top: 0;
|
||||
}
|
||||
|
||||
.pgadmin-storage-body {
|
||||
min-width: 750px !important;
|
||||
min-height: 380px !important;
|
||||
}
|
||||
|
||||
.pgadmin-storage-body .ajs-content {
|
||||
top: 0px !important;
|
||||
left: 0 !important;
|
||||
right: 0 !important;
|
||||
height: 100% !important;
|
||||
width: 100% !important;
|
||||
}
|
||||
|
||||
.storage_dialog {
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.storage_content {
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.file_manager {
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
position: absolute;
|
||||
right: 0;
|
||||
top: 0;
|
||||
}
|
||||
|
||||
.btn-group.filemanager-btn-group .btn:not(:first-child):not(:last-child) {
|
||||
border-left: 1px solid #A9A9A9;
|
||||
}
|
||||
|
||||
.file_manager #uploader .filemanager-path-group {
|
||||
padding: 0;
|
||||
display: block;
|
||||
border: 1px solid darkgrey;
|
||||
height: 30px;
|
||||
border-radius: 5px;
|
||||
-webkit-border-radius: 5px;
|
||||
-moz-border-radius: 5px;
|
||||
float: left;
|
||||
margin-right: 10px;
|
||||
}
|
||||
|
||||
.file_manager #uploader .btn-group .btn[disabled] {
|
||||
color: #888;
|
||||
background-color: #ccc;
|
||||
}
|
||||
|
||||
.file_manager #uploader .filemanager-btn-group {
|
||||
border: 1px solid darkgrey;
|
||||
border-radius: 5px;
|
||||
-webkit-border-radius: 5px;
|
||||
-moz-border-radius: 5px;
|
||||
width: auto;
|
||||
float: left;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.file_manager .btn-group {
|
||||
margin: 2px 3px;
|
||||
}
|
||||
|
||||
.file_manager .fileinfo {
|
||||
height: calc(100% - 109px);
|
||||
overflow: hidden;
|
||||
position: relative;
|
||||
top: 35px;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.file_manager .fileinfo #contents{
|
||||
padding: 5px;
|
||||
text-align: left;
|
||||
display: inline-block;
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
.file_manager .fileinfo #contents thead tr{
|
||||
position: relative;
|
||||
display: block;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.file_manager .fileinfo #contents thead tr th:nth-child(1),
|
||||
.file_manager .fileinfo #contents tbody tr td:nth-child(1) {
|
||||
position: relative;
|
||||
width: 100%;
|
||||
min-width: 100%;
|
||||
max-width: 100%;
|
||||
}
|
||||
|
||||
.file_manager .fileinfo #contents thead tr th:nth-child(2) {
|
||||
width: 152px;
|
||||
min-width: 152px;
|
||||
max-width: 152px;
|
||||
}
|
||||
|
||||
.file_manager .fileinfo #contents tbody tr td:nth-child(2) {
|
||||
width: 150px;
|
||||
min-width: 150px;
|
||||
max-width: 150px;
|
||||
}
|
||||
|
||||
.file_manager .fileinfo #contents thead tr th:nth-child(3) {
|
||||
width: 197px;
|
||||
min-width: 197px;
|
||||
max-width: 197px;
|
||||
}
|
||||
|
||||
.file_manager .fileinfo #contents tbody tr td:nth-child(3) {
|
||||
width: 180px;
|
||||
min-width: 180px;
|
||||
max-width: 180px;
|
||||
}
|
||||
|
||||
.file_manager .fileinfo #contents tbody {
|
||||
display: block;
|
||||
overflow: auto;
|
||||
width: 100%;
|
||||
height: calc(100% - 30px);
|
||||
}
|
||||
.file_manager .fileinfo #contents tbody tr{
|
||||
display: table;
|
||||
max-width: 100%;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.file_manager .upload_file {
|
||||
display: none;
|
||||
z-index: 1;
|
||||
margin-bottom: auto;
|
||||
top: 0;
|
||||
margin-top: 0;
|
||||
height: calc(100% - 5px);
|
||||
width: 100%;
|
||||
border: none;
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
background-color: black;
|
||||
padding: 0px;
|
||||
padding-top: 22px;
|
||||
padding-left: 10px;
|
||||
}
|
||||
|
||||
.file_manager .upload_file #dropzone-container {
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.file_manager .upload_file #multiple-uploads {
|
||||
background: black;
|
||||
color: white;
|
||||
padding: 0px !important;
|
||||
height: calc(100% - 20px);
|
||||
overflow: auto;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.fileinfo .prompt-info {
|
||||
text-align: center;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.file_manager #uploader .btn-group .show_selected_file {
|
||||
float: left;
|
||||
text-align: left;
|
||||
vertical-align: middle;
|
||||
padding: 4px 0 0 5px;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.fileinfo .file_listing {
|
||||
display: block;
|
||||
height: calc(100% - 35px);
|
||||
border: 1px solid #ccc;
|
||||
border-bottom: none;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.fileinfo .allowed_file_types {
|
||||
display: block;
|
||||
height: 25px;
|
||||
position: absolute;
|
||||
right: 0;
|
||||
border-top: 1px solid #ccc;
|
||||
padding-top: 4px;
|
||||
bottom: 4px;
|
||||
width: 100%;
|
||||
background: #fff;
|
||||
}
|
||||
|
||||
.allowed_file_types .create_input {
|
||||
position: absolute;
|
||||
left: 5px;
|
||||
width: 230px;
|
||||
}
|
||||
|
||||
.allowed_file_types .create_input span {
|
||||
padding-right: 5px;
|
||||
}
|
||||
|
||||
.allowed_file_types .create_input input[type="text"] {
|
||||
border-radius: 3px;
|
||||
-webkit-border-radius: 3px;
|
||||
-moz-border-radius: 3px;
|
||||
height: 22px;
|
||||
width: 140px;
|
||||
font-size: 13px;
|
||||
display: inline;
|
||||
}
|
||||
|
||||
.allowed_file_types .change_file_types {
|
||||
position: absolute;
|
||||
top: 4px;
|
||||
right: 0;
|
||||
}
|
||||
|
||||
.allowed_file_types .change_file_types select {
|
||||
width: 75px;
|
||||
margin-left: 10px;
|
||||
float: right;
|
||||
height: 22px;
|
||||
}
|
||||
|
||||
.allowed_file_types .change_file_types label {
|
||||
float: right;
|
||||
padding-top: 3px;
|
||||
}
|
||||
|
||||
.upload_file .file_upload_main {
|
||||
position: relative;
|
||||
height: 127px;;
|
||||
width: 120px;
|
||||
display: inline-block;
|
||||
margin: 0 15px 15px 0 !important;
|
||||
border: 1px solid white;
|
||||
position: relative;
|
||||
border-radius: 5px;
|
||||
-webkit-border-radius: 5px;
|
||||
-moz-border-radius: 5px;
|
||||
background: #fff;
|
||||
margin: 2px;
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.upload_file .file_upload_main .show_error {
|
||||
padding: 10px 0 0 10px;
|
||||
color: #000;
|
||||
}
|
||||
|
||||
.upload_file .file_upload_main .show_error p.size {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.upload_file .file_upload_main .show_error p.name {
|
||||
font-size: 13px;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.file_upload_main .dz-preview {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.file_upload_main .dz-progress {
|
||||
top: 83px !important;
|
||||
border: 1px solid #8a6d3b;
|
||||
border-radius: 0 !important;
|
||||
-moz-border-radius: 0 !important;
|
||||
-webkit-border-radius: 0 !important;
|
||||
}
|
||||
|
||||
.file_upload_main .dz-progress .dz-upload {
|
||||
background: #d9edf7 !important;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.file_upload_main .dz-progress .dz-upload.success {
|
||||
background: green !important;
|
||||
}
|
||||
|
||||
a.dz-remove {
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
.upload_file .file_upload_main a.dz_file_remove {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
right: 0;
|
||||
color: #FF0000;
|
||||
cursor: pointer;
|
||||
border-radius: 5px;
|
||||
-moz-border-radius: 5px;
|
||||
-webkit-border-radius: 5px;
|
||||
font-size: large;
|
||||
}
|
||||
|
||||
.upload_file .file_upload_main a.dz_file_remove:hover {
|
||||
border: 1px solid black;
|
||||
}
|
||||
|
||||
.fileinfo .delete_item, .fileinfo .replace_file {
|
||||
display: none;
|
||||
padding: 7px 5px;
|
||||
opacity: 0.8;
|
||||
color: #fff;
|
||||
border: 1px solid darkgrey;
|
||||
background: #000;
|
||||
box-shadow: 1px 0px 3px 1px red;
|
||||
}
|
||||
|
||||
.fileinfo .delete_item span.pull-right .btn,
|
||||
.fileinfo .replace_file span.pull-right .btn {
|
||||
padding: 0px 5px;
|
||||
color: #000;
|
||||
background: #fff;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.fileinfo .delete_item span,
|
||||
.fileinfo .replace_file span {
|
||||
margin-right: 10px;
|
||||
}
|
||||
|
||||
.upload_file .dz_cross_btn {
|
||||
color: #fff;
|
||||
font-size: x-large;
|
||||
right: -4px;
|
||||
position: absolute;
|
||||
top: -1px;
|
||||
background: transparent;
|
||||
border: none;
|
||||
}
|
||||
|
||||
.file_manager .fileinfo #contents .fm_lock_icon {
|
||||
color: red;
|
||||
position: absolute;
|
||||
top: 6px;
|
||||
right: 0;
|
||||
left: 19px;
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
.file_manager .fileinfo #contents .fa-lock.tbl_lock_icon {
|
||||
color: red;
|
||||
position: absolute;
|
||||
left: 29px;
|
||||
top: 5px;
|
||||
font-size: 10px;
|
||||
}
|
||||
|
||||
.fileinfo .activity {
|
||||
position: absolute;
|
||||
left: 50%;
|
||||
font-size: xx-large;
|
||||
top: 30%;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
.file_manager button.ON {
|
||||
background: #F9F8F7;
|
||||
border: 1px inset #ccc;
|
||||
}
|
|
@ -0,0 +1,55 @@
|
|||
<html>
|
||||
<head>
|
||||
<script type="text/javascript" src="{{ url_for('static', filename='js/jquery.dropzone/dropzone.js') }}"></script>
|
||||
<script type="text/javascript" src="{{ url_for('file_manager.index') }}utility.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<div class="file_manager">
|
||||
<form id="uploader" method="post" 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>
|
||||
</div>
|
||||
<div class="btn-group filemanager-btn-group" role="group">
|
||||
<div class="uploadresponse"></div>
|
||||
<input class="mode" name="mode" type="hidden" value="add" />
|
||||
<input class="currentpath" name="currentpath" type="hidden" />
|
||||
<button type="button" title="Refresh" class="btn fa fa-refresh refresh"></button>
|
||||
<button type="submit" title="Upload File" value="Upload" class="btn fa fa-upload upload"><span></span></button>
|
||||
<button type="button" title="Download File" class="btn fa fa-download download" disabled><span></span></button>
|
||||
<button name="delete" type="button" title="Delete File/Folder" class="btn fa fa-trash delete" disabled><span></span></button>
|
||||
<button name="rename" type="button" title="Rename File/Folder" class="btn fa fa-pencil-square-o rename"><span></span></button>
|
||||
<button name="newfolder" type="button" title="Create new folder" value="New Folder" class="btn fa fa-folder-open create"><span></span></button>
|
||||
<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 class="fileinfo">
|
||||
<span class="activity">
|
||||
<img src="{{ url_for('browser.static', filename='css/aciTree/image/load-root.gif') }}">
|
||||
</span>
|
||||
<div class='delete_item'>
|
||||
<span>Are you sure you want to delete this item ?</span>
|
||||
<span class="pull-right">
|
||||
<button type='button' class='btn btn_yes'>YES</button>
|
||||
<button type='button' class='btn btn_no'>NO</button>
|
||||
</span>
|
||||
</div>
|
||||
<div class='replace_file'>
|
||||
<span>Are you sure you want to replace this file ?</span>
|
||||
<span class="pull-right">
|
||||
<button type='button' class='btn btn_yes'>YES</button>
|
||||
<button type='button' class='btn btn_no'>NO</button>
|
||||
</span>
|
||||
</div>
|
||||
<div class="file_listing">
|
||||
</div>
|
||||
<div class="upload_file dropzone">
|
||||
</div>
|
||||
<div class="allowed_file_types">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,591 @@
|
|||
define([
|
||||
'jquery', 'underscore', 'alertify'],
|
||||
|
||||
// This defines File Manager dialog
|
||||
function($, _, alertify) {
|
||||
pgAdmin = pgAdmin || window.pgAdmin || {};
|
||||
|
||||
/*
|
||||
* Hmm... this module is already been initialized, we can refer to the old
|
||||
* object from here.
|
||||
*/
|
||||
if (pgAdmin.FileManager)
|
||||
return pgAdmin.FileManager;
|
||||
|
||||
pgAdmin.FileManager = {
|
||||
init: function() {
|
||||
if (this.initialized)
|
||||
return;
|
||||
|
||||
this.initialized = true;
|
||||
|
||||
var module_url = "{{ url_for('file_manager.index') }}",
|
||||
fileConnector = module_url + "filemanager/";
|
||||
|
||||
// send a request to get transaction id
|
||||
var getTransId = function(configs) {
|
||||
return $.ajax({
|
||||
data: configs,
|
||||
type: "POST",
|
||||
async: false,
|
||||
url: module_url + "get_trans_id",
|
||||
dataType: "json",
|
||||
contentType: "application/json; charset=utf-8",
|
||||
});
|
||||
};
|
||||
|
||||
// Function to remove trans id from session
|
||||
var removeTransId = function() {
|
||||
return $.ajax({
|
||||
type: "GET",
|
||||
async: false,
|
||||
url: module_url + "del_trans_id/" + trans_id,
|
||||
dataType: "json",
|
||||
contentType: "application/json; charset=utf-8",
|
||||
});
|
||||
};
|
||||
// Declare the Storage dialog
|
||||
alertify.dialog('storageManagerDlg', function() {
|
||||
var controls = [], // Keep tracking of all the backform controls
|
||||
// Dialog containter
|
||||
$container = $("<div class='storage_dialog'></div>");
|
||||
|
||||
/*
|
||||
* Function: renderStoragePanel
|
||||
*
|
||||
* Renders the FileManager in the content div based on the given
|
||||
* configuration parameters.
|
||||
*/
|
||||
var renderStoragePanel = function(params) {
|
||||
/*
|
||||
* Clear the existing html in the storage content
|
||||
*/
|
||||
var content = $container.find('.storage_content');
|
||||
content.empty();
|
||||
|
||||
$.get("{{ url_for('file_manager.index') }}", function(data) {
|
||||
content.append(data);
|
||||
});
|
||||
|
||||
transId = getTransId(params);
|
||||
if (transId.readyState == 4)
|
||||
t_res = JSON.parse(transId.responseText);
|
||||
trans_id = t_res.data.fileTransId;
|
||||
|
||||
};
|
||||
|
||||
// Dialog property
|
||||
return {
|
||||
main: function(params) {
|
||||
// Set title and button name
|
||||
if (_.isUndefined(params['dialog_title']))
|
||||
params['dialog_title'] = 'Storage manager';
|
||||
this.set('title', params['dialog_title']);
|
||||
if (_.isUndefined(params['btn_primary']))
|
||||
params['btn_primary'] = 'Select';
|
||||
this.set('label', params['btn_primary']);
|
||||
|
||||
var trans_id;
|
||||
this.title = params.dialog_title;
|
||||
|
||||
params = JSON.stringify(params);
|
||||
$container.find('.storage_content').remove();
|
||||
$container.append("<div class='storage_content'></div>");
|
||||
renderStoragePanel(params);
|
||||
this.show();
|
||||
},
|
||||
settings: {
|
||||
label: undefined
|
||||
},
|
||||
settingUpdated: function (key, oldValue, newValue) {
|
||||
switch (key) {
|
||||
case 'message':
|
||||
this.setMessage(newValue);
|
||||
break;
|
||||
case 'label':
|
||||
if (this.__internal.buttons[0].element) {
|
||||
this.__internal.buttons[0].element.innerHTML = newValue;
|
||||
}
|
||||
break;
|
||||
}
|
||||
},
|
||||
setup:function() {
|
||||
return {
|
||||
buttons:[
|
||||
{
|
||||
text: "{{ _('Select') }}", key: 13, className: "btn btn-primary fa fa-file file_manager_ok pg-alertify-button disabled"
|
||||
},
|
||||
{
|
||||
text: "{{ _('Cancel') }}", className: "btn btn-danger fa fa-times pg-alertify-button"
|
||||
}
|
||||
],
|
||||
focus: { element: 0 },
|
||||
options: {
|
||||
closableByDimmer: false,
|
||||
|
||||
}
|
||||
};
|
||||
},
|
||||
callback: function(closeEvent) {
|
||||
if (closeEvent.button.key == 13) {
|
||||
//closeEvent.cancel = true;
|
||||
}
|
||||
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;
|
||||
newFile = newFile.substr(1);
|
||||
pgAdmin.Browser.Events.trigger('pgadmin-storage:finish_btn:storage_dialog', newFile);
|
||||
}
|
||||
removeTransId(trans_id);
|
||||
},
|
||||
build: function() {
|
||||
this.elements.content.appendChild($container.get(0));
|
||||
},
|
||||
hooks: {
|
||||
onshow: function() {
|
||||
$(this.elements.body).addClass('pgadmin-storage-body');
|
||||
}
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
// Declare the Selection dialog
|
||||
alertify.dialog('fileSelectionDlg', function() {
|
||||
var controls = [], // Keep tracking of all the backform controls
|
||||
// Dialog containter
|
||||
$container = $("<div class='storage_dialog file_selection_dlg'></div>");
|
||||
|
||||
// send a request to get transaction id
|
||||
/*
|
||||
* Function: renderStoragePanel
|
||||
*
|
||||
* Renders the FileManager in the content div based on the given
|
||||
* configuration parameters.
|
||||
*/
|
||||
var renderStoragePanel = function(configs) {
|
||||
/*
|
||||
* Clear the existing html in the storage content
|
||||
*/
|
||||
var content = $container.find('.storage_content');
|
||||
content.empty();
|
||||
|
||||
$.get("{{ url_for('file_manager.index') }}", function(data) {
|
||||
content.append(data);
|
||||
});
|
||||
|
||||
transId = getTransId(configs);
|
||||
if (transId.readyState == 4)
|
||||
t_res = JSON.parse(transId.responseText);
|
||||
trans_id = t_res.data.fileTransId;
|
||||
};
|
||||
|
||||
// Dialog property
|
||||
return {
|
||||
main: function(params) {
|
||||
// Set title and button name
|
||||
if (_.isUndefined(params['dialog_title']))
|
||||
params['dialog_title'] = 'Select file';
|
||||
this.set('title', params['dialog_title']);
|
||||
if (_.isUndefined(params['btn_primary']))
|
||||
params['btn_primary'] = 'Select';
|
||||
this.set('label', params['btn_primary']);
|
||||
|
||||
var trans_id;
|
||||
this.title = params.dialog_title;
|
||||
|
||||
params = JSON.stringify(params);
|
||||
$container.find('.storage_content').remove();
|
||||
$container.append("<div class='storage_content'></div>");
|
||||
renderStoragePanel(params);
|
||||
this.show();
|
||||
},
|
||||
settings: {
|
||||
label: undefined
|
||||
},
|
||||
settingUpdated: function (key, oldValue, newValue) {
|
||||
switch (key) {
|
||||
case 'message':
|
||||
this.setMessage(newValue);
|
||||
break;
|
||||
case 'label':
|
||||
if (this.__internal.buttons[0].element) {
|
||||
this.__internal.buttons[0].element.innerHTML = newValue;
|
||||
}
|
||||
break;
|
||||
}
|
||||
},
|
||||
setup:function() {
|
||||
return {
|
||||
buttons:[
|
||||
{
|
||||
text: "{{ _('Select') }}", key: 13, className: "btn btn-primary fa fa-file file_manager_ok pg-alertify-button disabled"
|
||||
},
|
||||
{
|
||||
text: "{{ _('Cancel') }}", key: 27, className: "btn btn-danger fa fa-times pg-alertify-button"
|
||||
}
|
||||
],
|
||||
focus: { element: 0 },
|
||||
options: {
|
||||
closableByDimmer: false,
|
||||
maximizable: false,
|
||||
closable: false,
|
||||
movable: true
|
||||
}
|
||||
};
|
||||
},
|
||||
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;
|
||||
newFile = newFile.substr(1);
|
||||
pgAdmin.Browser.Events.trigger('pgadmin-storage:finish_btn:select_file', newFile);
|
||||
}
|
||||
removeTransId(trans_id);
|
||||
},
|
||||
build: function() {
|
||||
this.elements.content.appendChild($container.get(0));
|
||||
},
|
||||
hooks: {
|
||||
onshow: function() {
|
||||
$(this.elements.body).addClass('pgadmin-storage-body');
|
||||
}
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
// Declare the Folder Selection dialog
|
||||
alertify.dialog('folderSelectionDlg', function() {
|
||||
var controls = [], // Keep tracking of all the backform controls
|
||||
// Dialog containter
|
||||
$container = $("<div class='storage_dialog folder_selection_dlg'></div>");
|
||||
|
||||
// send a request to get transaction id
|
||||
/*
|
||||
* Function: renderStoragePanel
|
||||
*
|
||||
* Renders the FileManager in the content div based on the given
|
||||
* configuration parameters.
|
||||
*/
|
||||
var renderStoragePanel = function(params) {
|
||||
/*
|
||||
* Clear the existing html in the storage content
|
||||
*/
|
||||
var content = $container.find('.storage_content');
|
||||
content.empty();
|
||||
|
||||
$.get("{{ url_for('file_manager.index') }}", function(data) {
|
||||
content.append(data);
|
||||
});
|
||||
|
||||
transId = getTransId(params);
|
||||
if (transId.readyState == 4)
|
||||
t_res = JSON.parse(transId.responseText);
|
||||
trans_id = t_res.data.fileTransId;
|
||||
|
||||
};
|
||||
|
||||
// Dialog property
|
||||
return {
|
||||
main: function(params) {
|
||||
// Set title and button name
|
||||
if (_.isUndefined(params['dialog_title']))
|
||||
params['dialog_title'] = 'Select folder';
|
||||
this.set('title', params['dialog_title']);
|
||||
if (_.isUndefined(params['btn_primary']))
|
||||
params['btn_primary'] = 'Select';
|
||||
this.set('label', params['btn_primary']);
|
||||
|
||||
var trans_id;
|
||||
params = JSON.stringify(params);
|
||||
$container.find('.storage_content').remove();
|
||||
$container.append("<div class='storage_content'></div>");
|
||||
renderStoragePanel(params);
|
||||
this.show();
|
||||
},
|
||||
settings: {
|
||||
label: undefined
|
||||
},
|
||||
settingUpdated: function (key, oldValue, newValue) {
|
||||
switch (key) {
|
||||
case 'message':
|
||||
this.setMessage(newValue);
|
||||
break;
|
||||
case 'label':
|
||||
if (this.__internal.buttons[0].element) {
|
||||
this.__internal.buttons[0].element.innerHTML = newValue;
|
||||
}
|
||||
break;
|
||||
}
|
||||
},
|
||||
setup:function() {
|
||||
return {
|
||||
buttons:[
|
||||
{
|
||||
text: "{{ _('Select') }}", key: 13, className: "btn btn-primary fa fa-file file_manager_ok pg-alertify-button disabled"
|
||||
},
|
||||
{
|
||||
text: "{{ _('Cancel') }}", key: 27, className: "btn btn-danger fa fa-times pg-alertify-button"
|
||||
}
|
||||
],
|
||||
focus: { element: 0 },
|
||||
options: {
|
||||
closableByDimmer: false,
|
||||
maximizable: false,
|
||||
closable: false,
|
||||
movable: true
|
||||
}
|
||||
};
|
||||
},
|
||||
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;
|
||||
newFile = newFile.substr(1);
|
||||
pgAdmin.Browser.Events.trigger('pgadmin-storage:finish_btn:select_folder', newFile);
|
||||
}
|
||||
removeTransId(trans_id);
|
||||
},
|
||||
build: function() {
|
||||
this.elements.content.appendChild($container.get(0));
|
||||
},
|
||||
hooks: {
|
||||
onshow: function() {
|
||||
$(this.elements.body).addClass('pgadmin-storage-body');
|
||||
}
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
// Declare the Create mode dialog
|
||||
alertify.dialog('createModeDlg', function() {
|
||||
var controls = [], // Keep tracking of all the backform controls
|
||||
// Dialog containter
|
||||
$container = $("<div class='storage_dialog create_mode_dlg'></div>");
|
||||
|
||||
/*
|
||||
* Function: renderStoragePanel
|
||||
*
|
||||
* Renders the FileManager in the content div based on the given
|
||||
* configuration parameters.
|
||||
*/
|
||||
var renderStoragePanel = function(params) {
|
||||
/*
|
||||
* Clear the existing html in the storage content
|
||||
*/
|
||||
var content = $container.find('.storage_content');
|
||||
content.empty();
|
||||
|
||||
$.get("{{ url_for('file_manager.index') }}", function(data) {
|
||||
content.append(data);
|
||||
});
|
||||
|
||||
transId = getTransId(params);
|
||||
if (transId.readyState == 4)
|
||||
t_res = JSON.parse(transId.responseText);
|
||||
trans_id = t_res.data.fileTransId;
|
||||
|
||||
};
|
||||
|
||||
// Dialog property
|
||||
return {
|
||||
main: function(params) {
|
||||
var trans_id;
|
||||
// Set title and button name
|
||||
if (_.isUndefined(params['dialog_title']))
|
||||
params['dialog_title'] = 'Create file';
|
||||
this.set('title', params['dialog_title']);
|
||||
if (_.isUndefined(params['btn_primary']))
|
||||
params['btn_primary'] = 'Create';
|
||||
this.set('label', params['btn_primary']);
|
||||
|
||||
params = JSON.stringify(params);
|
||||
$container.find('.storage_content').remove();
|
||||
$container.append("<div class='storage_content'></div>");
|
||||
renderStoragePanel(params);
|
||||
this.show();
|
||||
},
|
||||
settings: {
|
||||
label: undefined
|
||||
},
|
||||
settingUpdated: function (key, oldValue, newValue) {
|
||||
switch (key) {
|
||||
case 'message':
|
||||
this.setMessage(newValue);
|
||||
break;
|
||||
case 'label':
|
||||
if (this.__internal.buttons[0].element) {
|
||||
this.__internal.buttons[0].element.innerHTML = newValue;
|
||||
}
|
||||
break;
|
||||
}
|
||||
},
|
||||
setup:function() {
|
||||
return {
|
||||
buttons:[
|
||||
{
|
||||
text: "{{ _('Create') }}", key: 13, className: "btn btn-primary fa fa-file file_manager_create file_manager_ok pg-alertify-button disabled"
|
||||
},
|
||||
{
|
||||
text: "{{ _('Cancel') }}", key: 27, className: "btn btn-danger fa fa-times file_manager_create_cancel pg-alertify-button"
|
||||
}
|
||||
],
|
||||
focus: { element: 0 },
|
||||
options: {
|
||||
closableByDimmer: false,
|
||||
maximizable: false,
|
||||
closable: false,
|
||||
movable: true
|
||||
}
|
||||
};
|
||||
},
|
||||
replace_file: function() {
|
||||
$('.replace_file').show();
|
||||
$('.replace_file .btn_yes').click(function(self) {
|
||||
$('.replace_file').hide();
|
||||
var selected_item = $('.allowed_file_types .create_input input[type="text"]').val(),
|
||||
newFile = $('.currentpath').val() + selected_item,
|
||||
newFile = newFile.substr(1);
|
||||
pgAdmin.Browser.Events.trigger('pgadmin-storage:finish_btn:create_file', newFile);
|
||||
$('.file_manager_create_cancel').trigger('click');
|
||||
});
|
||||
$('.replace_file .btn_no').click(function() {
|
||||
$('.replace_file').hide();
|
||||
});
|
||||
},
|
||||
is_file_exist: function() {
|
||||
var selected_item = $('.allowed_file_types .create_input input[type="text"]').val(),
|
||||
is_exist = false;
|
||||
|
||||
var file_data = {
|
||||
'path': $('.currentpath').val(),
|
||||
'name': selected_item,
|
||||
'mode': 'is_file_exist'
|
||||
};
|
||||
|
||||
$.ajax({
|
||||
type: 'POST',
|
||||
data: JSON.stringify(file_data),
|
||||
url: fileConnector + trans_id+'/',
|
||||
dataType: 'json',
|
||||
contentType: "application/x-download; charset=utf-8",
|
||||
async: false,
|
||||
success: function(resp){
|
||||
data = resp.data.result;
|
||||
if(data['Code'] === 0){
|
||||
is_exist = true;
|
||||
} else {
|
||||
is_exist = false;
|
||||
}
|
||||
}
|
||||
});
|
||||
return is_exist;
|
||||
},
|
||||
create_file: function() {
|
||||
var selected_item = $('.allowed_file_types .create_input input[type="text"]').val(),
|
||||
is_exist = false,
|
||||
is_created = false;
|
||||
|
||||
var post_data = {
|
||||
'path': $('.currentpath').val(),
|
||||
'name': selected_item,
|
||||
'mode': 'create_file'
|
||||
};
|
||||
|
||||
$.ajax({
|
||||
type: 'POST',
|
||||
data: JSON.stringify(post_data),
|
||||
url: fileConnector + trans_id+'/',
|
||||
dataType: 'json',
|
||||
contentType: "application/x-download; charset=utf-8",
|
||||
async: false,
|
||||
success: function(resp){
|
||||
data = resp.data.result;
|
||||
if(data['Code'] === 0){
|
||||
alertify.success("New File created successfully.");
|
||||
is_created = true;
|
||||
} else {
|
||||
alertify.error(data['Error']);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
});
|
||||
return is_created;
|
||||
},
|
||||
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;
|
||||
newFile = newFile.substr(1);
|
||||
if(!_.isUndefined(selected_item) && selected_item !== '' && this.is_file_exist()) {
|
||||
this.replace_file();
|
||||
closeEvent.cancel = true;
|
||||
}
|
||||
else {
|
||||
var is_created = this.create_file();
|
||||
if (is_created) {
|
||||
pgAdmin.Browser.Events.trigger('pgadmin-storage:finish_btn:create_file', newFile);
|
||||
removeTransId(trans_id);
|
||||
}
|
||||
else {
|
||||
closeEvent.cancel = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (closeEvent.button.text == "{{ _('Cancel') }}"){
|
||||
removeTransId(trans_id);
|
||||
}
|
||||
},
|
||||
build: function() {
|
||||
this.elements.content.appendChild($container.get(0));
|
||||
},
|
||||
hooks: {
|
||||
onshow: function() {
|
||||
$(this.elements.body).addClass('pgadmin-storage-body');
|
||||
}
|
||||
}
|
||||
};
|
||||
});
|
||||
},
|
||||
show_storage_dlg: function(params) {
|
||||
alertify.storageManagerDlg(params).resizeTo('60%', '80%');
|
||||
},
|
||||
show_file_selection: function(params) {
|
||||
alertify.fileSelectionDlg(params).resizeTo('60%', '80%');
|
||||
},
|
||||
show_folder_selection: function(params) {
|
||||
alertify.folderSelectionDlg(params).resizeTo('60%', '80%');
|
||||
},
|
||||
show_create_dlg: function(params) {
|
||||
alertify.createModeDlg(params).resizeTo('60%', '80%');
|
||||
},
|
||||
// call dialogs subject to dialog_type param
|
||||
show_dialog: function(params) {
|
||||
if(params.dialog_type == 'select_file') {
|
||||
this.show_file_selection(params);
|
||||
}
|
||||
else if (params.dialog_type == 'select_folder') {
|
||||
this.show_folder_selection(params);
|
||||
}
|
||||
else if (params.dialog_type == 'create_file') {
|
||||
this.show_create_dlg(params);
|
||||
}
|
||||
else {
|
||||
this.show_storage_dlg(params);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
return pgAdmin.FileManager;
|
||||
});
|
|
@ -0,0 +1,25 @@
|
|||
{
|
||||
"options": {
|
||||
"culture": "en",
|
||||
"lang": "py",
|
||||
"defaultViewMode": "grid",
|
||||
"autoload": true,
|
||||
"showFullPath": false,
|
||||
"dialog_type": "{{data.dialog_type}}",
|
||||
"fileRoot": "{{data.fileroot}}",
|
||||
"capabilities": [{% for i in data.capabilities %}{% if loop.index != 1 %}, {% endif %}"{{i}}"{% endfor %}],
|
||||
"allowed_file_types": [{% for i in data.supported_types %}{% if loop.index != 1 %}, {% endif %}"{{i}}"{% endfor %}],
|
||||
"platform_type": "{{ data.platform_type }}",
|
||||
"show_volumes":"{{data.show_volumes}}"
|
||||
},
|
||||
"security": {
|
||||
"uploadPolicy": "{{ data.security.uploadPolicy }}",
|
||||
"uploadRestrictions": [{% for i in data.security.uploadRestrictions %}{% if loop.index != 1 %}, {% endif %}"{{i}}"{% endfor %}]
|
||||
},
|
||||
"upload": {
|
||||
"multiple": "{{ data.upload.multiple }}",
|
||||
"number": 20,
|
||||
"fileSizeLimit": "{{ data.upload.fileSizeLimit }}",
|
||||
"imagesOnly": false
|
||||
}
|
||||
}
|
|
@ -0,0 +1,41 @@
|
|||
{
|
||||
"LANGUAGE_FILE_NOT_FOUND": "Language file not found.",
|
||||
"upload_success": "File uploaded successfully.",
|
||||
"upload_error": "Error uploading file",
|
||||
"browse": "Browse...",
|
||||
"bytes": " bytes",
|
||||
"cancel": "Cancel",
|
||||
"close": "Close",
|
||||
"confirmation_delete": "Are you sure you wish to delete this file?",
|
||||
"current_folder": "",
|
||||
"del": "Delete",
|
||||
"download": "Download",
|
||||
"dz_dictDefaultMessage": "Drop files here to upload",
|
||||
"dz_dictFallbackMessage": "Your browser does not support drag'n'drop file uploads.",
|
||||
"dz_dictMaxFilesExceeded": "Only %s simultaneous uploads are allowed.",
|
||||
"dz_dictInvalidFileType": "You can't upload files of this type.",
|
||||
"edit": "Edit file",
|
||||
"file_size_limit": "The file size limit (per file) is ",
|
||||
"file_too_big": "The file is too big.",
|
||||
"gb": "gb",
|
||||
"grid_view": "Switch to grid view.",
|
||||
"items": "items",
|
||||
"kb": "kb",
|
||||
"list_view": "Switch to list view.",
|
||||
"mb": "mb",
|
||||
"modified": "Modified",
|
||||
"move": "Move to ...",
|
||||
"name": "Name",
|
||||
"new_folder": "New Folder",
|
||||
"no": "No",
|
||||
"no_foldername": "No folder name was provided.",
|
||||
"rename": "Rename",
|
||||
"save": "Save",
|
||||
"select": "Select",
|
||||
"size": "Size",
|
||||
"successful_added_folder": "New folder added successfully.",
|
||||
"successful_delete": "Delete successful.",
|
||||
"successful_rename": "Rename successful.",
|
||||
"upload": "Upload",
|
||||
"yes": "Yes"
|
||||
}
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,388 @@
|
|||
/*
|
||||
* The MIT License
|
||||
* Copyright (c) 2012 Matias Meno <m@tias.me>
|
||||
*/
|
||||
@-webkit-keyframes passing-through {
|
||||
0% {
|
||||
opacity: 0;
|
||||
-webkit-transform: translateY(40px);
|
||||
-moz-transform: translateY(40px);
|
||||
-ms-transform: translateY(40px);
|
||||
-o-transform: translateY(40px);
|
||||
transform: translateY(40px); }
|
||||
30%, 70% {
|
||||
opacity: 1;
|
||||
-webkit-transform: translateY(0px);
|
||||
-moz-transform: translateY(0px);
|
||||
-ms-transform: translateY(0px);
|
||||
-o-transform: translateY(0px);
|
||||
transform: translateY(0px); }
|
||||
100% {
|
||||
opacity: 0;
|
||||
-webkit-transform: translateY(-40px);
|
||||
-moz-transform: translateY(-40px);
|
||||
-ms-transform: translateY(-40px);
|
||||
-o-transform: translateY(-40px);
|
||||
transform: translateY(-40px); } }
|
||||
@-moz-keyframes passing-through {
|
||||
0% {
|
||||
opacity: 0;
|
||||
-webkit-transform: translateY(40px);
|
||||
-moz-transform: translateY(40px);
|
||||
-ms-transform: translateY(40px);
|
||||
-o-transform: translateY(40px);
|
||||
transform: translateY(40px); }
|
||||
30%, 70% {
|
||||
opacity: 1;
|
||||
-webkit-transform: translateY(0px);
|
||||
-moz-transform: translateY(0px);
|
||||
-ms-transform: translateY(0px);
|
||||
-o-transform: translateY(0px);
|
||||
transform: translateY(0px); }
|
||||
100% {
|
||||
opacity: 0;
|
||||
-webkit-transform: translateY(-40px);
|
||||
-moz-transform: translateY(-40px);
|
||||
-ms-transform: translateY(-40px);
|
||||
-o-transform: translateY(-40px);
|
||||
transform: translateY(-40px); } }
|
||||
@keyframes passing-through {
|
||||
0% {
|
||||
opacity: 0;
|
||||
-webkit-transform: translateY(40px);
|
||||
-moz-transform: translateY(40px);
|
||||
-ms-transform: translateY(40px);
|
||||
-o-transform: translateY(40px);
|
||||
transform: translateY(40px); }
|
||||
30%, 70% {
|
||||
opacity: 1;
|
||||
-webkit-transform: translateY(0px);
|
||||
-moz-transform: translateY(0px);
|
||||
-ms-transform: translateY(0px);
|
||||
-o-transform: translateY(0px);
|
||||
transform: translateY(0px); }
|
||||
100% {
|
||||
opacity: 0;
|
||||
-webkit-transform: translateY(-40px);
|
||||
-moz-transform: translateY(-40px);
|
||||
-ms-transform: translateY(-40px);
|
||||
-o-transform: translateY(-40px);
|
||||
transform: translateY(-40px); } }
|
||||
@-webkit-keyframes slide-in {
|
||||
0% {
|
||||
opacity: 0;
|
||||
-webkit-transform: translateY(40px);
|
||||
-moz-transform: translateY(40px);
|
||||
-ms-transform: translateY(40px);
|
||||
-o-transform: translateY(40px);
|
||||
transform: translateY(40px); }
|
||||
30% {
|
||||
opacity: 1;
|
||||
-webkit-transform: translateY(0px);
|
||||
-moz-transform: translateY(0px);
|
||||
-ms-transform: translateY(0px);
|
||||
-o-transform: translateY(0px);
|
||||
transform: translateY(0px); } }
|
||||
@-moz-keyframes slide-in {
|
||||
0% {
|
||||
opacity: 0;
|
||||
-webkit-transform: translateY(40px);
|
||||
-moz-transform: translateY(40px);
|
||||
-ms-transform: translateY(40px);
|
||||
-o-transform: translateY(40px);
|
||||
transform: translateY(40px); }
|
||||
30% {
|
||||
opacity: 1;
|
||||
-webkit-transform: translateY(0px);
|
||||
-moz-transform: translateY(0px);
|
||||
-ms-transform: translateY(0px);
|
||||
-o-transform: translateY(0px);
|
||||
transform: translateY(0px); } }
|
||||
@keyframes slide-in {
|
||||
0% {
|
||||
opacity: 0;
|
||||
-webkit-transform: translateY(40px);
|
||||
-moz-transform: translateY(40px);
|
||||
-ms-transform: translateY(40px);
|
||||
-o-transform: translateY(40px);
|
||||
transform: translateY(40px); }
|
||||
30% {
|
||||
opacity: 1;
|
||||
-webkit-transform: translateY(0px);
|
||||
-moz-transform: translateY(0px);
|
||||
-ms-transform: translateY(0px);
|
||||
-o-transform: translateY(0px);
|
||||
transform: translateY(0px); } }
|
||||
@-webkit-keyframes pulse {
|
||||
0% {
|
||||
-webkit-transform: scale(1);
|
||||
-moz-transform: scale(1);
|
||||
-ms-transform: scale(1);
|
||||
-o-transform: scale(1);
|
||||
transform: scale(1); }
|
||||
10% {
|
||||
-webkit-transform: scale(1.1);
|
||||
-moz-transform: scale(1.1);
|
||||
-ms-transform: scale(1.1);
|
||||
-o-transform: scale(1.1);
|
||||
transform: scale(1.1); }
|
||||
20% {
|
||||
-webkit-transform: scale(1);
|
||||
-moz-transform: scale(1);
|
||||
-ms-transform: scale(1);
|
||||
-o-transform: scale(1);
|
||||
transform: scale(1); } }
|
||||
@-moz-keyframes pulse {
|
||||
0% {
|
||||
-webkit-transform: scale(1);
|
||||
-moz-transform: scale(1);
|
||||
-ms-transform: scale(1);
|
||||
-o-transform: scale(1);
|
||||
transform: scale(1); }
|
||||
10% {
|
||||
-webkit-transform: scale(1.1);
|
||||
-moz-transform: scale(1.1);
|
||||
-ms-transform: scale(1.1);
|
||||
-o-transform: scale(1.1);
|
||||
transform: scale(1.1); }
|
||||
20% {
|
||||
-webkit-transform: scale(1);
|
||||
-moz-transform: scale(1);
|
||||
-ms-transform: scale(1);
|
||||
-o-transform: scale(1);
|
||||
transform: scale(1); } }
|
||||
@keyframes pulse {
|
||||
0% {
|
||||
-webkit-transform: scale(1);
|
||||
-moz-transform: scale(1);
|
||||
-ms-transform: scale(1);
|
||||
-o-transform: scale(1);
|
||||
transform: scale(1); }
|
||||
10% {
|
||||
-webkit-transform: scale(1.1);
|
||||
-moz-transform: scale(1.1);
|
||||
-ms-transform: scale(1.1);
|
||||
-o-transform: scale(1.1);
|
||||
transform: scale(1.1); }
|
||||
20% {
|
||||
-webkit-transform: scale(1);
|
||||
-moz-transform: scale(1);
|
||||
-ms-transform: scale(1);
|
||||
-o-transform: scale(1);
|
||||
transform: scale(1); } }
|
||||
.dropzone, .dropzone * {
|
||||
box-sizing: border-box; }
|
||||
|
||||
.dropzone {
|
||||
min-height: 150px;
|
||||
border: 2px solid rgba(0, 0, 0, 0.3);
|
||||
background: white;
|
||||
padding: 20px 20px; }
|
||||
.dropzone.dz-clickable {
|
||||
cursor: pointer; }
|
||||
.dropzone.dz-clickable * {
|
||||
cursor: default; }
|
||||
.dropzone.dz-clickable .dz-message, .dropzone.dz-clickable .dz-message * {
|
||||
cursor: pointer; }
|
||||
.dropzone.dz-started .dz-message {
|
||||
display: none; }
|
||||
.dropzone.dz-drag-hover {
|
||||
border-style: solid; }
|
||||
.dropzone.dz-drag-hover .dz-message {
|
||||
opacity: 0.5; }
|
||||
.dropzone .dz-message {
|
||||
text-align: center;
|
||||
margin: 2em 0; }
|
||||
.dropzone .dz-preview {
|
||||
position: relative;
|
||||
display: inline-block;
|
||||
vertical-align: top;
|
||||
margin: 16px;
|
||||
min-height: 100px; }
|
||||
.dropzone .dz-preview:hover {
|
||||
z-index: 1000; }
|
||||
.dropzone .dz-preview:hover .dz-details {
|
||||
opacity: 1; }
|
||||
.dropzone .dz-preview.dz-file-preview .dz-image {
|
||||
border-radius: 20px;
|
||||
background: #999;
|
||||
background: linear-gradient(to bottom, #eee, #ddd); }
|
||||
.dropzone .dz-preview.dz-file-preview .dz-details {
|
||||
opacity: 1; }
|
||||
.dropzone .dz-preview.dz-image-preview {
|
||||
background: white; }
|
||||
.dropzone .dz-preview.dz-image-preview .dz-details {
|
||||
-webkit-transition: opacity 0.2s linear;
|
||||
-moz-transition: opacity 0.2s linear;
|
||||
-ms-transition: opacity 0.2s linear;
|
||||
-o-transition: opacity 0.2s linear;
|
||||
transition: opacity 0.2s linear; }
|
||||
.dropzone .dz-preview .dz-remove {
|
||||
font-size: 14px;
|
||||
text-align: center;
|
||||
display: block;
|
||||
cursor: pointer;
|
||||
border: none; }
|
||||
.dropzone .dz-preview .dz-remove:hover {
|
||||
text-decoration: underline; }
|
||||
.dropzone .dz-preview:hover .dz-details {
|
||||
opacity: 1; }
|
||||
.dropzone .dz-preview .dz-details {
|
||||
z-index: 20;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
opacity: 0;
|
||||
font-size: 13px;
|
||||
min-width: 100%;
|
||||
max-width: 100%;
|
||||
padding: 2em 1em;
|
||||
text-align: center;
|
||||
color: rgba(0, 0, 0, 0.9);
|
||||
line-height: 150%; }
|
||||
.dropzone .dz-preview .dz-details .dz-size {
|
||||
margin-bottom: 1em;
|
||||
font-size: 16px; }
|
||||
.dropzone .dz-preview .dz-details .dz-filename {
|
||||
white-space: nowrap; }
|
||||
.dropzone .dz-preview .dz-details .dz-filename:hover span {
|
||||
border: 1px solid rgba(200, 200, 200, 0.8);
|
||||
background-color: rgba(255, 255, 255, 0.8); }
|
||||
.dropzone .dz-preview .dz-details .dz-filename:not(:hover) {
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis; }
|
||||
.dropzone .dz-preview .dz-details .dz-filename:not(:hover) span {
|
||||
border: 1px solid transparent; }
|
||||
.dropzone .dz-preview .dz-details .dz-filename span, .dropzone .dz-preview .dz-details .dz-size span {
|
||||
background-color: rgba(255, 255, 255, 0.4);
|
||||
padding: 0 0.4em;
|
||||
border-radius: 3px; }
|
||||
.dropzone .dz-preview:hover .dz-image img {
|
||||
-webkit-transform: scale(1.05, 1.05);
|
||||
-moz-transform: scale(1.05, 1.05);
|
||||
-ms-transform: scale(1.05, 1.05);
|
||||
-o-transform: scale(1.05, 1.05);
|
||||
transform: scale(1.05, 1.05);
|
||||
-webkit-filter: blur(8px);
|
||||
filter: blur(8px); }
|
||||
.dropzone .dz-preview .dz-image {
|
||||
border-radius: 20px;
|
||||
overflow: hidden;
|
||||
width: 120px;
|
||||
height: 120px;
|
||||
position: relative;
|
||||
display: block;
|
||||
z-index: 10; }
|
||||
.dropzone .dz-preview .dz-image img {
|
||||
display: block; }
|
||||
.dropzone .dz-preview.dz-success .dz-success-mark {
|
||||
-webkit-animation: passing-through 3s cubic-bezier(0.77, 0, 0.175, 1);
|
||||
-moz-animation: passing-through 3s cubic-bezier(0.77, 0, 0.175, 1);
|
||||
-ms-animation: passing-through 3s cubic-bezier(0.77, 0, 0.175, 1);
|
||||
-o-animation: passing-through 3s cubic-bezier(0.77, 0, 0.175, 1);
|
||||
animation: passing-through 3s cubic-bezier(0.77, 0, 0.175, 1); }
|
||||
.dropzone .dz-preview.dz-error .dz-error-mark {
|
||||
opacity: 1;
|
||||
-webkit-animation: slide-in 3s cubic-bezier(0.77, 0, 0.175, 1);
|
||||
-moz-animation: slide-in 3s cubic-bezier(0.77, 0, 0.175, 1);
|
||||
-ms-animation: slide-in 3s cubic-bezier(0.77, 0, 0.175, 1);
|
||||
-o-animation: slide-in 3s cubic-bezier(0.77, 0, 0.175, 1);
|
||||
animation: slide-in 3s cubic-bezier(0.77, 0, 0.175, 1); }
|
||||
.dropzone .dz-preview .dz-success-mark, .dropzone .dz-preview .dz-error-mark {
|
||||
pointer-events: none;
|
||||
opacity: 0;
|
||||
z-index: 500;
|
||||
position: absolute;
|
||||
display: block;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
margin-left: -27px;
|
||||
margin-top: -27px; }
|
||||
.dropzone .dz-preview .dz-success-mark svg, .dropzone .dz-preview .dz-error-mark svg {
|
||||
display: block;
|
||||
width: 54px;
|
||||
height: 54px; }
|
||||
.dropzone .dz-preview.dz-processing .dz-progress {
|
||||
opacity: 1;
|
||||
-webkit-transition: all 0.2s linear;
|
||||
-moz-transition: all 0.2s linear;
|
||||
-ms-transition: all 0.2s linear;
|
||||
-o-transition: all 0.2s linear;
|
||||
transition: all 0.2s linear; }
|
||||
.dropzone .dz-preview.dz-complete .dz-progress {
|
||||
opacity: 0;
|
||||
-webkit-transition: opacity 0.4s ease-in;
|
||||
-moz-transition: opacity 0.4s ease-in;
|
||||
-ms-transition: opacity 0.4s ease-in;
|
||||
-o-transition: opacity 0.4s ease-in;
|
||||
transition: opacity 0.4s ease-in; }
|
||||
.dropzone .dz-preview:not(.dz-processing) .dz-progress {
|
||||
-webkit-animation: pulse 6s ease infinite;
|
||||
-moz-animation: pulse 6s ease infinite;
|
||||
-ms-animation: pulse 6s ease infinite;
|
||||
-o-animation: pulse 6s ease infinite;
|
||||
animation: pulse 6s ease infinite; }
|
||||
.dropzone .dz-preview .dz-progress {
|
||||
opacity: 1;
|
||||
z-index: 1000;
|
||||
pointer-events: none;
|
||||
position: absolute;
|
||||
height: 16px;
|
||||
left: 50%;
|
||||
top: 50%;
|
||||
margin-top: -8px;
|
||||
width: 80px;
|
||||
margin-left: -40px;
|
||||
background: rgba(255, 255, 255, 0.9);
|
||||
-webkit-transform: scale(1);
|
||||
border-radius: 8px;
|
||||
overflow: hidden; }
|
||||
.dropzone .dz-preview .dz-progress .dz-upload {
|
||||
background: #333;
|
||||
background: linear-gradient(to bottom, #666, #444);
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
bottom: 0;
|
||||
width: 0;
|
||||
-webkit-transition: width 300ms ease-in-out;
|
||||
-moz-transition: width 300ms ease-in-out;
|
||||
-ms-transition: width 300ms ease-in-out;
|
||||
-o-transition: width 300ms ease-in-out;
|
||||
transition: width 300ms ease-in-out; }
|
||||
.dropzone .dz-preview.dz-error .dz-error-message {
|
||||
display: block; }
|
||||
.dropzone .dz-preview.dz-error:hover .dz-error-message {
|
||||
opacity: 1;
|
||||
pointer-events: auto; }
|
||||
.dropzone .dz-preview .dz-error-message {
|
||||
pointer-events: none;
|
||||
z-index: 1000;
|
||||
position: absolute;
|
||||
display: block;
|
||||
display: none;
|
||||
opacity: 0;
|
||||
-webkit-transition: opacity 0.3s ease;
|
||||
-moz-transition: opacity 0.3s ease;
|
||||
-ms-transition: opacity 0.3s ease;
|
||||
-o-transition: opacity 0.3s ease;
|
||||
transition: opacity 0.3s ease;
|
||||
border-radius: 8px;
|
||||
font-size: 13px;
|
||||
top: 130px;
|
||||
left: -10px;
|
||||
width: 140px;
|
||||
background: #be2626;
|
||||
background: linear-gradient(to bottom, #be2626, #a92222);
|
||||
padding: 0.5em 1.2em;
|
||||
color: white; }
|
||||
.dropzone .dz-preview .dz-error-message:after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: -6px;
|
||||
left: 64px;
|
||||
width: 0;
|
||||
height: 0;
|
||||
border-left: 6px solid transparent;
|
||||
border-right: 6px solid transparent;
|
||||
border-bottom: 6px solid #be2626; }
|
|
@ -1104,4 +1104,55 @@ div.backform_control_notes label.control-label {
|
|||
|
||||
form[name="change_password_form"] .help-block {
|
||||
color: #A94442 !important;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
.file_selection_ctrl .create_input span {
|
||||
padding-right: 10px;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.file_selection_ctrl .create_input input[type="text"] {
|
||||
height: 23px;
|
||||
padding: 2px;
|
||||
width: 194px;
|
||||
border-radius: 2px;
|
||||
}
|
||||
|
||||
.file_selection_ctrl .browse_file_input {
|
||||
display: inline-block;
|
||||
width: 220px;
|
||||
margin-right: 0;
|
||||
}
|
||||
|
||||
.file_selection_ctrl button.select_item {
|
||||
display: inline;
|
||||
background: #777;
|
||||
background: -webkit-linear-gradient(#777, #999999);
|
||||
background: -o-linear-gradient(#777, #999);
|
||||
background: -moz-linear-gradient(#777, #999);
|
||||
background: linear-gradient(#777, #999);
|
||||
color: #fff;
|
||||
padding: 9px 0px 9px 0px;
|
||||
margin-left: 0px;
|
||||
margin-right: -7px;
|
||||
margin-top: -4px;
|
||||
min-width: 30px;
|
||||
border-top-left-radius: 0;
|
||||
border-bottom-left-radius: 0;
|
||||
}
|
||||
|
||||
.file_selection_ctrl button.select_item:focus,
|
||||
.file_selection_ctrl button.select_item:active {
|
||||
outline: none;
|
||||
box-shadow: none;
|
||||
}
|
||||
|
||||
.file_selection_ctrl input[type="text"] {
|
||||
width: calc(100% - 17px);
|
||||
border: none;
|
||||
margin-left: -6px;
|
||||
margin-top: -3px;
|
||||
height: 32px;
|
||||
padding-left: 5px;
|
||||
}
|
||||
|
|
|
@ -2041,5 +2041,71 @@
|
|||
].join("\n"))
|
||||
});
|
||||
|
||||
/*
|
||||
* Input File Control: This control is used with Storage Manager Dialog,
|
||||
* It allows user to perform following operations:
|
||||
* - Select File
|
||||
* - Select Folder
|
||||
* - Create File
|
||||
* - Opening Storage Manager Dialog itself.
|
||||
*/
|
||||
var FileControl = Backform.FileControl = Backform.InputControl.extend({
|
||||
defaults: {
|
||||
type: "text",
|
||||
label: "",
|
||||
min: undefined,
|
||||
max: undefined,
|
||||
maxlength: 255,
|
||||
extraClasses: [],
|
||||
dialog_title: '',
|
||||
btn_primary: '',
|
||||
helpMessage: null,
|
||||
dialog_type: 'select_file'
|
||||
},
|
||||
initialize: function(){
|
||||
Backform.InputControl.prototype.initialize.apply(this, arguments);
|
||||
|
||||
// Listen click events of Storage Manager dialog buttons
|
||||
pgAdmin.Browser.Events.on('pgadmin-storage:finish_btn:'+this.field.get('dialog_type'), this.storage_dlg_hander, this);
|
||||
},
|
||||
template: _.template([
|
||||
'<label class="<%=Backform.controlLabelClassName%>"><%=label%></label>',
|
||||
'<div class="<%=Backform.controlsClassName%>">',
|
||||
'<div class="file_selection_ctrl form-control">',
|
||||
'<input type="<%=type%>" class="browse_file_input form-control <%=extraClasses.join(\' \')%>" name="<%=name%>" min="<%=min%>" max="<%=max%>"maxlength="<%=maxlength%>" value="<%-value%>" placeholder="<%-placeholder%>" <%=disabled ? "disabled" : ""%> <%=required ? "required" : ""%> />',
|
||||
'<button class="btn fa fa-ellipsis-h select_item pull-right" <%=disabled ? "disabled" : ""%> ></button>',
|
||||
'<% if (helpMessage && helpMessage.length) { %>',
|
||||
'<span class="<%=Backform.helpMessageClassName%>"><%=helpMessage%></span>',
|
||||
'<% } %>',
|
||||
'</div>',
|
||||
'</div>'
|
||||
].join("\n")),
|
||||
events: {
|
||||
"click .select_item": "onSelect",
|
||||
},
|
||||
onSelect: function(e) {
|
||||
var dialog_type = this.field.get('dialog_type');
|
||||
supp_types = this.field.get('supp_types'),
|
||||
btn_primary = this.field.get('btn_primary'),
|
||||
dialog_title = this.field.get('dialog_title');
|
||||
var params = {
|
||||
supported_types: supp_types,
|
||||
dialog_type: dialog_type,
|
||||
dialog_title: dialog_title,
|
||||
btn_primary: btn_primary
|
||||
};
|
||||
pgAdmin.FileManager.init();
|
||||
pgAdmin.FileManager.show_dialog(params);
|
||||
},
|
||||
storage_dlg_hander: function(value) {
|
||||
var field = _.defaults(this.field.toJSON(), this.defaults),
|
||||
attrArr = this.field.get("name").split('.'),
|
||||
name = attrArr.shift();
|
||||
|
||||
// Set selected value into the model
|
||||
this.model.set(name, decodeURI(value));
|
||||
}
|
||||
});
|
||||
|
||||
return Backform;
|
||||
}));
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -11,6 +11,7 @@ from flask import Blueprint
|
|||
from collections import defaultdict
|
||||
from operator import attrgetter
|
||||
from .preferences import Preferences
|
||||
from .paths import get_storage_directory
|
||||
|
||||
|
||||
class PgAdminModule(Blueprint):
|
||||
|
|
|
@ -0,0 +1,68 @@
|
|||
##########################################################################
|
||||
#
|
||||
# pgAdmin 4 - PostgreSQL Tools
|
||||
#
|
||||
# Copyright (C) 2013 - 2016, The pgAdmin Development Team
|
||||
# This software is released under the PostgreSQL Licence
|
||||
#
|
||||
#########################################################################
|
||||
|
||||
"""This file contains functions fetching different utility paths."""
|
||||
|
||||
import os
|
||||
from flask.ext.security import current_user, login_required
|
||||
import config
|
||||
|
||||
|
||||
@login_required
|
||||
def get_storage_directory():
|
||||
|
||||
if config.SERVER_MODE is not True:
|
||||
return None
|
||||
|
||||
storage_dir = getattr(
|
||||
config, 'STORAGE_DIR',
|
||||
os.path.join(
|
||||
os.path.realpath(
|
||||
os.path.expanduser('~/.pgadmin/')
|
||||
), 'storage'
|
||||
)
|
||||
)
|
||||
username = current_user.email.split('@')[0]
|
||||
if len(username) == 0 or username[0].isdigit():
|
||||
username = 'pga_user_' + username
|
||||
|
||||
storage_dir = os.path.join(storage_dir, username)
|
||||
print(storage_dir)
|
||||
|
||||
if not os.path.exists(storage_dir):
|
||||
os.makedirs(storage_dir, int('700', 8))
|
||||
|
||||
return storage_dir
|
||||
|
||||
|
||||
def init_app(app):
|
||||
|
||||
if config.SERVER_MODE is not True:
|
||||
return None
|
||||
|
||||
storage_dir = getattr(
|
||||
config, 'STORAGE_DIR',
|
||||
os.path.join(
|
||||
os.path.realpath(
|
||||
os.path.expanduser('~/.pgadmin/')
|
||||
), 'storage'
|
||||
)
|
||||
)
|
||||
|
||||
if not os.path.isdir(storage_dir):
|
||||
if os.path.exists(storage_dir):
|
||||
raise Exception(
|
||||
'The value specified for as the storage directory is not a directory!'
|
||||
)
|
||||
os.makedirs(storage_dir, int('700', 8))
|
||||
|
||||
if not os.access(storage_dir, os.W_OK | os.R_OK):
|
||||
raise Exception(
|
||||
'The user does not have permission to read, write on the specified storage directory!'
|
||||
)
|
Loading…
Reference in New Issue