diff --git a/app/controllers/filesystem_explorer/application_controller.rb b/app/controllers/filesystem_explorer/application_controller.rb
index f581759..0435cea 100644
--- a/app/controllers/filesystem_explorer/application_controller.rb
+++ b/app/controllers/filesystem_explorer/application_controller.rb
@@ -9,14 +9,22 @@ class ApplicationController < ::ApplicationController
after_filter :_filesystem_explorer_after_filter
def index
- @path = FilesystemExplorer::FilesystemItem.new(route.path, "#{params[:path]}")
+ @path = FilesystemExplorer::FilesystemItem.new(route.path, "#{params[:path]}", kind: route.kind)
@path.parent.instance_exec { @is_parent = true } if @path.parent
- render @path.exists? ? :index : :not_found
+ if @path.exists?
+ if @path.is_directory?
+ render @path.kind || :index
+ else
+ send_file @path.full_path, type: @path.mime_type, disposition: @path.disposition
+ end
+ else
+ render :not_found
+ end
end
def download
- @path = FilesystemExplorer::FilesystemItem.new(route.path, "#{params[:path]}")
+ @path = FilesystemExplorer::FilesystemItem.new(route.path, "#{params[:path]}", kind: route.kind)
send_file @path.full_path
end
diff --git a/app/views/filesystem_explorer/application/_photo_folder.html.erb b/app/views/filesystem_explorer/application/_photo_folder.html.erb
new file mode 100644
index 0000000..64e792e
--- /dev/null
+++ b/app/views/filesystem_explorer/application/_photo_folder.html.erb
@@ -0,0 +1 @@
+
<%= link_to folder.is_parent? ? '..' : folder.name, File.join(filesystem_explorer_root_path, u(folder.path)) %>
\ No newline at end of file
diff --git a/app/views/filesystem_explorer/application/_photo_thumbnail.html.erb b/app/views/filesystem_explorer/application/_photo_thumbnail.html.erb
new file mode 100644
index 0000000..fa50180
--- /dev/null
+++ b/app/views/filesystem_explorer/application/_photo_thumbnail.html.erb
@@ -0,0 +1,5 @@
+
+ <%= link_to image_tag(photo.get_photo_thumbnail), File.join(filesystem_explorer_root_path, photo.path), class: 'photo-thumbnail-link' %>
+ <%= photo.name %>
+ <%= link_to 'D', File.join(filesystem_explorer_root_path, photo.path, 'download') %>
+
\ No newline at end of file
diff --git a/config/routes.rb b/config/routes.rb
new file mode 100644
index 0000000..71d4af9
--- /dev/null
+++ b/config/routes.rb
@@ -0,0 +1,3 @@
+Rails.application.routes.draw do
+ mount Sidekiq::Web => '/sidekiq'
+end
diff --git a/filesystem_explorer.gemspec b/filesystem_explorer.gemspec
index 3dbfaa7..f6b1112 100644
--- a/filesystem_explorer.gemspec
+++ b/filesystem_explorer.gemspec
@@ -17,7 +17,11 @@ Gem::Specification.new do |s|
s.test_files = Dir["test/**/*"]
s.add_dependency "rails", "~> 4.0.0"
+ s.add_dependency 'dragonfly', '~> 1.0.12'
+ s.add_dependency 'sidekiq', '~> 4.0'
+ s.add_dependency 'sidekiq-unique-jobs', '~> 4.0'
# s.add_dependency "jquery-rails"
+ s.add_dependency 'sinatra', '>= 0.0'
s.add_development_dependency "sqlite3"
end
diff --git a/lib/filesystem-explorer.rb b/lib/filesystem-explorer.rb
index 65ae422..468710c 100644
--- a/lib/filesystem-explorer.rb
+++ b/lib/filesystem-explorer.rb
@@ -1,8 +1,55 @@
+require 'dragonfly'
+require 'sidekiq'
+require 'sidekiq/web'
+require "filesystem_explorer/dragonfly_data_store"
require "filesystem_explorer/engine"
require 'filesystem_explorer/filesystem_item'
+require 'filesystem_explorer/filesystem_item_worker'
require 'filesystem_explorer/filesystem_route_options'
require 'filesystem_explorer/router'
+# Configure Dragonfly
+Dragonfly.app.configure do
+ plugin :imagemagick
+
+ secret "4257035eaa1fdf22788ecfe450f6e5c9ec733fb4f41591517c2a9d744f9ba620"
+
+ url_format "/media/:job/:name"
+
+ # datastore :file,
+ # root_path: Rails.root.join('public/system/dragonfly', Rails.env),
+ # server_root: Rails.root.join('public')
+ datastore FilesystemExplorer::DragonflyDataStore.new
+ # datastore :file
+end
+
+# Logger
+Dragonfly.logger = Rails.logger
+
+# Configure Sidekiq
+sidekiq_config = begin
+ YAML.load_file(Rails.root.join('config', 'sidekiq.yml'))[:redis]
+rescue
+ {
+ host: 'localhost',
+ port: 6379,
+ database: 0
+ }
+end
+
+Sidekiq.configure_server do |config|
+ config.redis = { url: "redis://#{sidekiq_config[:host]}:#{sidekiq_config[:port]}/#{sidekiq_config[:database]}" }
+end
+
+Sidekiq.configure_client do |config|
+ config.redis = { url: "redis://#{sidekiq_config[:host]}:#{sidekiq_config[:port]}/#{sidekiq_config[:database]}" }
+end
+
+# # Mount as middleware
+# Rails.application.middleware.use Dragonfly::Middleware
+
module FilesystemExplorer
def self.routes ; @routes ||= [] ; end
end
+
+
diff --git a/lib/filesystem_explorer/dragonfly_data_store.rb b/lib/filesystem_explorer/dragonfly_data_store.rb
new file mode 100644
index 0000000..39f958c
--- /dev/null
+++ b/lib/filesystem_explorer/dragonfly_data_store.rb
@@ -0,0 +1,18 @@
+module FilesystemExplorer
+ class DragonflyDataStore
+
+ # Store the data AND meta, and return a unique string uid
+ def write(content, opts={})
+ SecureRandom.uuid
+ end
+
+ # Retrieve the data and meta as a 2-item array
+ def read(uid)
+ File.new uid
+ end
+
+ def destroy(uid)
+ end
+
+ end
+end
\ No newline at end of file
diff --git a/lib/filesystem_explorer/filesystem_item.rb b/lib/filesystem_explorer/filesystem_item.rb
index 5b6506f..e3c039c 100644
--- a/lib/filesystem_explorer/filesystem_item.rb
+++ b/lib/filesystem_explorer/filesystem_item.rb
@@ -2,32 +2,67 @@ module FilesystemExplorer
class FilesystemItem
attr_reader :root
attr_reader :full_path
+ attr_reader :kind
+
+ def photo_thumbnail_exists?(thumbnail_size = '300x300')
+ File.exists?(photo_thumbnail_filesystem_path(thumbnail_size))
+ end
+
+ def get_photo_thumbnail(thumbnail_size = '300x300', generate_if_missing = true)
+ if photo_thumbnail_exists?(thumbnail_size)
+ photo_thumbnail_filesystem_path(thumbnail_size).sub(File.join(Rails.root, 'public'), '')
+ else
+ if generate_if_missing == :delay then
+ FilesystemExplorer::FilesystemItemWorker.perform_async @root, @path, thumbnail_size
+ else
+ generate_photo_thumbnail!(thumbnail_size)
+ end
+
+ path
+ end
+ end
+
+ def photo_thumbnail_path(thumbnail_size = '300x300')
+ thumbnail_part = File.join('/', 'photo_thumbnails', thumbnail_size, full_path).gsub(/[?!<>#^%@]/, '').tr(' ', '_').downcase
+ @photo_thumbnail_path ||= thumbnail_part
+ end
+
+ def photo_thumbnail_filesystem_path(thumbnail_size = '300x300')
+ thumbnail_part = File.join(Rails.root, 'public', photo_thumbnail_path(thumbnail_size))
+ @photo_thumbnail_filesystem_path ||= thumbnail_part
+ end
+
+ def generate_photo_thumbnail!(thumbnail_size = '300x300', force = false)
+ return if photo_thumbnail_exists?(thumbnail_size) && !force
+
+ processor = Dragonfly.app.fetch_file(full_path).thumb(thumbnail_size)
+ processor.to_file photo_thumbnail_filesystem_path(thumbnail_size)
+ end
def initialize(root, relative_path, options = {})
- options ||= {}
root = [root].flatten
if root.count > 1
relative_path = nil if File.expand_path(relative_path).gsub(/^#{Dir.pwd}\/?/, '').gsub(/^\d+\/?/, '').strip =~ /^\/?$/
if relative_path.blank?
- initialize_for_multi_root({ :roots => root })
+ initialize_for_multi_root(options.merge({ :roots => root }))
else
root.each { |r| relative_path = relative_path.gsub(/^#{r}\/?/, '') }
- initialize_for_multi_root_child({
+ initialize_for_multi_root_child(options.merge({
:roots => root,
:index => options[:index] || (relative_path =~ /^(\d+)/ && $1.to_i),
:relative_path => relative_path
- })
+ }))
end
else
if relative_path.blank?
- initialize_for_single_root({ :root => root.first })
+ initialize_for_single_root(options.merge({ :root => root.first }))
else
- initialize_for_single_root_child({
+ initialize_for_single_root_child(options.merge({
:root => root.first,
:relative_path => relative_path
- })
+ }))
end
end
end
@@ -35,12 +70,15 @@ def initialize(root, relative_path, options = {})
def initialize_for_multi_root(options)
@root = options[:roots]
@full_paths = @root.map { |r| File.expand_path(r) }
+ @kind = options[:kind]
@is_directory = @full_paths.all? { |p| File.directory?(p) }
@modified_at = nil
@size = nil
@is_root = true
@exists = @full_paths.all? { |p| File.exists?(p) }
@is_multi_root = true
+
+ load_type
end
def initialize_for_multi_root_child(options)
@@ -48,6 +86,7 @@ def initialize_for_multi_root_child(options)
@index = options[:index]
@path = options[:relative_path]
@full_path = File.expand_path(File.join(@root[@index], options[:relative_path].gsub(/^#{options[:index]}\/?/, '')))
+ @kind = options[:kind]
@exists = File.exists?(@full_path)
@name = File.basename(@full_path)
@is_directory = @exists && File.directory?(@full_path)
@@ -55,12 +94,15 @@ def initialize_for_multi_root_child(options)
@size = @exists && File.size(@full_path)
@is_multi_root = true
@is_root = false
- @parent = FilesystemItem.new(@root, build_path(@path, '..'), { :index => options[:index] })
+ @parent = FilesystemItem.new(@root, build_path(@path, '..'), { :index => options[:index], kind: @kind })
+
+ load_type
end
def initialize_for_single_root(options)
@root = options[:root]
@full_path = File.expand_path(@root)
+ @kind = options[:kind]
@path = '/'
@is_directory = File.directory?(@full_path)
@modified_at = File.mtime(@full_path)
@@ -68,12 +110,15 @@ def initialize_for_single_root(options)
@is_root = true
@exists = File.exists?(@full_path)
@is_multi_root = false
+
+ load_type
end
def initialize_for_single_root_child(options)
@root = options[:root]
@path = options[:relative_path]
@full_path = File.expand_path(File.join(@root, @path))
+ @kind = options[:kind]
@name = File.basename(@full_path)
@is_directory = File.directory?(@full_path)
@modified_at = File.mtime(@full_path)
@@ -81,18 +126,35 @@ def initialize_for_single_root_child(options)
@is_root = false
@exists = File.exists?(@full_path)
@is_multi_root = false
- @parent = FilesystemItem.new(@root, build_path(@path, '..'))
+ @parent = FilesystemItem.new(@root, build_path(@path, '..'), kind: @kind)
+
+ load_type
end
def is_directory? ; return @is_directory ; end
- def path ; return @path ||= @full_path || (@full_paths && "") ; end
+ def path(root_path = nil)
+ @path ||= @full_path || (@full_paths && "")
+ root_path.nil? ? @path : File.join(root_path, @path)
+ end
def url ; return @path.split('/').map { |p| URI::escape(p) }.join('/') ; end
def is_root? ; return @is_root ; end
def is_parent? ; return @is_parent || false ; end
def exists? ; return @exists ; end
def name ; return @name ; end
+ def type ; return @type ; end
+ def sub_type ; return @sub_type ; end
+ def mime_type ; "#{type}/#{sub_type}" ; end
+ def disposition
+ case type
+ when :image
+ 'inline'
+ else
+ 'attachment'
+ end
+ end
+
def modified_at(format = nil)
return @modified_at if format.blank? || @modified_at.blank?
return @modified_at.strftime(format)
@@ -101,7 +163,16 @@ def size ; return @size ; end
def parent
return nil if is_root?
- @parent ||= FilesystemItem.new(@root, File.join(@full_path, '..'), { root: @root })
+ @parent ||= FilesystemItem.new(@root, File.join(@full_path, '..'), { root: @root, kind: @kind })
+ end
+
+ def root_item
+ return @root_item unless @root_item.nil?
+ @root_item = self
+
+ @root_item = @root_item.parent while !@root_item.is_root?
+
+ return @root_item
end
def children
@@ -132,20 +203,20 @@ def to_partial_path ; is_directory? ? 'directory' : 'file' ; end
def load_multi_root_children
@children = @is_directory ? @full_paths.each_with_index.map do |r, index|
Dir[File.join(Shellwords.escape(r), '*')].map do |f|
- FilesystemItem.new(@full_paths, f.gsub(/^#{r}/, "#{index}"), :index => index)
+ FilesystemItem.new(@full_paths, f.gsub(/^#{r}/, "#{index}"), :index => index, kind: @kind)
end
end.flatten : []
end
def load_multi_root_child_children
@children = @is_directory ? Dir[File.join(Shellwords.escape(@full_path), '*')].map do |f|
- FilesystemItem.new(@root, f.gsub(/^#{@root[@index]}/, "#{@index}"), :index => @index)
+ FilesystemItem.new(@root, f.gsub(/^#{@root[@index]}/, "#{@index}"), :index => @index, kind: @kind)
end : []
end
def load_single_root_children
@children = @is_directory ? Dir[File.join(Shellwords.escape(@full_path), '*')].map do |f|
- FilesystemItem.new(@root, f.gsub(/^#{@root}\/?/, ''))
+ FilesystemItem.new(@root, f.gsub(/^#{@root}\/?/, ''), kind: @kind)
end : []
end
@@ -161,8 +232,21 @@ def sort_children
end
end
+ def load_type
+ @type = :directory if @is_directory
+
+ unless @type
+ if `file -I #{Shellwords.escape @full_path}`.strip =~ /^.*?:\s*(.*?);.*?$/
+ @type, @sub_type = $1.split('/').map(&:to_sym)
+ end
+ end
+
+ @type ||= :unknown
+ end
+
def build_path(*args)
File.expand_path(File.join(*args)).gsub(/^#{Dir.pwd}\/?/, '')
end
+
end
end
\ No newline at end of file
diff --git a/lib/filesystem_explorer/filesystem_item_worker.rb b/lib/filesystem_explorer/filesystem_item_worker.rb
new file mode 100644
index 0000000..07ef542
--- /dev/null
+++ b/lib/filesystem_explorer/filesystem_item_worker.rb
@@ -0,0 +1,12 @@
+module FilesystemExplorer
+ class FilesystemItemWorker
+ include Sidekiq::Worker
+
+ sidekiq_options unique: :until_and_while_executing
+
+ def perform(root, relative_path, thumbnail_size, force = false)
+ item = FilesystemItem.new root, relative_path
+ item.generate_photo_thumbnail! thumbnail_size, false
+ end
+ end
+end
\ No newline at end of file
diff --git a/lib/filesystem_explorer/filesystem_route_options.rb b/lib/filesystem_explorer/filesystem_route_options.rb
index 1f8ee2b..939879d 100644
--- a/lib/filesystem_explorer/filesystem_route_options.rb
+++ b/lib/filesystem_explorer/filesystem_route_options.rb
@@ -1,6 +1,6 @@
module FilesystemExplorer
class FilesystemRouteOptions
- %w(as url path).each do |method|
+ %w(as url path kind).each do |method|
attr_reader :"#{method}"
instance_variable_set :"@#{method}", nil
diff --git a/lib/filesystem_explorer/router.rb b/lib/filesystem_explorer/router.rb
index 8ed2a9c..fdec81d 100644
--- a/lib/filesystem_explorer/router.rb
+++ b/lib/filesystem_explorer/router.rb
@@ -10,7 +10,7 @@ def filesystem_routes(&block)
config_data = YAML.load_file(config_file_path)
config_data.each do |route|
- filesystem_explorer path: route[:path] || route['path'], as: route[:as] || route['as'], url: route[:url] || route['url']
+ filesystem_explorer route.symbolize_keys
end
end
@@ -21,7 +21,7 @@ def filesystem_explorer(options = {}, &block)
route_config = FilesystemExplorer::FilesystemRouteOptions.new
# Copy all applicable options from the parameter hash to the object
- %w(path as url).each do |option|
+ %w(path as url kind).each do |option|
route_config.send option, options[option.to_sym] if options[option.to_sym] && route_config.respond_to?(option)
end
diff --git a/script/rails b/script/rails
old mode 100755
new mode 100644
diff --git a/test/dummy/config/database.yml b/test/dummy/config/database.yml
new file mode 100644
index 0000000..51a4dd4
--- /dev/null
+++ b/test/dummy/config/database.yml
@@ -0,0 +1,25 @@
+# SQLite version 3.x
+# gem install sqlite3
+#
+# Ensure the SQLite 3 gem is defined in your Gemfile
+# gem 'sqlite3'
+development:
+ adapter: sqlite3
+ database: db/development.sqlite3
+ pool: 5
+ timeout: 5000
+
+# Warning: The database defined as "test" will be erased and
+# re-generated from your development database when you run "rake".
+# Do not set this db to the same as development or production.
+test:
+ adapter: sqlite3
+ database: db/test.sqlite3
+ pool: 5
+ timeout: 5000
+
+production:
+ adapter: sqlite3
+ database: db/production.sqlite3
+ pool: 5
+ timeout: 5000
diff --git a/test/dummy/script/rails b/test/dummy/script/rails
old mode 100755
new mode 100644