alchemy_cms 4.0.0.rc2 → 4.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.codeclimate.yml +1 -1
- data/.travis.yml +2 -2
- data/CHANGELOG.md +18 -0
- data/Gemfile +2 -1
- data/README.md +4 -11
- data/alchemy_cms.gemspec +3 -3
- data/app/assets/stylesheets/alchemy/_extends.scss +1 -2
- data/app/assets/stylesheets/alchemy/_mixins.scss +0 -8
- data/app/assets/stylesheets/alchemy/_variables.scss +2 -0
- data/app/assets/stylesheets/alchemy/archive.scss +23 -52
- data/app/assets/stylesheets/alchemy/base.scss +10 -8
- data/app/assets/stylesheets/alchemy/buttons.scss +3 -4
- data/app/assets/stylesheets/alchemy/dashboard.scss +0 -2
- data/app/assets/stylesheets/alchemy/dialogs.scss +13 -14
- data/app/assets/stylesheets/alchemy/elements.scss +41 -51
- data/app/assets/stylesheets/alchemy/forms.scss +0 -3
- data/app/assets/stylesheets/alchemy/frame.scss +0 -3
- data/app/assets/stylesheets/alchemy/image_library.scss +0 -3
- data/app/assets/stylesheets/alchemy/jquery.datetimepicker.scss +0 -2
- data/app/assets/stylesheets/alchemy/lists.scss +1 -0
- data/app/assets/stylesheets/alchemy/navigation.scss +1 -4
- data/app/assets/stylesheets/alchemy/pagination.scss +1 -3
- data/app/assets/stylesheets/alchemy/selects.scss +2 -2
- data/app/assets/stylesheets/alchemy/sitemap.scss +1 -1
- data/app/assets/stylesheets/alchemy/upload.scss +0 -1
- data/app/assets/stylesheets/tinymce/skins/alchemy/skin.min.css.scss +0 -2
- data/app/helpers/alchemy/elements_helper.rb +12 -2
- data/app/models/alchemy/essence_picture.rb +28 -10
- data/app/models/alchemy/essence_picture_view.rb +1 -1
- data/app/models/alchemy/picture/transformations.rb +2 -3
- data/app/models/alchemy/picture/url.rb +10 -2
- data/app/views/alchemy/admin/contents/create.js.erb +1 -3
- data/app/views/alchemy/admin/essence_pictures/crop.html.erb +4 -3
- data/app/views/alchemy/essences/_essence_file_editor.html.erb +1 -1
- data/app/views/alchemy/essences/shared/_essence_picture_tools.html.erb +1 -1
- data/config/brakeman.ignore +5 -5
- data/config/locales/alchemy.de.yml +2 -2
- data/config/locales/alchemy.en.yml +2 -2
- data/config/locales/alchemy.es.yml +1 -1
- data/config/locales/alchemy.fr.yml +1 -1
- data/config/locales/alchemy.it.yml +1 -1
- data/config/locales/alchemy.nl.yml +1 -1
- data/config/locales/alchemy.ru.yml +1 -1
- data/db/migrate/20160422195310_add_image_file_format_to_alchemy_pictures.rb +0 -12
- data/lib/alchemy/errors.rb +6 -1
- data/lib/alchemy/logger.rb +1 -1
- data/lib/alchemy/permissions.rb +0 -7
- data/lib/alchemy/tasks/tidy.rb +130 -0
- data/lib/alchemy/test_support/factories/attachment_factory.rb +2 -2
- data/lib/alchemy/test_support/factories/cell_factory.rb +3 -3
- data/lib/alchemy/test_support/factories/content_factory.rb +2 -2
- data/lib/alchemy/test_support/factories/dummy_user_factory.rb +2 -2
- data/lib/alchemy/test_support/factories/element_factory.rb +2 -2
- data/lib/alchemy/test_support/factories/essence_file_factory.rb +2 -2
- data/lib/alchemy/test_support/factories/essence_picture_factory.rb +2 -2
- data/lib/alchemy/test_support/factories/essence_text_factory.rb +2 -2
- data/lib/alchemy/test_support/factories/language_factory.rb +2 -2
- data/lib/alchemy/test_support/factories/page_factory.rb +4 -4
- data/lib/alchemy/test_support/factories/picture_factory.rb +2 -2
- data/lib/alchemy/test_support/factories/site_factory.rb +2 -2
- data/lib/alchemy/upgrader/three_point_four.rb +25 -0
- data/lib/alchemy/version.rb +1 -1
- data/lib/tasks/alchemy/tidy.rake +1 -129
- data/lib/tasks/alchemy/upgrade.rake +9 -1
- metadata +22 -9
@@ -56,23 +56,35 @@ module Alchemy
|
|
56
56
|
# @option options crop [Boolean]
|
57
57
|
# If set to true the picture will be cropped to fit the size value.
|
58
58
|
#
|
59
|
+
# @return [String]
|
59
60
|
def picture_url(options = {})
|
60
61
|
return if picture.nil?
|
61
62
|
|
62
|
-
options
|
63
|
-
|
64
|
-
crop_from: crop_from,
|
65
|
-
crop_size: crop_size
|
66
|
-
}.merge(options)
|
63
|
+
picture.url picture_url_options.merge(options)
|
64
|
+
end
|
67
65
|
|
68
|
-
|
66
|
+
# Picture rendering options
|
67
|
+
#
|
68
|
+
# Returns the +default_render_format+ of the associated +Alchemy::Picture+
|
69
|
+
# together with the +crop_from+ and +crop_size+ values
|
70
|
+
#
|
71
|
+
# @return [HashWithIndifferentAccess]
|
72
|
+
def picture_url_options
|
73
|
+
return {} if picture.nil?
|
74
|
+
|
75
|
+
{
|
76
|
+
format: picture.default_render_format,
|
77
|
+
crop_from: crop_from.presence,
|
78
|
+
crop_size: crop_size.presence
|
79
|
+
}.with_indifferent_access
|
69
80
|
end
|
70
81
|
|
71
|
-
#
|
82
|
+
# Returns an url for the thumbnail representation of the assigned picture
|
72
83
|
#
|
73
84
|
# It takes cropping values into account, so it always represents the current
|
74
85
|
# image displayed in the frontend.
|
75
86
|
#
|
87
|
+
# @return [String]
|
76
88
|
def thumbnail_url(options = {})
|
77
89
|
return if picture.nil?
|
78
90
|
|
@@ -96,6 +108,7 @@ module Alchemy
|
|
96
108
|
# @param max [Integer]
|
97
109
|
# The maximum length of the text returned.
|
98
110
|
#
|
111
|
+
# @return [String]
|
99
112
|
def preview_text(max = 30)
|
100
113
|
return "" if picture.nil?
|
101
114
|
picture.name.to_s[0..max - 1]
|
@@ -103,6 +116,7 @@ module Alchemy
|
|
103
116
|
|
104
117
|
# A Hash of coordinates suitable for the graphical image cropper.
|
105
118
|
#
|
119
|
+
# @return [Hash]
|
106
120
|
def cropping_mask
|
107
121
|
return if crop_from.blank? || crop_size.blank?
|
108
122
|
crop_from = point_from_string(read_attribute(:crop_from))
|
@@ -112,6 +126,8 @@ module Alchemy
|
|
112
126
|
end
|
113
127
|
|
114
128
|
# Returns a serialized ingredient value for json api
|
129
|
+
#
|
130
|
+
# @return [String]
|
115
131
|
def serialized_ingredient
|
116
132
|
picture_url(content.settings)
|
117
133
|
end
|
@@ -132,13 +148,15 @@ module Alchemy
|
|
132
148
|
private
|
133
149
|
|
134
150
|
def fix_crop_values
|
135
|
-
%
|
136
|
-
|
151
|
+
%i(crop_from crop_size).each do |crop_value|
|
152
|
+
if self[crop_value].is_a?(String)
|
153
|
+
write_attribute crop_value, normalize_crop_value(crop_value)
|
154
|
+
end
|
137
155
|
end
|
138
156
|
end
|
139
157
|
|
140
158
|
def normalize_crop_value(crop_value)
|
141
|
-
|
159
|
+
self[crop_value].split('x').map { |n| normalize_number(n) }.join('x')
|
142
160
|
end
|
143
161
|
|
144
162
|
def normalize_number(number)
|
@@ -111,12 +111,11 @@ module Alchemy
|
|
111
111
|
end
|
112
112
|
|
113
113
|
# An Image smaller than dimensions
|
114
|
-
# can not be cropped to
|
114
|
+
# can not be cropped to given size - unless upsample is true.
|
115
115
|
#
|
116
116
|
def can_be_cropped_to(string, upsample = false)
|
117
|
-
dimensions = sizes_from_string(string)
|
118
117
|
return true if upsample
|
119
|
-
is_bigger_than(
|
118
|
+
is_bigger_than sizes_from_string(string)
|
120
119
|
end
|
121
120
|
|
122
121
|
# Returns true if the class we're included in has a meaningful render_size attribute
|
@@ -2,6 +2,8 @@
|
|
2
2
|
|
3
3
|
module Alchemy
|
4
4
|
module Picture::Url
|
5
|
+
include Alchemy::Logger
|
6
|
+
|
5
7
|
TRANSFORMATION_OPTIONS = [
|
6
8
|
:crop,
|
7
9
|
:crop_from,
|
@@ -30,6 +32,9 @@ module Alchemy
|
|
30
32
|
image = encoded_image(image, options)
|
31
33
|
|
32
34
|
image.url(options.except(*TRANSFORMATION_OPTIONS).merge(name: name))
|
35
|
+
rescue MissingImageFileError, WrongImageFormatError => e
|
36
|
+
log_warning e.message
|
37
|
+
nil
|
33
38
|
end
|
34
39
|
|
35
40
|
private
|
@@ -41,7 +46,7 @@ module Alchemy
|
|
41
46
|
|
42
47
|
return image unless size.present? && has_convertible_format?
|
43
48
|
|
44
|
-
if options[:
|
49
|
+
if options[:crop]
|
45
50
|
crop(size, options[:crop_from], options[:crop_size], upsample)
|
46
51
|
else
|
47
52
|
resize(size, upsample)
|
@@ -55,7 +60,10 @@ module Alchemy
|
|
55
60
|
#
|
56
61
|
def encoded_image(image, options = {})
|
57
62
|
target_format = options[:format] || default_render_format
|
58
|
-
|
63
|
+
|
64
|
+
unless target_format.in?(Alchemy::Picture.allowed_filetypes)
|
65
|
+
raise WrongImageFormatError.new(self, target_format)
|
66
|
+
end
|
59
67
|
|
60
68
|
options = {
|
61
69
|
flatten: target_format != 'gif' && image_file_format == 'gif'
|
@@ -1,7 +1,5 @@
|
|
1
1
|
var editor_html = '<%= j(render "alchemy/essences/#{@content.essence_partial_name}_editor", {
|
2
|
-
content: @content,
|
3
|
-
options: options_from_params.symbolize_keys,
|
4
|
-
html_options: @html_options.symbolize_keys
|
2
|
+
content: @content, options: options_from_params, html_options: @html_options
|
5
3
|
}) %>';
|
6
4
|
|
7
5
|
<% if params[:was_missing] %>
|
@@ -19,11 +19,12 @@
|
|
19
19
|
<%= f.hidden_field :crop_from %>
|
20
20
|
<%= f.hidden_field :crop_size %>
|
21
21
|
<%= hidden_field_tag :content_id, @content.id %>
|
22
|
-
<%=
|
23
|
-
<%= link_to Alchemy.t('Reset Imagemask'), '#', {
|
22
|
+
<%= button_tag Alchemy.t('Reset Imagemask'), {
|
24
23
|
onclick: 'Alchemy.ImageCropper.reset(); return false',
|
25
|
-
class: 'reset_mask'
|
24
|
+
class: 'reset_mask',
|
25
|
+
type: 'reset'
|
26
26
|
} %>
|
27
|
+
<%= f.button Alchemy.t(:apply) %>
|
27
28
|
<% end %>
|
28
29
|
</div>
|
29
30
|
<% end %>
|
data/config/brakeman.ignore
CHANGED
@@ -7,7 +7,7 @@
|
|
7
7
|
"check_name": "MassAssignment",
|
8
8
|
"message": "Parameters should be whitelisted for mass assignment",
|
9
9
|
"file": "app/controllers/alchemy/admin/resources_controller.rb",
|
10
|
-
"line":
|
10
|
+
"line": 130,
|
11
11
|
"link": "http://brakemanscanner.org/docs/warning_types/mass_assignment/",
|
12
12
|
"code": "params.require(resource_handler.namespaced_resource_name).permit!",
|
13
13
|
"render_path": null,
|
@@ -23,13 +23,13 @@
|
|
23
23
|
{
|
24
24
|
"warning_type": "Dynamic Render Path",
|
25
25
|
"warning_code": 15,
|
26
|
-
"fingerprint": "
|
26
|
+
"fingerprint": "79e194e21561d40888d86ebc7fd2ab474fdb0ce32d605dbe9ac6e8984ecc5e92",
|
27
27
|
"check_name": "Render",
|
28
28
|
"message": "Render path contains parameter value",
|
29
29
|
"file": "app/views/alchemy/admin/contents/create.js.erb",
|
30
30
|
"line": 1,
|
31
31
|
"link": "http://brakemanscanner.org/docs/warning_types/dynamic_render_path/",
|
32
|
-
"code": "render(action => \"alchemy/essences/#{Content.create_from_scratch(Element.find(params[:content][:element_id]), content_params).essence_partial_name}_editor\", { :content => Content.create_from_scratch(Element.find(params[:content][:element_id]), content_params), :options => options_from_params
|
32
|
+
"code": "render(action => \"alchemy/essences/#{Content.create_from_scratch(Element.find(params[:content][:element_id]), content_params).essence_partial_name}_editor\", { :content => Content.create_from_scratch(Element.find(params[:content][:element_id]), content_params), :options => options_from_params, :html_options => ((params[:html_options] or {})) })",
|
33
33
|
"render_path": [{"type":"controller","class":"Alchemy::Admin::ContentsController","method":"create","line":21,"file":"app/controllers/alchemy/admin/contents_controller.rb"}],
|
34
34
|
"location": {
|
35
35
|
"type": "template",
|
@@ -60,6 +60,6 @@
|
|
60
60
|
"note": "`Alchemy::Content` is a polymorphic association of any kind of model extending `Alchemy::Essence`. Since we can't know the attributes of all potential essences we need to permit all attributes. As this all happens inside the password protected /admin namespace this can be considered a false positive."
|
61
61
|
}
|
62
62
|
],
|
63
|
-
"updated": "2017-
|
64
|
-
"brakeman_version": "
|
63
|
+
"updated": "2017-10-23 11:49:41 +0200",
|
64
|
+
"brakeman_version": "4.0.1"
|
65
65
|
}
|
@@ -252,7 +252,7 @@ de:
|
|
252
252
|
"Remove item from clipboard": "Inhalt aus der Zwischenablage entfernen"
|
253
253
|
"Remove tag filter": "Filter entfernen"
|
254
254
|
"Remove this content": "Diesen Inhalt entfernen"
|
255
|
-
"Reset Imagemask": "
|
255
|
+
"Reset Imagemask": "zurücksetzen"
|
256
256
|
"Reset password instructions": "Anweisungen um Ihr Passwort zurückzusetzen"
|
257
257
|
"Select all": "Alle auswählen"
|
258
258
|
"Select an content": "Wählen Sie einen Inhalt aus"
|
@@ -365,7 +365,7 @@ de:
|
|
365
365
|
element_of_type: "Element"
|
366
366
|
element_saved: "Element wurde gespeichert."
|
367
367
|
enter_external_link: "Geben Sie hier die Adresse der Seite ein zu der Sie einen Link setzen wollen."
|
368
|
-
explain_cropping: "<p>Sie können den Rahmen verschieben und in der Größe verändern um
|
368
|
+
explain_cropping: "<p>Sie können den Rahmen mit der Maus und den Pfeiltasten verschieben und in der Größe verändern, um einen eigenen Bildausschnitt festzulegen. Wenn Sie zufrieden sind, dann klicken Sie bitte auf \"übernehmen\".</p><p>Um zu dem zentrierten Bildausschnitt zurückzukehren, wie er im Layout vorgesehen wurde, klicken Sie bitte auf \"zurücksezen\" und \"übernehmen\" anschließend die Änderungen.</p>"
|
369
369
|
explain_publishing: "Die gecachte Version vom Server löschen und die aktuellen Änderungen veröffentlichen"
|
370
370
|
explain_sitemap_dragndrop_sorting: "Tip: Halten Sie zum Sortieren der Seiten das Seitensymbol mit der Maus fest und bewegen Sie sie an ihre neue Position."
|
371
371
|
explain_unlocking: "Die Seite verlassen und für andere Benutzer zum Bearbeiten freigeben."
|
@@ -251,7 +251,7 @@ en:
|
|
251
251
|
"Remove item from clipboard": "Remove item from clipboard"
|
252
252
|
"Remove tag filter": "Remove tag filter"
|
253
253
|
"Remove this content": "Remove this content"
|
254
|
-
"Reset Imagemask": "Reset
|
254
|
+
"Reset Imagemask": "Reset mask"
|
255
255
|
"Reset password instructions": "Reset password instructions"
|
256
256
|
"Select all": "Select all"
|
257
257
|
"Select an content": "Select a content"
|
@@ -363,7 +363,7 @@ en:
|
|
363
363
|
element_of_type: "Element"
|
364
364
|
element_saved: "Saved element."
|
365
365
|
enter_external_link: "Please enter the URL you want to link with"
|
366
|
-
explain_cropping: "<p>Move the frame and change its size to adjust the image
|
366
|
+
explain_cropping: "<p>Move the frame and change its size with the mouse or arrow keys to adjust the image mask. Click on \"apply\" when you are satisfied with your selection.</p><p>If you want to return to the original centered image mask like it was defined in the layout, click \"reset\" and \"apply\" afterwards.</p>"
|
367
367
|
explain_publishing: "Publish the page and remove the cached version from the server."
|
368
368
|
explain_sitemap_dragndrop_sorting: "Tip: Drag the pages at the icon in order to sort them."
|
369
369
|
explain_unlocking: "Leave page and unlock it for other users."
|
@@ -252,7 +252,7 @@ es:
|
|
252
252
|
"Remove item from clipboard": "Quitar elemento del portapapeles"
|
253
253
|
"Remove tag filter": "Quitar filtro de etiquetas"
|
254
254
|
"Remove this content": "Quitar este contenido"
|
255
|
-
"Reset Imagemask": "Reiniciar
|
255
|
+
"Reset Imagemask": "Reiniciar"
|
256
256
|
"Reset password instructions": "Instrucciones para restablecer la contraseña"
|
257
257
|
"Select all": "Seleccionar todo"
|
258
258
|
"Select an content": "Seleccionar contenido"
|
@@ -271,7 +271,7 @@ fr:
|
|
271
271
|
"Remove item from clipboard": "Supprimer le contenu du presse-papiers"
|
272
272
|
"Remove tag filter": "Enlever filtre"
|
273
273
|
"Remove this content": "Retirer ce contenu"
|
274
|
-
"Reset Imagemask": "Enlever
|
274
|
+
"Reset Imagemask": "Enlever"
|
275
275
|
"Reset password instructions": "Instructions pour réinitialiser votre mot de passe"
|
276
276
|
"Select all": "Sélectionner tous"
|
277
277
|
"Select an content": "Sélectionnez un contenu de"
|
@@ -252,7 +252,7 @@ it:
|
|
252
252
|
"Remove item from clipboard": "Rimuovi elemento dagli appunti"
|
253
253
|
"Remove tag filter": "Rimuovi filtro tag"
|
254
254
|
"Remove this content": "Rimuovi questo contenuto"
|
255
|
-
"Reset Imagemask": "Ripristina
|
255
|
+
"Reset Imagemask": "Ripristina"
|
256
256
|
"Reset password instructions": "Ripristina le istruzioni per la password"
|
257
257
|
"Select all": "Seleziona tutto"
|
258
258
|
"Select an content": "Seleziona un contenuto"
|
@@ -250,7 +250,7 @@ nl:
|
|
250
250
|
"Remove item from clipboard": "Item van klembord verwijderen"
|
251
251
|
"Remove tag filter": "Filter verwijderen"
|
252
252
|
"Remove this content": "Inhoud verwijderen"
|
253
|
-
"Reset Imagemask": "
|
253
|
+
"Reset Imagemask": "resetten"
|
254
254
|
"Reset password instructions": "Instructies om het wachtwoord te resetten."
|
255
255
|
"Select all": "Alles selecteren"
|
256
256
|
"Select an content": "Selecteer de inhoud"
|
@@ -251,7 +251,7 @@ ru:
|
|
251
251
|
"Remove item from clipboard": "Удалить элемент из буфера обмена"
|
252
252
|
"Remove tag filter": "Удалить фильтр меток"
|
253
253
|
"Remove this content": "Удалить этот контент"
|
254
|
-
"Reset Imagemask": "Сбросить
|
254
|
+
"Reset Imagemask": "Сбросить"
|
255
255
|
"Reset password instructions": "Инструкция по сбросу пароля"
|
256
256
|
"Select all": "Выбрать все"
|
257
257
|
"Select an content": "Выбрать контент"
|
@@ -1,18 +1,6 @@
|
|
1
1
|
class AddImageFileFormatToAlchemyPictures < ActiveRecord::Migration[4.2]
|
2
2
|
def up
|
3
3
|
add_column :alchemy_pictures, :image_file_format, :string
|
4
|
-
|
5
|
-
say_with_time "Storing file format of existing pictures" do
|
6
|
-
Alchemy::Picture.all.each do |pic|
|
7
|
-
begin
|
8
|
-
format = pic.image_file.identify('-ping -format "%m"')
|
9
|
-
pic.update_column('image_file_format', format.to_s.chomp.downcase)
|
10
|
-
rescue Dragonfly::Job::Fetch::NotFound => e
|
11
|
-
say(e.message, true)
|
12
|
-
end
|
13
|
-
end
|
14
|
-
Alchemy::Picture.count
|
15
|
-
end
|
16
4
|
end
|
17
5
|
|
18
6
|
def down
|
data/lib/alchemy/errors.rb
CHANGED
@@ -57,9 +57,14 @@ module Alchemy
|
|
57
57
|
|
58
58
|
# Raised if calling +image_file+ on a Picture object returns nil.
|
59
59
|
class WrongImageFormatError < StandardError
|
60
|
+
def initialize(image, requested_format)
|
61
|
+
@image = image
|
62
|
+
@requested_format = requested_format
|
63
|
+
end
|
64
|
+
|
60
65
|
def message
|
61
66
|
allowed_filetypes = Alchemy::Picture.allowed_filetypes.map(&:upcase).to_sentence
|
62
|
-
"Requested image format is not one of allowed filetypes (#{allowed_filetypes})."
|
67
|
+
"Requested image format (#{@requested_format.inspect}) for #{@image.inspect} is not one of allowed filetypes (#{allowed_filetypes})."
|
63
68
|
end
|
64
69
|
end
|
65
70
|
|
data/lib/alchemy/logger.rb
CHANGED
data/lib/alchemy/permissions.rb
CHANGED
@@ -37,7 +37,6 @@ module Alchemy
|
|
37
37
|
def alchemy_guest_user_rules
|
38
38
|
can([:show, :download], Alchemy::Attachment) { |a| !a.restricted? }
|
39
39
|
can :see, Alchemy::Page, restricted: false, visible: true
|
40
|
-
can(:display, Alchemy::Picture) { |p| !p.restricted? }
|
41
40
|
|
42
41
|
can :read, Alchemy::Content, Alchemy::Content.available.not_restricted do |c|
|
43
42
|
c.public? && !c.restricted? && !c.trashed?
|
@@ -67,7 +66,6 @@ module Alchemy
|
|
67
66
|
can [:show, :download], Alchemy::Attachment
|
68
67
|
can :read, Alchemy::Page, Alchemy::Page.published, &:public?
|
69
68
|
can :see, Alchemy::Page, restricted: true, visible: true
|
70
|
-
can :display, Alchemy::Picture
|
71
69
|
|
72
70
|
can :read, Alchemy::Content, Alchemy::Content.available do |c|
|
73
71
|
c.public? && !c.trashed?
|
@@ -217,11 +215,6 @@ module Alchemy
|
|
217
215
|
:unlock,
|
218
216
|
:visit,
|
219
217
|
to: :edit_content
|
220
|
-
|
221
|
-
alias_action :show,
|
222
|
-
:thumbnail,
|
223
|
-
:zoom,
|
224
|
-
to: :display
|
225
218
|
end
|
226
219
|
|
227
220
|
# Include the role specific permissions.
|
@@ -0,0 +1,130 @@
|
|
1
|
+
require 'alchemy/shell'
|
2
|
+
|
3
|
+
module Alchemy
|
4
|
+
class Tidy
|
5
|
+
extend Shell
|
6
|
+
|
7
|
+
class << self
|
8
|
+
def create_missing_cells(page_layouts, cells)
|
9
|
+
page_layouts.each do |layout|
|
10
|
+
next if layout['cells'].blank?
|
11
|
+
cells_for_layout = cells.select { |cell| layout['cells'].include? cell['name'] }
|
12
|
+
Alchemy::Page.where(page_layout: layout['name']).each do |page|
|
13
|
+
cells_for_layout.each do |cell_for_layout|
|
14
|
+
cell = Alchemy::Cell.find_or_initialize_by(name: cell_for_layout['name'], page_id: page.id)
|
15
|
+
if cell.new_record?
|
16
|
+
log "Creating cell #{cell.name} for page #{page.name}"
|
17
|
+
else
|
18
|
+
log "Cell #{cell.name} for page #{page.name} already present", :skip
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
def update_element_positions
|
26
|
+
Alchemy::Page.all.each do |page|
|
27
|
+
if page.elements.any?
|
28
|
+
puts "\n## Updating element positions of page `#{page.name}`"
|
29
|
+
end
|
30
|
+
page.elements.group_by(&:cell_id).each do |_cell_id, elements|
|
31
|
+
elements.each_with_index do |element, idx|
|
32
|
+
position = idx + 1
|
33
|
+
if element.position != position
|
34
|
+
log "Updating position for element ##{element.id} to #{position}"
|
35
|
+
element.update_column(:position, position)
|
36
|
+
else
|
37
|
+
log "Position for element ##{element.id} is already correct (#{position})", :skip
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
def update_content_positions
|
45
|
+
Alchemy::Element.all.each do |element|
|
46
|
+
if element.contents.any?
|
47
|
+
puts "\n## Updating content positions of element `#{element.name}`"
|
48
|
+
end
|
49
|
+
element.contents.group_by(&:essence_type).each do |essence_type, contents|
|
50
|
+
puts "-> Contents of type `#{essence_type}`"
|
51
|
+
contents.each_with_index do |content, idx|
|
52
|
+
position = idx + 1
|
53
|
+
if content.position != position
|
54
|
+
log "Updating position for content ##{content.id} to #{position}"
|
55
|
+
content.update_column(:position, position)
|
56
|
+
else
|
57
|
+
log "Position for content ##{content.id} is already correct (#{position})", :skip
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
def remove_orphaned_cells
|
65
|
+
puts "\n## Removing orphaned cells"
|
66
|
+
cells = Alchemy::Cell.unscoped.all
|
67
|
+
if cells.any?
|
68
|
+
orphaned_cells = cells.select do |cell|
|
69
|
+
cell.page.nil? && cell.page_id.present?
|
70
|
+
end
|
71
|
+
if orphaned_cells.any?
|
72
|
+
log "Found #{orphaned_cells.size} orphaned cells"
|
73
|
+
destroy_orphaned_records(orphaned_cells, 'cell')
|
74
|
+
else
|
75
|
+
log "No orphaned cells found", :skip
|
76
|
+
end
|
77
|
+
else
|
78
|
+
log "No cells found", :skip
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
def remove_orphaned_elements
|
83
|
+
puts "\n## Removing orphaned elements"
|
84
|
+
elements = Alchemy::Element.unscoped.all
|
85
|
+
if elements.any?
|
86
|
+
orphaned_elements = elements.select do |element|
|
87
|
+
element.page.nil? && element.page_id.present? ||
|
88
|
+
element.cell.nil? && element.cell_id.present?
|
89
|
+
end
|
90
|
+
if orphaned_elements.any?
|
91
|
+
log "Found #{orphaned_elements.size} orphaned elements"
|
92
|
+
destroy_orphaned_records(orphaned_elements, 'element')
|
93
|
+
else
|
94
|
+
log "No orphaned elements found", :skip
|
95
|
+
end
|
96
|
+
else
|
97
|
+
log "No elements found", :skip
|
98
|
+
end
|
99
|
+
end
|
100
|
+
|
101
|
+
def remove_orphaned_contents
|
102
|
+
puts "\n## Removing orphaned contents"
|
103
|
+
contents = Alchemy::Content.unscoped.all
|
104
|
+
if contents.any?
|
105
|
+
orphaned_contents = contents.select do |content|
|
106
|
+
content.essence.nil? && content.essence_id.present? ||
|
107
|
+
content.element.nil? && content.element_id.present?
|
108
|
+
end
|
109
|
+
if orphaned_contents.any?
|
110
|
+
log "Found #{orphaned_contents.size} orphaned contents"
|
111
|
+
destroy_orphaned_records(orphaned_contents, 'content')
|
112
|
+
else
|
113
|
+
log "No orphaned contents found", :skip
|
114
|
+
end
|
115
|
+
else
|
116
|
+
log "No contents found", :skip
|
117
|
+
end
|
118
|
+
end
|
119
|
+
|
120
|
+
private
|
121
|
+
|
122
|
+
def destroy_orphaned_records(records, class_name)
|
123
|
+
records.each do |record|
|
124
|
+
log "Destroy orphaned #{class_name}: #{record.inspect}"
|
125
|
+
record.destroy
|
126
|
+
end
|
127
|
+
end
|
128
|
+
end
|
129
|
+
end
|
130
|
+
end
|