First version.
This commit is contained in:
commit
6495986188
|
@ -0,0 +1,14 @@
|
||||||
|
source "https://rubygems.org"
|
||||||
|
|
||||||
|
# Declare your gem's dependencies in bulletin.gemspec.
|
||||||
|
# Bundler will treat runtime dependencies like base dependencies, and
|
||||||
|
# development dependencies will be added by default to the :development group.
|
||||||
|
gemspec
|
||||||
|
|
||||||
|
# Declare any dependencies that are still in development here instead of in
|
||||||
|
# your gemspec. These might include edge Rails or gems from your path or
|
||||||
|
# Git. Remember to move these dependencies to your gemspec before releasing
|
||||||
|
# your gem to rubygems.org.
|
||||||
|
|
||||||
|
# To use debugger
|
||||||
|
# gem 'debugger'
|
|
@ -0,0 +1,6 @@
|
||||||
|
# Add your own tasks in files placed in lib/tasks ending in .rake,
|
||||||
|
# for example lib/tasks/capistrano.rake, and they will automatically be available to Rake.
|
||||||
|
|
||||||
|
require_relative 'config/application'
|
||||||
|
|
||||||
|
Rails.application.load_tasks
|
|
@ -0,0 +1,13 @@
|
||||||
|
// This is a manifest file that'll be compiled into application.js, which will include all the files
|
||||||
|
// listed below.
|
||||||
|
//
|
||||||
|
// Any JavaScript/Coffee file within this directory, lib/assets/javascripts, vendor/assets/javascripts,
|
||||||
|
// or any plugin's vendor/assets/javascripts directory can be referenced here using a relative path.
|
||||||
|
//
|
||||||
|
// It's not advisable to add code directly here, but if you do, it'll appear at the bottom of the
|
||||||
|
// compiled file. JavaScript code in this file should be added after the last require_* statement.
|
||||||
|
//
|
||||||
|
// Read Sprockets README (https://github.com/rails/sprockets#sprockets-directives) for details
|
||||||
|
// about supported directives.
|
||||||
|
//
|
||||||
|
//= require_tree .
|
|
@ -0,0 +1,563 @@
|
||||||
|
var csrf_token = document
|
||||||
|
.querySelector("meta[name='csrf-token']")
|
||||||
|
.getAttribute("content");
|
||||||
|
$.ajaxSetup({
|
||||||
|
headers: {
|
||||||
|
// 'Content-Type': 'multipart/form-data',
|
||||||
|
// 使用 multipart/form-data 在此不需要設定 Content-Type。
|
||||||
|
'X-Requested-With': 'XMLHttpRequest',
|
||||||
|
'X-CSRF-Token': csrf_token,
|
||||||
|
}
|
||||||
|
});
|
||||||
|
function replace_explorer_section(file_manager_section, exclude_upload_section){
|
||||||
|
var org_file_manager_section = $('#file_manager_section');
|
||||||
|
if(org_file_manager_section.find(' > .file-input').length != 0 && file_manager_section.find('> .file-uploader-script').length != 0){
|
||||||
|
org_file_manager_section.find(' > *').each(function(i,v){
|
||||||
|
if(!($(v).hasClass('file-input') || $(v).hasClass('file-uploader-script'))){
|
||||||
|
$(v).replaceWith(file_manager_section.find('>*').eq(i).clone());
|
||||||
|
}
|
||||||
|
})
|
||||||
|
if(!exclude_upload_section){
|
||||||
|
$("#file-uploader").fileinput('clear');
|
||||||
|
var fileinput_data = $("#file-uploader").data('fileinput');
|
||||||
|
fileinput_data.uploadUrl = "/xhr/file_manager_upload/" + window.current_path + (window.explorer_setting_id ? ('?setting_id='+window.explorer_setting_id) : '');
|
||||||
|
}
|
||||||
|
}else{
|
||||||
|
org_file_manager_section.find('#index_table').html(file_manager_section);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
function reload_explorer_section(url, exclude_upload_section, callback,...args){
|
||||||
|
if(url == undefined){
|
||||||
|
url = get_path();
|
||||||
|
}
|
||||||
|
$.get(url).done(function(data){
|
||||||
|
var $file_manager_section = $(data);
|
||||||
|
replace_explorer_section($file_manager_section, exclude_upload_section);
|
||||||
|
if(window.scroll_top){
|
||||||
|
$(window).scrollTop(window.scroll_top);
|
||||||
|
}
|
||||||
|
if(typeof(callback) == 'function'){
|
||||||
|
callback.apply( this, args );
|
||||||
|
}
|
||||||
|
}).fail(function() {
|
||||||
|
alert('No such file or directory!');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
function reload_page(url, is_recycle_bin, extra_params){
|
||||||
|
var tmp_url = url, request_url;
|
||||||
|
if(url == undefined){
|
||||||
|
request_url = get_path(undefined,undefined, extra_params);
|
||||||
|
}else{
|
||||||
|
request_url = get_path(url,is_recycle_bin, extra_params);
|
||||||
|
}
|
||||||
|
$.get(request_url).done(function(data){
|
||||||
|
var $file_manager_section = $(data);
|
||||||
|
replace_explorer_section($file_manager_section);
|
||||||
|
if(!window.only_select_folder){
|
||||||
|
push_history_url(request_url);
|
||||||
|
}
|
||||||
|
if(window.scroll_top){
|
||||||
|
$(window).scrollTop(window.scroll_top);
|
||||||
|
}
|
||||||
|
}).fail(function() {
|
||||||
|
alert('No such file or directory!');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
function push_history_url(new_url, state){
|
||||||
|
if(state == undefined)
|
||||||
|
state = {body: $('body').prop('outerHTML')};
|
||||||
|
if(history.state == null){
|
||||||
|
history.replaceState({need_reload: true} ,$('title').text(), window.location.href);
|
||||||
|
}
|
||||||
|
window.history.pushState(state ,$('title').text(), new_url);
|
||||||
|
}
|
||||||
|
function get_current_url(path, override_recycle_bin, extra_params){
|
||||||
|
if(override_recycle_bin == undefined){
|
||||||
|
override_recycle_bin = window.is_recycle_bin;
|
||||||
|
}
|
||||||
|
var params = {};
|
||||||
|
if(override_recycle_bin){
|
||||||
|
path = '';
|
||||||
|
params['recycle_bin'] = true
|
||||||
|
}
|
||||||
|
if(extra_params){
|
||||||
|
Object.keys(extra_params).forEach(function(k){
|
||||||
|
params[k] = extra_params[k];
|
||||||
|
})
|
||||||
|
}
|
||||||
|
var url = '';
|
||||||
|
if(window.path_mode == 'params'){
|
||||||
|
params["path"] = path;
|
||||||
|
url = window.location.pathname
|
||||||
|
}else{
|
||||||
|
url = "/xhr/file_manager/" + path;
|
||||||
|
}
|
||||||
|
if(window.only_select_folder){
|
||||||
|
params['select_mode'] = true;
|
||||||
|
}
|
||||||
|
return url + params_to_query(params);
|
||||||
|
}
|
||||||
|
function get_path(path, override_recycle_bin, extra_params){
|
||||||
|
var params = get_params();
|
||||||
|
if(path == undefined){
|
||||||
|
path = window.location.pathname;
|
||||||
|
}
|
||||||
|
if(override_recycle_bin == undefined){
|
||||||
|
override_recycle_bin = window.is_recycle_bin;
|
||||||
|
}
|
||||||
|
if(override_recycle_bin){
|
||||||
|
path = ""
|
||||||
|
params["recycle_bin"] = true;
|
||||||
|
window.is_recycle_bin = true;
|
||||||
|
}else{
|
||||||
|
delete params["recycle_bin"];
|
||||||
|
window.is_recycle_bin = false;
|
||||||
|
}
|
||||||
|
if(window.only_select_folder){
|
||||||
|
params['select_mode'] = true;
|
||||||
|
}else{
|
||||||
|
delete params["select_mode"];
|
||||||
|
}
|
||||||
|
delete params["page"];
|
||||||
|
delete params["file_id"];
|
||||||
|
if(window.explorer_setting_id){
|
||||||
|
params['setting_id'] = window.explorer_setting_id;
|
||||||
|
}
|
||||||
|
if(extra_params){
|
||||||
|
Object.keys(extra_params).forEach(function(k){
|
||||||
|
if (extra_params[k] != null)
|
||||||
|
params[k] = extra_params[k];
|
||||||
|
})
|
||||||
|
}
|
||||||
|
if(window.use_params_request){
|
||||||
|
params['path'] = path;
|
||||||
|
path = '';
|
||||||
|
}else{
|
||||||
|
delete params['path'];
|
||||||
|
}
|
||||||
|
return path + params_to_query(params);
|
||||||
|
}
|
||||||
|
function get_basename(file){
|
||||||
|
var tmp = file.split('/');
|
||||||
|
return tmp[tmp.length - 1] || '';
|
||||||
|
}
|
||||||
|
function pathJoin(parts, sep){
|
||||||
|
var separator = sep || '/';
|
||||||
|
var replace = new RegExp(separator+'{1,}', 'g');
|
||||||
|
if(parts.length > 1){
|
||||||
|
parts.forEach(function(p, i){
|
||||||
|
if(p[0] == '/'){
|
||||||
|
for(var k=0; k<i; k++){
|
||||||
|
parts[k] = "";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
parts = parts.filter(function(s){ return s != "" });
|
||||||
|
var new_path = parts.join(separator).replace(replace, separator).replace(new RegExp(`[^${separator}]+${separator}\\.\\.${separator}`, 'g'),'');
|
||||||
|
return new_path.replace(/^(http|ftp):\//,'$1://').replace(/file:\//,'file:///').replace(new RegExp(`^.{1,2}${separator}`),'').replace(/^\/\.\.\//,'/').replace(/\.\//g,'');
|
||||||
|
}
|
||||||
|
function get_current_path(path, override_recycle_bin){
|
||||||
|
if(path == undefined){
|
||||||
|
path = window.current_path;
|
||||||
|
}
|
||||||
|
if(override_recycle_bin == undefined){
|
||||||
|
override_recycle_bin = window.is_recycle_bin;
|
||||||
|
}
|
||||||
|
if(override_recycle_bin && window.recycle_bin_text){
|
||||||
|
return window.recycle_bin_text;
|
||||||
|
}else{
|
||||||
|
return path;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
function change_folder(id, folder, is_recycle_bin){
|
||||||
|
window.scroll_top = $('#file_manager_section thead').scrollTop();
|
||||||
|
if (!folder) {
|
||||||
|
folder = window.module_key;
|
||||||
|
}
|
||||||
|
var new_path = window.file_manager_path + '/' + folder + '/module';
|
||||||
|
new_path = get_path(new_path, (is_recycle_bin ? true : false), {"file_id": id});
|
||||||
|
console.log(new_path, id);
|
||||||
|
$.get(new_path).done(function(data){
|
||||||
|
var $file_manager_section = $(data);
|
||||||
|
replace_explorer_section($file_manager_section);
|
||||||
|
window.module_key = folder;
|
||||||
|
if(!window.only_select_folder){
|
||||||
|
push_history_url(new_path);
|
||||||
|
}
|
||||||
|
if(window.scroll_top){
|
||||||
|
$(window).scrollTop(window.scroll_top);
|
||||||
|
}
|
||||||
|
}).fail(function() {
|
||||||
|
alert('No such file or directory!');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
function get_params(){
|
||||||
|
var search_split = window.location.search.split("?");
|
||||||
|
var params = {}
|
||||||
|
if( search_split.length >= 2 ){
|
||||||
|
$(search_split[1].split("&")).each(function(i, s){
|
||||||
|
var tmp = s.split("=");
|
||||||
|
var k = tmp[0], v = tmp[1];
|
||||||
|
if(k.search('[]') != -1){
|
||||||
|
k = k.split('[]')[0];
|
||||||
|
if(params.hasOwnProperty(k)){
|
||||||
|
params[k].push(v);
|
||||||
|
}else{
|
||||||
|
params[k] = [v];
|
||||||
|
}
|
||||||
|
}else{
|
||||||
|
params[k] = v;
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
return params;
|
||||||
|
}
|
||||||
|
function params_to_query(params){
|
||||||
|
var query_text = '';
|
||||||
|
var keys = Object.keys(params);
|
||||||
|
if(keys.length != 0){
|
||||||
|
keys.forEach(function(k, i){
|
||||||
|
if(i == 0){
|
||||||
|
query_text += '?';
|
||||||
|
}else{
|
||||||
|
query_text += '&';
|
||||||
|
}
|
||||||
|
var v = params[k];
|
||||||
|
if(Array.isArray(v)){
|
||||||
|
v.forEach(function(vv, j){
|
||||||
|
if(j != 0){
|
||||||
|
query_text += '&';
|
||||||
|
}
|
||||||
|
query_text += `${k}[]=${vv}`;
|
||||||
|
})
|
||||||
|
}else{
|
||||||
|
query_text += `${k}=${v}`;
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
return query_text;
|
||||||
|
}
|
||||||
|
$(document).on("click",".delete",function(){
|
||||||
|
var that = $(this);
|
||||||
|
window.scroll_top = $(window).scrollTop();
|
||||||
|
if( window.confirm(that.data("tmp-confirm")) ){
|
||||||
|
if( window.confirm(that.data("tmp-confirm")) ){
|
||||||
|
$.ajax({
|
||||||
|
url: that.data("link"),
|
||||||
|
method: 'DELETE',
|
||||||
|
contentType: false, //required
|
||||||
|
processData: false, // required
|
||||||
|
statusCode: {
|
||||||
|
204: reload_explorer_section,
|
||||||
|
404: function() {
|
||||||
|
alert( "Delete failed!\nFile not exist!" );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
$(document).on("click",".delete_file",function(){
|
||||||
|
var that = $(this);
|
||||||
|
window.scroll_top = $(window).scrollTop();
|
||||||
|
if( window.confirm(that.data("tmp-confirm")) ){
|
||||||
|
if( window.confirm(that.data("tmp-confirm")) ){
|
||||||
|
$.ajax({
|
||||||
|
url: that.data("link"),
|
||||||
|
method: 'DELETE',
|
||||||
|
contentType: false, //required
|
||||||
|
processData: false, // required
|
||||||
|
dataType: 'json',
|
||||||
|
statusCode: {
|
||||||
|
200: function(data) {
|
||||||
|
var params = get_params();
|
||||||
|
var extra_params;
|
||||||
|
if (data.id) {
|
||||||
|
extra_params = {"version": data.version, "file_id": data.id};
|
||||||
|
} else {
|
||||||
|
extra_params = {};
|
||||||
|
}
|
||||||
|
var url = get_path(undefined, undefined, extra_params);
|
||||||
|
var callback = push_history_url;
|
||||||
|
reload_explorer_section(url, false, callback, url);
|
||||||
|
},
|
||||||
|
404: function() {
|
||||||
|
alert( "Delete failed!\nFile not exist!" );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
$(document).on("click",".edit",function(){
|
||||||
|
$(this).hide();
|
||||||
|
var pre_height = $('#file_manager_section pre').height();
|
||||||
|
$('#file_manager_section textarea.edit-area').html($('#file_manager_section pre').html());
|
||||||
|
$('#file_manager_section textarea.edit-area').removeClass('hide');
|
||||||
|
$('#file_manager_section pre').addClass('hide');
|
||||||
|
$('#file_manager_section textarea.edit-area').height(pre_height);
|
||||||
|
$(".save").removeClass('hide');
|
||||||
|
})
|
||||||
|
$(document).on("click",".save",function(){
|
||||||
|
$(this).hide();
|
||||||
|
var data = new FormData();
|
||||||
|
data.append("content", $('#file_manager_section textarea.edit-area').val());
|
||||||
|
$.ajax({
|
||||||
|
url: '/admin/file_managers/' + $(this).data('id') + '/save',
|
||||||
|
method: 'POST',
|
||||||
|
data: data,
|
||||||
|
contentType: false, //required
|
||||||
|
processData: false, // required
|
||||||
|
dataType: 'json',
|
||||||
|
mimeType: 'multipart/form-data',
|
||||||
|
statusCode: {
|
||||||
|
200: function(data) {
|
||||||
|
console.log(data);
|
||||||
|
var params = get_params(), url;
|
||||||
|
var callback = push_history_url;
|
||||||
|
if(params.version){
|
||||||
|
var new_version = Number(params.version) + 1;
|
||||||
|
url = get_path(undefined, undefined, {"version": new_version, "file_id": data.id});
|
||||||
|
params.version = new_version.toString();
|
||||||
|
} else {
|
||||||
|
url = get_path(undefined, undefined, {"file_id": data.id});
|
||||||
|
}
|
||||||
|
reload_explorer_section(url, false, callback, url);
|
||||||
|
$(".save").addClass('hide');
|
||||||
|
$(".edit").removeClass('hide');
|
||||||
|
},
|
||||||
|
400: function() {
|
||||||
|
alert( "Bad Request!\nThere are no content of this file!" );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
$(document).on("click",".module-key",function(){
|
||||||
|
change_folder(null, $(this).data("key"));
|
||||||
|
})
|
||||||
|
$(document).on("click",".directory, .file",function(){
|
||||||
|
change_folder($(this).data("id"));
|
||||||
|
})
|
||||||
|
$(document).on("click",".breadcrumb-index",function(){
|
||||||
|
var path = window.current_path[0] == '/' ? window.current_path : ('/' + window.current_path);
|
||||||
|
$("#breadcrumb-relative-url").addClass('hide');
|
||||||
|
$("#breadcrumb-editable").text(path).removeClass("hide").focus();
|
||||||
|
window.tmp_current_path = path;
|
||||||
|
})
|
||||||
|
$(document).on("click",'#breadcrumb-relative-url .directory, #breadcrumb-editable',function(e){
|
||||||
|
e.stopPropagation();
|
||||||
|
})
|
||||||
|
$(document).on("blur","#breadcrumb-editable",function(){
|
||||||
|
if(window.tmp_current_path != $(this).text().trim()){
|
||||||
|
change_folder($(this).text().trim());
|
||||||
|
}
|
||||||
|
$("#breadcrumb-relative-url").removeClass('hide');
|
||||||
|
$(this).addClass('hide');
|
||||||
|
})
|
||||||
|
$(document).on("keydown","#breadcrumb-editable",function(evt){
|
||||||
|
if(evt.keyCode == 13){
|
||||||
|
evt.preventDefault();
|
||||||
|
$(this).trigger("blur");
|
||||||
|
}
|
||||||
|
})
|
||||||
|
$(document).on("click", ".copyButton", function(){
|
||||||
|
var link = $(this).data('link');
|
||||||
|
// Copy the link
|
||||||
|
navigator.clipboard.writeText(link);
|
||||||
|
if (!($(this).data('org-text')))
|
||||||
|
$(this).data('org-text', $(this).find('.tooltiptext').text());
|
||||||
|
$(this).find('.tooltiptext').text($(this).data('prompt') + ': ' + link);
|
||||||
|
})
|
||||||
|
$(document).on("mouseout", ".copyButton", function(){
|
||||||
|
var org_text = $(this).data('org-text');
|
||||||
|
if (org_text)
|
||||||
|
$(this).find('.tooltiptext').text(org_text);
|
||||||
|
})
|
||||||
|
$(document).on("click", ".redirect_recycle_bin", function(){
|
||||||
|
change_folder(null, null, true);
|
||||||
|
})
|
||||||
|
$(document).on("click", ".recycle_bin", function(){
|
||||||
|
change_folder(null, $(this).data('key'), true);
|
||||||
|
})
|
||||||
|
$(document).on("click", ".recover_all", function(){
|
||||||
|
var that = $(this);
|
||||||
|
var prompt_message = that.data("prompt");
|
||||||
|
$.ajax({
|
||||||
|
url: '/admin/file_managers/' + window.module_key + '/get_trash_count',
|
||||||
|
method: 'POST',
|
||||||
|
contentType: false, //required
|
||||||
|
processData: false, // required
|
||||||
|
dataType: 'json',
|
||||||
|
mimeType: 'multipart/form-data',
|
||||||
|
statusCode: {
|
||||||
|
200: function(data) {
|
||||||
|
var count = data["count"] + that.data('unit');
|
||||||
|
prompt_message = prompt_message.replace('${num}', count);
|
||||||
|
if(window.confirm(prompt_message)){
|
||||||
|
if(window.confirm(prompt_message)){
|
||||||
|
$.ajax({
|
||||||
|
url: '/admin/file_managers/' + window.module_key + '/recover_all',
|
||||||
|
method: 'POST',
|
||||||
|
contentType: false, //required
|
||||||
|
processData: false, // required
|
||||||
|
dataType: 'json',
|
||||||
|
mimeType: 'multipart/form-data',
|
||||||
|
statusCode: {
|
||||||
|
200: function(data) {
|
||||||
|
var thread_id = data["id"];
|
||||||
|
var thread_title = data["title"];
|
||||||
|
reload_explorer_section(get_path(), false, function show_modal(thread_id, thread_title){
|
||||||
|
$("#threadModal").data('id', thread_id);
|
||||||
|
$("#threadModal .modal-header h3").text(thread_title);
|
||||||
|
$("#threadModal").removeClass('hide').modal('show');
|
||||||
|
}, thread_id, thread_title);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
$(document).on("click",".clean_up_recycle_bin", function(){
|
||||||
|
var that = $(this);
|
||||||
|
var prompt_message = that.data("prompt");
|
||||||
|
$.ajax({
|
||||||
|
url: '/admin/file_managers/' + window.module_key + '/get_trash_count',
|
||||||
|
method: 'POST',
|
||||||
|
contentType: false, //required
|
||||||
|
processData: false, // required
|
||||||
|
dataType: 'json',
|
||||||
|
mimeType: 'multipart/form-data',
|
||||||
|
statusCode: {
|
||||||
|
200: function(data) {
|
||||||
|
var count = data["count"] + that.data('unit');
|
||||||
|
prompt_message = prompt_message.replace('${num}', count);
|
||||||
|
if(window.confirm(prompt_message)){
|
||||||
|
if(window.confirm(prompt_message)){
|
||||||
|
$.ajax({
|
||||||
|
url: '/admin/file_managers/' + window.module_key + '/clean_trashes',
|
||||||
|
method: 'POST',
|
||||||
|
contentType: false, //required
|
||||||
|
processData: false, // required
|
||||||
|
dataType: 'json',
|
||||||
|
mimeType: 'multipart/form-data',
|
||||||
|
statusCode: {
|
||||||
|
200: function(data) {
|
||||||
|
var thread_id = data["id"];
|
||||||
|
var thread_title = data["title"];
|
||||||
|
reload_explorer_section(get_path(), false, function show_modal(thread_id, thread_title){
|
||||||
|
$("#threadModal").data('id', thread_id);
|
||||||
|
$("#threadModal .modal-header h3").text(thread_title);
|
||||||
|
$("#threadModal").removeClass('hide').modal('show');
|
||||||
|
}, thread_id, thread_title);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
$(document).on('hidden.bs.modal', "#threadModal", function(){
|
||||||
|
window.clearTimeout(window.time_out_id);
|
||||||
|
})
|
||||||
|
$(document).on('shown.bs.modal', "#threadModal", function(){
|
||||||
|
var thread_id = $(this).data('id');
|
||||||
|
window.time_out_id = window.setTimeout(function(){
|
||||||
|
update_thread(thread_id, reload_page);
|
||||||
|
}, 1000);
|
||||||
|
})
|
||||||
|
$(document).on('click', ".change_version", function(){
|
||||||
|
var that = $(this);
|
||||||
|
var version = that.data('version');
|
||||||
|
var file_id = that.data('id');
|
||||||
|
reload_page(undefined,undefined,{"version": version, "file_id": file_id});
|
||||||
|
})
|
||||||
|
$(document).on('click', ".recover", function(){
|
||||||
|
var that = $(this);
|
||||||
|
window.scroll_top = $(window).scrollTop();
|
||||||
|
if( window.confirm(that.data("tmp-confirm")) ){
|
||||||
|
$.ajax({
|
||||||
|
url: that.data("link"),
|
||||||
|
method: 'POST',
|
||||||
|
contentType: false, //required
|
||||||
|
processData: false, // required
|
||||||
|
statusCode: {
|
||||||
|
204: reload_explorer_section,
|
||||||
|
404: function() {
|
||||||
|
alert( "Recover failed!\nFile not exist!" );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
function update_thread(id, finish_callback, ...args){
|
||||||
|
var data = new FormData();
|
||||||
|
data.append('id',id);
|
||||||
|
$.ajax({
|
||||||
|
url: "/admin/threads/get_status",
|
||||||
|
method: 'POST',
|
||||||
|
data: data,
|
||||||
|
contentType: false, //required
|
||||||
|
processData: false, // required
|
||||||
|
dataType: 'json',
|
||||||
|
mimeType: 'multipart/form-data',
|
||||||
|
statusCode: {
|
||||||
|
200: function(data){
|
||||||
|
var finish_percent = data["finish_percent"];
|
||||||
|
var current_count = data["current_count"];
|
||||||
|
var all_count = data["all_count"];
|
||||||
|
var is_finish = (data["status"] == "finish");
|
||||||
|
if(finish_percent){
|
||||||
|
$("#threadModal .modal-body .thread-status").text(data["status"]);
|
||||||
|
if(data["status"] != 'Processing'){
|
||||||
|
$("#threadModal .modal-body .thread-current-count").text(current_count);
|
||||||
|
$("#threadModal .modal-body .thread-all-count").text(all_count);
|
||||||
|
}
|
||||||
|
$("#threadModal .modal-body .thread-finish_percent").text(finish_percent);
|
||||||
|
}else if(is_finish){
|
||||||
|
$("#threadModal .modal-body .thread-status").text(data["status"]);
|
||||||
|
$("#threadModal .modal-body .thread-finish_percent").text(100);
|
||||||
|
}
|
||||||
|
var that = this;
|
||||||
|
if(!is_finish){
|
||||||
|
var tmp_arguments = arguments;
|
||||||
|
window.time_out_id = window.setTimeout(function(){
|
||||||
|
update_thread.apply(that, tmp_arguments);
|
||||||
|
}, 1000);
|
||||||
|
}else{
|
||||||
|
if(window.time_out_id)
|
||||||
|
window.clearTimeout(window.time_out_id);
|
||||||
|
var wait_time = 3000;
|
||||||
|
if(finish_callback)
|
||||||
|
wait_time = 0;
|
||||||
|
window.setTimeout(function(){
|
||||||
|
$("#threadModal").modal("hide");
|
||||||
|
if(finish_callback){
|
||||||
|
finish_callback.apply(that, args);
|
||||||
|
}
|
||||||
|
alert(data["status"]);
|
||||||
|
}, wait_time);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
window.onpopstate = function(event) {
|
||||||
|
var state = event.state;
|
||||||
|
if(state != null){
|
||||||
|
if(state.need_reload){
|
||||||
|
window.location.reload();
|
||||||
|
}else if(state.body){
|
||||||
|
var $body = $(state.body);
|
||||||
|
$('.breadcrumb-index').replaceWith($body.find('.breadcrumb-index'));
|
||||||
|
$('#file_manager_section').replaceWith($body.find('#file_manager_section'));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
|
@ -0,0 +1,41 @@
|
||||||
|
function rename(id, name) {
|
||||||
|
window.scroll_top = $(window).scrollTop();
|
||||||
|
var path = pathJoin([name,".."]);
|
||||||
|
var basename = get_basename(name);
|
||||||
|
var new_name = prompt("Enter the new name for this element.", basename);
|
||||||
|
if (new_name != null) {
|
||||||
|
new_name = pathJoin([path, new_name]);
|
||||||
|
var data = new FormData();
|
||||||
|
data.append("new_name", new_name);
|
||||||
|
var csrf_token = document
|
||||||
|
.querySelector("meta[name='csrf-token']")
|
||||||
|
.getAttribute("content");
|
||||||
|
var csrf_param = document
|
||||||
|
.querySelector("meta[name='csrf-param']")
|
||||||
|
.getAttribute("content");
|
||||||
|
if (csrf_token && csrf_param) {
|
||||||
|
data.append(csrf_param, csrf_token);
|
||||||
|
}
|
||||||
|
$.ajax({
|
||||||
|
url: '/admin/file_managers/' + id + '/rename',
|
||||||
|
method: 'PUT',
|
||||||
|
data: data,
|
||||||
|
headers: {
|
||||||
|
// 'Content-Type': 'multipart/form-data',
|
||||||
|
// 使用 multipart/form-data 在此不需要設定 Content-Type。
|
||||||
|
'X-Requested-With': 'XMLHttpRequest',
|
||||||
|
'Authorization': `Bearer ${ csrf_token }`,
|
||||||
|
},
|
||||||
|
contentType: false, //required
|
||||||
|
processData: false, // required
|
||||||
|
mimeType: 'multipart/form-data',
|
||||||
|
statusCode: {
|
||||||
|
204: reload_explorer_section,
|
||||||
|
403: function() {
|
||||||
|
alert( "Rename failed!\nFile exist!" );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
|
@ -0,0 +1,3 @@
|
||||||
|
function abc(){
|
||||||
|
console.log(12341111222444551111111111111111111111111111111111111111)
|
||||||
|
}
|
|
@ -0,0 +1,270 @@
|
||||||
|
/*
|
||||||
|
* This is a manifest file that'll be compiled into application.css, which will include all the files
|
||||||
|
* listed below.
|
||||||
|
*
|
||||||
|
* Any CSS and SCSS file within this directory, lib/assets/stylesheets, vendor/assets/stylesheets,
|
||||||
|
* or any plugin's vendor/assets/stylesheets directory can be referenced here using a relative path.
|
||||||
|
*
|
||||||
|
* You're free to add application-wide styles to this file and they'll appear at the bottom of the
|
||||||
|
* compiled file so the styles you add here take precedence over styles defined in any other CSS/SCSS
|
||||||
|
* files in this directory. Styles in this file should be added after the last require_* statement.
|
||||||
|
* It is generally better to create a new file per style scope.
|
||||||
|
*
|
||||||
|
*= require_self
|
||||||
|
*= require fontawesome-5.15.4.min
|
||||||
|
*/
|
||||||
|
/* Common */
|
||||||
|
input[disabled], select[disabled], textarea[disabled], input[readonly], select[readonly], textarea[readonly] {
|
||||||
|
cursor: not-allowed;
|
||||||
|
background-color: #eeeeee;
|
||||||
|
}
|
||||||
|
body#file_manager_body, html{
|
||||||
|
font-size: 1rem !important;
|
||||||
|
}
|
||||||
|
a {
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
a:hover {
|
||||||
|
text-decoration: underline;
|
||||||
|
}
|
||||||
|
#file_manager_section h3 {
|
||||||
|
float: left;
|
||||||
|
}
|
||||||
|
#file_manager_section .btn-wrapper {
|
||||||
|
float: right;
|
||||||
|
}
|
||||||
|
#file_manager_section .btn-wrapper .upload-button {
|
||||||
|
display: inline-block;
|
||||||
|
}
|
||||||
|
#file_manager_section table {
|
||||||
|
border-collapse: collapse;
|
||||||
|
width: 100%;
|
||||||
|
text-align: left;
|
||||||
|
table-layout: fixed;
|
||||||
|
}
|
||||||
|
.modal table {
|
||||||
|
white-space: unset;
|
||||||
|
}
|
||||||
|
.modal.fade:not(.in) {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
.modal-backdrop.fade:not(.in) {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
#file_manager_section th {
|
||||||
|
font-size: 1.17em;
|
||||||
|
background-color: rgb(76, 102, 175);
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
#file_manager_section tr:hover {
|
||||||
|
background-color: #ffffff;
|
||||||
|
}
|
||||||
|
#file_manager_section th, #file_manager_section td {
|
||||||
|
padding: 4px 12px;
|
||||||
|
border: 1px solid;
|
||||||
|
border-color: #ddd;
|
||||||
|
}
|
||||||
|
#file_manager_section td a {
|
||||||
|
display: block;
|
||||||
|
width: 100%;
|
||||||
|
overflow-wrap: break-word;
|
||||||
|
}
|
||||||
|
/* Light mode: */
|
||||||
|
a.delete,
|
||||||
|
a.delete:visited {
|
||||||
|
color: rgb(223, 132, 136);
|
||||||
|
}
|
||||||
|
a.rename, a.recover {
|
||||||
|
color: deepskyblue;
|
||||||
|
}
|
||||||
|
.date {
|
||||||
|
font-family: 'Courier New', Courier, monospace;
|
||||||
|
}
|
||||||
|
.name {
|
||||||
|
width: 100%;
|
||||||
|
white-space: normal;
|
||||||
|
}
|
||||||
|
.fa {
|
||||||
|
padding: 0 6px 0 0;
|
||||||
|
}
|
||||||
|
.download, .create_folder, .edit, .save, .recover_all, .clean_up_recycle_bin, .delete_file,
|
||||||
|
.download:link, .create_folder:link, .edit:link, .save:link, .recover_all:link, .clean_up_recycle_bin:link, .delete_file:link,
|
||||||
|
.download:visited, .create_folder:visited, .edit:visited, .save:visited, .clean_up_recycle_bin:visited, .delete_file:visited {
|
||||||
|
color: white;
|
||||||
|
display: inline-block;
|
||||||
|
font-size: 1.17em;
|
||||||
|
padding: 12px 18px;
|
||||||
|
text-align: center;
|
||||||
|
text-decoration: none;
|
||||||
|
white-space: pre-wrap;
|
||||||
|
border: none;
|
||||||
|
cursor: pointer;
|
||||||
|
box-sizing: border-box;
|
||||||
|
line-height: 1.0;
|
||||||
|
}
|
||||||
|
input[type=file]#file{
|
||||||
|
display: inline-block;
|
||||||
|
}
|
||||||
|
form {
|
||||||
|
display: inline-block;
|
||||||
|
}
|
||||||
|
#file_manager_section pre,#file_manager_section textarea {
|
||||||
|
padding: 10px;
|
||||||
|
white-space: pre-wrap;
|
||||||
|
word-wrap: break-word;
|
||||||
|
}
|
||||||
|
.save.hide{
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
.breadcrumb-index {
|
||||||
|
display: inline-flex;
|
||||||
|
margin: 0.67em 0;
|
||||||
|
font-size: 2em;
|
||||||
|
}
|
||||||
|
.breadcrumb-index, #breadcrumb-editable {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
breadcrumb-relative-url{
|
||||||
|
max-width: 100%;
|
||||||
|
overflow-wrap: break-word;
|
||||||
|
}
|
||||||
|
.breadcrumb-index a {
|
||||||
|
color: #0366d6;
|
||||||
|
font-weight: normal;
|
||||||
|
}
|
||||||
|
.breadcrumb-index a.base_url {
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
.separator {
|
||||||
|
padding: 0 10px;
|
||||||
|
}
|
||||||
|
.create_folder {
|
||||||
|
float: right;
|
||||||
|
background: forestgreen;
|
||||||
|
}
|
||||||
|
.recover_all {
|
||||||
|
background: deepskyblue;
|
||||||
|
}
|
||||||
|
.create_folder, .clean_up_recycle_bin {
|
||||||
|
float: right;
|
||||||
|
background: red;
|
||||||
|
}
|
||||||
|
.fa-folder {
|
||||||
|
color: rgba(3,47,98,.5);
|
||||||
|
}
|
||||||
|
.fa-file {
|
||||||
|
color: rgb(155, 155, 155);
|
||||||
|
}
|
||||||
|
.download {
|
||||||
|
background-color: rgb(60, 75, 209);
|
||||||
|
}
|
||||||
|
.delete_file {
|
||||||
|
background: #be0606;
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
.edit {
|
||||||
|
background: #a67030;
|
||||||
|
}
|
||||||
|
.save{
|
||||||
|
background: #00e300;
|
||||||
|
color: black;
|
||||||
|
}
|
||||||
|
#file_manager_section pre,#file_manager_section textarea {
|
||||||
|
background-color: #d3d3d3;
|
||||||
|
color: black;
|
||||||
|
}
|
||||||
|
#file_manager_section textarea{
|
||||||
|
width: calc(100% - 20px);
|
||||||
|
}
|
||||||
|
.redirect_recycle_bin{
|
||||||
|
float: right;
|
||||||
|
}
|
||||||
|
.dropdown-menu.upload-box {
|
||||||
|
right: 0;
|
||||||
|
left: auto;
|
||||||
|
}
|
||||||
|
.tooltip {
|
||||||
|
position: relative;
|
||||||
|
display: inline-block;
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tooltip .tooltiptext {
|
||||||
|
visibility: hidden;
|
||||||
|
width: 140px;
|
||||||
|
background-color: #555;
|
||||||
|
color: #fff;
|
||||||
|
text-align: center;
|
||||||
|
border-radius: 6px;
|
||||||
|
padding: 5px;
|
||||||
|
position: absolute;
|
||||||
|
z-index: 1;
|
||||||
|
bottom: 150%;
|
||||||
|
left: 50%;
|
||||||
|
margin-left: -75px;
|
||||||
|
opacity: 0;
|
||||||
|
transition: opacity 0.3s;
|
||||||
|
overflow-wrap: break-word;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tooltip .tooltiptext::after {
|
||||||
|
content: "";
|
||||||
|
position: absolute;
|
||||||
|
top: 100%;
|
||||||
|
left: 50%;
|
||||||
|
margin-left: -5px;
|
||||||
|
border-width: 5px;
|
||||||
|
border-style: solid;
|
||||||
|
border-color: #555 transparent transparent transparent;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tooltip:hover .tooltiptext {
|
||||||
|
visibility: visible;
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
@media (prefers-color-scheme: dark) {
|
||||||
|
body#file_manager_body, #file_manager_section tbody {
|
||||||
|
background-color: black;
|
||||||
|
color: rgba(249, 249, 250, 0.8);
|
||||||
|
}
|
||||||
|
.fa-folder {
|
||||||
|
color: rgb(2, 69, 146);
|
||||||
|
}
|
||||||
|
.fa-file {
|
||||||
|
color: rgb(177, 177, 177);
|
||||||
|
}
|
||||||
|
a .filename {
|
||||||
|
color: rgb(47, 121, 165);
|
||||||
|
}
|
||||||
|
a.delete, a.create_folder, a.clean_up_recycle_bin, a.edit, a.delete_file,
|
||||||
|
a.delete:link, a.create_folder:link, a.clean_up_recycle_bin:link, a.edit:link, a.delete_file:link,
|
||||||
|
a.delete:visited, a.create_folder:visited, a.clean_up_recycle_bin:visited, a.edit:visited, a.delete_file:visited {
|
||||||
|
color: rgb(223, 132, 136);
|
||||||
|
}
|
||||||
|
tr:hover {
|
||||||
|
background-color: #3b3b3b;
|
||||||
|
}
|
||||||
|
th, td {
|
||||||
|
border-color: rgb(138, 138, 138);
|
||||||
|
}
|
||||||
|
.download {
|
||||||
|
background-color: rgb(29, 42, 163);
|
||||||
|
}
|
||||||
|
.create_folder {
|
||||||
|
background: darkgreen;
|
||||||
|
}
|
||||||
|
.recover_all {
|
||||||
|
background: darkred;
|
||||||
|
}
|
||||||
|
.edit {
|
||||||
|
background: #653700;
|
||||||
|
}
|
||||||
|
.save {
|
||||||
|
background: green;
|
||||||
|
color: #fff;
|
||||||
|
}
|
||||||
|
#file_manager_section pre,#file_manager_section textarea {
|
||||||
|
background-color: #3b3b3b;
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,35 @@
|
||||||
|
li {
|
||||||
|
list-style: none;
|
||||||
|
}
|
||||||
|
ul.tab_nav {
|
||||||
|
list-style-type: none;
|
||||||
|
padding: 0;
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
font-family: "Roboto", "微軟正黑體", "Helvetica Neue", Helvetica, sans-serif;
|
||||||
|
}
|
||||||
|
ul.tab_nav li {
|
||||||
|
padding: 0.5em 1em;
|
||||||
|
background: #32D9C3;
|
||||||
|
margin: 0.2em;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: all 0.5s;
|
||||||
|
-moz-transition: all 0.5s;
|
||||||
|
-webkit-transition: all 0.5s;
|
||||||
|
-o-transition: all 0.5s;
|
||||||
|
}
|
||||||
|
ul.tab_nav li.active {
|
||||||
|
background: #19524b;
|
||||||
|
color: #fff;
|
||||||
|
}
|
||||||
|
@media (prefers-color-scheme: dark) {
|
||||||
|
ul.tab_nav li {
|
||||||
|
background-color: #f3f3f3;
|
||||||
|
color: #ccc;
|
||||||
|
}
|
||||||
|
ul.tab_nav li.active {
|
||||||
|
background-color: #969696;
|
||||||
|
color: #fff;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
File diff suppressed because one or more lines are too long
|
@ -0,0 +1,499 @@
|
||||||
|
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
|
|
@ -0,0 +1,97 @@
|
||||||
|
class FileManagersController < ApplicationController
|
||||||
|
include ActionView::Helpers::NumberHelper
|
||||||
|
before_action :check_login? , :set_base_url, except: [:download]
|
||||||
|
layout "file_manager"
|
||||||
|
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 self.custom_widget_data
|
||||||
|
@root = FileManagerRoot.first
|
||||||
|
@settings = @root.file_manager_settings rescue []
|
||||||
|
ac = ActionController::Base.new
|
||||||
|
ac.render_to_string("file_managers/custom_widget_data",:locals=>{:@custom_data_field=>@custom_data_field,:@field_name=>@field_name,:@settings=>@settings})
|
||||||
|
end
|
||||||
|
def check_login?
|
||||||
|
@current_user = current_user
|
||||||
|
@current_user_id = current_user.id
|
||||||
|
if @current_user
|
||||||
|
if ['index_backend','path'].include?( params[:action] ) && params[:setting_id].blank?
|
||||||
|
module_app = ModuleApp.where(:key=>'file_manager').first
|
||||||
|
unless (@current_user.is_admin_for_module?(module_app) rescue true)
|
||||||
|
render_403 and return
|
||||||
|
end
|
||||||
|
end
|
||||||
|
else
|
||||||
|
render_403 and return
|
||||||
|
end
|
||||||
|
end
|
||||||
|
def download
|
||||||
|
upload = FileManagerUpload.where(:id=>params[:id]).first
|
||||||
|
if upload
|
||||||
|
if upload.is_trash
|
||||||
|
if upload.file_manager_trash
|
||||||
|
send_file(upload.file_manager_trash.trash_path)
|
||||||
|
else
|
||||||
|
render_404
|
||||||
|
end
|
||||||
|
else
|
||||||
|
options = {}
|
||||||
|
if params[:preview]
|
||||||
|
options[:disposition] = 'inline'
|
||||||
|
end
|
||||||
|
send_file(upload.get_real_path, options)
|
||||||
|
end
|
||||||
|
else
|
||||||
|
render_404
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
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 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(tmp_params=params)
|
||||||
|
@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'
|
||||||
|
if tmp_params[:setting_id].present?
|
||||||
|
@setting = FileManagerSetting.find(tmp_params[:setting_id]) rescue nil
|
||||||
|
end
|
||||||
|
@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
|
||||||
|
@only_select_folder = (tmp_params[:select_mode] == 'true')
|
||||||
|
end
|
||||||
|
end
|
|
@ -0,0 +1,54 @@
|
||||||
|
module FileManagersHelper
|
||||||
|
def fa_icon(icon_name, text="")
|
||||||
|
"<i class=\"fa fa-#{icon_name}\">#{text}</i>".html_safe
|
||||||
|
end
|
||||||
|
def generate_breadcrumb
|
||||||
|
breadcrumb_contents = ""
|
||||||
|
url_infos = [
|
||||||
|
{
|
||||||
|
type: "key",
|
||||||
|
key: "all",
|
||||||
|
trans: I18n.t(:all)
|
||||||
|
}
|
||||||
|
]
|
||||||
|
if @module_key != 'all'
|
||||||
|
url_infos << {
|
||||||
|
type: "key",
|
||||||
|
key: @module_key,
|
||||||
|
trans: @module_title
|
||||||
|
}
|
||||||
|
end
|
||||||
|
if @recycle_bin || (@upload_item && @upload_item.is_trash)
|
||||||
|
url_infos << {
|
||||||
|
type: "recycle",
|
||||||
|
key: (@upload_item ? @upload_item.module : nil),
|
||||||
|
trans: I18n.t("file_manager.recycle_bin")
|
||||||
|
}
|
||||||
|
end
|
||||||
|
if @file
|
||||||
|
filename = @upload_item.filename
|
||||||
|
version = @upload_item.version
|
||||||
|
if @upload_item.is_trash && @upload_item.file_manager_trash.record_only
|
||||||
|
filename += " (version #{version})"
|
||||||
|
end
|
||||||
|
url_infos << {
|
||||||
|
type: "file",
|
||||||
|
trans: filename
|
||||||
|
}
|
||||||
|
end
|
||||||
|
last_idx = url_infos.count - 1
|
||||||
|
url_infos.each_with_index do |info, i|
|
||||||
|
if i == last_idx
|
||||||
|
breadcrumb_contents += info[:trans]
|
||||||
|
else
|
||||||
|
if info[:type] == 'recycle'
|
||||||
|
breadcrumb_contents += "<a href=\"javascript:void(0)\" class=\"recycle_bin\" data-key=\"#{info[:key]}\">#{info[:trans]}</a>"
|
||||||
|
else
|
||||||
|
breadcrumb_contents += "<a href=\"javascript:void(0)\" data-key=\"#{info[:key]}\" class=\"module-key\">#{info[:trans]}</a>"
|
||||||
|
end
|
||||||
|
breadcrumb_contents += "<span class=\"separator\">/</span>"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
breadcrumb_contents.html_safe
|
||||||
|
end
|
||||||
|
end
|
|
@ -0,0 +1,8 @@
|
||||||
|
class FileManagerRoot
|
||||||
|
include Mongoid::Document
|
||||||
|
include Mongoid::Timestamps
|
||||||
|
RootPath = "public/"
|
||||||
|
field :disable_path_traversal, :type => Boolean, :default => true
|
||||||
|
has_many :file_manager_settings, :autosave => true, :dependent => :destroy
|
||||||
|
accepts_nested_attributes_for :file_manager_settings, :allow_destroy => true
|
||||||
|
end
|
|
@ -0,0 +1,284 @@
|
||||||
|
class FileManagerSetting
|
||||||
|
include Mongoid::Document
|
||||||
|
include Mongoid::Timestamps
|
||||||
|
field :only_editable_for_uploader, type: Boolean, default: false
|
||||||
|
field :root_path, type: String, default: ""
|
||||||
|
field :modules_saved_info, type: Hash, default: {}
|
||||||
|
field :modules_keys, type: Array, default: []
|
||||||
|
belongs_to :file_manager_root
|
||||||
|
has_many :file_manager_uploads
|
||||||
|
has_many :file_manager_trashes
|
||||||
|
|
||||||
|
@@mutex = Mutex.new
|
||||||
|
@@resource = ConditionVariable.new
|
||||||
|
|
||||||
|
def self.get_models_uploaders
|
||||||
|
if !self.class_variable_defined?(:@@get_models_uploaders)
|
||||||
|
@@get_models_uploaders = false
|
||||||
|
end
|
||||||
|
if @@get_models_uploaders
|
||||||
|
@@mutex.synchronize {
|
||||||
|
@@resource.wait(@@mutex)
|
||||||
|
}
|
||||||
|
end
|
||||||
|
@@get_models_uploaders = true
|
||||||
|
if self.class_variable_defined?(:@@modules_info)
|
||||||
|
@@get_models_uploaders = false
|
||||||
|
return @@modules_info
|
||||||
|
end
|
||||||
|
setting = self.first
|
||||||
|
if setting.nil?
|
||||||
|
setting = self.create
|
||||||
|
end
|
||||||
|
modules_info = {}
|
||||||
|
file_only_models = []
|
||||||
|
user_id_fields = ['user_id', 'create_user_id', 'update_user_id']
|
||||||
|
user_id_field_info = {}
|
||||||
|
Rails::Engine.descendants.each do |engine|
|
||||||
|
unless engine.abstract_railtie? || engine.initializers.blank?
|
||||||
|
module_name = engine.initializers[0].name
|
||||||
|
if ModuleApp.where(:key=> module_name).count > 0
|
||||||
|
models_path = Dir.glob("#{engine.root}/app/models/*")
|
||||||
|
uploaders = {}
|
||||||
|
models_path.each do |path|
|
||||||
|
begin
|
||||||
|
require File.basename(path)
|
||||||
|
m = File.basename(path).sub(/\..+/, '').camelize.constantize
|
||||||
|
if m.uploaders.count > 0
|
||||||
|
user_id_field = nil
|
||||||
|
user_id_fields.each do |f|
|
||||||
|
if m.fields.include?(f)
|
||||||
|
user_id_field = f
|
||||||
|
break
|
||||||
|
end
|
||||||
|
end
|
||||||
|
if user_id_field.nil?
|
||||||
|
user_id_field = m.fields.select{|f| f.include?('user_id')}.first
|
||||||
|
end
|
||||||
|
user_id_field_info[m] = user_id_field
|
||||||
|
uploaders_info = {}
|
||||||
|
obj = m.new(:id=>nil)
|
||||||
|
m.uploaders.each do |field, uploader_klass|
|
||||||
|
uploaders_info[field] = {
|
||||||
|
uploader_klass => "public/" + obj.send(field).store_dir
|
||||||
|
}
|
||||||
|
end
|
||||||
|
self.set_record_callback(setting, module_name, m, user_id_field)
|
||||||
|
uploaders[m] = uploaders_info
|
||||||
|
m_str = m.to_s
|
||||||
|
if m_str.match(/(File|Image)$/)
|
||||||
|
file_only_models << m_str
|
||||||
|
else
|
||||||
|
belongs_to_fields = m.relations.select{|k,v| v.macro == :belongs_to}.keys
|
||||||
|
if belongs_to_fields.count > 0 && (m.relations.count - belongs_to_fields.count) == 0
|
||||||
|
fields = m.fields.keys - belongs_to_fields.map{|s| "#{s}_id"}
|
||||||
|
fields = fields - ["_id", "created_at", "updated_at", "description", "title", "title_translations", "rss2_id", "order", "choose_lang", "privacy_type", "should_destroy", "file_title", "download_count", "sort_number"]
|
||||||
|
if (fields - m.uploaders.keys.map(&:to_s)).count == 0
|
||||||
|
file_only_models << m_str
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
rescue LoadError
|
||||||
|
end
|
||||||
|
end
|
||||||
|
if uploaders.count > 0
|
||||||
|
modules_info[module_name] = uploaders
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
begin
|
||||||
|
require 'asset'
|
||||||
|
module_name = 'asset'
|
||||||
|
m = Asset
|
||||||
|
uploaders = {}
|
||||||
|
if m.uploaders.count > 0
|
||||||
|
uploaders_info = {}
|
||||||
|
obj = m.new(:id=>nil)
|
||||||
|
m.uploaders.each do |field, uploader_klass|
|
||||||
|
uploaders_info[field] = {
|
||||||
|
uploader_klass => "public/" + obj.send(field).store_dir
|
||||||
|
}
|
||||||
|
end
|
||||||
|
uploaders[m] = uploaders_info
|
||||||
|
user_id_field = nil
|
||||||
|
user_id_fields.each do |f|
|
||||||
|
if m.fields.include?(f)
|
||||||
|
user_id_field = f
|
||||||
|
break
|
||||||
|
end
|
||||||
|
end
|
||||||
|
if user_id_field.nil?
|
||||||
|
user_id_field = m.fields.select{|f| f.include?('user_id')}.first
|
||||||
|
end
|
||||||
|
user_id_field_info[m] = user_id_field
|
||||||
|
self.set_record_callback(setting, module_name, m, user_id_field)
|
||||||
|
user_id_field
|
||||||
|
end
|
||||||
|
modules_info[module_name] = uploaders
|
||||||
|
file_only_models << m.to_s
|
||||||
|
rescue LoadError
|
||||||
|
end
|
||||||
|
@@file_only_models = file_only_models
|
||||||
|
@@user_id_field_info = user_id_field_info
|
||||||
|
@@modules_info = modules_info
|
||||||
|
@@mutex.synchronize {
|
||||||
|
@@get_models_uploaders = false
|
||||||
|
@@resource.signal
|
||||||
|
}
|
||||||
|
modules_info
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.set_record_callback(setting, module_name, m, user_id_field)
|
||||||
|
uploader_fields = m.uploaders.keys
|
||||||
|
m_str = m.to_s
|
||||||
|
m.before_save do |record|
|
||||||
|
uploader_fields.each{|f| record.instance_variable_set("@#{f}_changed", record.send("#{f}_changed?"))}
|
||||||
|
true
|
||||||
|
end
|
||||||
|
m.after_save do |record|
|
||||||
|
uploader_fields.each do |f|
|
||||||
|
f_changed = "@#{f}_changed"
|
||||||
|
if record.instance_variable_get(f_changed)
|
||||||
|
store_path = "public#{URI.decode(record.send(f).url)}"
|
||||||
|
if user_id_field
|
||||||
|
user_id = record.send(user_id_field)
|
||||||
|
else
|
||||||
|
user_id = nil
|
||||||
|
end
|
||||||
|
basic_info = {:module=> module_name, :model=>m_str, :related_field=> f, :related_id => record.id}
|
||||||
|
other_info = {:path=> store_path, :user_id=> user_id, :is_default=>true, :created_at => record.created_at, :updated_at => record.updated_at}
|
||||||
|
full_info = other_info.merge(basic_info)
|
||||||
|
old_record = setting.file_manager_uploads.where(basic_info).first
|
||||||
|
if old_record.nil?
|
||||||
|
setting.file_manager_uploads.create(full_info)
|
||||||
|
else
|
||||||
|
old_record.update_attributes(other_info)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
record.remove_instance_variable(f_changed) unless record.instance_variable_get(f_changed).nil?
|
||||||
|
end
|
||||||
|
true
|
||||||
|
end
|
||||||
|
m.after_destroy do |record|
|
||||||
|
unless record.instance_variable_get(:@skip_remove_callback)
|
||||||
|
setting.file_manager_uploads.where(:model=>m_str, :related_id => record.id).destroy
|
||||||
|
end
|
||||||
|
true
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.file_only_models
|
||||||
|
unless self.class_variable_defined?(:@@file_only_models)
|
||||||
|
self.get_models_uploaders
|
||||||
|
end
|
||||||
|
return @@file_only_models
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.get_module_info(key)
|
||||||
|
if key == 'all'
|
||||||
|
title = I18n.t("file_manager.all_files")
|
||||||
|
link = nil
|
||||||
|
icon_class = 'icons-disk'
|
||||||
|
sidebar = nil
|
||||||
|
elsif key == 'asset'
|
||||||
|
title = I18n.t('filemanager')
|
||||||
|
link = Rails.application.routes.url_helpers.admin_assets_path
|
||||||
|
icon_class = 'icons-folder'
|
||||||
|
sidebar = nil
|
||||||
|
else
|
||||||
|
module_app = ModuleApp.where(:key=>key).first
|
||||||
|
return if module_app.nil?
|
||||||
|
t = module_app.get_registration
|
||||||
|
return if t.nil?
|
||||||
|
sidebar = t.get_side_bar
|
||||||
|
if sidebar
|
||||||
|
t = sidebar
|
||||||
|
icon_class = t.get_icon_class
|
||||||
|
module_label = t.instance_variable_get(:@head_label)
|
||||||
|
link = t.instance_variable_get(:@head_link)
|
||||||
|
if link
|
||||||
|
link = Rails.application.routes.url_helpers.send(link)
|
||||||
|
end
|
||||||
|
else
|
||||||
|
icon_class = module_app.icon_class_no_sidebar
|
||||||
|
module_label = t.instance_variable_get(:@module_label)
|
||||||
|
plugin_t = OrbitApp::Plugin::Registration.find_by_module_app_name(module_app.title)
|
||||||
|
link = plugin_t.admin_partial_path if plugin_t
|
||||||
|
end
|
||||||
|
title = I18n.t(module_label)
|
||||||
|
end
|
||||||
|
{
|
||||||
|
:title => title,
|
||||||
|
:link => link,
|
||||||
|
:icon_class => icon_class,
|
||||||
|
:sidebar => sidebar
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
def create_records
|
||||||
|
modules_info = self.class.get_models_uploaders
|
||||||
|
models_dict = self.modules_saved_info
|
||||||
|
modules_info.each do |module_key, uploaders|
|
||||||
|
uploaders.each do |m, uploaders_info|
|
||||||
|
m_str = m.to_s
|
||||||
|
if models_dict[m_str].nil?
|
||||||
|
models_dict[m_str] = nil
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
self.modules_saved_info = models_dict
|
||||||
|
self.modules_keys = modules_info.keys
|
||||||
|
self.save
|
||||||
|
modules_info.each do |module_key, uploaders|
|
||||||
|
# module_app = ModuleApp.where(:key=> module_key).first
|
||||||
|
# I18n.t("module_name.#{module_key}")
|
||||||
|
uploaders.each do |m, uploaders_info|
|
||||||
|
user_id_field = @@user_id_field_info[m]
|
||||||
|
uploader_fields = uploaders_info.keys
|
||||||
|
records = m.any_of(uploader_fields.map{|f| {f => {"$ne" => nil}}})
|
||||||
|
m_str = m.to_s
|
||||||
|
if self.modules_saved_info[m_str]
|
||||||
|
records = records.where(:created_at.gte=> (self.modules_saved_info[m_str] - 1))
|
||||||
|
end
|
||||||
|
records = records.order_by(:created_at=> :asc).to_a
|
||||||
|
records_last_idx = records.length - 1
|
||||||
|
records.each_with_index do |r, i|
|
||||||
|
uploader_fields.each do |f|
|
||||||
|
f_value = r.send(f)
|
||||||
|
if f_value.present?
|
||||||
|
store_path = "public#{URI.decode(f_value.url)}"
|
||||||
|
unless File.exist?(store_path)
|
||||||
|
next
|
||||||
|
end
|
||||||
|
if user_id_field
|
||||||
|
user_id = r.send(user_id_field)
|
||||||
|
else
|
||||||
|
user_id = nil
|
||||||
|
end
|
||||||
|
basic_info = {:module=> module_key, :model=> m.to_s, :related_field=> f, :related_id => r.id, :is_default=> true}
|
||||||
|
other_info = {:path=> store_path, :user_id=> user_id, :created_at => r.created_at, :updated_at => r.updated_at}
|
||||||
|
full_info = other_info.merge(basic_info)
|
||||||
|
old_record = self.file_manager_uploads.where(basic_info).first
|
||||||
|
if old_record.nil?
|
||||||
|
self.file_manager_uploads.create(full_info)
|
||||||
|
else
|
||||||
|
old_record.update_attributes(other_info)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
if i % 10 || i == records_last_idx
|
||||||
|
puts "#{m} updated at #{r.created_at}"
|
||||||
|
self.class.where(:id=>self.id).update_all("modules_saved_info.#{m}" => r.created_at.to_i)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def get_modules_keys
|
||||||
|
_modules_keys = self.modules_keys.sort
|
||||||
|
_modules_keys = ["all"] + ["asset"] + (_modules_keys - ["asset"])
|
||||||
|
end
|
||||||
|
end
|
|
@ -0,0 +1,83 @@
|
||||||
|
class FileManagerTrash
|
||||||
|
include Mongoid::Document
|
||||||
|
include Mongoid::Timestamps
|
||||||
|
Restrict_dirs = ["/", "/home/", "/etc/", "/var/*/", "/root/", "/usr/*/"]
|
||||||
|
field :is_file, type: Boolean, default: true
|
||||||
|
field :module, type: String, default: ""
|
||||||
|
field :path, type: String, default: ""
|
||||||
|
field :trash_path, type: String, default: ""
|
||||||
|
field :user_id
|
||||||
|
field :is_slave, type: Boolean, default: false
|
||||||
|
field :record_only, type: Boolean, default: false
|
||||||
|
belongs_to :file_manager_upload, index: true
|
||||||
|
belongs_to :file_manager_setting
|
||||||
|
index({module: 1}, { unique: false, background: true })
|
||||||
|
index({created_at: -1}, { unique: false, background: true })
|
||||||
|
before_create do
|
||||||
|
if self.trash_path.blank?
|
||||||
|
self.trash_path = Pathname.new("#{FileManagerRoot::RootPath}").join(self.file_manager_setting && self.file_manager_setting.class == FileManagerSetting ? self.file_manager_setting.root_path : '').join(".trash/#{self.file_manager_setting_id}/#{self.id}/#{File.basename(self.path)}")
|
||||||
|
trash_dir = File.dirname(self.trash_path)
|
||||||
|
FileUtils.mkdir_p(trash_dir)
|
||||||
|
upload = self.file_manager_upload
|
||||||
|
if File.exist?(upload.get_real_path)
|
||||||
|
FileUtils.mv(upload.get_real_path, self.trash_path)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
before_destroy do
|
||||||
|
upload = self.file_manager_upload
|
||||||
|
if upload
|
||||||
|
if upload.is_default
|
||||||
|
upload.remove_file_callback
|
||||||
|
end
|
||||||
|
if self.record_only
|
||||||
|
upload.destroy
|
||||||
|
else
|
||||||
|
FileManagerUpload.where(:path=> upload.path).destroy
|
||||||
|
end
|
||||||
|
end
|
||||||
|
true
|
||||||
|
end
|
||||||
|
def get_version
|
||||||
|
return (self.file_manager_upload ? self.file_manager_upload.version : 1)
|
||||||
|
end
|
||||||
|
def delete_file
|
||||||
|
self.class.delete_file_permanent(self.path, File.dirname(self.trash_path), self.record_only)
|
||||||
|
unless self.is_file
|
||||||
|
self.class.where(:trash_path=>/^#{::Regexp.escape(self.trash_path)}/,:is_slave=>true).destroy
|
||||||
|
end
|
||||||
|
self.destroy
|
||||||
|
end
|
||||||
|
def self.check_restricted(path)
|
||||||
|
return Restrict_dirs.map{|d| path.match("^#{d.gsub('/*/','/.*')}[^/]+(/|)$")}.compact.length > 0
|
||||||
|
end
|
||||||
|
def self.delete_file_permanent(path, trash_path=nil, only_self=false)
|
||||||
|
unless self.check_restricted(path)
|
||||||
|
dir = File.dirname(path)
|
||||||
|
basename = File.basename(path)
|
||||||
|
if trash_path
|
||||||
|
FileUtils.rm_rf(trash_path)
|
||||||
|
else
|
||||||
|
FileUtils.rm_rf(path)
|
||||||
|
end
|
||||||
|
unless only_self
|
||||||
|
Dir.glob("#{dir}/.versions/*/#{basename}").each{|f| FileUtils.rm_rf(f)}
|
||||||
|
end
|
||||||
|
else
|
||||||
|
puts "Path: #{path}"
|
||||||
|
puts "File or Directory restricted!"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
def recover_file
|
||||||
|
if File.exist?(self.trash_path)
|
||||||
|
dir = File.dirname(self.path)
|
||||||
|
FileUtils.mkdir_p(dir)
|
||||||
|
FileUtils.mv(self.trash_path, self.path)
|
||||||
|
end
|
||||||
|
self.file_manager_upload.update(:is_trash=>false) if self.file_manager_upload
|
||||||
|
unless self.is_file
|
||||||
|
self.class.where(:trash_path=>/^#{::Regexp.escape(self.trash_path)}/,:is_slave=>true).delete
|
||||||
|
end
|
||||||
|
self.delete
|
||||||
|
end
|
||||||
|
end
|
|
@ -0,0 +1,172 @@
|
||||||
|
class FileManagerUpload
|
||||||
|
include Mongoid::Document
|
||||||
|
include Mongoid::Timestamps
|
||||||
|
Restrict_dirs = ["/", "/home/", "/etc/", "/var/*/", "/root/", "/usr/*/"]
|
||||||
|
field :module, type: String, default: ""
|
||||||
|
field :model, type: String, default: ""
|
||||||
|
field :related_field, type: String, default: ""
|
||||||
|
field :related_id, type: BSON::ObjectId
|
||||||
|
field :is_file, type: Boolean, default: true
|
||||||
|
field :filename, type: String, default: ""
|
||||||
|
field :path, type: String, default: ""
|
||||||
|
field :version, type: Integer, default: 1
|
||||||
|
field :is_default, type: Boolean, default: false
|
||||||
|
field :user_id
|
||||||
|
field :is_trash, type: Boolean, default: false
|
||||||
|
belongs_to :file_manager_setting
|
||||||
|
has_one :file_manager_trash
|
||||||
|
index({model: 1, related_field: 1, related_id: 1}, { unique: false, background: true })
|
||||||
|
index({module: 1}, { unique: false, background: true })
|
||||||
|
index({version: -1}, { unique: false, background: true })
|
||||||
|
index({created_at: -1}, { unique: false, background: true })
|
||||||
|
index({path: 1}, { unique: false, background: true })
|
||||||
|
index({filename: 1}, { unique: false, background: true })
|
||||||
|
index({is_trash: 1}, { unique: false, background: true })
|
||||||
|
|
||||||
|
before_save do
|
||||||
|
self.filename = File.basename(self.path)
|
||||||
|
if self.filename_changed? && self.related_id.present?
|
||||||
|
m = self.model.constantize
|
||||||
|
info = {self.related_field => self.filename}
|
||||||
|
if self.model == 'Asset'
|
||||||
|
I18n.available_locales.each do |l|
|
||||||
|
info["title.#{l}"] = self.filename
|
||||||
|
end
|
||||||
|
end
|
||||||
|
m.where(:id=> self.related_id).update_all(info)
|
||||||
|
end
|
||||||
|
true
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.check_restricted(path)
|
||||||
|
return Restrict_dirs.map{|d| path.match("^#{d.gsub('/*/','/.*')}[^/]+(/|)$")}.compact.length > 0
|
||||||
|
end
|
||||||
|
|
||||||
|
def get_related
|
||||||
|
begin
|
||||||
|
m = self.model.constantize
|
||||||
|
related = m.where(:id=> self.related_id).first
|
||||||
|
rescue => e
|
||||||
|
related = nil
|
||||||
|
puts "get_related: #{e}"
|
||||||
|
end
|
||||||
|
related
|
||||||
|
end
|
||||||
|
|
||||||
|
def generate_new_upload
|
||||||
|
clone_record = self.clone
|
||||||
|
clone_record.is_trash = false
|
||||||
|
clone_record.created_at = Time.now
|
||||||
|
clone_record.updated_at = Time.now
|
||||||
|
self.update_path(self.path, self.is_default ,true)
|
||||||
|
clone_record.version += 1
|
||||||
|
clone_record.save
|
||||||
|
clone_record
|
||||||
|
end
|
||||||
|
def delete_file(record_only=false,override_user_id=nil, real_delete=false, is_slave=false, trash_path=nil)
|
||||||
|
if !real_delete
|
||||||
|
unless self.is_trash
|
||||||
|
unless self.is_file
|
||||||
|
if !is_slave && Dir.glob(Pathname.new(self.path).join("*").to_s).count == 0 #remove empty dir
|
||||||
|
self.delete_file_permanent
|
||||||
|
return
|
||||||
|
end
|
||||||
|
end
|
||||||
|
override_user_id = self.user_id if override_user_id.nil?
|
||||||
|
unless self.class.check_restricted(self.path)
|
||||||
|
setting = self.file_manager_setting
|
||||||
|
setting = FileManagerRoot.first if setting.nil?
|
||||||
|
self.file_manager_trash = FileManagerTrash.create(:record_only=>record_only,:file_manager_upload=>self,:is_file=>is_file,:user_id=>override_user_id,:module=>self.module,:path=>self.path,:file_manager_setting=>setting,:is_slave=>is_slave, :trash_path=>trash_path)
|
||||||
|
self.update(:is_trash => true)
|
||||||
|
if record_only
|
||||||
|
other_records = self.class.where(:path=>self.path,:id.ne=>self.id,:is_trash=>false).order_by(:version=>-1).to_a
|
||||||
|
if other_records.count != 0
|
||||||
|
newest_record = other_records[0]
|
||||||
|
if self.is_default
|
||||||
|
newest_record.update_path(self.path, true)
|
||||||
|
end
|
||||||
|
newest_record
|
||||||
|
else
|
||||||
|
nil
|
||||||
|
end
|
||||||
|
end
|
||||||
|
else
|
||||||
|
puts "Path: #{path}"
|
||||||
|
puts "File or Directory restricted!"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
else
|
||||||
|
self.delete_file_permanent
|
||||||
|
end
|
||||||
|
end
|
||||||
|
def delete_file_permanent(only_self=false)
|
||||||
|
if only_self
|
||||||
|
real_path = self.get_real_path
|
||||||
|
FileManagerTrash.delete_file_permanent(real_path, nil, true)
|
||||||
|
unless self.is_default
|
||||||
|
dir = File.dirname(real_path)
|
||||||
|
if Dir.entries(dir).empty?
|
||||||
|
FileUtils.rm_rf(dir)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
related = self.get_related
|
||||||
|
if self.is_default
|
||||||
|
other_records = self.class.where(:file_manager_setting=>self.file_manager_setting,:path=>self.path,:id.ne=>self.id).order_by(:version=>-1)
|
||||||
|
if other_records.count != 0
|
||||||
|
newest_record = other_records[0]
|
||||||
|
real_old_path = newest_record.get_real_path(self.path)
|
||||||
|
newest_record.update_path(self.path, true)
|
||||||
|
dir = File.dirname(real_old_path)
|
||||||
|
if (Dir.entries(dir) - ['.','..']).empty?
|
||||||
|
FileUtils.rm_rf(dir)
|
||||||
|
end
|
||||||
|
self.update(:is_default => false)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
self.destroy
|
||||||
|
else
|
||||||
|
FileManagerTrash.delete_file_permanent(self.path)
|
||||||
|
self.class.where(:file_manager_setting=>self.file_manager_setting,:path=>self.path,:is_trash=>false).destroy
|
||||||
|
end
|
||||||
|
end
|
||||||
|
def get_real_path(tmp_path=self.path, override_default=self.is_default)
|
||||||
|
return (override_default ? tmp_path : "#{File.dirname(tmp_path)}/.versions/v#{self.version}/#{File.basename(tmp_path)}#{self.is_file ? '' : '/'}")
|
||||||
|
end
|
||||||
|
def update_path(new_path, override_default = nil, move_to_old_version=false, copy_mode=false)
|
||||||
|
override_default = self.is_default if override_default.nil?
|
||||||
|
real_old_path = self.get_real_path(self.path)
|
||||||
|
if move_to_old_version
|
||||||
|
real_new_path = self.get_real_path(new_path, false)
|
||||||
|
self.is_default = false
|
||||||
|
else
|
||||||
|
self.is_default = override_default
|
||||||
|
real_new_path = self.get_real_path(new_path, override_default)
|
||||||
|
end
|
||||||
|
if File.exist?(real_old_path)
|
||||||
|
dir = File.dirname(real_new_path)
|
||||||
|
FileUtils.mkdir_p(dir)
|
||||||
|
if real_old_path != real_new_path
|
||||||
|
FileUtils.mv(real_old_path, real_new_path)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
self.path = new_path
|
||||||
|
self.save
|
||||||
|
if self.is_default && self.related_id.present?
|
||||||
|
m = self.model.constantize
|
||||||
|
m.where(:id=>self.related_id).update_all(self.related_field => self.filename)
|
||||||
|
end
|
||||||
|
self
|
||||||
|
end
|
||||||
|
|
||||||
|
def remove_file_callback
|
||||||
|
m = self.model.constantize
|
||||||
|
if FileManagerSetting.file_only_models.include?(self.model)
|
||||||
|
m.where(:id=>self.related_id).to_a.each do |record|
|
||||||
|
record.instance_variable_set(:@skip_remove_callback, true)
|
||||||
|
record.destroy
|
||||||
|
end
|
||||||
|
else
|
||||||
|
m.where(:id=>self.related_id).update_all(self.related_field => nil)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -0,0 +1,95 @@
|
||||||
|
<h3><%= generate_breadcrumb %></h3>
|
||||||
|
<section id="file_manager_section">
|
||||||
|
<span class="btn-wrapper">
|
||||||
|
<% if !@upload_item.is_trash && @all_uploads && @all_uploads.count > 1 %>
|
||||||
|
<%= link_to "javascript:void(0)", class: 'delete_file', data: { "tmp-confirm"=> "Are you sure you want to delete `#{URI.decode(@relative_path) + ' - Version ' + @active_version.to_s}`?", "link"=> admin_file_manager_path(@upload_item.id, :type=> 'upload'), "version"=> @active_version } do %>
|
||||||
|
<%= fa_icon 'trash' %> <%= t("file_manager.delete") %>
|
||||||
|
<% end %>
|
||||||
|
<% end %>
|
||||||
|
<%= link_to "/xhr/file_manager_download?id=#{@upload_item.id}", class: 'download' do %>
|
||||||
|
<%= fa_icon 'download' %> Download
|
||||||
|
<% end %>
|
||||||
|
<% if !@upload_item.is_trash %>
|
||||||
|
<% if !@is_img && !@too_big && !@unknown_encoding %>
|
||||||
|
<%= link_to "javascript:void(0)", class: 'edit' do %>
|
||||||
|
<%= fa_icon 'edit' %> Edit
|
||||||
|
<% end %>
|
||||||
|
<%= link_to "javascript:void(0)", class: 'save hide', data: {id: @upload_item.id.to_s} do %>
|
||||||
|
<%= fa_icon 'save' %> Save
|
||||||
|
<% end %>
|
||||||
|
<% end %>
|
||||||
|
<div class="dropup upload-button">
|
||||||
|
<button class="btn btn-primary dropdown-toggle" type="button" data-toggle="dropdown">
|
||||||
|
<i class="icon-upload-alt icon-white"></i><%= t('file_manager.upload') %>
|
||||||
|
<span class="caret"></span>
|
||||||
|
</button>
|
||||||
|
<div class="dropdown-menu upload-box">
|
||||||
|
<form action="<%= upload_file_admin_file_manager_path(@upload_item.id) %>" method="post" enctype="multipart/form-data" class="upload_form">
|
||||||
|
<%= hidden_field_tag :authenticity_token, form_authenticity_token %>
|
||||||
|
<input type="file" name="file" >
|
||||||
|
<input type="hidden" name="url" value="<%= request.fullpath %>">
|
||||||
|
<input type="hidden" name="type" value="update">
|
||||||
|
<button class="btn btn-primary" type="submit"><%= t(:submit) %></button>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<% end %>
|
||||||
|
</span>
|
||||||
|
<% if @all_uploads && @all_uploads.count > 1 %>
|
||||||
|
<ul class="tab_nav custom_tabs" style="clear: left;">
|
||||||
|
<% @all_uploads.each do |r| %>
|
||||||
|
<li class="<%= 'active' if r.version == @active_version %>"><a title="Version <%= r.version %> (<%= r.created_at.strftime('%Y/%m/%d %H:%M') %>)" class="change_version" data-version="<%= r.version %>" data-id="<%= r.id.to_s %>">Version <%= r.version %></a></li>
|
||||||
|
<% end %>
|
||||||
|
</ul>
|
||||||
|
<% end %>
|
||||||
|
<div style="clear: both;">
|
||||||
|
<% if @is_img %>
|
||||||
|
<div style="clear: both;"></div>
|
||||||
|
<img src="/xhr/file_manager_download?id=<%=@id%>" />
|
||||||
|
<% elsif File.extname(@upload_item.path) == '.pdf' %>
|
||||||
|
<iframe src="/xhr/file_manager_download?id=<%=@id%>&preview=true" style="width: 100%; height: 100vw;" />
|
||||||
|
<% elsif @too_big %>
|
||||||
|
<pre><%= @file %></pre>
|
||||||
|
<% else %>
|
||||||
|
<textarea class="edit-area hide"></textarea>
|
||||||
|
<pre><%= @file %></pre>
|
||||||
|
<% end %>
|
||||||
|
</div>
|
||||||
|
<script>
|
||||||
|
window.recycle_bin_text = "<%=t('file_manager.recycle_bin')%>";
|
||||||
|
window.current_path = "<%= @relative_path %>"
|
||||||
|
<% if @only_select_folder %>
|
||||||
|
window.only_select_folder = true;
|
||||||
|
<% end %>
|
||||||
|
<% if @setting %>
|
||||||
|
window.explorer_setting_id = "<%= @setting.id %>";
|
||||||
|
<% end %>
|
||||||
|
<% unless @disable_path_traversal %>
|
||||||
|
window.path_mode = "params";
|
||||||
|
window.use_params_request = true;
|
||||||
|
<% end %>
|
||||||
|
if (typeof(FormData) != typeof(undefined)) {
|
||||||
|
$('.upload_form').submit(function(evt){
|
||||||
|
evt.preventDefault();
|
||||||
|
var formData = new FormData(this);
|
||||||
|
formData.append('remote', true);
|
||||||
|
$.ajax({
|
||||||
|
url: $(this).attr('action'),
|
||||||
|
type: 'POST',
|
||||||
|
data: formData,
|
||||||
|
async: false,
|
||||||
|
cache: false,
|
||||||
|
contentType: false,
|
||||||
|
enctype: 'multipart/form-data',
|
||||||
|
processData: false,
|
||||||
|
success: function (data) {
|
||||||
|
reload_page(undefined, false, {"file_id": data.id});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return false;
|
||||||
|
|
||||||
|
});
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
</section>
|
|
@ -0,0 +1,22 @@
|
||||||
|
<div class="path_setting_block">
|
||||||
|
<span class="remove_btn<%= '_old' unless form_setting.new_record?%>">X<%= f.hidden_field :_destroy, :class=>"should_destroy"%></span>
|
||||||
|
<h4>
|
||||||
|
<%= t('file_manager.setting', :num=> (form_setting.new_record? ? 'settings_count' : "#{i + 1}")) %>
|
||||||
|
</h4>
|
||||||
|
<div class="control-group">
|
||||||
|
<%= f.label :only_editable_for_uploader, t("file_manager.only_editable_for_uploader") , :class=>"control-label" %>
|
||||||
|
<div class="controls">
|
||||||
|
<%= f.check_box :only_editable_for_uploader %>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="control-group">
|
||||||
|
<%= f.label :root_path, t("file_manager.root_path") , :class=>"control-label" %>
|
||||||
|
<div class="controls">
|
||||||
|
<%= f.text_field :root_path %>
|
||||||
|
<button class="browse_path btn btn-primary" type="button"><%=t("file_manager.browse")%></button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<% unless form_setting.new_record? %>
|
||||||
|
<%= f.hidden_field :id %>
|
||||||
|
<% end %>
|
||||||
|
</div>
|
|
@ -0,0 +1,152 @@
|
||||||
|
<% if @module_key == 'asset' %>
|
||||||
|
<style type="text/css">
|
||||||
|
.file-input.file-input-ajax-new {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
<% end %>
|
||||||
|
<h3><%= generate_breadcrumb %></h3>
|
||||||
|
<span class="btn-wrapper">
|
||||||
|
<a href="javascript:void(0)" class="download redirect_recycle_bin"><%=t('file_manager.recycle_bin')%></a>
|
||||||
|
</span>
|
||||||
|
<table>
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<% if @display_module_name %>
|
||||||
|
<th><%= t("file_manager.module") %></th>
|
||||||
|
<% end %>
|
||||||
|
<th><%= t("file_manager.name") %></th>
|
||||||
|
<th><%= t("file_manager.size") %></th>
|
||||||
|
<th><%= t("file_manager.updated_at") %></th>
|
||||||
|
<% unless @only_select_folder %>
|
||||||
|
<th><%= t("file_manager.rename") %></th>
|
||||||
|
<th><%= t("file_manager.delete") %></th>
|
||||||
|
<th><%= t("download") %></th>
|
||||||
|
<th><%= t("file_manager.upload") %></th>
|
||||||
|
<th><%= t("file_manager.link") %></th>
|
||||||
|
<% end %>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
<% @directory.each do |entry| %>
|
||||||
|
<% next if @relative_path.blank? && entry[:relative] == '..' %>
|
||||||
|
<tr>
|
||||||
|
<% if @display_module_name %>
|
||||||
|
<td class="module">
|
||||||
|
<%
|
||||||
|
module_info = @module_info_dict[entry[:module]]
|
||||||
|
module_title = (module_info ? module_info[:title] : nil)
|
||||||
|
%>
|
||||||
|
<% if module_title %>
|
||||||
|
<%= link_to(module_title, "javascript:void(0)", class: "module-key", data: {'key'=>entry[:module]}) %>
|
||||||
|
<% end %>
|
||||||
|
</td>
|
||||||
|
<% end %>
|
||||||
|
<td class="name">
|
||||||
|
<%= link_to "javascript:void(0)", class: entry[:type].to_s, data: {'entry'=>entry[:entry], 'id'=> entry[:id]} do %>
|
||||||
|
<%= fa_icon (entry[:type] == :file ? 'file' : 'folder') %>
|
||||||
|
<span class="filename"><%= entry[:entry] %></span>
|
||||||
|
<% end %>
|
||||||
|
</td>
|
||||||
|
<td class="size">
|
||||||
|
<%= entry[:size] %>
|
||||||
|
</td>
|
||||||
|
<td class="date">
|
||||||
|
<%= entry[:date] %>
|
||||||
|
</td>
|
||||||
|
<% unless @only_select_folder %>
|
||||||
|
<td>
|
||||||
|
<% if entry[:is_editable] && entry[:entry] != './' && entry[:entry] != '../' %>
|
||||||
|
<%= link_to 'javascript:void(0)', class: 'rename', onclick: "rename('#{entry[:id]}', '#{URI.decode(entry[:relative])}')" do %>
|
||||||
|
<%= fa_icon 'i-cursor' %> <%= t("file_manager.rename") %>
|
||||||
|
<% end %>
|
||||||
|
<% end %>
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
<% if entry[:is_editable] && entry[:entry] != './' && entry[:entry] != '../' %>
|
||||||
|
<%= link_to "javascript:void(0)", class: 'delete', data: { "tmp-confirm"=> "Are you sure you want to delete `#{URI.decode(entry[:relative])}`?", "link"=> admin_file_manager_path(entry[:id]) } do %>
|
||||||
|
<%= fa_icon 'trash' %> <%= t("file_manager.delete") %>
|
||||||
|
<% end %>
|
||||||
|
<% end %>
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
<% if entry[:id] && entry[:is_file] %>
|
||||||
|
<a class="download" href="/xhr/file_manager_download?id=<%=entry[:id]%>" title="<%= t("download") + ' ' + entry[:entry] %>" target="_blank"><%= fa_icon 'download' %> <%= t("download") %></a>
|
||||||
|
<% end %>
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
<div class="dropup upload-button">
|
||||||
|
<button class="btn btn-primary dropdown-toggle" type="button" data-toggle="dropdown">
|
||||||
|
<i class="icon-upload-alt icon-white"></i><%= t('file_manager.upload') %>
|
||||||
|
<span class="caret"></span>
|
||||||
|
</button>
|
||||||
|
<div class="dropdown-menu upload-box">
|
||||||
|
<form action="<%= upload_file_admin_file_manager_path(entry[:id]) %>" method="post" enctype="multipart/form-data" class="upload_form">
|
||||||
|
<%= hidden_field_tag :authenticity_token, form_authenticity_token %>
|
||||||
|
<input type="file" name="file" >
|
||||||
|
<input type="hidden" name="url" value="<%= request.fullpath %>">
|
||||||
|
<input type="hidden" name="type" value="update">
|
||||||
|
<button class="btn btn-primary" type="submit"><%= t(:submit) %></button>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
<div class="tooltip">
|
||||||
|
<button class="copyButton btn btn-success" class="input-group-addon btn" data-link="<%= entry[:absolute].sub('public', '') %>" data-prompt="<%= t("file_manager.copied") %>">
|
||||||
|
<span class="tooltiptext"><%= t("file_manager.copy_link") %></span>
|
||||||
|
<i class="fa fa-clipboard" aria-hidden="true"></i>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
<% end %>
|
||||||
|
</tr>
|
||||||
|
<% end %>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
<%=
|
||||||
|
content_tag :div, class: "bottomnav clearfix" do
|
||||||
|
content_tag(:div, paginate(@uploads), class: "pagination pagination-centered")
|
||||||
|
end
|
||||||
|
%>
|
||||||
|
</section>
|
||||||
|
<script>
|
||||||
|
<% unless @disable_path_traversal %>
|
||||||
|
window.path_mode = "params";
|
||||||
|
window.use_params_request = true;
|
||||||
|
<% end %>
|
||||||
|
if (typeof(FormData) != typeof(undefined)) {
|
||||||
|
$('.upload_form').submit(function(evt){
|
||||||
|
evt.preventDefault();
|
||||||
|
var formData = new FormData(this);
|
||||||
|
formData.append('remote', true);
|
||||||
|
var tr = $(this).parents('tr').eq(0);
|
||||||
|
var file_link = tr.find('td.name a');
|
||||||
|
var size_td = tr.find('td.size');
|
||||||
|
var date_td = tr.find('td.date');
|
||||||
|
$.ajax({
|
||||||
|
url: $(this).attr('action'),
|
||||||
|
type: 'POST',
|
||||||
|
data: formData,
|
||||||
|
async: false,
|
||||||
|
cache: false,
|
||||||
|
contentType: false,
|
||||||
|
enctype: 'multipart/form-data',
|
||||||
|
processData: false,
|
||||||
|
success: function (data) {
|
||||||
|
file_link.attr('data-entry', data.filename);
|
||||||
|
file_link.data('entry', data.filename);
|
||||||
|
file_link.attr('data-id', data.id);
|
||||||
|
file_link.data('id', data.id);
|
||||||
|
file_link.find('.filename').text(data.filename);
|
||||||
|
size_td.text(data.size);
|
||||||
|
date_td.text(data.date);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return false;
|
||||||
|
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
</script>
|
|
@ -0,0 +1,100 @@
|
||||||
|
<section id="file_manager_section">
|
||||||
|
<h3><%= generate_breadcrumb %></h3>
|
||||||
|
<span class="btn-wrapper">
|
||||||
|
<% if @directory.count != 0 %>
|
||||||
|
<a href="javascript:void(0)" class="recover_all" data-prompt="<%=t('file_manager.are_you_sure_to_recover_all')%>" data-num="<%=@directory.count%>"data-unit="<%=t('file_manager.file_unit')%>" title="<%=t('file_manager.clean_up_recycle_bin')%>">
|
||||||
|
<%=t('file_manager.recover_all')%>
|
||||||
|
</a>
|
||||||
|
<a href="javascript:void(0)" class="clean_up_recycle_bin" data-prompt="<%=t('file_manager.are_you_sure_to_delete_files_permanently')%>" data-num="<%=@directory.count%>"data-unit="<%=t('file_manager.file_unit')%>" title="<%=t('file_manager.clean_up_recycle_bin')%>">
|
||||||
|
<%=t('file_manager.clean_up_recycle_bin')%>
|
||||||
|
</a>
|
||||||
|
<% end %>
|
||||||
|
</span>
|
||||||
|
<table>
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<% if @display_module_name %>
|
||||||
|
<th><%= t("file_manager.module") %></th>
|
||||||
|
<% end %>
|
||||||
|
<th><%= t("file_manager.name") %></th>
|
||||||
|
<th><%= t("file_manager.size") %></th>
|
||||||
|
<th><%= t("file_manager.deleted_at") %></th>
|
||||||
|
<th><%= t("file_manager.updated_at") %></th>
|
||||||
|
<% unless @only_select_folder %>
|
||||||
|
<th><%= t("file_manager.recover") %></th>
|
||||||
|
<th><%= t("file_manager.delete_permanently") %></th>
|
||||||
|
<% end %>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
<% @directory.each do |entry| %>
|
||||||
|
<% next if @relative_path.blank? && entry[:relative] == '..' %>
|
||||||
|
<tr>
|
||||||
|
<% if @display_module_name %>
|
||||||
|
<td>
|
||||||
|
<%
|
||||||
|
module_info = @module_info_dict[entry[:module]]
|
||||||
|
module_title = (module_info ? module_info[:title] : nil)
|
||||||
|
%>
|
||||||
|
<% if module_title %>
|
||||||
|
<%= link_to(module_title, "javascript:void(0)", class: "module-key", data: {'key'=>entry[:module]}) %>
|
||||||
|
<% end %>
|
||||||
|
</td>
|
||||||
|
<% end %>
|
||||||
|
<td class="name">
|
||||||
|
<%= link_to "javascript:void(0)", class: entry[:type].to_s, data: {'entry'=>entry[:entry], 'id'=> entry[:upload_id]} do %>
|
||||||
|
<%= fa_icon (entry[:type] == :file ? 'file' : 'folder') %>
|
||||||
|
<span class="filename"><%= entry[:name] %></span>
|
||||||
|
<% end %>
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
<%= entry[:size] %>
|
||||||
|
</td>
|
||||||
|
<td class="date">
|
||||||
|
<%= entry[:deleted_at] %>
|
||||||
|
</td>
|
||||||
|
<td class="date">
|
||||||
|
<%= entry[:date] %>
|
||||||
|
</td>
|
||||||
|
<% unless @only_select_folder %>
|
||||||
|
<td>
|
||||||
|
<% if entry[:entry] != './' && entry[:entry] != '../' %>
|
||||||
|
<%= link_to 'javascript:void(0)', class: 'recover', data: { "tmp-confirm"=> t("file_manager.are_you_sure_to_recover"), "link"=> recover_admin_file_manager_path(entry[:id]) } do %>
|
||||||
|
<%= fa_icon 'trash-restore' %> <%= t("file_manager.recover") %>
|
||||||
|
<% end %>
|
||||||
|
<% end %>
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
<% if entry[:entry] != './' && entry[:entry] != '../' %>
|
||||||
|
<%= link_to "javascript:void(0)", class: 'delete', data: { "tmp-confirm"=> t("file_manager.are_you_sure_you_want_to_delete_file_permanently",:file=>entry[:org_path]), "link"=> admin_file_manager_path(entry[:id], permanent: true) } do %>
|
||||||
|
<%= fa_icon 'trash' %> <%= t("file_manager.delete_permanently") %>
|
||||||
|
<% end %>
|
||||||
|
<% end %>
|
||||||
|
</td>
|
||||||
|
<% end %>
|
||||||
|
</tr>
|
||||||
|
<% end %>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</section>
|
||||||
|
<div class="bottomnav clearfix">
|
||||||
|
<%= content_tag(:div, paginate(@trashes), class: "pagination pagination-centered") %>
|
||||||
|
<div class="pull-right">
|
||||||
|
<% if @thread %>
|
||||||
|
<button class="show_progress btn btn-primary" type="button"><%= t("file_manager.show_progress") %></button>
|
||||||
|
<% end %>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<script>
|
||||||
|
window.is_recycle_bin = true;
|
||||||
|
</script>
|
||||||
|
<script>
|
||||||
|
$(document).ready(function(){
|
||||||
|
if($("#threadModal").data('id')){
|
||||||
|
$("#threadModal").modal("show");
|
||||||
|
$(".show_progress").click(function(){
|
||||||
|
$("#threadModal").modal("show");
|
||||||
|
})
|
||||||
|
}
|
||||||
|
})
|
||||||
|
</script>
|
|
@ -0,0 +1,39 @@
|
||||||
|
<div class="box-content">
|
||||||
|
<ul style="display: flex; flex-wrap: wrap;">
|
||||||
|
<% modules_info = @setting.class.get_models_uploaders
|
||||||
|
@setting.get_modules_keys.each do |k|
|
||||||
|
if k == 'all'
|
||||||
|
info = @setting.class.get_module_info(k)
|
||||||
|
else
|
||||||
|
next if modules_info[k].keys.detect{|_model| @setting.modules_saved_info[_model.to_s]}.nil?
|
||||||
|
info = @setting.class.get_module_info(k)
|
||||||
|
next if info[:sidebar] && !display_sidebar?(info[:sidebar])
|
||||||
|
next if info.nil?
|
||||||
|
end
|
||||||
|
%>
|
||||||
|
<li>
|
||||||
|
<span>
|
||||||
|
<a href="<%= module_admin_file_manager_path(k) %>" title="<%= info[:title] %>">
|
||||||
|
<i class="<%= info[:icon_class] %> icon_big"></i>
|
||||||
|
<%= info[:title] %>
|
||||||
|
</a>
|
||||||
|
</span>
|
||||||
|
</li>
|
||||||
|
<% end %>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
<style type="text/css">
|
||||||
|
i.icon_big{
|
||||||
|
font-size: 3em;
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
#main-wrap li{
|
||||||
|
font-size: 1.3em;
|
||||||
|
list-style: none;
|
||||||
|
float: left;
|
||||||
|
margin: 0.8em;
|
||||||
|
}
|
||||||
|
#main-wrap a{
|
||||||
|
color: black;
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -0,0 +1,90 @@
|
||||||
|
<% content_for :page_specific_css do %>
|
||||||
|
<%= stylesheet_link_tag 'file_manager/default_theme', media: 'all' %>
|
||||||
|
<%= stylesheet_link_tag 'file_manager/application', media: 'all' %>
|
||||||
|
<%= stylesheet_link_tag "bootstrap-fileinput/css/fileinput.min" %>
|
||||||
|
<%= stylesheet_link_tag "https://cdn.jsdelivr.net/npm/bootstrap-icons@1.5.0/font/bootstrap-icons.min.css", :crossorigin => "anonymous" %>
|
||||||
|
<% end %>
|
||||||
|
<% content_for :page_specific_javascript do %>
|
||||||
|
<%= javascript_include_tag 'file_manager/application' %>
|
||||||
|
<%= javascript_include_tag "file-upload/tmpl.min.js" %>
|
||||||
|
<%= javascript_include_tag "file-upload/load-image.min.js" %>
|
||||||
|
<%= javascript_include_tag "file-upload/canvas-to-blob.min.js" %>
|
||||||
|
<%= javascript_include_tag "file-upload/jquery.iframe-transport.js" %>
|
||||||
|
<%= javascript_include_tag "file-upload/drop-zone.js" %>
|
||||||
|
<%= javascript_include_tag "bootstrap-fileinput/js/plugins/piexif.min" %>
|
||||||
|
<%= javascript_include_tag "bootstrap-fileinput/js/plugins/sortable.min" %>
|
||||||
|
<%= javascript_include_tag "bootstrap-fileinput/js/fileinput.min" %>
|
||||||
|
<%= javascript_include_tag "bootstrap-fileinput/js/locales/#{I18n.locale}" %>
|
||||||
|
<% end %>
|
||||||
|
|
||||||
|
<div id="threadModal" class="modal hide fade in" tabindex="-1" role="dialog" aria-labelledby="threadModal" aria-hidden="false" data-id="<%= @thread.id if @thread %>">
|
||||||
|
<div class="modal-dialog" role="document">
|
||||||
|
<div class="modal-content">
|
||||||
|
<div class="modal-header">
|
||||||
|
<h3><%= @thread_title %></h3>
|
||||||
|
</div>
|
||||||
|
<div class="modal-body">
|
||||||
|
<% if @thread %>
|
||||||
|
<div class="thread-status"><%= @thread.status[:status] %></div>
|
||||||
|
<div><span class="thread-current-count"><%= @thread.status[:current_count].to_i %></span>/<span class="thread-all-count"><%= @thread.status[:all_count].to_i %></span></div>
|
||||||
|
<span class="thread-finish_percent"><%= @thread.status[:finish_percent].to_i %></span> % finished
|
||||||
|
<% else %>
|
||||||
|
<div class="thread-status">Processing</div>
|
||||||
|
<div><span class="thread-current-count">0</span>/<span class="thread-all-count">0</span></div>
|
||||||
|
<span class="thread-finish_percent">0</span> % finished
|
||||||
|
<% end %>
|
||||||
|
</div>
|
||||||
|
<div class="modal-footer">
|
||||||
|
<button class="btn" id="modal-close-btn" style="width: 4em;" data-dismiss="modal" aria-hidden="true"><%=t('close')%></button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<%= render_filter @filter_fields, "index_table" %>
|
||||||
|
<section id="file_manager_section">
|
||||||
|
<% if @display_uploader_region %>
|
||||||
|
<input id="file-uploader" type="file" name="file" multiple>
|
||||||
|
<style type="text/css">
|
||||||
|
.file-input.file-input-ajax-new {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
<script class="file-uploader-script">
|
||||||
|
var $el;
|
||||||
|
$(document).ready(function() {
|
||||||
|
if($('#file_manager_section .file-input').length == 0){
|
||||||
|
$el = $("#file-uploader");
|
||||||
|
var footerTemplate = '<div class="file-thumbnail-footer" style ="height:94px">\n' +
|
||||||
|
' <div>{caption}</div> ' +
|
||||||
|
' <div class="small" style="margin:15px 0 2px 0">{size}</div> {progress}\n{indicator}\n{actions}\n' +
|
||||||
|
'</div>';
|
||||||
|
$el.fileinput({
|
||||||
|
language: "<%= I18n.locale %>",
|
||||||
|
uploadUrl: "<%= upload_file_admin_file_manager_path(@module_key) %>",
|
||||||
|
previewFileType: 'any',
|
||||||
|
uploadAsync: true,
|
||||||
|
layoutTemplates: {footer: footerTemplate}
|
||||||
|
}).on('filebatchselected', function(event, files) {
|
||||||
|
//console.log(files);
|
||||||
|
}).on('fileuploaded', function(event, data) {
|
||||||
|
reload_explorer_section(undefined, $('#file_manager_section .file-input .progress-bar:not(.progress-bar-success)').length != 0);
|
||||||
|
}).on('filebatchuploadcomplete', function(event, data) {
|
||||||
|
reload_explorer_section();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
<% end %>
|
||||||
|
<script>
|
||||||
|
window.file_manager_path = "<%= admin_file_managers_path(:locale=> I18n.locale) %>";
|
||||||
|
window.module_key = "<%= @module_key %>";
|
||||||
|
</script>
|
||||||
|
<span id="index_table">
|
||||||
|
<% if @file %>
|
||||||
|
<%= render :partial => 'file' %>
|
||||||
|
<% elsif @recycle_bin %>
|
||||||
|
<%= render :partial => 'recycle_bin' %>
|
||||||
|
<% else %>
|
||||||
|
<%= render :partial => 'module' %>
|
||||||
|
<% end %>
|
||||||
|
</span>
|
|
@ -0,0 +1,106 @@
|
||||||
|
<%= stylesheet_link_tag 'file_manager/application', media: 'all' %>
|
||||||
|
<%= javascript_include_tag 'file_manager/file_manager' %>
|
||||||
|
<style>
|
||||||
|
.path_setting_block{
|
||||||
|
border: 1px solid black;
|
||||||
|
padding: 1em;
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
.remove_btn_old, .remove_btn{
|
||||||
|
color: red;
|
||||||
|
float: right;
|
||||||
|
position: absolute;
|
||||||
|
right: 0;
|
||||||
|
top: 0;
|
||||||
|
background: #ffc3c3;
|
||||||
|
padding: 0.1em 0.5em;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
#browse_model .modal-body{
|
||||||
|
font-size: 1rem;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
<div class="modal hide fade in" id="browse_model" tabindex="-1" role="dialog" data-backdrop="true">
|
||||||
|
<div class="modal-dialog" role="document">
|
||||||
|
<div class="modal-content">
|
||||||
|
<div class="modal-header">
|
||||||
|
<a class="close" data-dismiss="modal">×</a>
|
||||||
|
<h3><%= t("file_manager.select_folder") %></h3>
|
||||||
|
</div>
|
||||||
|
<div class="modal-body">
|
||||||
|
</div>
|
||||||
|
<div class="modal-footer">
|
||||||
|
<a href="#" class="btn" data-dismiss="modal"><%= t("file_manager.select_current_folder") %></a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<h3><%= t('file_manager.path_setting') %></h3>
|
||||||
|
<%= form_for( @root, url: admin_file_managers_update_settings_path, html: {class: 'form-horizontal'} ) do |f| %>
|
||||||
|
<% if current_user.is_admin? %>
|
||||||
|
<div class="control-group">
|
||||||
|
<%= f.label :disable_path_traversal, t("file_manager.disable_path_traversal") , :class=>"control-label" %>
|
||||||
|
<div class="controls">
|
||||||
|
<%= f.check_box :disable_path_traversal %>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<% end %>
|
||||||
|
<% @root.file_manager_settings.each_with_index do |setting, i| %>
|
||||||
|
<%= f.fields_for :file_manager_settings, setting do |f| %>
|
||||||
|
<%= render :partial => 'form_setting', object: f.object, locals: {f: f, i: i} %>
|
||||||
|
<% end %>
|
||||||
|
<% end %>
|
||||||
|
<a id="">
|
||||||
|
|
||||||
|
</a>
|
||||||
|
<!-- Add -->
|
||||||
|
<div class="add-target">
|
||||||
|
</div>
|
||||||
|
<p class="add-btn">
|
||||||
|
<%= hidden_field_tag 'file_manager_settings_count', @root.file_manager_settings.count %>
|
||||||
|
<a id="add_file_manager_setting" class="trigger btn btn-small btn-primary"><i class="icons-plus"></i> <%= t(:add) %></a>
|
||||||
|
</p>
|
||||||
|
<!-- Form Actions -->
|
||||||
|
<div class="form-actions">
|
||||||
|
<%= get_referer_url[:action] rescue "" %>
|
||||||
|
<%= f.submit t('submit'), class: 'btn btn-primary' %>
|
||||||
|
<input type="hidden" name="referer_url" value="<%= get_referer_url %>">
|
||||||
|
</div>
|
||||||
|
<script>
|
||||||
|
$(document).on('click', '#add_file_manager_setting', function(){
|
||||||
|
var new_id = $(this).prev().attr('value');
|
||||||
|
var old_id = new RegExp("new_file_manager_settings", "g");
|
||||||
|
$(this).prev().attr('value', parseInt(new_id) + 1);
|
||||||
|
$(this).parent().siblings('.add-target').append(("<%= escape_javascript(add_attribute 'form_setting', f, :file_manager_settings) %>").replace(old_id, new_id).replace('settings_count', $('.path_setting_block').length + 1));
|
||||||
|
})
|
||||||
|
$(document).on('click', '.remove_btn_old', function(){
|
||||||
|
if(window.confirm('<%=t('file_manager.are_you_sure_to_delete')%>')){
|
||||||
|
$(this).children('.should_destroy').attr('value', 1);
|
||||||
|
$(this).parent().hide();
|
||||||
|
}
|
||||||
|
})
|
||||||
|
$(document).on('click', '.remove_btn', function(){
|
||||||
|
if(window.confirm('<%=t('file_manager.are_you_sure_to_delete')%>')){
|
||||||
|
$(this).parent().remove();
|
||||||
|
}
|
||||||
|
})
|
||||||
|
$(document).on('click', '.browse_path', function(){
|
||||||
|
var that = $(this)
|
||||||
|
var path = that.siblings('input').val();
|
||||||
|
window.current_browse_input = that.siblings('input');
|
||||||
|
window.current_path = path;
|
||||||
|
$.get("/xhr/file_manager/" + "?select_mode=true&path="+path).done(function(data){
|
||||||
|
window.only_select_folder = true;
|
||||||
|
$("#browse_model .modal-body").html('<h4 class="breadcrumb-index"></h4>'+ data);
|
||||||
|
$("#browse_model").modal('show');
|
||||||
|
change_breadcrumb();
|
||||||
|
}).fail(function(){
|
||||||
|
alert("No such file or directory!");
|
||||||
|
that.siblings('input').val('');
|
||||||
|
});
|
||||||
|
})
|
||||||
|
$("#browse_model").on('hidden.bs.modal', function () {
|
||||||
|
window.current_browse_input.val(window.current_path);
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
<% end %>
|
|
@ -0,0 +1,9 @@
|
||||||
|
<% active_setting_id = @custom_data_field[:setting_id] || @settings.first.id rescue nil %>
|
||||||
|
<% if @settings.length != 0 %>
|
||||||
|
<div class="control-group input-content">
|
||||||
|
<label class="control-label" for="custom_setting_id"><%=t("file_manager.path_setting")%> :</label>
|
||||||
|
<div class="controls">
|
||||||
|
<%= select_tag("#{@field_name}[custom_data_field][setting_id]", options_for_select(@settings.map.with_index{|setting, i| [t("file_manager.setting",:num=>i+1),setting.id]},active_setting_id),{:id=>"custom_setting_id"})%>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<% end %>
|
|
@ -0,0 +1,56 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<title>FileManager</title>
|
||||||
|
<%= csrf_meta_tags %>
|
||||||
|
<%= stylesheet_link_tag 'file_manager/default_theme', media: 'all' %>
|
||||||
|
<%= stylesheet_link_tag 'file_manager/application', media: 'all' %>
|
||||||
|
<%= javascript_include_tag 'file_manager/application' %>
|
||||||
|
<%= javascript_include_tag "file-upload/tmpl.min.js" %>
|
||||||
|
<%= javascript_include_tag "file-upload/load-image.min.js" %>
|
||||||
|
<%= javascript_include_tag "file-upload/canvas-to-blob.min.js" %>
|
||||||
|
<%= javascript_include_tag "file-upload/jquery.iframe-transport.js" %>
|
||||||
|
<%= javascript_include_tag "file-upload/drop-zone.js" %>
|
||||||
|
<%= stylesheet_link_tag "bootstrap-fileinput/css/fileinput.min" %>
|
||||||
|
<%= javascript_include_tag "bootstrap-fileinput/js/plugins/piexif.min" %>
|
||||||
|
<%= javascript_include_tag "bootstrap-fileinput/js/plugins/sortable.min" %>
|
||||||
|
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/js/bootstrap.min.js" integrity="sha384-Tc5IQib027qvyjSMfHjOMaLkfuWVxZxUPnCJA7l2mCWNIpG9mGCD8wGNIcPD7Txa" crossorigin="anonymous"></script>
|
||||||
|
<%= javascript_include_tag "bootstrap-fileinput/js/fileinput.min" %>
|
||||||
|
<%= javascript_include_tag "bootstrap-fileinput/js/locales/#{I18n.locale}" %>
|
||||||
|
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css" integrity="sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u" crossorigin="anonymous">
|
||||||
|
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.5.0/font/bootstrap-icons.min.css" crossorigin="anonymous">
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body id="file_manager_body">
|
||||||
|
<div id="threadModal" class="modal hide fade in" tabindex="-1" role="dialog" aria-labelledby="threadModal" aria-hidden="false" data-id="<%= @thread.id if @thread %>">
|
||||||
|
<div class="modal-dialog" role="document">
|
||||||
|
<div class="modal-content">
|
||||||
|
<div class="modal-header">
|
||||||
|
<h3><%=t("file_manager.clean_up_recycle_bin")%></h3>
|
||||||
|
</div>
|
||||||
|
<div class="modal-body">
|
||||||
|
<% if @thread %>
|
||||||
|
<div class="thread-status"><%= @thread.status[:status] %></div>
|
||||||
|
<div><span class="thread-current-count"><%= @thread.status[:current_count].to_i %></span>/<span class="thread-all-count"><%= @thread.status[:all_count].to_i %></span></div>
|
||||||
|
<span class="thread-finish_percent"><%= @thread.status[:finish_percent].to_i %></span> % finished
|
||||||
|
<% else %>
|
||||||
|
<div class="thread-status">Processing</div>
|
||||||
|
<div><span class="thread-current-count">0</span>/<span class="thread-all-count">0</span></div>
|
||||||
|
<span class="thread-finish_percent">0</span> % finished
|
||||||
|
<% end %>
|
||||||
|
</div>
|
||||||
|
<div class="modal-footer">
|
||||||
|
<button class="btn" id="modal-close-btn" style="width: 4em;" data-dismiss="modal" aria-hidden="true"><%=t('close')%></button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<h3 class="breadcrumb-index">
|
||||||
|
<span id="breadcrumb-relative-url">
|
||||||
|
<%= generate_breadcrumb(@relative_path) %>
|
||||||
|
</span>
|
||||||
|
<div id="breadcrumb-editable" contenteditable="" class="hide">/</div>
|
||||||
|
</h3>
|
||||||
|
<%= yield %>
|
||||||
|
</body>
|
||||||
|
</html>
|
|
@ -0,0 +1,18 @@
|
||||||
|
#!/usr/bin/env ruby
|
||||||
|
# This command will automatically be run when you run "rails" with Rails 4 gems installed from the root of your application.
|
||||||
|
|
||||||
|
ENGINE_ROOT = File.expand_path('../..', __FILE__)
|
||||||
|
ENGINE_PATH = File.expand_path('../../lib/file_manager/engine', __FILE__)
|
||||||
|
|
||||||
|
# Set up gems listed in the Gemfile.
|
||||||
|
ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../../Gemfile', __FILE__)
|
||||||
|
require 'bundler/setup' if File.exist?(ENV['BUNDLE_GEMFILE'])
|
||||||
|
|
||||||
|
# require 'rails/all'
|
||||||
|
# require 'rails/engine/commands'
|
||||||
|
require "action_controller/railtie"
|
||||||
|
require "action_mailer/railtie"
|
||||||
|
require "sprockets/railtie"
|
||||||
|
require "rails/test_unit/railtie"
|
||||||
|
require 'rails/engine/commands'
|
||||||
|
require "mongoid/railtie"
|
|
@ -0,0 +1,41 @@
|
||||||
|
en:
|
||||||
|
module_name:
|
||||||
|
file_manager: File Manager
|
||||||
|
file_manager:
|
||||||
|
module: "Module"
|
||||||
|
all_files: "All Files"
|
||||||
|
root: "root"
|
||||||
|
file_unit: ""
|
||||||
|
only_editable_for_uploader: Only editable for Uploader
|
||||||
|
root_path: Root Path
|
||||||
|
path_setting: Path Setting
|
||||||
|
file_manager: File Manager
|
||||||
|
settings: Settings
|
||||||
|
create_folder: Create Folder
|
||||||
|
new_folder: New Folder
|
||||||
|
setting: "Setting %{num}"
|
||||||
|
are_you_sure_to_recover: "Are you sure to recover file?"
|
||||||
|
are_you_sure_to_delete: "Are you sure to delete?"
|
||||||
|
are_you_sure_to_recover_all: "Are you sure to recover ${num} files?"
|
||||||
|
are_you_sure_to_delete_files_permanently: "Are you sure to delete ${num} files permanently?"
|
||||||
|
are_you_sure_you_want_to_delete_file_permanently: "Are you sure you want to delete `%{file}` permanently?"
|
||||||
|
browse: "Browse"
|
||||||
|
select_folder: "Select Folder"
|
||||||
|
select_current_folder: "Select Current Folder"
|
||||||
|
disable_path_traversal: "Disable path traversal"
|
||||||
|
upload: "Upload"
|
||||||
|
delete: "Delete"
|
||||||
|
rename: "Rename"
|
||||||
|
recycle_bin: "Recycle Bin"
|
||||||
|
delete_permanently: "Delete permanently"
|
||||||
|
recover: "Recover"
|
||||||
|
recover_all: "Recover All"
|
||||||
|
name: "Name"
|
||||||
|
size: "Size"
|
||||||
|
original_path: "Original Path"
|
||||||
|
updated_at: "Updated at"
|
||||||
|
deleted_at: "Deleted at"
|
||||||
|
clean_up_recycle_bin: "Clean Up Recycle Bin"
|
||||||
|
link: "Link"
|
||||||
|
copy_link: "Click to copy"
|
||||||
|
copied: "Copied"
|
|
@ -0,0 +1,41 @@
|
||||||
|
zh_tw:
|
||||||
|
module_name:
|
||||||
|
file_manager: 檔案管理
|
||||||
|
file_manager:
|
||||||
|
module: "模組"
|
||||||
|
all_files: "全部檔案"
|
||||||
|
root: "根目錄"
|
||||||
|
file_unit: "個"
|
||||||
|
only_editable_for_uploader: 僅供上傳者編輯
|
||||||
|
root_path: 根路徑
|
||||||
|
path_setting: 路徑設定
|
||||||
|
file_manager: 檔案管理
|
||||||
|
settings: 設定
|
||||||
|
create_folder: 創建資料夾
|
||||||
|
new_folder: 新增資料夾
|
||||||
|
setting: "設定%{num}"
|
||||||
|
are_you_sure_to_recover: "你確定要復原檔案嗎?"
|
||||||
|
are_you_sure_to_delete: "你確定要刪除嗎?"
|
||||||
|
are_you_sure_to_recover_all: "你確定要還原這${num}檔案嗎?"
|
||||||
|
are_you_sure_to_delete_files_permanently: "你確定要永久刪除這${num}檔案嗎?"
|
||||||
|
are_you_sure_you_want_to_delete_file_permanently: "你確定要永久刪除`%{file}`?"
|
||||||
|
browse: "瀏覽"
|
||||||
|
select_folder: "選擇資料夾"
|
||||||
|
select_current_folder: "選擇當前資料夾"
|
||||||
|
disable_path_traversal: "禁止使用者訪問其他路徑"
|
||||||
|
upload: "上傳"
|
||||||
|
delete: "刪除"
|
||||||
|
rename: "重新命名"
|
||||||
|
recycle_bin: "資源回收桶"
|
||||||
|
delete_permanently: "永久刪除"
|
||||||
|
recover: "復原"
|
||||||
|
recover_all: "還原所有項目"
|
||||||
|
name: "名稱"
|
||||||
|
size: "大小"
|
||||||
|
original_path: "原始位置"
|
||||||
|
updated_at: "修改日期"
|
||||||
|
deleted_at: "刪除日期"
|
||||||
|
clean_up_recycle_bin: "清理資源回收桶"
|
||||||
|
link: "連結"
|
||||||
|
copy_link: "複製連結"
|
||||||
|
copied: "已複製"
|
|
@ -0,0 +1,53 @@
|
||||||
|
Rails.application.routes.draw do
|
||||||
|
if ENV['worker_num']=='0' && File.basename($0) != 'rake' && !Rails.const_defined?('Console')
|
||||||
|
count = FileManagerRoot.count
|
||||||
|
if count == 0
|
||||||
|
root = FileManagerRoot.create
|
||||||
|
else
|
||||||
|
if count > 1
|
||||||
|
FileManagerRoot.all[1..-1].each{|f| f.destroy}
|
||||||
|
end
|
||||||
|
root = FileManagerRoot.first
|
||||||
|
end
|
||||||
|
FileManagerUpload.create_indexes
|
||||||
|
FileManagerTrash.create_indexes
|
||||||
|
setting = root.file_manager_settings.first
|
||||||
|
if setting.nil?
|
||||||
|
setting = root.file_manager_settings.create
|
||||||
|
end
|
||||||
|
Thread.new do
|
||||||
|
setting.create_records
|
||||||
|
end
|
||||||
|
end
|
||||||
|
locales = Site.first.in_use_locales rescue I18n.available_locales
|
||||||
|
|
||||||
|
scope "(:locale)", locale: Regexp.new(locales.join("|")) do
|
||||||
|
namespace :admin do
|
||||||
|
resources :file_managers do
|
||||||
|
collection do
|
||||||
|
get :settings
|
||||||
|
patch :update_settings
|
||||||
|
end
|
||||||
|
member do
|
||||||
|
get :module
|
||||||
|
put :rename
|
||||||
|
post :recover
|
||||||
|
post :save
|
||||||
|
post :recover_all
|
||||||
|
post :clean_trashes
|
||||||
|
post :get_trash_count
|
||||||
|
post :upload_file
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
get '/xhr/file_manager_download', to: 'file_managers#download'
|
||||||
|
get '/xhr/file_manager', to: 'file_managers#index_backend'
|
||||||
|
post '/xhr/file_manager_upload', to: 'file_managers#upload_index'
|
||||||
|
post "/xhr/file_manager_create_folder/(:path)", to: 'file_managers#create_folder', constraints: { path: /.*/ }
|
||||||
|
post "/xhr/file_manager_save/(:path)", to: 'file_managers#save', constraints: { path: /.*/ }
|
||||||
|
get '/xhr/file_manager/:path', to: 'file_managers#path', constraints: { path: /.+/ }
|
||||||
|
put '/xhr/file_manager/(:path)', to: 'file_managers#rename', constraints: { path: /.+/ }
|
||||||
|
post '/xhr/file_manager_upload/:path', to: 'file_managers#upload', constraints: { path: /.+/ }
|
||||||
|
delete '/xhr/file_manager/(:path)', to: 'file_managers#delete', constraints: { path: /.+/ }
|
||||||
|
end
|
||||||
|
end
|
|
@ -0,0 +1,106 @@
|
||||||
|
# -*- encoding: utf-8 -*-
|
||||||
|
$:.push File.expand_path("../lib", __FILE__)
|
||||||
|
# Maintain your gem's version:
|
||||||
|
require "file_manager/version"
|
||||||
|
bundle_update_flag = ARGV[0]=='update' || ARGV[0]=='install'
|
||||||
|
if bundle_update_flag
|
||||||
|
require "json"
|
||||||
|
env_pwd = ENV['PWD']
|
||||||
|
begin
|
||||||
|
require ::File.expand_path('app/helpers/bundler_helper.rb', env_pwd)
|
||||||
|
extend BundlerHelper
|
||||||
|
rescue LoadError
|
||||||
|
def bundler_with_clean_env
|
||||||
|
if block_given?
|
||||||
|
if Bundler.respond_to?(:with_unbundled_env)
|
||||||
|
Bundler.with_unbundled_env(&Proc.new)
|
||||||
|
else
|
||||||
|
Bundler.with_clean_env(&Proc.new)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
app_path = File.expand_path(__dir__)
|
||||||
|
if Dir.exist?("#{app_path}/modules")
|
||||||
|
template_path = env_pwd + '/app/templates'
|
||||||
|
all_template = Dir.glob(template_path+'/*/')
|
||||||
|
default_file_manager_widget_info = JSON.parse(File.read("#{app_path}/modules/file-manager/info.json"))["widgets"].sort_by{|h| h["filename"].to_i} rescue []
|
||||||
|
all_template.each do |folder|
|
||||||
|
if !folder.include?('mobile')
|
||||||
|
info_json_file = "#{folder}modules/file-manager/info.json"
|
||||||
|
if File.exist?(info_json_file)
|
||||||
|
#bundler_with_clean_env{system ('cp -f '+ app_path + '/modules/file-manager/show.html.erb ' + "#{folder}modules/file-manager/.")}
|
||||||
|
begin
|
||||||
|
file_text = File.read(info_json_file) rescue ""
|
||||||
|
encode_file_text = file_text.encode("UTF-8", "UTF-8", invalid: :replace, replace: "???")
|
||||||
|
next if (encode_file_text.include?("???") rescue true)
|
||||||
|
info = JSON.parse(encode_file_text) rescue {}
|
||||||
|
flag = (info.count != 0 rescue false)
|
||||||
|
if flag
|
||||||
|
puts "Checking file-manager widgets"
|
||||||
|
widget_info = info["widgets"].sort_by{|h| h["filename"].to_i} rescue []
|
||||||
|
update_flag = false
|
||||||
|
last_index = widget_info[-1]["filename"].match(/\d+/)[0].to_i rescue nil
|
||||||
|
if !last_index.nil?
|
||||||
|
idx_regex = /^(\d+[\. \t]*)|[ \t]+$/
|
||||||
|
default_file_manager_widget_info.each do |h|
|
||||||
|
name_without_index = h["name"]["zh_tw"].gsub(idx_regex,'')
|
||||||
|
widget_info_index = (widget_info.index{|hh| hh["name"]["zh_tw"].gsub(idx_regex,'') == name_without_index}||-1 rescue -1)
|
||||||
|
if widget_info_index == -1
|
||||||
|
update_flag = true
|
||||||
|
copy_h = h.dup
|
||||||
|
h.delete("force_cover")
|
||||||
|
last_index = last_index + 1
|
||||||
|
copy_h["filename"] = copy_h["filename"].sub(/\d+/){|ff| last_index.to_s}
|
||||||
|
copy_h["name"].keys.each do |locale|
|
||||||
|
copy_h["name"][locale] = copy_h["name"][locale].sub(/\d+/){|ff| last_index.to_s}
|
||||||
|
end
|
||||||
|
widget_info << copy_h
|
||||||
|
bundler_with_clean_env{%x[cp -f #{app_path}/modules/file-manager/_#{h["filename"]}.html.erb #{folder}modules/file-manager/_#{copy_h["filename"]}.html.erb]}
|
||||||
|
elsif h["force_cover"] == "true"
|
||||||
|
bundler_with_clean_env{%x[cp -f #{app_path}/modules/file-manager/_#{h["filename"]}.html.erb #{folder}modules/file-manager/_#{widget_info[widget_info_index]["filename"]}.html.erb]}
|
||||||
|
end
|
||||||
|
end
|
||||||
|
if update_flag
|
||||||
|
info["widgets"] = widget_info
|
||||||
|
puts "Writing json #{info["widgets"].count} in #{info_json_file}"
|
||||||
|
begin
|
||||||
|
info_json = JSON.pretty_generate(info).gsub(":[",":[\n").gsub(":{",":{\n")
|
||||||
|
rescue
|
||||||
|
info_json = info.to_s.gsub("=>",": \n")
|
||||||
|
end
|
||||||
|
File.open(info_json_file,"w+"){|f| f.write(info_json)}
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
rescue => e
|
||||||
|
puts e
|
||||||
|
puts "There has some error when checking file-manager widgets"
|
||||||
|
end
|
||||||
|
else
|
||||||
|
if !Dir.exist?(File.dirname(info_json_file)) && Dir.exist?(File.dirname(File.dirname(info_json_file)))
|
||||||
|
bundler_with_clean_env{system ('cp -r '+ app_path + '/modules/ ' + folder)}
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
Gem::Specification.new do |s|
|
||||||
|
s.name = "file-manager"
|
||||||
|
s.version = FileManager::VERSION
|
||||||
|
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
||||||
|
s.require_paths = ["lib"]
|
||||||
|
s.authors = ["Bohung Chiu"]
|
||||||
|
s.date = "2024-05-12"
|
||||||
|
s.description = "file-manager plugin."
|
||||||
|
s.email = ["bohung@rulingcom.com"]
|
||||||
|
s.files = ["{app,config,lib}/**/*", "MIT-LICENSE", "README.rdoc", "Rakefile"]
|
||||||
|
s.homepage = "https://orbitek.co"
|
||||||
|
s.licenses = ["MIT"]
|
||||||
|
s.summary = "file-manager plugin."
|
||||||
|
s.test_files = ["test/test_helper.rb", "test/controllers/index_test.rb"]
|
||||||
|
|
||||||
|
# s.add_dependency "charlock_holmes"
|
||||||
|
# s.add_dependency "custom_gallery"
|
||||||
|
end
|
|
@ -0,0 +1,4 @@
|
||||||
|
require "file_manager/engine"
|
||||||
|
module FileManager
|
||||||
|
|
||||||
|
end
|
|
@ -0,0 +1,29 @@
|
||||||
|
module FileManager
|
||||||
|
class Engine < ::Rails::Engine
|
||||||
|
initializer "file_manager" do
|
||||||
|
Rails.application.config.to_prepare do
|
||||||
|
FileManagerSetting.get_models_uploaders
|
||||||
|
OrbitApp.registration "FileManager", :type => "ModuleApp" do
|
||||||
|
module_label "file_manager.file_manager"
|
||||||
|
base_url File.expand_path File.dirname(__FILE__)
|
||||||
|
# widget_methods []
|
||||||
|
# widget_settings [{"enable_custom_widget_data"=>true}]
|
||||||
|
authorizable
|
||||||
|
# frontend_enabled
|
||||||
|
side_bar do
|
||||||
|
head_label_i18n 'file_manager.file_manager', icon_class: "icon-folder-open"
|
||||||
|
available_for "managers"
|
||||||
|
active_for_controllers (['admin/file_managers'])
|
||||||
|
head_link_path "admin_file_managers_path"
|
||||||
|
|
||||||
|
context_link 'file_manager.file_manager',
|
||||||
|
:link_path=>"admin_file_managers_path" ,
|
||||||
|
:priority=>1,
|
||||||
|
:active_for_action=>{'admin/file_managers'=>'index'},
|
||||||
|
:available_for => 'managers'
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -0,0 +1,3 @@
|
||||||
|
module FileManager
|
||||||
|
VERSION = "0.0.1"
|
||||||
|
end
|
|
@ -0,0 +1,8 @@
|
||||||
|
desc 'Remove duplicated bulletins (announcements) created by preview'
|
||||||
|
|
||||||
|
namespace :bulletin do
|
||||||
|
task :remove_preview_bulletins => [:environment] do
|
||||||
|
bulletins = Bulletin.where(is_preview: true)
|
||||||
|
bulletins.destroy_all
|
||||||
|
end
|
||||||
|
end
|
|
@ -0,0 +1,62 @@
|
||||||
|
require 'test_helper'
|
||||||
|
|
||||||
|
class IndexTest < ActionDispatch::IntegrationTest
|
||||||
|
def test_success_root
|
||||||
|
get '/'
|
||||||
|
|
||||||
|
assert_response :success
|
||||||
|
assert_template :index
|
||||||
|
directory = assigns(:directory)
|
||||||
|
assert_equal Dir.entries('.').sort, directory.map { |d| d[:relative] }.sort
|
||||||
|
end
|
||||||
|
|
||||||
|
def test_success_file
|
||||||
|
filename = 'Gemfile'
|
||||||
|
get "/#{filename}"
|
||||||
|
|
||||||
|
assert_response :success
|
||||||
|
assert_template :file
|
||||||
|
assert_equal File.read(filename), assigns(:file)
|
||||||
|
end
|
||||||
|
|
||||||
|
def test_out_of_directory_access
|
||||||
|
assert_raise(ArgumentError) do
|
||||||
|
get "/../../test"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def test_move
|
||||||
|
old_filename = 'tmp/myfile'
|
||||||
|
new_filename = 'tmp/movedfile'
|
||||||
|
FileUtils.touch old_filename
|
||||||
|
|
||||||
|
put "/#{old_filename}", params: { new_name: new_filename }
|
||||||
|
|
||||||
|
assert_response :success
|
||||||
|
assert File.exists?(new_filename)
|
||||||
|
assert !File.exists?(old_filename)
|
||||||
|
ensure
|
||||||
|
FileUtils.rm_rf old_filename
|
||||||
|
FileUtils.rm_rf new_filename
|
||||||
|
end
|
||||||
|
|
||||||
|
def test_delete_file
|
||||||
|
file_to_delete = 'tmp/new_file_to_delete'
|
||||||
|
FileUtils.touch file_to_delete
|
||||||
|
assert File.exists?(file_to_delete)
|
||||||
|
|
||||||
|
delete "/#{file_to_delete}"
|
||||||
|
assert_response :success
|
||||||
|
assert !File.exists?(file_to_delete)
|
||||||
|
end
|
||||||
|
|
||||||
|
def test_upload
|
||||||
|
filename = 'Gemfile'
|
||||||
|
uploaded_filename = "tmp/#{filename}"
|
||||||
|
assert !File.exists?(uploaded_filename)
|
||||||
|
post '/tmp/', params: { file: Rack::Test::UploadedFile.new(filename, 'text/plain') }
|
||||||
|
assert File.exists?(uploaded_filename)
|
||||||
|
ensure
|
||||||
|
FileUtils.rm_rf uploaded_filename
|
||||||
|
end
|
||||||
|
end
|
|
@ -0,0 +1,7 @@
|
||||||
|
ENV['RAILS_ENV'] ||= 'test'
|
||||||
|
require File.expand_path('../../config/environment', __FILE__)
|
||||||
|
require 'rails/test_help'
|
||||||
|
|
||||||
|
class ActiveSupport::TestCase
|
||||||
|
# Add more helper methods to be used by all tests here...
|
||||||
|
end
|
Loading…
Reference in New Issue