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,51 @@
1
+ module Alchemy::Custom::Model::ElFinder
2
+ module Volumes
3
+ class AlchemyFiles < AlchemyFile
4
+
5
+ def initialize(options = {root: '/files', name: 'Alchemy Library - Files', id: 'alchemy_library_files', url: '/'})
6
+ super
7
+ end
8
+
9
+ def files(target = '.')
10
+ super(root_path)
11
+ end
12
+
13
+ def decode(hash)
14
+ super do |path|
15
+ Paths::File.new(@root, path, volume: self)
16
+ end
17
+ end
18
+
19
+ def upload(target, upload)
20
+ super do |file|
21
+ f = ::Alchemy::Attachment.create(
22
+ file: file
23
+ )
24
+ root_path.build_file_path(f)
25
+ end
26
+ end
27
+
28
+ def search(type:, q:)
29
+
30
+ super do
31
+ search_query_build(klass: ::Alchemy::Attachment, type: type, q: q, mime_attribute: :file_mime_type)
32
+ end
33
+
34
+ end
35
+
36
+ def disabled_commands
37
+
38
+ super + ['rm']
39
+
40
+ end
41
+
42
+ private
43
+
44
+ def root_path
45
+ Paths::Files.new(@root, '.', volume: self)
46
+ end
47
+
48
+ end
49
+ end
50
+ end
51
+
@@ -0,0 +1,69 @@
1
+ module Alchemy::Custom::Model::ElFinder
2
+ module Volumes
3
+ class AlchemyImages < AlchemyFile
4
+
5
+ def initialize(options = {root: '/images', name: acm_t("volume",scope:'elfinder.alchemy_images'), id: 'alchemy_library_images', url: '/'})
6
+ super
7
+ end
8
+
9
+ def files(target = '.')
10
+ super(root_path)
11
+ end
12
+
13
+ def decode(hash)
14
+ super do |path|
15
+ Paths::Image.new(@root, path, volume: self)
16
+ end
17
+ end
18
+
19
+ def upload(target, upload)
20
+ super do |file|
21
+ img = ::Alchemy::Picture.new(
22
+ image_file: file
23
+ )
24
+ img.name = img.humanized_name
25
+ img.save!
26
+ root_path.build_file_path(img)
27
+ end
28
+ end
29
+
30
+ def search(type:, q:)
31
+
32
+ super do
33
+ search_query_build(klass: ::Alchemy::Picture, type: type, q: q, mime_attribute: :file_mime_type)
34
+ end
35
+
36
+ end
37
+
38
+ def disabled_commands
39
+
40
+ super + ['rm']
41
+
42
+ end
43
+
44
+ def duplicate(t)
45
+
46
+ new_path = Rails.root.join('tmp', "copy_#{File.basename(t.name)}")
47
+
48
+ ::FileUtils.cp(t.file.path, new_path)
49
+
50
+ img = ::Alchemy::Picture.new(
51
+ image_file: new_path
52
+ )
53
+ img.name = img.humanized_name
54
+ img.save
55
+ root_path.build_file_path(img)
56
+ end
57
+
58
+ def duplicable?(target)
59
+ true
60
+ end
61
+
62
+
63
+ def root_path
64
+ Paths::Images.new(@root, '.', volume: self)
65
+ end
66
+
67
+ end
68
+ end
69
+ end
@@ -0,0 +1,149 @@
1
+ module Alchemy::Custom::Model
2
+ module ElFinder
3
+ module Volumes
4
+ class Base
5
+
6
+ include TranslationScope
7
+
8
+ attr_reader :id, :name, :root, :url
9
+
10
+ def initialize(options)
11
+ [:id, :name, :root, :url].each do |opt|
12
+ raise(ArgumentError, "Missing required #{opt} option") unless options.key?(opt)
13
+ end
14
+
15
+ @id = options[:id]
16
+ @name = options[:name]
17
+ @root = options[:root]
18
+ @url = options[:url]
19
+
20
+ # @options = {
21
+ # upload_file_mode: 0644,
22
+ # original_filename_method: lambda {|file| file.original_filename.respond_to?(:force_encoding) ? file.original_filename.force_encoding('utf-8') : file.original_filename}
23
+ # }
24
+ end
25
+
26
+ def contains?(hash)
27
+ hash.start_with?("#{@id}_")
28
+ end
29
+
30
+ def cwd(target = '.')
31
+ # {
32
+ # "name" : "Images", // (String) name of file/dir. Required
33
+ # "hash" : "l0_SW1hZ2Vz", // (String) hash of current file/dir path, first symbol must be letter, symbols before _underline_ - volume id, Required.
34
+ # "phash" : "l0_Lw", // (String) hash of parent directory. Required except roots dirs.
35
+ # "mime" : "directory", // (String) mime type. Required.
36
+ # "ts" : 1334163643, // (Number) file modification time in unix timestamp. Required.
37
+ # "date" : "30 Jan 2010 14:25", // (String) last modification time (mime). Depricated but yet supported. Use ts instead.
38
+ # "size" : 12345, // (Number) file size in bytes
39
+ # "dirs" : 1, // (Number) Only for directories. Marks if directory has child directories inside it. 0 (or not set) - no, 1 - yes. Do not need to calculate amount.
40
+ # "read" : 1, // (Number) is readable
41
+ # "write" : 1, // (Number) is writable
42
+ # "locked" : 0, // (Number) is file locked. If locked that object cannot be deleted and renamed
43
+ # "tmb" : 'bac0d45b625f8d4633435ffbd52ca495.png' // (String) Only for images. Thumbnail file name, if file do not have thumbnail yet, but it can be generated than it must have value "1"
44
+ # "alias" : "files/images", // (String) For symlinks only. Symlink target path.
45
+ # "thash" : "l1_c2NhbnMy", // (String) For symlinks only. Symlink target hash.
46
+ # "dim" : "640x480" // (String) For images - file dimensions. Optionally.
47
+ # "volumeid" : "l1_" // (String) Volume id. For root dir only.
48
+ # }
49
+ # {
50
+ # name: @name,
51
+ # hash: encode('.'),
52
+ # mime: 'directory',
53
+ # ts: File.mtime(@root).to_i,
54
+ # size: 0,
55
+ # dirs: 0,
56
+ # read: 1,
57
+ # write: 1,
58
+ # locked: 0,
59
+ # volumeid: "#{@id}_"
60
+ # }
61
+ path_info(PathName.new(@root, target))
62
+ end
63
+
64
+ # def path_info(target)
65
+ # is_dir = File.directory?(target.realpath)
66
+ # mime = is_dir ? 'directory' : ElFinder::MimeType.for(target.realpath)
67
+ # name = @name if target.is_root?
68
+ # name ||= target.basename.to_s
69
+ #
70
+ # dirs = 0
71
+ # if is_dir
72
+ # # check if has sub directories
73
+ # dirs = 1 if Dir[File.join(target.realpath, '*/')].count > 0
74
+ # end
75
+ #
76
+ # size = 0
77
+ # unless is_dir
78
+ # size = File.size(target.realpath)
79
+ # end
80
+ #
81
+ # result = {
82
+ # name: name,
83
+ # hash: encode(target.path.to_s),
84
+ # mime: mime,
85
+ # ts: File.mtime(target.realpath).to_i,
86
+ # size: size,
87
+ # dirs: dirs,
88
+ # read: 1,
89
+ # write: 1,
90
+ # locked: 0
91
+ # }
92
+ # if target.is_root?
93
+ # result[:volumeid] = "#{@id}_"
94
+ # else
95
+ # result[:phash] = encode(target.dirname.path.to_s)
96
+ # end
97
+ #
98
+ # result
99
+ # end
100
+
101
+
102
+ def decode(hash)
103
+ hash = hash.slice(("#{@id}_".length)..-1) if hash.start_with?("#{@id}_")
104
+ hash = hash.tr('-_.', '+/=')
105
+ # restore missing '='
106
+ len = hash.length % 4
107
+ hash += '==' if len == 1 or len == 2
108
+ hash += '=' if len == 3
109
+ path = Base64.strict_decode64(hash)
110
+
111
+ if block_given?
112
+ yield path
113
+ else
114
+ PathName.new(@root, path)
115
+ end
116
+
117
+ end
118
+
119
+ def encode(path)
120
+ # creates hash for the path
121
+ # path = ElFinder::Pathname.new(@root, path).path.to_s
122
+ hash = Base64.strict_encode64(path)
123
+ hash.tr!('+/=', '-_.')
124
+ hash.gsub!(/\.+\Z/, '')
125
+ "#{@id}_#{hash}"
126
+ end
127
+
128
+ # def files(target = '.')
129
+ # target = ElFinder::Pathname.new(@root, target)
130
+ # files = []
131
+ # # files = target.children.map {|p| path_info(p)}
132
+ # files << cwd(target)
133
+ # files
134
+ # end
135
+
136
+ def pathname(target)
137
+ raise "Absolute Path for file"
138
+ end
139
+
140
+ ##
141
+ # Elenco comandi disabilitati
142
+ def disabled_commands
143
+ []
144
+ end
145
+
146
+ end
147
+ end
148
+ end
149
+ end
@@ -0,0 +1,130 @@
1
+ module Alchemy::Custom::Model::ElFinder
2
+ module Volumes
3
+ class ComponentAttribute < AlchemyFile
4
+
5
+ attr_accessor :record, :attribute, :file_link_ref, :tags
6
+
7
+ ##
8
+ # Elenco opzioni da utilizzare per la configurazione
9
+ # - attribute = relazione dell'oggetto a cui si riferisce il collegamento del volume (la join table per esempio)
10
+ # - object = oggetto identificato con un global_id signed
11
+ # - file_link_ref = rispetto ad un'istanza della relazione come fare a trovare il valore del record alchemy(Picture o Attachment),
12
+ # nel caso avessimo più metodi da chiamare possiamo spezzarlo con il . fra un metodo e l'altro
13
+ # il programma dovrà spezzare per il . e usare dig per chiamare i valori definitivi
14
+ # - tags = Array di stringhe per i tag da associare direttamente ai files caricati
15
+ def initialize(opts = {})
16
+
17
+ self.record = GlobalID::Locator.locate_signed opts.delete(:object)
18
+ self.attribute = opts.delete(:attribute)
19
+ self.file_link_ref = opts.delete(:file_link_ref)
20
+ self.tags = opts.delete(:tags) {[]}
21
+
22
+ identificativo_volume = "component_#{self.attribute}" #"_#{record.id}"
23
+
24
+ # identificativo_volume='test_semplice_nome'
25
+
26
+ # unless self.record.send(self.attribute).klass.new.respond_to?(:alchemy_file_instance)
27
+ # raise "Attenzione, non è stata impostata la relazione per collegare la join table con l'elemento il record di alchemy [:alchemy_file_instance]"
28
+ # end
29
+
30
+ super({root: "/#{identificativo_volume}", name: 'Elementi associati', id: "#{identificativo_volume}", url: '/'})
31
+ end
32
+
33
+ def files(target = '.')
34
+ super(root_path)
35
+ end
36
+
37
+ def root_path
38
+ Paths::ComponentFiles.new(@root, '.', volume: self)
39
+ end
40
+
41
+ def copy(src)
42
+ new_rec = associate_record_to_active_instance(src.active_record_instance)
43
+ root_path.build_file_path(new_rec)
44
+ end
45
+
46
+ def decode(hash)
47
+ super do |path|
48
+ Paths::ComponentFile.new(@root, path, volume: self)
49
+ end
50
+ end
51
+
52
+ def upload(target, upload)
53
+ super do |file|
54
+
55
+ if remote_alchemy_relation == ::Alchemy::Picture
56
+ img = ::Alchemy::Picture.new(
57
+ image_file: file
58
+ )
59
+ img.name = img.humanized_name
60
+ img.save
61
+
62
+ #lego l'immagine ora al prodotto
63
+ new_rec = associate_record_to_active_instance(img)
64
+
65
+ root_path.build_file_path(new_rec)
66
+ else
67
+ raise "TO override"
68
+ end
69
+ end
70
+ end
71
+
72
+
73
+ ##
74
+ # Indica l'oggetto finale a cui viene legata la join table del componente
75
+ def remote_alchemy_relation
76
+ attribute_class.reflect_on_association(:alchemy_file_instance).klass
77
+ end
78
+
79
+ def attribute_class
80
+ self.record.send(self.attribute).klass
81
+ end
82
+
83
+ def search(type:, q:)
84
+
85
+ query_search = search_query_build(klass: remote_alchemy_relation, type: type, q: q, mime_attribute: :image_file_format)
86
+
87
+ self.record.send(self.attribute).joins(:alchemy_file_instance).merge(query_search).uniq.collect do |p|
88
+ path_info(root_path.build_file_path(p))
89
+ end
90
+
91
+ end
92
+
93
+
94
+ def duplicate(t)
95
+ new_path = Rails.root.join('tmp', "copy_#{File.basename(t.name)}")
96
+
97
+ ::FileUtils.cp(t.file.path, new_path)
98
+
99
+ img = ::Alchemy::Picture.new(
100
+ image_file: new_path
101
+ )
102
+ img.name = img.humanized_name
103
+ img.save
104
+
105
+ new_rec = associate_record_to_active_instance(img)
106
+ root_path.build_file_path(new_rec)
107
+ end
108
+
109
+
110
+ def duplicable?(target)
111
+ remote_alchemy_relation == ::Alchemy::Picture
112
+ end
113
+
114
+ ##
115
+ # Nel caso del componente, rm vuol dire dissociare il target elementi
116
+ def rm(target)
117
+ target.active_record_instance.destroy
118
+ end
119
+
120
+ private
121
+
122
+ def associate_record_to_active_instance(rec)
123
+ self.record.class.transaction do
124
+ rec.update_attributes(tag_list: (self.tags + rec.tag_list).flatten.compact)
125
+ self.record.send(self.attribute).create(:alchemy_file_instance => rec)
126
+ end
127
+ end
128
+ end
129
+ end
130
+ end
@@ -0,0 +1,40 @@
1
+ module Alchemy
2
+ module Custom
3
+ module Model
4
+ class Engine < ::Rails::Engine
5
+ isolate_namespace Alchemy::Custom::Model
6
+ #engine_name 'alchemy'
7
+
8
+ config.autoload_paths << config.root.join('lib')
9
+
10
+ initializer "alchemy_custom_model.assets.precompile" do |app|
11
+ app.config.assets.precompile << 'alchemy_custom_model_manifest.js'
12
+ app.config.assets.precompile << 'elfinder/css/elfinder.min.css'
13
+ app.config.assets.precompile << 'elfinder/css/theme.css'
14
+ app.config.assets.paths << config.root.join("vendor")
15
+
16
+ end
17
+
18
+ config.to_prepare do
19
+
20
+ Alchemy::PagesController.include(PagesControllerDec)
21
+
22
+ # load degli helpers per alchemy
23
+ [
24
+ Alchemy::Custom::Model::Engine.root.join('app', 'helpers', 'alchemy', 'pages_helper_decorator.rb')
25
+ ].each do |f|
26
+ Rails.configuration.cache_classes ? require(f) : load(f)
27
+ end
28
+
29
+
30
+ end
31
+
32
+
33
+ # initializer "alchemy_richmedia_essences.register_ability" do
34
+ # Alchemy.register_ability Alchemy::Richmedia::Essences::Ability
35
+ # end
36
+
37
+ end
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,35 @@
1
+ require 'active_support/concern'
2
+
3
+ module Alchemy
4
+ module Custom
5
+ module Model
6
+ module GlobalIdSetter
7
+ extend ActiveSupport::Concern
8
+
9
+ module ClassMethods
10
+
11
+ private
12
+
13
+ ##
14
+ # Metodo per ricevere un global id ed associare il relativo modello
15
+ #
16
+ def global_id_setter(field)
17
+
18
+ alias_method "_old_#{field}=".to_sym, "#{field}=".to_sym
19
+
20
+ define_method "#{field}=" do |v|
21
+ if v.is_a?(String)
22
+ v = GlobalID::Locator.locate_signed v
23
+ end
24
+ send("_old_#{field}=", v)
25
+ end
26
+
27
+
28
+ end
29
+
30
+
31
+ end
32
+ end
33
+ end
34
+ end
35
+ end