diff options
-rw-r--r-- | data/assets/js/admin/tabs/files.js | 116 | ||||
-rw-r--r-- | src/guff/apis.cr | 21 | ||||
-rw-r--r-- | src/guff/models/file.cr | 37 | ||||
-rw-r--r-- | src/guff/models/site.cr | 15 | ||||
-rw-r--r-- | src/guff/views/pages/admin.cr | 6 |
5 files changed, 160 insertions, 35 deletions
diff --git a/data/assets/js/admin/tabs/files.js b/data/assets/js/admin/tabs/files.js index 56fb6c9..bfd1943 100644 --- a/data/assets/js/admin/tabs/files.js +++ b/data/assets/js/admin/tabs/files.js @@ -49,7 +49,7 @@ jQuery(function($) { "<div class='btn-group btn-group-sm'>", "<a ", "href='#' ", - "class='btn btn-default btn-sm' ", + "class='crumb btn btn-default btn-sm' ", "data-path='%{path|h}' ", ">", "%{name|h}", @@ -64,8 +64,62 @@ jQuery(function($) { delete_confirm: [ "Are you sure you want to delete \"%{name|h}\"?", ], + + sites: [ + "<div class='btn-group btn-group-sm'>", + "<a ", + "href='#' ", + "class='btn btn-default' ", + "title='Choose site.' ", + "data-toggle='dropdown' ", + ">", + "Site: <span>%{name|h}</span>", + " ", + "<i class='fa fa-caret-down'></i>", + "</a>", + + "<ul class='dropdown-menu'>", + "%{body}", + "</ul>", + "</div><!-- btn-group -->", + ], + + site: [ + "<li class='%{css|h}'>", + "<a ", + "href='#' ", + "title='%{text|h}' ", + "data-id='%{site_id|h}' ", + "data-name='%{name|h}' ", + ">", + "%{name|h}", + "</a>", + "</li>", + ], }); + function get_selected_site() { + var site_id = $('#files').data('site_id'); + + return $.grep(DATA.sites, function(row) { + return site_id == row.site_id; + })[0]; + } + + function make_site_dropdown() { + var curr_site = get_selected_site(); + + return TEMPLATES.run('sites', { + name: curr_site.name, + body: $.map(DATA.sites, function(row) { + return TEMPLATES.run('site', $.extend({}, row, { + css: (row.site_id == curr_site.site_id) ? 'active' : '', + text: 'Show files for this site.', // FIXME + })); + }).join(''), + }); + } + function get_crumbs(path) { var r = []; @@ -83,17 +137,18 @@ jQuery(function($) { var NO_FILES = TEMPLATES.run('no_files'); function reload() { - var btn = $('#files-reload'), + var btns = $('#files-reload, #files-crumbs .btn'), list = $('#files'); // show loading - btn.toggleClass('disabled').find('.loading').toggleClass('hidden'); + btns.toggleClass('disabled').find('.loading').toggleClass('hidden'); // list.html(TEMPLATES.run('loading')); send('file/list', { - path: $('#files').data('path') + site_id: $('#files').data('site_id'), + path: $('#files').data('path'), }).always(function() { - btn.toggleClass('disabled').find('.loading').toggleClass('hidden'); + btns.toggleClass('disabled').find('.loading').toggleClass('hidden'); // list.html(''); }).fail(function(r) { var error = r.responseText; @@ -111,12 +166,14 @@ jQuery(function($) { // draw crumbs var crumbs = get_crumbs($('#files').data('path')); - $('#files-crumbs').html($.map(crumbs, function(crumb) { + $('#files-crumbs').html([ + make_site_dropdown() + ].concat($.map(crumbs, function(crumb) { return TEMPLATES.run('crumb', { path: crumb, name: crumb.replace(/^.*\/(.+)/, '$1'), }); - }).join('')); + })).join('')); // draw files $('#files').html((r.length > 0) ? $.map(r, function(row) { @@ -160,7 +217,7 @@ jQuery(function($) { return false; }); - $('#files-crumbs').on('click', 'a.btn', function() { + $('#files-crumbs').on('click', 'a.crumb', function() { $('#files').data('path', $(this).data('path')); reload(); @@ -168,6 +225,33 @@ jQuery(function($) { return false; }); + $('#files-crumbs').on('click', 'ul.dropdown-menu a', function() { + var data = $(this).data(), + ul = $(this).parents('ul'); + + // hide dropdown + $('body').trigger('click'); + + // refresh selection + ul.find('li.active').removeClass('active'); + $(this).parent('li').addClass('active'); + + // update button text + ul.prev('a').find('span').text(data.name); + + // save site id and path + $('#files').data({ + site_id: data.id, + path: '/', + }); + + // reload file list + reload(); + + // stop event + return false; + }); + $('#file-actions').on('click', 'a', function() { var action_id = $(this).data('id'), data = $('#files .active').data(); @@ -178,11 +262,12 @@ jQuery(function($) { if (data) { if (action_id == 'download') { if (data.url) - location.href = url; + location.href = data.url; } else if (action_id == 'move') { var dst_path = prompt(TEMPLATES.run('move_prompt', data), data.path); if (dst_path) { send('file/move', { + site_id: $('#files').data('site_id'), src_path: data.path, dst_path: dst_path, }).always(function() { @@ -205,7 +290,8 @@ jQuery(function($) { } else if (action_id == 'delete') { if (confirm(TEMPLATES.run('delete_confirm', data))) { send('file/delete', { - path: data.path, + site_id: $('#files').data('site_id'), + path: data.path, }).always(function() { // TODO: need loading handler }).fail(function(r) { @@ -253,7 +339,8 @@ jQuery(function($) { me.toggleClass('disabled').find('.loading').toggleClass('hidden'); send('file/mkdir', { - path: ($('#files').data('path') + '/' + path).replace(/\/\/+/, '/') + site_id: $('#files').data('site_id'), + path: ($('#files').data('path') + '/' + path).replace(/\/\/+/, '/') }).always(function() { // enable button, hide spinner me.toggleClass('disabled').find('.loading').toggleClass('hidden'); @@ -276,7 +363,12 @@ jQuery(function($) { return false; }); - $('#files').data('path', '/'); + // set defaults + $('#files').data({ + path: '/', + site_id: DATA.default_site_id, + }); + $('#files-reload').click(reload); // load users diff --git a/src/guff/apis.cr b/src/guff/apis.cr index bbedd3f..f5424dd 100644 --- a/src/guff/apis.cr +++ b/src/guff/apis.cr @@ -190,26 +190,32 @@ module Guff::APIs module FileAPI def do_file_list(params : HTTP::Params) - @context.models.file.list(URI.unescape(params["path"])) + @context.models.file.list( + site_id: params["site_id"].not_nil!.to_i64, + path: URI.unescape(params["path"]), + ) end def do_file_add(params : HTTP::Params) # TODO: upload data will be cached in context, then retreived in # file model @context.models.file.add( - URI.unescape(params["path"].not_nil!), - params["upload_data"].not_nil!, + site_id: params["site_id"].not_nil!.to_i64, + path: URI.unescape(params["path"].not_nil!), + upload_data: params["upload_data"].not_nil!, ) end def do_file_mkdir(params : HTTP::Params) - # TODO: uploaded data is cached in context, then retreived in file - # model - @context.models.file.mkdir(URI.unescape(params["path"].not_nil!)) + @context.models.file.mkdir( + site_id: params["site_id"].not_nil!.to_i64, + path: URI.unescape(params["path"].not_nil!), + ) end def do_file_move(params : HTTP::Params) @context.models.file.move( + site_id: params["site_id"].not_nil!.to_i64, src_path: URI.unescape(params["src_path"].not_nil!), dst_path: URI.unescape(params["dst_path"].not_nil!), ) @@ -217,7 +223,8 @@ module Guff::APIs def do_file_delete(params : HTTP::Params) @context.models.file.delete( - URI.unescape(params["path"].not_nil!), + site_id: params["site_id"].not_nil!.to_i64, + path: URI.unescape(params["path"].not_nil!), ) end end diff --git a/src/guff/models/file.cr b/src/guff/models/file.cr index b001c2f..f55037c 100644 --- a/src/guff/models/file.cr +++ b/src/guff/models/file.cr @@ -1,9 +1,9 @@ require "./model" class Guff::Models::FileModel < Guff::Models::Model - def list(path : String) + def list(site_id : Int64, path : String) # build absolute path - abs_path = expand_path(path) + abs_path = expand_path(site_id, path) # make sure directory exists unless Dir.exists?(abs_path) @@ -11,8 +11,12 @@ class Guff::Models::FileModel < Guff::Models::Model end # build base file path - base_path = File.expand_path(path) - base_url = File.join("/guff/api/file/download", base_path) + base_path = File.expand_path(path, "/") + base_url = File.join( + "/guff/api/file/download", + @context.models.site.get_name(site_id), + base_path + ) Dir.entries(abs_path).select { |file| # exclude hidden files @@ -34,14 +38,18 @@ class Guff::Models::FileModel < Guff::Models::Model } end - def add(path : String, upload_data : String) + def add( + site_id : Int64, + path : String, + upload_data : String + ) # TODO nil end - def mkdir(path : String) + def mkdir(site_id : Int64, path : String) # build absolute path - abs_path = expand_path(path) + abs_path = expand_path(site_id, path) # create directory Dir.mkdir(abs_path) @@ -49,10 +57,10 @@ class Guff::Models::FileModel < Guff::Models::Model nil end - def move(src_path : String, dst_path : String) + def move(site_id : Int64, src_path : String, dst_path : String) # build absolute paths - abs_src_path = expand_path(src_path) - abs_dst_path = expand_path(dst_path) + abs_src_path = expand_path(site_id, src_path) + abs_dst_path = expand_path(site_id, dst_path) # make sure src path exists unless File.exists?(abs_src_path) @@ -71,9 +79,9 @@ class Guff::Models::FileModel < Guff::Models::Model nil end - def delete(path : String) + def delete(site_id : Int64, path : String) # build absolute path - abs_path = expand_path(path) + abs_path = expand_path(site_id, path) if Dir.exists?(abs_path) Dir.rmdir(abs_path) @@ -87,14 +95,15 @@ class Guff::Models::FileModel < Guff::Models::Model nil end - private def expand_path(path : String) + private def expand_path(site_id : Int64, path : String) raise "invalid path" if path.includes?('\0') # return expanded path File.join( @context.config.data_dir, "files", - File.expand_path(path) + @context.models.site.get_name(site_id), + File.expand_path(path, "/") ) end end diff --git a/src/guff/models/site.cr b/src/guff/models/site.cr index 431911d..4672fa4 100644 --- a/src/guff/models/site.cr +++ b/src/guff/models/site.cr @@ -61,7 +61,16 @@ class Guff::Models::SiteModel < Guff::Models::Model JOIN themes b ON (b.theme_id = a.theme_id) WHERE a.site_id = ? - " + ", + + get_name: " + SELECT name + + FROM sites a + + WHERE site_id = ? + AND is_active + ", } def get_id(host : String?) : Int64? @@ -89,4 +98,8 @@ class Guff::Models::SiteModel < Guff::Models::Model def get(site_id : Int64) @context.dbs.ro.row(SQL[:get], [site_id.to_s]).not_nil! end + + def get_name(site_id : Int64) : String + @context.dbs.ro.one(SQL[:get_name], [site_id.to_s]).as(String) + end end diff --git a/src/guff/views/pages/admin.cr b/src/guff/views/pages/admin.cr index e385cea..f2296a8 100644 --- a/src/guff/views/pages/admin.cr +++ b/src/guff/views/pages/admin.cr @@ -99,7 +99,11 @@ class Guff::Views::Pages::Admin < Guff::Views::HTMLView end private def page_data - { post_types: POST_TYPES}.to_json + { + post_types: POST_TYPES, + sites: @context.models.site.get_sites, + default_site_id: @context.models.site.get_default_id, + }.to_json end ECR.def_to_s("src/views/pages/admin.ecr") |