udongo 5.1.0 → 5.2.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/app/assets/javascripts/backend/application.js +1 -1
- data/app/controllers/backend/assets_controller.rb +52 -0
- data/app/models/asset.rb +37 -0
- data/app/models/asset_image.rb +172 -0
- data/app/models/concerns/imageable.rb +13 -0
- data/app/models/image.rb +11 -0
- data/app/uploaders/asset_uploader.rb +36 -0
- data/app/views/backend/assets/_filter.html.erb +17 -0
- data/app/views/backend/assets/_form.html.erb +28 -0
- data/app/views/backend/assets/edit.html.erb +5 -0
- data/app/views/backend/assets/index.html.erb +48 -0
- data/app/views/backend/assets/new.html.erb +4 -0
- data/app/views/backend/snippets/edit.html.erb +1 -1
- data/app/views/backend/users/index.html.erb +2 -2
- data/app/views/layouts/backend/_top_navigation.html.erb +1 -0
- data/changelog.md +9 -0
- data/config/locales/en.yml +2 -1
- data/config/locales/en_backend.yml +6 -0
- data/config/locales/en_general.yml +1 -0
- data/config/locales/fr.yml +1 -1
- data/config/locales/nl.yml +2 -1
- data/config/locales/nl_backend.yml +5 -0
- data/config/locales/nl_general.yml +1 -0
- data/config/routes.rb +2 -0
- data/db/migrate/20170225144523_create_assets.rb +14 -0
- data/db/migrate/20170228183831_create_images.rb +18 -0
- data/lib/udongo/configs/assets.rb +10 -0
- data/lib/udongo/version.rb +1 -1
- data/readme.md +11 -0
- data/spec/factories/addresses.rb +6 -0
- data/spec/factories/admins.rb +10 -0
- data/spec/factories/assets.rb +5 -0
- data/spec/factories/comments.rb +9 -0
- data/spec/factories/content_columns.rb +10 -0
- data/spec/factories/content_images.rb +9 -0
- data/spec/factories/content_rows.rb +5 -0
- data/spec/factories/content_texts.rb +4 -0
- data/spec/factories/email_templates.rb +8 -0
- data/spec/factories/emails.rb +11 -0
- data/spec/factories/images.rb +5 -0
- data/spec/factories/logs.rb +4 -0
- data/spec/factories/meta.rb +7 -0
- data/spec/factories/navigation_items.rb +5 -0
- data/spec/factories/navigations.rb +6 -0
- data/spec/factories/notes.rb +6 -0
- data/spec/factories/pages.rb +5 -0
- data/spec/factories/queued_tasks.rb +5 -0
- data/spec/factories/redirects.rb +7 -0
- data/spec/factories/search_indices.rb +6 -0
- data/spec/factories/search_modules.rb +5 -0
- data/spec/factories/search_synonyms.rb +7 -0
- data/spec/factories/settings.rb +7 -0
- data/spec/factories/snippets.rb +6 -0
- data/spec/factories/stores.rb +6 -0
- data/spec/factories/tagged_items.rb +5 -0
- data/spec/factories/tags.rb +7 -0
- data/spec/factories/users.rb +9 -0
- data/spec/support/concerns/cacheable.rb +10 -0
- data/spec/support/concerns/commentable.rb +10 -0
- data/spec/support/concerns/content_type.rb +20 -0
- data/spec/support/concerns/deletable.rb +33 -0
- data/spec/support/concerns/draggable.rb +29 -0
- data/spec/support/concerns/emailable.rb +7 -0
- data/spec/support/concerns/flexible_content.rb +22 -0
- data/spec/support/concerns/imageable.rb +15 -0
- data/spec/support/concerns/locale.rb +21 -0
- data/spec/support/concerns/loggable.rb +19 -0
- data/spec/support/concerns/notable.rb +10 -0
- data/spec/support/concerns/parentable.rb +73 -0
- data/spec/support/concerns/person.rb +15 -0
- data/spec/support/concerns/publishable.rb +41 -0
- data/spec/support/concerns/searchable.rb +130 -0
- data/spec/support/concerns/seo.rb +54 -0
- data/spec/support/concerns/sortable.rb +67 -0
- data/spec/support/concerns/spammable.rb +53 -0
- data/spec/support/concerns/storable.rb +18 -0
- data/spec/support/concerns/taggable.rb +51 -0
- data/spec/support/concerns/translatable.rb +71 -0
- data/spec/support/concerns/visible.rb +64 -0
- metadata +66 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: e6a22b011bc3569827aedbcdd99d353e735fae6e
|
4
|
+
data.tar.gz: 3202da4e9d86005256c04c4826bc9b0b3eaede7b
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 0a90d20c8888527092dea454829c2f04cf507f7af964d1d747b3fa10e4473b5dd1753d2051295d7a6ed727d868f5f72a1ce8571f16425b3f1ae1731aa5728792
|
7
|
+
data.tar.gz: ce3492185bef64ce7b2ccc6786a41089560ac1555a0814ff7ae26c5ba4e92941d15b7f278639652c9b3a9fcc187677d3224a06796c3b8b9a5ec02a7ced5bb802
|
@@ -10,7 +10,7 @@
|
|
10
10
|
// Read Sprockets README (https://github.com/sstephenson/sprockets#sprockets-directives) for details
|
11
11
|
// about supported directives.
|
12
12
|
//
|
13
|
-
//= require
|
13
|
+
//= require jquery3
|
14
14
|
//= require jquery_ujs
|
15
15
|
//= require jquery-ui/autocomplete
|
16
16
|
//= require jquery-ui.autocomplete.html.js
|
@@ -0,0 +1,52 @@
|
|
1
|
+
class Backend::AssetsController < Backend::BaseController
|
2
|
+
include Concerns::PaginationController
|
3
|
+
|
4
|
+
before_action :find_model, only: [:show, :edit, :update, :destroy]
|
5
|
+
before_action -> { breadcrumb.add t('b.files'), backend_assets_path }
|
6
|
+
|
7
|
+
def index
|
8
|
+
@search = Asset.ransack params[:q]
|
9
|
+
@assets = @search.result(distinct: true).order('id DESC').page(page_number).per_page(per_page)
|
10
|
+
end
|
11
|
+
|
12
|
+
def show
|
13
|
+
redirect_to @model.filename.url
|
14
|
+
end
|
15
|
+
|
16
|
+
def new
|
17
|
+
@model = Asset.new
|
18
|
+
end
|
19
|
+
|
20
|
+
def create
|
21
|
+
@model = Asset.new allowed_params
|
22
|
+
|
23
|
+
if @model.save
|
24
|
+
redirect_to backend_assets_path, notice: translate_notice(:added, :file)
|
25
|
+
else
|
26
|
+
render :new
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
def update
|
31
|
+
if @model.update_attributes allowed_params
|
32
|
+
redirect_to backend_assets_path, notice: translate_notice(:edited, :file)
|
33
|
+
else
|
34
|
+
render :edit
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
def destroy
|
39
|
+
@model.destroy if @model.deletable?
|
40
|
+
redirect_to backend_assets_path, notice: translate_notice(:deleted, :file)
|
41
|
+
end
|
42
|
+
|
43
|
+
private
|
44
|
+
|
45
|
+
def find_model
|
46
|
+
@model = Asset.find params[:id]
|
47
|
+
end
|
48
|
+
|
49
|
+
def allowed_params
|
50
|
+
params[:asset].permit(:filename, :description)
|
51
|
+
end
|
52
|
+
end
|
data/app/models/asset.rb
ADDED
@@ -0,0 +1,37 @@
|
|
1
|
+
class Asset < ApplicationRecord
|
2
|
+
include Concerns::Taggable
|
3
|
+
|
4
|
+
mount_uploader :filename, AssetUploader
|
5
|
+
|
6
|
+
has_many :images, dependent: :destroy
|
7
|
+
|
8
|
+
before_save :update_filesize, :update_content_type
|
9
|
+
|
10
|
+
validates :filename, presence: true
|
11
|
+
|
12
|
+
def image?
|
13
|
+
content_type.to_s.include?('image')
|
14
|
+
end
|
15
|
+
|
16
|
+
def image
|
17
|
+
AssetImage.new(self) if image?
|
18
|
+
end
|
19
|
+
|
20
|
+
def actual_filename
|
21
|
+
read_attribute(:filename)
|
22
|
+
end
|
23
|
+
|
24
|
+
def deletable?
|
25
|
+
images.empty?
|
26
|
+
end
|
27
|
+
|
28
|
+
private
|
29
|
+
|
30
|
+
def update_filesize
|
31
|
+
self.filesize = filename.size
|
32
|
+
end
|
33
|
+
|
34
|
+
def update_content_type
|
35
|
+
self.content_type = filename.content_type
|
36
|
+
end
|
37
|
+
end
|
@@ -0,0 +1,172 @@
|
|
1
|
+
require 'mini_magick'
|
2
|
+
require 'fileutils'
|
3
|
+
|
4
|
+
class AssetImage
|
5
|
+
def initialize(asset)
|
6
|
+
@asset = asset
|
7
|
+
end
|
8
|
+
|
9
|
+
def filename(width = nil, height = nil, options = {})
|
10
|
+
options[:action] = :resize_to_limit unless options.key?(:action)
|
11
|
+
|
12
|
+
str = options[:action].to_s.split('_').last
|
13
|
+
|
14
|
+
if options[:quality]
|
15
|
+
str << '-q' + options[:quality].to_s
|
16
|
+
end
|
17
|
+
|
18
|
+
if options[:gravity]
|
19
|
+
str << '-g' + options[:gravity].to_s.underscore.split('_').map { |s| s[0,1] }.join
|
20
|
+
end
|
21
|
+
|
22
|
+
if options[:background]
|
23
|
+
str << '-b' + options[:background].to_s.parameterize
|
24
|
+
end
|
25
|
+
|
26
|
+
str << "-#{width}x#{height}-#{@asset.actual_filename}"
|
27
|
+
end
|
28
|
+
|
29
|
+
def url(width = nil, height = nil, options = {})
|
30
|
+
options[:action] = :resize_to_limit unless options.key?(:action)
|
31
|
+
|
32
|
+
if width.nil? && height.nil?
|
33
|
+
return "/uploads/assets/#{main_dir}/#{second_dir}/#{@asset.actual_filename}"
|
34
|
+
end
|
35
|
+
|
36
|
+
name = filename(width, height, options)
|
37
|
+
|
38
|
+
unless File.exists?(actual_path(name))
|
39
|
+
FileUtils.mkpath(File.dirname(actual_path(name)))
|
40
|
+
|
41
|
+
case options[:action].to_sym
|
42
|
+
when :resize_to_limit then resize_to_limit(width, height, options)
|
43
|
+
when :resize_to_fit then resize_to_fit(width, height, options)
|
44
|
+
when :resize_to_fill then resize_to_fill(width, height, options)
|
45
|
+
when :resize_and_pad then resize_and_pad(width, height, options)
|
46
|
+
else
|
47
|
+
raise "No such resize action '#{options[:action].to_s}'. Available are: resize_to_limit, resize_to_fit, resize_to_fill and resize_and_pad."
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
actual_url(name)
|
52
|
+
end
|
53
|
+
|
54
|
+
def actual_url(calculated_filename)
|
55
|
+
"/uploads/assets/_cache/#{main_dir}/#{second_dir}/#{calculated_filename}"
|
56
|
+
end
|
57
|
+
|
58
|
+
def path(width = nil, height = nil, options = {})
|
59
|
+
url(width, height, options) # Trigger the actual resize
|
60
|
+
actual_path(filename(width, height, options))
|
61
|
+
end
|
62
|
+
|
63
|
+
def actual_path(calculated_filename)
|
64
|
+
"#{Rails.root}/public/uploads/assets/_cache/#{main_dir}/#{second_dir}/#{calculated_filename}"
|
65
|
+
end
|
66
|
+
|
67
|
+
private
|
68
|
+
|
69
|
+
# Resize the image to fit within the specified dimensions while retaining
|
70
|
+
# the original aspect ratio. Will only resize the image if it is larger than the
|
71
|
+
# specified dimensions. The resulting image may be shorter or narrower than specified
|
72
|
+
# in the smaller dimension but will not be larger than the specified values.
|
73
|
+
#
|
74
|
+
def resize_to_limit(width, height, options = {})
|
75
|
+
img = MiniMagick::Image.open(@asset.filename.path)
|
76
|
+
img.combine_options do |c|
|
77
|
+
c.quality options[:quality] if options[:quality]
|
78
|
+
c.resize "#{width}x#{height}>"
|
79
|
+
end
|
80
|
+
|
81
|
+
img.write(actual_path(filename(width, height, options)))
|
82
|
+
end
|
83
|
+
|
84
|
+
# Resize the image to fit within the specified dimensions while retaining
|
85
|
+
# the original aspect ratio. The image may be shorter or narrower than
|
86
|
+
# specified in the smaller dimension but will not be larger than the specified values.
|
87
|
+
#
|
88
|
+
def resize_to_fit(width, height, options = {})
|
89
|
+
img = MiniMagick::Image.open(@asset.filename.path)
|
90
|
+
img.combine_options do |c|
|
91
|
+
c.quality options[:quality] if options[:quality]
|
92
|
+
c.resize "#{width}x#{height}"
|
93
|
+
end
|
94
|
+
|
95
|
+
img.write(actual_path(filename(width, height, options)))
|
96
|
+
end
|
97
|
+
|
98
|
+
# Resize the image to fit within the specified dimensions while retaining
|
99
|
+
# the aspect ratio of the original image. If necessary, crop the image in the
|
100
|
+
# larger dimension.
|
101
|
+
#
|
102
|
+
# Possible values for options[:gravity] are:
|
103
|
+
# NorthWest, North, NorthEast, West, Center, East, SouthWest, South, SouthEast
|
104
|
+
#
|
105
|
+
def resize_to_fill(width, height, options = {})
|
106
|
+
gravity = options.key?(:gravity) ? options[:gravity] : 'Center'
|
107
|
+
|
108
|
+
img = MiniMagick::Image.open(@asset.filename.path)
|
109
|
+
cols, rows = img[:dimensions]
|
110
|
+
|
111
|
+
img.combine_options do |cmd|
|
112
|
+
if width != cols || height != rows
|
113
|
+
scale_x = width/cols.to_f
|
114
|
+
scale_y = height/rows.to_f
|
115
|
+
|
116
|
+
if scale_x >= scale_y
|
117
|
+
cols = (scale_x * (cols + 0.5)).round
|
118
|
+
rows = (scale_x * (rows + 0.5)).round
|
119
|
+
cmd.resize "#{cols}"
|
120
|
+
else
|
121
|
+
cols = (scale_y * (cols + 0.5)).round
|
122
|
+
rows = (scale_y * (rows + 0.5)).round
|
123
|
+
cmd.resize "x#{rows}"
|
124
|
+
end
|
125
|
+
end
|
126
|
+
|
127
|
+
cmd.quality options[:quality] if options.key?(:quality)
|
128
|
+
cmd.gravity gravity
|
129
|
+
cmd.background 'rgba(255,255,255,0.0)'
|
130
|
+
cmd.extent "#{width}x#{height}" if cols != width || rows != height
|
131
|
+
end
|
132
|
+
|
133
|
+
img.write(actual_path(filename(width, height, options)))
|
134
|
+
end
|
135
|
+
|
136
|
+
# Resize the image to fit within the specified dimensions while retaining
|
137
|
+
# the original aspect ratio. If necessary, will pad the remaining area
|
138
|
+
# with the given color, which defaults to transparent (for gif and png,
|
139
|
+
# white for jpeg).
|
140
|
+
#
|
141
|
+
# Possible values for options[:gravity] are:
|
142
|
+
# NorthWest, North, NorthEast, West, Center, East, SouthWest, South, SouthEast
|
143
|
+
#
|
144
|
+
def resize_and_pad(width, height, options = {})
|
145
|
+
gravity = options.key?(:gravity) ? options[:gravity] : 'Center'
|
146
|
+
background = options.key?(:background) ? options[:background] : :transparant
|
147
|
+
|
148
|
+
img = MiniMagick::Image.open(@asset.filename.path)
|
149
|
+
img.combine_options do |cmd|
|
150
|
+
cmd.thumbnail "#{width}x#{height}>"
|
151
|
+
|
152
|
+
if background.to_sym == :transparent
|
153
|
+
cmd.background 'rgba(255, 255, 255, 0.0)'
|
154
|
+
else
|
155
|
+
cmd.background background
|
156
|
+
end
|
157
|
+
|
158
|
+
cmd.gravity gravity
|
159
|
+
cmd.extent "#{width}x#{height}"
|
160
|
+
end
|
161
|
+
|
162
|
+
img.write(actual_path(filename(width, height, options)))
|
163
|
+
end
|
164
|
+
|
165
|
+
def main_dir
|
166
|
+
@main_dir ||= Digest::MD5.hexdigest(@asset.id.to_s)[0,2]
|
167
|
+
end
|
168
|
+
|
169
|
+
def second_dir
|
170
|
+
@second_dir ||= Digest::MD5.hexdigest(@asset.id.to_s)[2,2]
|
171
|
+
end
|
172
|
+
end
|
data/app/models/image.rb
ADDED
@@ -0,0 +1,36 @@
|
|
1
|
+
class AssetUploader < CarrierWave::Uploader::Base
|
2
|
+
include CarrierWave::RMagick
|
3
|
+
|
4
|
+
storage :file
|
5
|
+
|
6
|
+
def filename
|
7
|
+
@name ||= "#{secure_token}.#{file.extension.downcase}" if original_filename
|
8
|
+
end
|
9
|
+
|
10
|
+
private
|
11
|
+
|
12
|
+
def extension_white_list
|
13
|
+
if model.image?
|
14
|
+
Udongo.config.assets.image_white_list
|
15
|
+
else
|
16
|
+
(Udongo.config.assets.image_white_list + Udongo.config.assets.file_white_list).flatten
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
def secure_token
|
21
|
+
ivar = "@#{mounted_as}_secure_token"
|
22
|
+
token = model.instance_variable_get(ivar)
|
23
|
+
token ||= model.instance_variable_set(ivar, SecureRandom.hex(4))
|
24
|
+
end
|
25
|
+
|
26
|
+
def store_dir
|
27
|
+
md5 = Digest::MD5.hexdigest(model.id.to_s)
|
28
|
+
"uploads/assets/#{md5[0,2]}/#{md5[2,2]}"
|
29
|
+
end
|
30
|
+
|
31
|
+
def image?(new_file)
|
32
|
+
new_file.content_type.include? 'image'
|
33
|
+
end
|
34
|
+
|
35
|
+
process resize_to_limit: [2560, 2560], if: :image?
|
36
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
<div class="card filter">
|
2
|
+
<div class="card-block">
|
3
|
+
<%= search_form_for [:backend, @search] do |f| %>
|
4
|
+
<div class="row">
|
5
|
+
<div class="col-md-6 form-group">
|
6
|
+
<%= f.search_field :description_cont, placeholder: t('b.description'), class: 'form-control' %>
|
7
|
+
</div>
|
8
|
+
</div>
|
9
|
+
|
10
|
+
<div class="row">
|
11
|
+
<div class="col-md-6">
|
12
|
+
<%= f.submit class: 'btn btn-primary btn-sm' %>
|
13
|
+
</div>
|
14
|
+
</div>
|
15
|
+
<% end %>
|
16
|
+
</div>
|
17
|
+
</div>
|
@@ -0,0 +1,28 @@
|
|
1
|
+
<%= render 'backend/general_form_error', object: @model %>
|
2
|
+
|
3
|
+
<%= simple_form_for [:backend, @model] do |f| %>
|
4
|
+
<div class="row">
|
5
|
+
<div class="col-md-6">
|
6
|
+
<div class="card">
|
7
|
+
<div class="card-header">
|
8
|
+
<%= t 'b.general' %>
|
9
|
+
</div>
|
10
|
+
|
11
|
+
<div class="card-block">
|
12
|
+
<%= f.input :filename, as: :file, label: false, required: @model.new_record? %>
|
13
|
+
<%= f.input :description, as: :string %>
|
14
|
+
</div>
|
15
|
+
</div>
|
16
|
+
</div>
|
17
|
+
|
18
|
+
<% unless @model.new_record? %>
|
19
|
+
<div class="col-md-6">
|
20
|
+
<!-- Tags -->
|
21
|
+
<%= render 'backend/tags', model: @model, locale: Udongo.config.i18n.app.default_locale %>
|
22
|
+
</div>
|
23
|
+
<% end %>
|
24
|
+
</div>
|
25
|
+
|
26
|
+
|
27
|
+
<%= render 'backend/form_actions', cancel_url: backend_assets_path %>
|
28
|
+
<% end %>
|
@@ -0,0 +1,48 @@
|
|
1
|
+
<%= render 'backend/breadcrumbs' %>
|
2
|
+
<%= render 'filter' %>
|
3
|
+
|
4
|
+
<p class="text-xs-right">
|
5
|
+
<%= link_to icon(:plus, t('b.add')), new_backend_asset_path, class: 'btn btn-primary btn-sm' %>
|
6
|
+
</p>
|
7
|
+
|
8
|
+
<% if @assets.any? %>
|
9
|
+
<table class="table table-striped table-hover">
|
10
|
+
<thead class="thead-inverse">
|
11
|
+
<tr>
|
12
|
+
<th><%= t 'b.file' %></th>
|
13
|
+
<th><%= t 'g.type' %></th>
|
14
|
+
<th><%= t 'b.size' %></th>
|
15
|
+
<th><%= t 'b.description' %></th>
|
16
|
+
<th><%= t 'b.last_changed_at' %></th>
|
17
|
+
<th> </th>
|
18
|
+
</tr>
|
19
|
+
</thead>
|
20
|
+
|
21
|
+
<tbody>
|
22
|
+
<% @assets.each do |a| %>
|
23
|
+
<tr>
|
24
|
+
<td>
|
25
|
+
<% if a.image? %>
|
26
|
+
<%= image_tag a.image.url(150, 150), alt: '', class: 'img-fluid' %>
|
27
|
+
<% else %>
|
28
|
+
<%= link_to icon(:download, t('b.download')), a.filename.url %>
|
29
|
+
<% end %>
|
30
|
+
</td>
|
31
|
+
<td><%= a.actual_filename.split('.').last.upcase %></td>
|
32
|
+
<td><%= number_to_human_size a.filesize %></td>
|
33
|
+
<td><%= simple_format a.description %></td>
|
34
|
+
<td><%= l a.updated_at %></td>
|
35
|
+
<td class="text-xs-right">
|
36
|
+
<%= link_to_edit [:backend, a] %>
|
37
|
+
<%= link_to_delete [:backend, a] if a.deletable? %>
|
38
|
+
</td>
|
39
|
+
</tr>
|
40
|
+
<% end %>
|
41
|
+
</tbody>
|
42
|
+
</table>
|
43
|
+
|
44
|
+
<%= udongo_paginate @assets %>
|
45
|
+
|
46
|
+
<% else %>
|
47
|
+
<p><%= t 'b.msg.no_items' %></p>
|
48
|
+
<% end %>
|