alchemy-custom-model 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (82) hide show
  1. checksums.yaml +7 -0
  2. data/MIT-LICENSE +20 -0
  3. data/README.md +147 -0
  4. data/Rakefile +32 -0
  5. data/app/assets/config/alchemy_custom_model_manifest.js +3 -0
  6. data/app/assets/images/alchemy-custom-model/no_image.png +0 -0
  7. data/app/assets/javascripts/alchemy-custom-model/change_language.js +11 -0
  8. data/app/assets/javascripts/alchemy-custom-model/common_init.js +16 -0
  9. data/app/assets/javascripts/alchemy-custom-model/custom_admin_element_editor.js.coffee +25 -0
  10. data/app/assets/javascripts/alchemy-custom-model/custom_tiny_mce.js.coffee.erb +95 -0
  11. data/app/assets/javascripts/alchemy-custom-model/el_finder.js.coffee.erb +241 -0
  12. data/app/assets/javascripts/alchemy-custom-model/elfinder_ui_manifest.js +4 -0
  13. data/app/assets/javascripts/alchemy-custom-model/manifest.js +23 -0
  14. data/app/assets/javascripts/alchemy-custom-model/nested_attributes_sortable.js.coffee +25 -0
  15. data/app/assets/javascripts/alchemy-custom-model/select2filter.js +51 -0
  16. data/app/assets/javascripts/alchemy-custom-model/subobjects.js +98 -0
  17. data/app/assets/javascripts/alchemy-custom-model/total_page_elfinder.js.coffee +15 -0
  18. data/app/assets/stylesheets/alchemy-custom-model/custom_elfinder.css.scss +130 -0
  19. data/app/assets/stylesheets/alchemy-custom-model/custom_style.css.scss +228 -0
  20. data/app/assets/stylesheets/alchemy-custom-model/custom_tiny_mce.scss +6 -0
  21. data/app/assets/stylesheets/alchemy-custom-model/manifest.css +16 -0
  22. data/app/controllers/alchemy/custom/model/admin/base_controller.rb +189 -0
  23. data/app/controllers/alchemy/custom/model/admin/files_controller.rb +106 -0
  24. data/app/controllers/alchemy/pages_controller_decorator.rb +1 -0
  25. data/app/helpers/alchemy/custom/model/admin/base_helper.rb +379 -0
  26. data/app/helpers/alchemy/custom/model/base_helper.rb +18 -0
  27. data/app/helpers/alchemy/custom/model/custom_model_helper.rb +112 -0
  28. data/app/helpers/alchemy/pages_helper_decorator.rb +95 -0
  29. data/app/models/admin_override_to_param.rb +9 -0
  30. data/app/models/application_record.rb +9 -0
  31. data/app/views/alchemy/custom/model/admin/base/_buttons_tool.html.erb +16 -0
  32. data/app/views/alchemy/custom/model/admin/base/_gallery_item.html.erb +4 -0
  33. data/app/views/alchemy/custom/model/admin/base/_language_tree_select.html.erb +9 -0
  34. data/app/views/alchemy/custom/model/admin/base/_search_box.html.erb +19 -0
  35. data/app/views/alchemy/custom/model/admin/base/_seo.html.erb +8 -0
  36. data/app/views/alchemy/custom/model/admin/base/_table.html.erb +33 -0
  37. data/app/views/alchemy/custom/model/admin/base/_title.html.erb +3 -0
  38. data/app/views/alchemy/custom/model/admin/base/edit.html.erb +18 -0
  39. data/app/views/alchemy/custom/model/admin/base/index.html.erb +39 -0
  40. data/app/views/alchemy/custom/model/admin/base/new.html.erb +19 -0
  41. data/app/views/alchemy/custom/model/admin/files/error_notice.json.jbuilder +1 -0
  42. data/app/views/alchemy/custom/model/admin/files/ui.html.erb +13 -0
  43. data/config/initializers/elfinder_abilities.rb +1 -0
  44. data/config/locales/en.yml +11 -0
  45. data/config/locales/it.yml +21 -0
  46. data/config/routes.rb +8 -0
  47. data/lib/alchemy-custom-model.rb +29 -0
  48. data/lib/alchemy/custom/model/el_finder.rb +17 -0
  49. data/lib/alchemy/custom/model/el_finder/ability.rb +16 -0
  50. data/lib/alchemy/custom/model/el_finder/connector.rb +435 -0
  51. data/lib/alchemy/custom/model/el_finder/image.rb +35 -0
  52. data/lib/alchemy/custom/model/el_finder/path_name.rb +220 -0
  53. data/lib/alchemy/custom/model/el_finder/paths.rb +21 -0
  54. data/lib/alchemy/custom/model/el_finder/paths/active_record_reference.rb +50 -0
  55. data/lib/alchemy/custom/model/el_finder/paths/base.rb +298 -0
  56. data/lib/alchemy/custom/model/el_finder/paths/component_file.rb +59 -0
  57. data/lib/alchemy/custom/model/el_finder/paths/component_files.rb +34 -0
  58. data/lib/alchemy/custom/model/el_finder/paths/file.rb +23 -0
  59. data/lib/alchemy/custom/model/el_finder/paths/files.rb +25 -0
  60. data/lib/alchemy/custom/model/el_finder/paths/image.rb +42 -0
  61. data/lib/alchemy/custom/model/el_finder/paths/images.rb +31 -0
  62. data/lib/alchemy/custom/model/el_finder/paths/root.rb +24 -0
  63. data/lib/alchemy/custom/model/el_finder/volumes.rb +18 -0
  64. data/lib/alchemy/custom/model/el_finder/volumes/alchemy_file.rb +197 -0
  65. data/lib/alchemy/custom/model/el_finder/volumes/alchemy_files.rb +51 -0
  66. data/lib/alchemy/custom/model/el_finder/volumes/alchemy_images.rb +69 -0
  67. data/lib/alchemy/custom/model/el_finder/volumes/base.rb +149 -0
  68. data/lib/alchemy/custom/model/el_finder/volumes/component_attribute.rb +130 -0
  69. data/lib/alchemy/custom/model/engine.rb +40 -0
  70. data/lib/alchemy/custom/model/global_id_setter.rb +35 -0
  71. data/lib/alchemy/custom/model/model_decoration.rb +82 -0
  72. data/lib/alchemy/custom/model/pages_controller_dec.rb +132 -0
  73. data/lib/alchemy/custom/model/translation_scope.rb +18 -0
  74. data/lib/alchemy/custom/model/version.rb +7 -0
  75. data/lib/alchemy/touching_decorator.rb +12 -0
  76. data/lib/tasks/alchemy_custom_model_tasks.rake +41 -0
  77. data/vendor/elfinder/css/elfinder.full.css +5117 -0
  78. data/vendor/elfinder/css/elfinder.min.css +9 -0
  79. data/vendor/elfinder/css/theme.css +349 -0
  80. data/vendor/elfinder/js/elfinder.full.js +34270 -0
  81. data/vendor/elfinder/js/elfinder.min.js +25 -0
  82. metadata +194 -0
