500 lines
18 KiB
Ruby
500 lines
18 KiB
Ruby
class Admin::FileManagersController < OrbitAdminController
|
|
include ActionView::Helpers::NumberHelper
|
|
before_action :set_base_url, except: [:settings, :update_settings]
|
|
def render_403
|
|
render :file => "#{Rails.root}/app/views/errors/403.html", :layout => false, :status => 403, :formats => [:html]
|
|
end
|
|
def render_404
|
|
render :file => "#{Rails.root}/app/views/errors/404.html", :layout => false, :status => 404, :formats => [:html]
|
|
end
|
|
def forbidden_error
|
|
render :body => nil, :status => 403
|
|
end
|
|
def index
|
|
end
|
|
|
|
def module
|
|
@current_user = current_user
|
|
@current_user_id = @current_user.id
|
|
@recycle_bin = params[:recycle_bin] == 'true'
|
|
@file = nil
|
|
if params[:file_id]
|
|
upload_item = FileManagerUpload.where(:id=> params[:file_id]).first
|
|
@upload_item = upload_item
|
|
if upload_item
|
|
@active_version = @upload_item.version
|
|
@id = @upload_item.id
|
|
@is_img = !(@upload_item.filename.match(/\.(jpg|gif|png|webp)$/i).nil?)
|
|
if upload_item.is_trash
|
|
absolute_path = upload_item.file_manager_trash.trash_path
|
|
else
|
|
absolute_path = upload_item.get_real_path
|
|
end
|
|
@all_uploads = get_uploads(upload_item.path, true, {:is_trash=>false})
|
|
if File.file?(absolute_path)
|
|
if params[:download]
|
|
send_file(absolute_path)
|
|
elsif File.size(absolute_path) > 1_000_000
|
|
@file = "File is too big!"
|
|
@too_big = true
|
|
else
|
|
@file = File.read(absolute_path)
|
|
@file = fix_encoding(@file)
|
|
end
|
|
end
|
|
end
|
|
end
|
|
if @recycle_bin
|
|
recycle_bin
|
|
else
|
|
check_path_exist('')
|
|
populate_directory(@module_key, '')
|
|
end
|
|
if request.xhr?
|
|
if @file
|
|
render :partial => "file"
|
|
elsif @recycle_bin
|
|
render :partial => "recycle_bin"
|
|
else
|
|
render :partial => "module"
|
|
end
|
|
end
|
|
end
|
|
|
|
def recycle_bin
|
|
@thread = Multithread.where(:key=>"clean_trashes_#{@current_user_id}").first
|
|
@thread_title = nil
|
|
if @thread && @thread.status[:status] == 'finish'
|
|
@thread = nil
|
|
elsif @thread
|
|
@thread_title = I18n.t("file_manager.clean_up_recycle_bin")
|
|
end
|
|
if @thread.nil?
|
|
@thread = Multithread.where(:key=>"recover_all_#{@current_user_id}").first
|
|
if @thread && @thread.status[:status] == 'finish'
|
|
@thread = nil
|
|
elsif @thread
|
|
@thread_title = I18n.t("file_manager.recover_all")
|
|
end
|
|
end
|
|
@relative_path = I18n.t("file_manager.recycle_bin")
|
|
# @relative_to_pwd = @root_path
|
|
query_hash = {is_slave: false}
|
|
if @only_editable_for_uploader
|
|
query_hash[:user_id] = @current_user_id
|
|
end
|
|
trashes = get_trashes(query_hash).page(params[:page]).per(10)
|
|
@trashes = trashes
|
|
@module_info_dict = trashes.map{|trash| trash.module}.uniq.map{|k| [k, FileManagerSetting.get_module_info(k)]}.to_h
|
|
@directory = trashes.map do |record|
|
|
file = record.trash_path
|
|
stat = File.stat(file) rescue nil
|
|
next if stat.nil?
|
|
is_file = record.is_file
|
|
real_path_absolute = File.expand_path(file)
|
|
file = file.sub(/^#{Regexp.escape(@root_path)}/,'/')
|
|
entry = "#{file}#{is_file ? '': '/'}"
|
|
version = (record.record_only ? record.get_version : nil)
|
|
org_path = record.path.sub(/^#{Regexp.escape(@root_path)}/,'')
|
|
file_name = File.basename(file)
|
|
if version
|
|
org_path += " (version #{version})"
|
|
file_name += " (version #{version})"
|
|
end
|
|
# is_editable = @default_editable || check_editable("#{@relative_to_pwd}/#{entry}", @current_user_id)
|
|
{
|
|
size: (is_file ? (number_to_human_size stat.size rescue '-'): '-'),
|
|
type: (is_file ? :file : :directory),
|
|
date: (stat.mtime.strftime(@format_time) rescue '-'),
|
|
relative: my_escape(file).gsub('%2F', '/'),
|
|
entry: entry,
|
|
absolute: real_path_absolute,
|
|
org_path: org_path,
|
|
deleted_at: (record.created_at.strftime(@format_time) rescue '-'),
|
|
name: file_name,
|
|
module: record.module,
|
|
id: record.id.to_s,
|
|
upload_id: record.file_manager_upload_id.to_s,
|
|
version: (record.record_only ? record.get_version : nil)
|
|
# is_editable: is_editable
|
|
}
|
|
end.compact
|
|
end
|
|
|
|
def recover_all
|
|
@current_user_id = current_user.id
|
|
thread = Multithread.where(:key=>"recover_all_#{@current_user_id}").first
|
|
if thread.nil?
|
|
thread = Multithread.create(:key=>"recover_all_#{@current_user_id}",:status=>{:status=>'Processing'})
|
|
else
|
|
thread.update(:status=>{:status=>'Processing'})
|
|
end
|
|
Thread.new do
|
|
query_hash = {is_slave: false}
|
|
if @only_editable_for_uploader
|
|
query_hash[:user_id] = @current_user_id
|
|
end
|
|
trashes = get_trashes(query_hash)
|
|
all_count = trashes.count
|
|
puts_every_count = [all_count * 3 / 100, 1].max
|
|
current_count = 0
|
|
finish_percent = 0
|
|
thread.update(:status=>{:status=>'Recovering','all_count'=>all_count,'current_count'=>current_count,'finish_percent'=>finish_percent})
|
|
trashes.each do |trash|
|
|
trash.recover_file
|
|
current_count += 1
|
|
if current_count % puts_every_count == 0
|
|
finish_percent = (current_count * 100.0 / all_count).round(1)
|
|
thread.update(:status=>{:status=>'Recovering','all_count'=>all_count,'current_count'=>current_count,'finish_percent'=>finish_percent})
|
|
end
|
|
end
|
|
thread.update(:status=>{:status=>'finish','all_count'=>all_count,'current_count'=>all_count,'finish_percent'=>100})
|
|
end
|
|
render :json => {id: thread.id.to_s, title: I18n.t("file_manager.recover_all")}
|
|
end
|
|
|
|
def clean_trashes
|
|
@current_user_id = current_user.id
|
|
thread = Multithread.where(:key=>"clean_trashes_#{@current_user_id}").first
|
|
if thread.nil?
|
|
thread = Multithread.create(:key=>"clean_trashes_#{@current_user_id}",:status=>{:status=>'Processing'})
|
|
else
|
|
thread.update(:status=>{:status=>'Processing'})
|
|
end
|
|
Thread.new do
|
|
query_hash = {is_slave: false}
|
|
if @only_editable_for_uploader
|
|
query_hash[:user_id] = @current_user_id
|
|
end
|
|
trashes = get_trashes(query_hash)
|
|
all_count = trashes.count
|
|
puts_every_count = [all_count * 3 / 100, 1].max
|
|
current_count = 0
|
|
finish_percent = 0
|
|
thread.update(:status=>{:status=>'Deleting','all_count'=>all_count,'current_count'=>current_count,'finish_percent'=>finish_percent})
|
|
trashes.each do |trash|
|
|
trash.delete_file
|
|
current_count += 1
|
|
if current_count % puts_every_count == 0
|
|
finish_percent = (current_count * 100.0 / all_count).round(1)
|
|
thread.update(:status=>{:status=>'Deleting','all_count'=>all_count,'current_count'=>current_count,'finish_percent'=>finish_percent})
|
|
end
|
|
end
|
|
thread.update(:status=>{:status=>'finish','all_count'=>all_count,'current_count'=>all_count,'finish_percent'=>100})
|
|
end
|
|
render :json => {id: thread.id.to_s, title: I18n.t("file_manager.clean_up_recycle_bin")}
|
|
end
|
|
|
|
def get_trash_count
|
|
@current_user_id = current_user.id
|
|
query_hash = {is_slave: false}
|
|
if @only_editable_for_uploader
|
|
query_hash[:user_id] = @current_user_id
|
|
end
|
|
trashes = get_trashes(query_hash)
|
|
render :json => {count: trashes.count}
|
|
end
|
|
|
|
def destroy
|
|
if params[:permanent] == 'true'
|
|
if params[:type] == 'upload'
|
|
upload_record = FileManagerUpload.where(:id=>params[:id]).first
|
|
upload_record.delete_file_permanent(true)
|
|
else
|
|
trash = FileManagerTrash.where(:id=>params[:id]).first
|
|
trash.delete_file
|
|
end
|
|
render :body => nil, :status => 204
|
|
else
|
|
upload_record = FileManagerUpload.where(:id=>params[:id]).first
|
|
if params[:type] == 'upload'
|
|
newest_record = upload_record.delete_file(true)
|
|
if newest_record
|
|
data = {'id': newest_record.id.to_s, 'version': newest_record.version}
|
|
else
|
|
data = {}
|
|
end
|
|
render :json => data and return
|
|
else
|
|
absolute_path = upload_record.path
|
|
if FileManagerUpload.check_restricted(absolute_path)
|
|
forbidden_error
|
|
else
|
|
relative_path = absolute_path
|
|
is_file = !(File.directory?(absolute_path))
|
|
unless is_file
|
|
relative_path += '/'
|
|
end
|
|
upload_records = get_uploads(relative_path, is_file, {:is_default=>true})
|
|
if is_file
|
|
upload_records.each{|record| record.delete_file(false,current_user.id, params[:force_delete] == 'true') }
|
|
else
|
|
has_matched = false
|
|
master_record_idx = upload_records.index{|record| record.path == relative_path}
|
|
master_record = upload_records[master_record_idx]
|
|
force_delete = (params[:force_delete] == 'true')
|
|
master_record.delete_file(false,current_user.id, force_delete)
|
|
upload_records.delete_at(master_record_idx)
|
|
if upload_records.count != 0
|
|
if master_record.file_manager_trash
|
|
trash_path = master_record.file_manager_trash.trash_path
|
|
upload_records.each do |record|
|
|
record.delete_file(false,current_user.id, force_delete, true, Pathname.new(trash_path).join(record.path.sub(/^#{Regexp.escape(relative_path)}/,'')).to_s)
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end
|
|
render :body => nil, :status => 204
|
|
end
|
|
end
|
|
|
|
def recover
|
|
trash = FileManagerTrash.where(:id=> params[:id]).first
|
|
if trash
|
|
trash.recover_file
|
|
render :body => nil, :status => 204
|
|
else
|
|
render :body => nil, :status => 404
|
|
end
|
|
end
|
|
|
|
def rename
|
|
upload = FileManagerUpload.where(:id=>params[:id]).first
|
|
absolute_path = upload.path
|
|
@root_path = File.dirname(absolute_path)
|
|
new_path = safe_expand_path(params[:new_name])
|
|
if new_path != absolute_path
|
|
if File.exists?(new_path)
|
|
render :body => nil, :status => 403
|
|
else
|
|
relative_path = absolute_path
|
|
new_relative_path = Pathname.new(@root_path).join(params[:new_name])
|
|
parent = new_path.split('/')[0..-2].join('/')
|
|
FileUtils.mkdir_p(parent)
|
|
upload_records = get_uploads(relative_path)
|
|
FileUtils.mv(absolute_path, new_path)
|
|
upload_records.each{|r| r.update_path(new_relative_path)}
|
|
render :body => nil, :status => 204
|
|
end
|
|
else
|
|
render :body => nil, :status => 200
|
|
end
|
|
end
|
|
|
|
def save
|
|
if params[:content].present?
|
|
upload_item = FileManagerUpload.where(:id=>params[:id]).first
|
|
if upload_item.is_trash
|
|
absolute_path = upload_item.file_manager_trash.trash_path
|
|
else
|
|
absolute_path = upload_item.path
|
|
end
|
|
forbidden_error and return unless File.exist?(absolute_path)
|
|
file_relative_path = absolute_path
|
|
is_file = true
|
|
new_upload = upload_item.generate_new_upload
|
|
File.open(absolute_path,"w+"){|f| f.write(params[:content])}
|
|
render :json => {id: new_upload.id.to_s}
|
|
else
|
|
render :body => nil, :status => 400
|
|
end
|
|
end
|
|
|
|
def upload_file
|
|
input_file = params[:file]
|
|
if input_file
|
|
if params[:type] == 'update'
|
|
upload_item = FileManagerUpload.where(:id=>params[:id]).first
|
|
relative_path = upload_item.path
|
|
if input_file.original_filename != upload_item.filename
|
|
new_relative_path = Pathname.new(File.dirname(relative_path)).join(input_file.original_filename)
|
|
upload_records = get_uploads(relative_path)
|
|
FileUtils.mv(relative_path, new_relative_path)
|
|
upload_records.each{|r| r.update_path(new_relative_path)}
|
|
upload_item.reload
|
|
else
|
|
new_relative_path = relative_path
|
|
end
|
|
upload_item = upload_item.generate_new_upload
|
|
File.open(new_relative_path, 'wb') do |file|
|
|
file.write(input_file.read)
|
|
end
|
|
else
|
|
if params[:id] != 'asset'
|
|
forbidden_error and return
|
|
end
|
|
asset = current_user.assets.new
|
|
asset.data = input_file
|
|
asset.title_translations = I18n.available_locales.map{|l| [l, input_file.original_filename]}.to_h
|
|
asset.save!
|
|
upload_item = FileManagerUpload.where(:model=> "Asset", :related_id=> asset.id).first
|
|
params[:remote] = true
|
|
end
|
|
end
|
|
if params[:remote].nil?
|
|
redirect_to params[:url]
|
|
else
|
|
render :json => {
|
|
id: upload_item.id.to_s,
|
|
filename: input_file.original_filename,
|
|
size: number_to_human_size(input_file.size),
|
|
date: upload_item.created_at.strftime(@format_time)
|
|
}
|
|
end
|
|
end
|
|
|
|
def settings
|
|
FileManagerRoot.create if FileManagerRoot.count == 0
|
|
@root = FileManagerRoot.first
|
|
end
|
|
|
|
def update_settings
|
|
@root = FileManagerRoot.first
|
|
@root.update_attributes(file_manager_root_params)
|
|
redirect_to admin_file_managers_settings_path
|
|
end
|
|
|
|
def file_manager_root_params
|
|
params.require(:file_manager_root).permit!
|
|
end
|
|
|
|
private
|
|
def get_trashes(query_hash={})
|
|
query_hash[:file_manager_setting_id] = @setting_id ? @setting_id : FileManagerRoot.first.id
|
|
if @module_key && @module_key != 'all'
|
|
query_hash[:module] = @module_key
|
|
end
|
|
@trashes = FileManagerTrash.where(query_hash).order(:created_at=>-1)
|
|
end
|
|
|
|
def get_uploads(tmp_path=nil, is_file=nil, extra_condition={}, order_hash={:version=>-1})
|
|
query_hash = {}
|
|
if tmp_path != nil
|
|
query_hash[:path] = tmp_path
|
|
end
|
|
if is_file != nil
|
|
query_hash[:is_file] = is_file
|
|
query_hash[:path] = /^#{Regexp.escape(query_hash[:path].to_s)}/ if query_hash[:path]
|
|
end
|
|
query_hash[:file_manager_setting_id] = @setting_id if @setting_id
|
|
FileManagerUpload.where(query_hash.merge(extra_condition)).order(order_hash).to_a
|
|
end
|
|
|
|
def fix_encoding(str)
|
|
if str.valid_encoding?
|
|
str
|
|
else
|
|
try_list = ["utf-8","utf-16", "big-5"]
|
|
success_flag = false
|
|
try_list.each do |enc|
|
|
begin
|
|
str.encode!(str.encoding, enc)
|
|
if str.valid_encoding?
|
|
success_flag = true
|
|
break
|
|
end
|
|
rescue => e
|
|
next
|
|
end
|
|
end
|
|
unless success_flag
|
|
str = "unknown encoding!"
|
|
@unknown_encoding = true
|
|
end
|
|
str
|
|
end
|
|
end
|
|
|
|
def check_editable(path=nil, current_user_id=nil)
|
|
query_hash = {:path=>path,:user_id=>current_user_id}
|
|
query_hash[:file_manager_setting_id] = @setting_id
|
|
FileManagerUpload.where(query_hash).count != 0
|
|
end
|
|
|
|
def my_escape(string)
|
|
string.gsub(/([^ a-zA-Z0-9_.-]+)/) do
|
|
'%' + $1.unpack('H2' * $1.bytesize).join('%').upcase
|
|
end
|
|
end
|
|
|
|
def populate_directory(module_key, current_url)
|
|
if module_key == 'all'
|
|
@uploads = FileManagerUpload.where(:is_default=> true)
|
|
else
|
|
@uploads = FileManagerUpload.where(:module=> module_key, :is_default=> true)
|
|
end
|
|
@uploads = @uploads.where(:is_trash=> false)
|
|
if params[:keywords].present?
|
|
@uploads = @uploads.where(:filename=> /#{::Regexp.escape(params[:keywords])}/)
|
|
end
|
|
@uploads = @uploads.order_by(:created_at => :desc)
|
|
@uploads = @uploads.page(params[:page]).per(10)
|
|
current_url = '' if current_url == '/'
|
|
@module_info_dict = @uploads.map{|upload_item| upload_item.module}.uniq.map{|k| [k, FileManagerSetting.get_module_info(k)]}.to_h
|
|
@directory = @uploads.map do |upload_item|
|
|
file = upload_item.filename
|
|
real_path_absolute = upload_item.path
|
|
stat = (File.exist?(real_path_absolute) ? File.stat(real_path_absolute) : nil)
|
|
next if stat.nil?
|
|
is_file = stat.file?
|
|
entry = "#{file}#{is_file ? '': '/'}"
|
|
file_relative_path = upload_item.path
|
|
is_editable = @default_editable || check_editable(file_relative_path, @current_user_id)
|
|
{
|
|
module: upload_item.module,
|
|
is_file: is_file,
|
|
size: ((is_file && stat) ? number_to_human_size(stat.size) : '-'),
|
|
type: (is_file ? :file : :directory),
|
|
date: (stat ? stat.mtime.strftime(@format_time) : '-'),
|
|
relative: my_escape("#{current_url}#{file}").gsub('%2F', '/'),
|
|
entry: entry,
|
|
absolute: real_path_absolute,
|
|
is_editable: is_editable,
|
|
id: (upload_item ? upload_item.id.to_s : nil)
|
|
}
|
|
end.compact
|
|
end
|
|
|
|
def safe_expand_path(path)
|
|
current_directory = File.expand_path(@root_path)
|
|
tested_path = File.expand_path(path, @root_path)
|
|
if @disable_path_traversal && !(tested_path.starts_with?(current_directory))
|
|
raise ArgumentError, 'Should not be parent of root'
|
|
end
|
|
tested_path
|
|
end
|
|
|
|
def check_path_exist(path)
|
|
@absolute_path = safe_expand_path(path)
|
|
@relative_path = path
|
|
raise ActionController::RoutingError, 'Not Found' unless File.exists?(@absolute_path)
|
|
@absolute_path
|
|
end
|
|
|
|
def set_base_url
|
|
@base_url = ENV['BASE_URL'] || 'root'
|
|
@root_path = ENV['BASE_DIRECTORY'] || FileManagerRoot::RootPath
|
|
@root = FileManagerRoot.first
|
|
@disable_path_traversal = @root.disable_path_traversal
|
|
@format_time = I18n.locale.to_s == 'zh_tw' ? '%Y/%m/%d %H:%M' : '%d %b %Y %H:%M'
|
|
@setting = FileManagerSetting.first
|
|
@only_editable_for_uploader = false
|
|
@setting_id = nil
|
|
if @setting
|
|
@setting_id = @setting.id
|
|
@root_path = Pathname.new(@root_path).join(@setting.root_path).to_s
|
|
@only_editable_for_uploader = @setting.only_editable_for_uploader
|
|
end
|
|
@default_editable = !@only_editable_for_uploader
|
|
@module_key = params[:id]
|
|
@display_module_name = (@module_key == 'all')
|
|
@display_uploader_region = (@module_key == 'asset')
|
|
@module = ModuleApp.where(:key=> @module_key).first
|
|
module_info = FileManagerSetting.get_module_info(@module_key)
|
|
@module_title = (module_info ? module_info[:title] : nil)
|
|
@only_select_folder = (params[:select_mode] == 'true')
|
|
end
|
|
end
|