neofiles 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (32) hide show
  1. checksums.yaml +7 -0
  2. data/LICENSE +1 -0
  3. data/README.md +417 -0
  4. data/Rakefile +40 -0
  5. data/app/assets/images/neofiles/loading.gif +0 -0
  6. data/app/assets/images/neofiles/swf-thumb-100x100.png +0 -0
  7. data/app/assets/images/neofiles/watermark.png +0 -0
  8. data/app/assets/javascripts/neofiles/index.js.coffee +3 -0
  9. data/app/assets/javascripts/neofiles/jquery.fileupload.js +1128 -0
  10. data/app/assets/javascripts/neofiles/jquery.iframe-transport.js +172 -0
  11. data/app/assets/javascripts/neofiles/jquery.neofiles.js.coffee +191 -0
  12. data/app/assets/stylesheets/neofiles/index.css.scss +3 -0
  13. data/app/assets/stylesheets/neofiles/neofiles.css.scss +149 -0
  14. data/app/controllers/concerns/neofiles/not_found.rb +21 -0
  15. data/app/controllers/neofiles/admin_controller.rb +228 -0
  16. data/app/controllers/neofiles/admin_test_controller.rb +5 -0
  17. data/app/controllers/neofiles/files_controller.rb +28 -0
  18. data/app/controllers/neofiles/images_controller.rb +130 -0
  19. data/app/helpers/neofiles/neofiles_helper.rb +188 -0
  20. data/app/models/neofiles/file.rb +319 -0
  21. data/app/models/neofiles/file_chunk.rb +18 -0
  22. data/app/models/neofiles/image.rb +119 -0
  23. data/app/models/neofiles/swf.rb +45 -0
  24. data/app/views/neofiles/admin/_file_compact.html.haml +85 -0
  25. data/app/views/neofiles/admin/file_compact.html.haml +1 -0
  26. data/app/views/neofiles/admin_test/file_compact.erb +7 -0
  27. data/config/locales/ru.yml +21 -0
  28. data/config/routes.rb +1 -0
  29. data/lib/neofiles.rb +94 -0
  30. data/lib/neofiles/engine.rb +33 -0
  31. data/lib/neofiles/version.rb +3 -0
  32. metadata +131 -0