@@ -0,0 +1 @@
1
+ Alchemy.register_ability(Alchemy::Custom::Model::ElFinder::Ability)
@@ -0,0 +1,11 @@
1
+ en:
2
+ alchemy_custom_model:
3
+ you_are_editing_site_language: "You are editing the <strong>%{language}</strong> language of the site <strong>%{site}</strong>"
4
+ title_action: "%{action} %{model}"
5
+ actions:
6
+ new: New
7
+ edit: Edit
8
+ show: View
9
+ update: Edit
10
+ index: View all
11
+ create: New
@@ -0,0 +1,21 @@
1
+ it:
2
+ alchemy_custom_model:
3
+ you_are_editing_site_language: "Stai modificando la lingua <strong>%{language}</strong> del sito <strong>%{site}</strong>"
4
+ title_action: "%{action} %{model}"
5
+ actions:
6
+ new: Nuovo
7
+ edit: Modifica
8
+ show: Visualizza
9
+ update: Modifica
10
+ index: Visualizza tutti
11
+ create: Nuovo
12
+
13
+ elfinder:
14
+ alchemy_images:
15
+ volume: "Immagini"
16
+
17
+ sub_objects:
18
+ title_add: Aggiungi
19
+ minimize_all: Minimizza
20
+ maxzimize_all: Massimizza
21
+
data/config/routes.rb ADDED
@@ -0,0 +1,8 @@
1
+ Alchemy::Custom::Model::Engine.routes.draw do
2
+
3
+ namespace :admin do
4
+ match '/elfinder_ui' => 'files#ui', via: [:get]
5
+ match '/elfinder' => 'files#elfinder', via: [:get, :post]
6
+ end
7
+
8
+ end
@@ -0,0 +1,29 @@
1
+ require "alchemy_cms"
2
+ require "jquery/ui/rails"
3
+ require "el_finder"
4
+ require "friendly_id"
5
+ require "alchemy/custom/model/engine"
6
+
7
+
8
+ module Alchemy
9
+ module Custom
10
+ module Model
11
+ extend ActiveSupport::Autoload
12
+
13
+ autoload :GlobalIdSetter
14
+ autoload :ModelDecoration
15
+ autoload :TranslationScope
16
+ autoload :PagesControllerDec
17
+
18
+
19
+ mattr_accessor :base_admin_controller_class
20
+
21
+ @@base_admin_controller_class = 'Alchemy::Admin::BaseController'
22
+
23
+
24
+ def self.admin_controller_class
25
+ @@base_admin_controller_class.constantize
26
+ end
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,17 @@
1
+ module Alchemy
2
+ module Custom
3
+ module Model
4
+ module ElFinder
5
+ extend ActiveSupport::Autoload
6
+
7
+ autoload :Ability
8
+ autoload :Connector
9
+ autoload :Image
10
+ autoload :PathName
11
+
12
+ # eager_autoload do
13
+ # end
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,16 @@
1
+ module Alchemy::Custom::Model
2
+ module ElFinder
3
+ class Ability
4
+ include CanCan::Ability
5
+
6
+ def initialize(user)
7
+ if user.present? && user.is_admin?
8
+ can :usage, Alchemy::Custom::Model::ElFinder
9
+ can :ui_usage, Alchemy::Custom::Model::ElFinder
10
+ end
11
+ end
12
+ end
13
+ end
14
+ end
15
+
16
+
@@ -0,0 +1,435 @@
1
+ module Alchemy::Custom::Model::ElFinder
2
+ class Connector
3
+
4
+ VERSION = 2.1
5
+
6
+ # Valid commands to run.
7
+ # @see #run
8
+ VALID_COMMANDS = %w[open tree file paste upload search rm ls dim resize duplicate] #%w[archive duplicate edit extract mkdir mkfile open parents paste ping read rename resize rm tmb upload]
9
+
10
+ # Default options for instances.
11
+ # @see #initialize
12
+ DEFAULT_OPTIONS = {
13
+ :mime_handler => ::ElFinder::MimeType,
14
+ # :image_handler => ElFinder::Image,
15
+ :original_filename_method => lambda {|file| file.original_filename.respond_to?(:force_encoding) ? file.original_filename.force_encoding('utf-8') : file.original_filename},
16
+ :disabled_commands => [],
17
+ :allow_dot_files => true,
18
+ :upload_max_size => '50M',
19
+ # :upload_file_mode => 0644,
20
+ # :archivers => {},
21
+ # :extractors => {},
22
+ :home => 'Home',
23
+ :default_perms => {:read => true, :write => true, :rm => true, :hidden => false},
24
+ :perms => [],
25
+ :thumbs => false,
26
+ :thumbs_directory => '.thumbs',
27
+ :thumbs_size => 48,
28
+ :thumbs_at_once => 5,
29
+ # :tree_sub_folders => true, # list sub/sub folders in the tree
30
+ }
31
+
32
+ # Initializes new instance.
33
+ # @param [Hash] options Instance options. :url and :root options are required.
34
+ # @option options [String] :url Entry point of ElFinder router.
35
+ # @option options [String] :root Root directory of ElFinder directory structure.
36
+ # @see DEFAULT_OPTIONS
37
+ def initialize(options)
38
+ @options = DEFAULT_OPTIONS.merge(options)
39
+
40
+ @options[:disabled_commands] += ['mkdir', 'rename']
41
+
42
+ raise(ArgumentError, "Missing required :volumes option") unless @options.key?(:volumes)
43
+ # raise(ArgumentError, "Missing required :url option") unless @options.key?(:url)
44
+ # raise(ArgumentError, "Missing required :root option") unless @options.key?(:root)
45
+ raise(ArgumentError, "Mime Handler is invalid") unless mime_handler.respond_to?(:for)
46
+ # raise(ArgumentError, "Image Handler is invalid") unless image_handler.nil? || ([:size, :resize, :thumbnail].all? {|m| image_handler.respond_to?(m)})
47
+
48
+ @volumes = options[:volumes]
49
+ @root = PathName.new(options[:root])
50
+
51
+ @headers = {}
52
+ @response = {}
53
+ end
54
+
55
+ # of initialize
56
+
57
+ # Runs request-response cycle.
58
+ # @param [Hash] params Request parameters. :cmd option is required.
59
+ # @option params [String] :cmd Command to be performed.
60
+ # @see VALID_COMMANDS
61
+ def run(params)
62
+ @params = params.dup
63
+ @headers = {}
64
+ @response = {}
65
+ @response[:errorData] = {}
66
+ @file = nil
67
+
68
+ if VALID_COMMANDS.include?(@params[:cmd])
69
+
70
+ if @options[:thumbs]
71
+ @thumb_directory = @root + @options[:thumbs_directory]
72
+ @thumb_directory.mkdir unless @thumb_directory.exist?
73
+ raise(RuntimeError, "Unable to create thumbs directory") unless @thumb_directory.directory?
74
+ end
75
+
76
+ @current = @params[:current] ? from_hash(@params[:current]) : nil
77
+ @volume, @target = (@params[:target] and !@params[:target].empty?) ? from_hash(@params[:target]) : [nil, nil]
78
+ if params[:targets]
79
+ @targets = @params[:targets].map {|t| from_hash(t)}
80
+ end
81
+
82
+ send("_#{@params[:cmd]}")
83
+ else
84
+ invalid_request
85
+ end
86
+
87
+ @response.delete(:errorData) if @response[:errorData].empty?
88
+
89
+ return @headers, @response, @file
90
+ end
91
+
92
+ # of run
93
+
94
+ #
95
+ def to_hash(pathname)
96
+ # note that '=' are removed
97
+ Base64.urlsafe_encode64(pathname.path.to_s).chomp.tr("=\n", "")
98
+ end
99
+
100
+ # of to_hash
101
+
102
+ #
103
+ def from_hash(hash)
104
+ volume = @volumes.find {|v| v.contains?(hash)}
105
+ if volume
106
+ [volume, volume.decode(hash)]
107
+ else
108
+ @response[:error] = 'errFileNotFound'
109
+ nil
110
+ end
111
+ # restore missing '='
112
+ # len = hash.length % 4
113
+ # hash += '==' if len == 1 or len == 2
114
+ # hash += '=' if len == 3
115
+
116
+ # decoded_hash = Base64.urlsafe_decode64(hash)
117
+ # decoded_hash = decoded_hash.respond_to?(:force_encoding) ? decoded_hash.force_encoding('utf-8') : decoded_hash
118
+ # pathname = @root + decoded_hash
119
+ # rescue ArgumentError => e
120
+ # if e.message != 'invalid base64'
121
+ # raise
122
+ # end
123
+ # nil
124
+ end
125
+
126
+ # of from_hash
127
+
128
+ # @!attribute [w] options
129
+ # Options setter.
130
+ # @param value [Hash] Options to be merged with instance ones.
131
+ # @return [Hash] Updated options.
132
+ def options=(value = {})
133
+ value.each_pair do |k, v|
134
+ @options[k.to_sym] = v
135
+ end
136
+ @options
137
+ end
138
+
139
+ # of options=
140
+
141
+ ################################################################################
142
+
143
+ protected
144
+
145
+ def _file
146
+ if @volume && @target
147
+ path = @volume.pathname(@target)
148
+ @file = {path: path}
149
+ end
150
+ end
151
+
152
+ def _search
153
+ type = @params.delete(:type) {"full_search"}
154
+ q = @params.delete(:q)
155
+ if @params[:mimes]
156
+ type = 'mimes'
157
+ q = @params[:mimes]
158
+ end
159
+
160
+ @response[:files] = @volume.search(type: type, q: q)
161
+
162
+ end
163
+
164
+ ##
165
+ # Funzione lanciata praticamente all'upload dei file, dato che non abbiamo problemi di duplicati
166
+ # dato che sono tutti in database, inviamo sempre un vuoto
167
+ # nel caso dobbiamo implementare invece questo: https://github.com/Studio-42/elFinder/wiki/Client-Server-API-2.1#ls
168
+ def _ls
169
+ @response[:list] = []
170
+ end
171
+
172
+ #
173
+ def _open(volume = nil, target = nil, payload: {})
174
+ if @params[:init]
175
+ _open_init
176
+ return
177
+ end
178
+
179
+ volume ||= @volume
180
+ target ||= @target
181
+
182
+ if volume.nil?
183
+ _open(@volumes[0], nil, payload: payload)
184
+ return
185
+ end
186
+
187
+ if target.nil?
188
+ _open(volume, '.', payload: payload)
189
+ return
190
+ end
191
+
192
+ # if perms_for(target)[:read] == false
193
+ # @response[:error] = 'Access Denied'
194
+ # return
195
+ # end
196
+
197
+ if target.file?
198
+ command_not_implemented
199
+ elsif target.directory?
200
+ @response[:cwd] = volume.cwd(target)
201
+ @response[:files] = volume.files(target)
202
+ @response.merge!(payload)
203
+
204
+ # @response[:cdc] = target.children.
205
+ # reject{ |child| perms_for(child)[:hidden]}.
206
+ # sort_by{|e| e.basename.to_s.downcase}.map{|e| cdc_for(e)}.compact
207
+
208
+ # if @params[:tree]
209
+ # @response[:tree] = {
210
+ # :name => @options[:home],
211
+ # :hash => to_hash(@root),
212
+ # :dirs => tree_for(@root),
213
+ # }.merge(perms_for(@root))
214
+ # end
215
+ @response[:options] = {}
216
+ if @params[:init]
217
+ @response = init_request(@response, volume: volume)
218
+ else
219
+ #se non siamo in init, allora inseriamo l'elenco dei comandi che il volume aperto non supporta
220
+ @response[:options][:disabled] = init_request(@response.dup, volume: volume)[:options][:disabled]
221
+ end
222
+
223
+ else
224
+ @response[:error] = "Directory does not exist"
225
+ _open(@root) if File.directory?(@root)
226
+ end
227
+
228
+ end
229
+
230
+ # of open
231
+
232
+
233
+ def _dim
234
+ if @target.is_a? ElFinder::Paths::Image
235
+ @response[:dim] = @target.dim
236
+ else
237
+ @response[:error] = "Operazione non supportata"
238
+ end
239
+ end
240
+
241
+
242
+ def _tree(volume = nil, target = nil)
243
+ volume ||= @volume
244
+ target ||= @target
245
+
246
+ @response[:api] = VERSION
247
+ @response[:tree] = volume.tree(target)
248
+
249
+ end
250
+
251
+ def init_request(payload, volume:)
252
+ payload[:api] = VERSION
253
+ # payload[:disabled] = @options[:disabled_commands]
254
+ payload[:options] = {
255
+ :dotFiles => @options[:allow_dot_files],
256
+ :uplMaxSize => @options[:upload_max_size],
257
+ :uploadMaxConn => -1, #configurazione per cui non deve spezzare in chucks i files
258
+ :archives => [], #@options[:archivers].keys,
259
+ :extract => [], #@options[:extractors].keys,
260
+ :url => @options[:url],
261
+ disabled: @options[:disabled_commands] + volume.disabled_commands
262
+ }
263
+
264
+ payload
265
+ end
266
+
267
+ def _open_init
268
+ @response[:cwd] = @volumes[0].cwd
269
+
270
+ files = []
271
+ @volumes.each {|v| files.concat(v.files)}
272
+ @response[:files] = files
273
+ @response = init_request(@response, volume: @volumes[0])
274
+ end
275
+
276
+ # of open_init
277
+
278
+ def _paste
279
+
280
+ moved_files = []
281
+ @targets.to_a.each do |src_v, src_f|
282
+
283
+ #scopro il volume di destinazione
284
+ dst_v, dst_f = from_hash(@params[:dst])
285
+
286
+ if dst_v.is_a?(Volumes::ComponentAttribute)
287
+ moved_files << dst_v.copy(src_f)
288
+ else
289
+ @response[:error] ||= 'Copia non consentita'
290
+ end
291
+
292
+ end
293
+ @params[:tree] = true
294
+
295
+ # dest_vlm = @targets.first[0]
296
+ dest_vlm = from_hash(@params[:dst])[0]
297
+
298
+ dest_target = dest_vlm.decode(dest_vlm.encode('.'))
299
+
300
+ _open(dest_vlm, dest_target, payload: {moved_files: moved_files.collect {|v| dest_vlm.path_info(v)}})
301
+ end
302
+
303
+ # of paste
304
+
305
+
306
+ def _rm
307
+ @response[:removed] = @targets.to_a.collect do |src_v, src_f|
308
+ src_v.rm(src_f)
309
+ src_v.encode(src_f.path.to_s)
310
+ end
311
+ end
312
+
313
+ #
314
+ def _resize
315
+ if @target.is_image?
316
+
317
+ file = @target.file
318
+ case @params[:mode]
319
+ when 'resize'
320
+ file = file.thumb("#{@params[:width]}x#{@params[:height]}!")
321
+ when 'crop'
322
+ file = file.thumb("#{@params[:width]}x#{@params[:height]}+#{@params[:x]}+#{@params[:y]}")
323
+ when 'rotate'
324
+
325
+ command = []
326
+ command << "-background #{@params[:bg]}" unless @params[:bg].blank?
327
+ command << "-rotate #{@params[:degree]}"
328
+
329
+ file = file.convert(command.join(' '))
330
+ else
331
+ @response[:error] = "Mode:#{@params[:mode]} not implemented"
332
+ return
333
+ end
334
+ file = file.convert("-quality #{@params[:quality]}") if @params[:quality].to_i < 100
335
+ @target.file = file
336
+ @target.save
337
+
338
+ @response[:changed] = [to_hash(@target)]
339
+ _open(@volume, @volume.root_path)
340
+ else
341
+ @response[:error] = "Unable to resize file. It does not exist"
342
+ end
343
+ end
344
+
345
+ # of resize
346
+
347
+ def _duplicate
348
+
349
+ @targets.each do |v, f|
350
+ unless f.is_image?
351
+ @response[:error] = "#{f.name} non è un'immagine"
352
+ return
353
+ end
354
+ end
355
+
356
+ @targets.each do |v, f|
357
+ unless v.duplicable?(f)
358
+ @response[:error] = "#{f.name} non può essere duplicato"
359
+ return
360
+ end
361
+ end
362
+
363
+ added = @targets.collect do |v, t|
364
+ v.path_info(v.duplicate(t))
365
+ end
366
+
367
+ @response[:added] = added
368
+ # _open(@targets.first[0], @targets.first[0].root_path)
369
+ end
370
+
371
+ # of duplicate
372
+
373
+
374
+ #
375
+ def _upload
376
+ if @volume && @target
377
+ response = @volume.upload(@target, @params[:upload])
378
+ @response.merge!(response)
379
+ # _open(@volume, @target)
380
+ else
381
+ @response[:error] = "errUploadCommon"
382
+ end
383
+ # if perms_for(@current)[:write] == false
384
+ # @response[:error] = 'Access Denied'
385
+ # return
386
+ # end
387
+ # select = []
388
+ # @params[:upload].to_a.each do |file|
389
+ # if file.respond_to?(:tempfile)
390
+ # the_file = file.tempfile
391
+ # else
392
+ # the_file = file
393
+ # end
394
+ # if upload_max_size_in_bytes > 0 && File.size(the_file.path) > upload_max_size_in_bytes
395
+ # @response[:error] ||= "Some files were not uploaded"
396
+ # @response[:errorData][@options[:original_filename_method].call(file)] = 'File exceeds the maximum allowed filesize'
397
+ # else
398
+ # dst = @current + @options[:original_filename_method].call(file)
399
+ # the_file.close
400
+ # src = the_file.path
401
+ # FileUtils.mv(src, dst.fullpath)
402
+ # FileUtils.chmod @options[:upload_file_mode], dst
403
+ # select << to_hash(dst)
404
+ # end
405
+ # end
406
+ # @response[:select] = select unless select.empty?
407
+ # _open(@current)
408
+ end
409
+
410
+ # of upload
411
+
412
+ private
413
+
414
+ def mime_handler
415
+ @options[:mime_handler]
416
+ end
417
+
418
+ #
419
+ # def image_handler
420
+ # @options[:image_handler]
421
+ # end
422
+
423
+ #
424
+ def invalid_request
425
+ @response[:error] = "Invalid command '#{@params[:cmd]}'"
426
+ end
427
+
428
+ # of invalid_request
429
+
430
+ #
431
+ def command_not_implemented
432
+ @response[:error] = "Command '#{@params[:cmd]}' not yet implemented"
433
+ end # of command_not_implemented
434
+ end
435
+ end