udongo 5.1.0 → 5.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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 %>
|