@@ -0,0 +1,45 @@
1
+ # Special case of Neofiles::File for dealing with SWF movies.
2
+ #
3
+ # Alongside usual file things, stores width & height of Flash clip.
4
+ #
5
+ require_dependency 'image_spec'
6
+
7
+ class Neofiles::Swf < Neofiles::File
8
+
9
+ class SwfFormatException < Exception; end
10
+
11
+ field :width, type: Integer
12
+ field :height, type: Integer
13
+
14
+ before_save :compute_dimensions
15
+
16
+ # Return array with width & height decorated with singleton function to_s returning 'WxH' string.
17
+ def dimensions
18
+ dim = [width, height]
19
+ def dim.to_s
20
+ join 'x'
21
+ end
22
+ dim
23
+ end
24
+
25
+ # Overrides parent "admin views" with square 100x100 Flash thumbnail.
26
+ def admin_compact_view(view_context)
27
+ view_context.neofiles_link self, view_context.tag(:img, src: view_context.image_path('neofiles/swf-thumb-100x100.png')), target: '_blank'
28
+ end
29
+
30
+
31
+
32
+ private
33
+
34
+ # Store dimensions on #save.
35
+ def compute_dimensions
36
+ return unless @file
37
+
38
+ spec = ::ImageSpec.new(@file)
39
+ if spec.content_type != 'application/x-shockwave-flash'
40
+ raise SwfFormatException.new I18n.t('neofiles.swf_type_incorrect', content_type: spec.content_type)
41
+ end
42
+ self.width = spec.width
43
+ self.height = spec.height
44
+ end
45
+ end
@@ -0,0 +1,85 @@
1
+ - widget_id ||= input_name.gsub(/\]\[|[^-a-zA-Z0-9:.]/, '_').sub(/_$/, '')
2
+ - desc = file.try(:description).try(:to_s).try!(:strip).try!(:truncate, 15).presence
3
+ - empty_desc = t 'neofiles.views.no_description'
4
+
5
+ - classes = []
6
+ - classes << 'neofiles-image-compact-empty' unless file
7
+ - classes << 'neofiles-image-compact-with-description' if file && with_desc
8
+ .neofiles-image-compact(id=widget_id data-url=neofiles_file_save_path){class: classes}
9
+
10
+ - if error.present?
11
+ %p.neofiles-error
12
+ = error
13
+
14
+ - if file
15
+ .neofiles-image-compact-view= file.admin_compact_view self
16
+
17
+ %a(href=neofiles_file_remove_path class="neofiles-image-compact-remove")
18
+ %i.icon-remove.glyphicon.glyphicon-remove
19
+
20
+ - if Neofiles.is_admin? self
21
+ - options_form = capture do
22
+ - if file.description.present?
23
+ %p= file.description
24
+
25
+ %p= t 'neofiles.views.created_at', local_date: l(file.created_at, format: t('neofiles.views.local_date_format'))
26
+
27
+ - file_options = []
28
+ - if file.respond_to? :dimensions
29
+ - file_options << "#{file.dimensions.join('×').html_safe} px"
30
+ - file_options << "#{number_to_human_size file.length, precision: 0}"
31
+ %p= file_options.join(', ')
32
+
33
+ - if file.owner_type.present? || file.owner_id.present?
34
+ %p #{t 'neofiles.views.owner'}: #{file.owner_type}#{file.owner_id}
35
+
36
+ - if file.respond_to? :no_wm
37
+ %p.checkbox
38
+ - uniq_name = "#{widget_id}_no_wm"
39
+ %label(for=uniq_name)
40
+ = check_box_tag uniq_name, '1', file.no_wm, class: 'neofiles-image-compact-nowm', data: {update_url: neofiles_file_update_path}
41
+ = t 'neofiles.views.no_wm'
42
+
43
+ %a(href="#" tabindex="0" class="neofiles-image-compact-options" data-toggle="popover" data-trigger="click" data-placement="top" data-html="true" data-animation="false" data-title="#{file.filename}" data-content=options_form)
44
+ %i.icon-wrench.glyphicon.glyphicon-wrench
45
+
46
+ - if with_desc
47
+ - description_form = capture do
48
+ - area_name = "#{widget_id}_description"
49
+ = text_area_tag area_name, file.description, class: 'neofiles-image-compact-description-input', rows: 10, data: {update_url: neofiles_file_update_path}
50
+ .text-center
51
+ %button.btn.btn-primary.btn-small.neofiles-image-compact-description-save= t 'neofiles.views.save'
52
+
53
+ - popover_template = capture do
54
+ .popover.neofiles-image-compact-description-popover(role="tooltip")
55
+ .arrow
56
+ %h3.popover-title
57
+ .popover-content
58
+
59
+ .neofiles-image-compact-description
60
+ %a.neofiles-image-compact-description-handle(href="#" data-empty=empty_desc data-toggle="popover" data-trigger="click" data-placement="bottom" data-html="true" data-animation="false" data-title="#{t 'neofiles.views.description'}" data-content=description_form data-template=popover_template){class: ('neofiles-image-compact-description-empty' unless desc)}= desc || empty_desc
61
+
62
+ %span.neofiles-image-compact-upload-icon
63
+ %i.icon-upload.glyphicon.glyphicon-upload
64
+
65
+ - common_options = {id: nil, disabled: disabled}
66
+ = hidden_field_tag input_name, file.try(:id), common_options.merge(class: 'neofiles-image-transfer-input')
67
+ = hidden_field_tag 'neofiles[id]', file.try(:id), common_options
68
+ = hidden_field_tag 'neofiles[input_name]', input_name, common_options
69
+ = hidden_field_tag 'neofiles[widget_id]', widget_id, common_options
70
+
71
+ = hidden_field_tag 'neofiles[clean_remove]', clean_remove ? 1 : 0, common_options
72
+ = hidden_field_tag 'neofiles[append_create]', file ? 0 : (append_create ? 1 : 0), common_options
73
+ = hidden_field_tag 'neofiles[multiple]', multiple ? 1 : 0, common_options
74
+ = hidden_field_tag 'neofiles[with_desc]', with_desc ? 1 : 0, common_options
75
+
76
+ = file_field_tag 'neofiles[file][]', id: nil, class: 'neofiles-image-compact-file', disabled: disabled, multiple: multiple && !file
77
+
78
+ :javascript
79
+ $(function() {
80
+ $("##{widget_id}").image();
81
+ });
82
+
83
+ - if append_create and file
84
+ = render partial: 'file_compact', locals: {file: nil, input_name: input_name, widget_id: widget_id + '_ap', clean_remove: clean_remove, append_create: true, error: nil, disabled: disabled, multiple: multiple, with_desc: with_desc}
85
+
@@ -0,0 +1 @@
1
+ = render partial: 'file_compact', locals: {file: @file, widget_id: @widget_id, input_name: @input_name, error: @error, clean_remove: @clean_remove, append_create: @append_create, disabled: @disabled, multiple: @multiple, with_desc: @with_desc}
@@ -0,0 +1,7 @@
1
+ <div id="zzz_cont"></div>
2
+
3
+ <script type="text/javascript">
4
+ $(function() {
5
+ $("#zzz_cont").load("<%= neofiles_file_compact_path(id: @file_id, input_name: 'zzz') %>");
6
+ });
7
+ </script>
@@ -0,0 +1,21 @@
1
+ ru:
2
+ neofiles:
3
+ 403_access_denied: Ошибка 403: недостаточно прав для получения файла в таком формате
4
+ 404_not_found: Ошибка 404: файл не найден
5
+ file_not_found: Файл не найден
6
+ data_not_passed: Не переданы данные для сохранения
7
+ file_not_passed: Не передан файл для сохранения
8
+ id_not_passed: Не передан ID
9
+ unknown_file_type: Не могу создать файл неизвестного типа %{type}
10
+ swf_type_incorrect: Файл с расширением SWF определяется как %{content_type}
11
+ mini_magick_error: Не могу обработать переданное изображение
12
+ unsupported_image_type: Формат картинок %{type} не поддерживается
13
+
14
+ views:
15
+ no_description: Без описания
16
+ local_date_format: %d.%m.%Y <span class="muted text-muted">в %H:%M</span>
17
+ created_at: Создан %{local_date}
18
+ owner: Владелец
19
+ no_wm: не ставить водяной знак
20
+ save: Сохранить
21
+ description: Описание
@@ -0,0 +1 @@
1
+ Neofiles::Engine.routes.draw(&Neofiles.routes_proc)
@@ -0,0 +1,94 @@
1
+ require 'neofiles/engine'
2
+
3
+ module Neofiles
4
+ # Attach Neofiles specific routes in your routes.rb file:
5
+ #
6
+ # instance_eval &Neofiles.routes_proc
7
+ mattr_accessor :routes_proc
8
+ @@routes_proc = proc do
9
+ scope 'neofiles', module: :neofiles do
10
+ # admin routes
11
+ get '/admin/file_compact/', to: 'admin#file_compact', as: :neofiles_file_compact
12
+ post '/admin/file_save/', to: 'admin#file_save', as: :neofiles_file_save
13
+ post '/admin/file_remove/', to: 'admin#file_remove', as: :neofiles_file_remove
14
+ post '/admin/file_update/', to: 'admin#file_update', as: :neofiles_file_update
15
+
16
+ # admin routes for WYSIWYG editor Redactor.js
17
+ post '/admin/redactor-upload/', to: 'admin#redactor_upload', as: :neofiles_redactor_upload
18
+ get '/admin/redactor-list/:owner_type/:owner_id/:type', to: 'admin#redactor_list', as: :neofiles_redactor_list
19
+
20
+ # web frontend for serving images and other files
21
+ get '/serve/:id', to: 'files#show', as: :neofiles_file
22
+ get '/serve-image/:id(/:format(/c:crop)(/q:quality))', to: 'images#show', as: :neofiles_image, constraints: {format: /[1-9]\d*x[1-9]\d*/, crop: /[10]/, quality: /[1-9]\d*/}
23
+
24
+ # serve images w/o watermark - path has prefix nowm_ to let Nginx ot other web server not cache these queries,
25
+ # unlike usual /serve-image/:id
26
+ get '/nowm-serve-image/:id', to: 'images#show', as: :neofiles_image_nowm, defaults: {nowm: true}
27
+ end
28
+ end
29
+
30
+ # Calculate image dimensions after resize. Returns [w, h] or nil if some info is lacking (e.g. image passed as ID so
31
+ # no width & height available).
32
+ #
33
+ # image_file - Neofiles::Image, ID or Hash
34
+ # width, height - max width and height after resize
35
+ # resize_options - {crop: '1'/'0'}, @see Neofiles::ImagesController#show
36
+ #
37
+ # Can call ImageMagick.
38
+ #
39
+ def resized_image_dimensions(image_file, width, height, resize_options)
40
+ # dimensions are equal to requested ones if cropping
41
+ return width, height if crop_requested? resize_options
42
+
43
+ # otherwise ask ImageMagick - prepare input vars...
44
+ image_file = Neofiles::Image.find image_file if image_file.is_a?(String)
45
+ return nil if image_file.nil?
46
+
47
+ if image_file.is_a? Neofiles::Image
48
+ image_file_width = image_file.width
49
+ image_file_height = image_file.height
50
+ else
51
+ image_file_width = image_file[:width]
52
+ image_file_height = image_file[:height]
53
+ end
54
+
55
+ return if image_file_width.blank? || image_file_height.blank?
56
+
57
+ # ... construct request ...
58
+ command = MiniMagick::CommandBuilder.new(:convert) # convert input file...
59
+ command.size([image_file_width, image_file_height].join 'x') # with the given dimensions...
60
+ command.xc('white') # and filled with whites...
61
+ command.resize([width, height].join 'x') # to fit in the given rectangle...
62
+ command.push('info:-') # return info about the resulting file
63
+
64
+ # ... and send it to ImageMagick
65
+ # the result will be: xc:white XC 54x100 54x100+0+0 16-bit DirectClass 0.070u 0:00.119
66
+ # extract dimensions and return them as array of integers
67
+ MiniMagick::Image.new(nil, nil).run(command).match(/ (\d+)x(\d+) /).values_at(1, 2).map(&:to_i)
68
+
69
+ rescue
70
+ nil
71
+ end
72
+
73
+ # Is request params hash contains crop request?
74
+ def crop_requested?(params)
75
+ params[:crop].present? and params[:crop] != '0'
76
+ end
77
+
78
+ # Is request params hash contains quality request?
79
+ def quality_requested?(params)
80
+ !!quality_requested(params)
81
+ end
82
+
83
+ # The quality value requested, from request params hash.
84
+ def quality_requested(params)
85
+ params[:quality].to_i if params[:quality].present? and params[:quality] != '0'
86
+ end
87
+
88
+ # Is current user considered "admin"? "Admin" means the user can fetch images w/o watermarks.
89
+ def is_admin?(context)
90
+ Rails.application.config.neofiles.try(:current_admin).try(:call, context)
91
+ end
92
+
93
+ module_function :resized_image_dimensions, :crop_requested?, :quality_requested?, :quality_requested, :is_admin?
94
+ end
@@ -0,0 +1,33 @@
1
+ module Neofiles
2
+ class Engine < ::Rails::Engine
3
+ config.autoload_paths << File.expand_path('../..', __FILE__)
4
+ config.neofiles = ActiveSupport::OrderedOptions.new
5
+
6
+ # mongo specific settings
7
+ config.neofiles.mongo_files_collection = 'files.files'
8
+ config.neofiles.mongo_chunks_collection = 'files.chunks'
9
+ config.neofiles.mongo_client = 'neofiles'
10
+ config.neofiles.mongo_default_chunk_size = 4.megabytes
11
+
12
+ # image related settings
13
+ config.neofiles.image_rotate_exif = true # rotate image, if exif contains orientation info
14
+ config.neofiles.image_clean_exif = true # clean all exif fields on save
15
+ config.neofiles.image_max_dimensions = nil # resize huge originals to meaningful size: [w, h], {width: w, height: h}, wh
16
+ config.neofiles.image_max_crop_width = 2000 # users can request resizing only up to this width
17
+ config.neofiles.image_max_crop_height = 2000 # users can request resizing only up to this height
18
+
19
+ # default watermarker — redefine to set special watermarking logic
20
+ # by default, watermark only images larger than 300x300 with watermark at the bottom center, taken from file
21
+ # /app/assets/images/neofiles/watermark.png
22
+ config.neofiles.watermarker = ->(image, no_watermark: false, watermark_width:, watermark_height:){
23
+ if watermark_width < 300 || watermark_height < 300 || no_watermark
24
+ return image.to_blob
25
+ end
26
+
27
+ image.composite(MiniMagick::Image.open(Rails.root.join('app', 'assets', 'images', 'neofiles', 'watermark.png'))) do |c|
28
+ c.gravity 'south'
29
+ c.geometry '200x+0+20'
30
+ end.to_blob
31
+ }
32
+ end
33
+ end
@@ -0,0 +1,3 @@
1
+ module Neofiles
2
+ VERSION = "1.0.0"
3
+ end
metadata ADDED
@@ -0,0 +1,131 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: neofiles
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.0.0
5
+ platform: ruby
6
+ authors:
7
+ - Konanykhin Ilya
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2016-11-23 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: rails
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - '='
18
+ - !ruby/object:Gem::Version
19
+ version: 4.0.0
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - '='
25
+ - !ruby/object:Gem::Version
26
+ version: 4.0.0
27
+ - !ruby/object:Gem::Dependency
28
+ name: mongoid
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - '='
32
+ - !ruby/object:Gem::Version
33
+ version: 5.0.0
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - '='
39
+ - !ruby/object:Gem::Version
40
+ version: 5.0.0
41
+ - !ruby/object:Gem::Dependency
42
+ name: ruby-imagespec
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - '='
46
+ - !ruby/object:Gem::Version
47
+ version: 0.4.1
48
+ type: :runtime
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - '='
53
+ - !ruby/object:Gem::Version
54
+ version: 0.4.1
55
+ - !ruby/object:Gem::Dependency
56
+ name: mini_magick
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - '='
60
+ - !ruby/object:Gem::Version
61
+ version: 3.7.0
62
+ type: :runtime
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - '='
67
+ - !ruby/object:Gem::Version
68
+ version: 3.7.0
69
+ description: 'Library for managing files: creating & storing, linking to file owners,
70
+ serving files from MongoDB'
71
+ email:
72
+ - rails@neolabs.kz
73
+ executables: []
74
+ extensions: []
75
+ extra_rdoc_files: []
76
+ files:
77
+ - LICENSE
78
+ - README.md
79
+ - Rakefile
80
+ - app/assets/images/neofiles/loading.gif
81
+ - app/assets/images/neofiles/swf-thumb-100x100.png
82
+ - app/assets/images/neofiles/watermark.png
83
+ - app/assets/javascripts/neofiles/index.js.coffee
84
+ - app/assets/javascripts/neofiles/jquery.fileupload.js
85
+ - app/assets/javascripts/neofiles/jquery.iframe-transport.js
86
+ - app/assets/javascripts/neofiles/jquery.neofiles.js.coffee
87
+ - app/assets/stylesheets/neofiles/index.css.scss
88
+ - app/assets/stylesheets/neofiles/neofiles.css.scss
89
+ - app/controllers/concerns/neofiles/not_found.rb
90
+ - app/controllers/neofiles/admin_controller.rb
91
+ - app/controllers/neofiles/admin_test_controller.rb
92
+ - app/controllers/neofiles/files_controller.rb
93
+ - app/controllers/neofiles/images_controller.rb
94
+ - app/helpers/neofiles/neofiles_helper.rb
95
+ - app/models/neofiles/file.rb
96
+ - app/models/neofiles/file_chunk.rb
97
+ - app/models/neofiles/image.rb
98
+ - app/models/neofiles/swf.rb
99
+ - app/views/neofiles/admin/_file_compact.html.haml
100
+ - app/views/neofiles/admin/file_compact.html.haml
101
+ - app/views/neofiles/admin_test/file_compact.erb
102
+ - config/locales/ru.yml
103
+ - config/routes.rb
104
+ - lib/neofiles.rb
105
+ - lib/neofiles/engine.rb
106
+ - lib/neofiles/version.rb
107
+ homepage: http://neoweb.kz
108
+ licenses:
109
+ - MIT
110
+ metadata: {}
111
+ post_install_message:
112
+ rdoc_options: []
113
+ require_paths:
114
+ - lib
115
+ required_ruby_version: !ruby/object:Gem::Requirement
116
+ requirements:
117
+ - - ">="
118
+ - !ruby/object:Gem::Version
119
+ version: '0'
120
+ required_rubygems_version: !ruby/object:Gem::Requirement
121
+ requirements:
122
+ - - ">="
123
+ - !ruby/object:Gem::Version
124
+ version: '0'
125
+ requirements: []
126
+ rubyforge_project:
127
+ rubygems_version: 2.5.1
128
+ signing_key:
129
+ specification_version: 4
130
+ summary: Serves and manages files & images.
131
+ test_files: []