alchemy_cms 4.2.0.rc1 → 4.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.

Potentially problematic release.


This version of alchemy_cms might be problematic. Click here for more details.

Files changed (52) hide show
  1. checksums.yaml +4 -4
  2. data/.github/FUNDING.yml +4 -0
  3. data/.rubocop.yml +4 -0
  4. data/CHANGELOG.md +28 -3
  5. data/Gemfile +1 -1
  6. data/README.md +1 -1
  7. data/alchemy_cms.gemspec +5 -6
  8. data/app/assets/javascripts/alchemy/alchemy.dialog.js.coffee +3 -3
  9. data/app/assets/javascripts/alchemy/alchemy.elements_window.js.coffee +1 -1
  10. data/app/assets/stylesheets/alchemy/buttons.scss +15 -2
  11. data/app/assets/stylesheets/alchemy/dashboard.scss +2 -0
  12. data/app/assets/stylesheets/alchemy/elements.scss +129 -127
  13. data/app/assets/stylesheets/alchemy/frame.scss +9 -29
  14. data/app/assets/stylesheets/alchemy/navigation.scss +30 -6
  15. data/app/assets/stylesheets/alchemy/preview_window.scss +4 -0
  16. data/app/assets/stylesheets/alchemy/spinner.scss +1 -1
  17. data/app/controllers/alchemy/pages_controller.rb +9 -4
  18. data/app/helpers/alchemy/base_helper.rb +3 -4
  19. data/app/models/alchemy/element.rb +1 -1
  20. data/app/models/alchemy/page/page_scopes.rb +1 -1
  21. data/app/views/alchemy/admin/dashboard/index.html.erb +1 -1
  22. data/app/views/alchemy/admin/dashboard/info.html.erb +23 -22
  23. data/app/views/alchemy/admin/pages/edit.html.erb +10 -10
  24. data/app/views/alchemy/admin/resources/_form.html.erb +0 -8
  25. data/app/views/alchemy/admin/resources/_table_header.html.erb +2 -1
  26. data/app/views/alchemy/essences/_essence_select_editor.html.erb +1 -1
  27. data/config/locales/alchemy.en.yml +6 -1
  28. data/lib/alchemy/on_page_layout.rb +1 -1
  29. data/lib/alchemy/permissions.rb +5 -2
  30. data/lib/alchemy/resources_helper.rb +12 -10
  31. data/lib/alchemy/shell.rb +11 -3
  32. data/lib/alchemy/test_support/factories.rb +3 -1
  33. data/lib/alchemy/test_support/factories/attachment_factory.rb +2 -0
  34. data/lib/alchemy/test_support/factories/content_factory.rb +5 -0
  35. data/lib/alchemy/test_support/factories/dummy_user_factory.rb +2 -0
  36. data/lib/alchemy/test_support/factories/element_factory.rb +3 -0
  37. data/lib/alchemy/test_support/factories/essence_file_factory.rb +2 -0
  38. data/lib/alchemy/test_support/factories/essence_picture_factory.rb +2 -0
  39. data/lib/alchemy/test_support/factories/essence_text_factory.rb +2 -0
  40. data/lib/alchemy/test_support/factories/language_factory.rb +2 -0
  41. data/lib/alchemy/test_support/factories/page_factory.rb +2 -0
  42. data/lib/alchemy/test_support/factories/picture_factory.rb +2 -0
  43. data/lib/alchemy/test_support/factories/site_factory.rb +2 -0
  44. data/lib/alchemy/upgrader/four_point_one.rb +16 -16
  45. data/lib/alchemy/upgrader/four_point_two.rb +37 -20
  46. data/lib/alchemy/upgrader/tasks/cells_upgrader.rb +3 -2
  47. data/lib/alchemy/upgrader/tasks/element_partial_name_variable_updater.rb +28 -0
  48. data/lib/alchemy/upgrader/tasks/picture_gallery_upgrader.rb +34 -19
  49. data/lib/alchemy/version.rb +1 -1
  50. data/lib/tasks/alchemy/upgrade.rake +7 -1
  51. metadata +32 -14
  52. data/.teatro.yml +0 -8
@@ -42,18 +42,15 @@ div#overlay_text_box {
42
42
  }
43
43
 
44
44
  #header {
45
+ display: flex;
45
46
  height: $header-height;
46
47
  line-height: $header-height;
47
48
  background: $header-background;
48
49
  border-bottom: $default-border;
49
50
  position: relative;
50
51
 
51
- a {
52
- display: inline-block;
53
-
54
- &:hover {
55
- text-decoration: none;
56
- }
52
+ a:hover {
53
+ text-decoration: none;
57
54
  }
58
55
 
59
56
  .page_status_and_name {
@@ -76,8 +73,13 @@ div#overlay_text_box {
76
73
  }
77
74
 
78
75
  .page_name {
76
+ display: inline-block;
77
+ max-width: 100px;
79
78
  margin-right: $default-margin;
80
79
  line-height: $header-height;
80
+ white-space: nowrap;
81
+ overflow: hidden;
82
+ text-overflow: ellipsis;
81
83
  }
82
84
  }
83
85
 
@@ -96,12 +98,10 @@ div#overlay_text_box {
96
98
  }
97
99
 
98
100
  #user_info {
99
- position: absolute;
100
- top: 0;
101
- right: 0;
102
101
  height: $header-height;
103
102
  font-size: $small-font-size;
104
103
  padding-left: 2*$default-padding;
104
+ margin-left: auto;
105
105
  border-bottom: $default-border;
106
106
  background-color: $header-background;
107
107
 
@@ -126,23 +126,3 @@ div#overlay_text_box {
126
126
  padding-right: 2*$default-padding;
127
127
  }
128
128
  }
129
-
130
- #locked_pages {
131
-
132
- label {
133
- float: left;
134
- font-size: $small-font-size;
135
- line-height: 27px;
136
- height: 27px;
137
- margin-left: 8px;
138
- margin-right: 4px;
139
- }
140
-
141
- img {
142
- float: left;
143
- }
144
-
145
- .subnavi_tab > a {
146
- padding-right: 0;
147
- }
148
- }
@@ -209,19 +209,42 @@
209
209
  }
210
210
  }
211
211
 
212
+ #locked_pages {
213
+ display: flex;
214
+ flex-grow: 1;
215
+ flex-wrap: nowrap;
216
+ height: $header-height;
217
+
218
+ label {
219
+ float: left;
220
+ font-size: $small-font-size;
221
+ line-height: 27px;
222
+ height: 27px;
223
+ margin-left: 8px;
224
+ margin-right: 4px;
225
+ }
226
+
227
+ img {
228
+ float: left;
229
+ }
230
+
231
+ .subnavi_tab > a {
232
+ padding-right: 0;
233
+ }
234
+ }
235
+
212
236
  .locked_page {
213
- position: relative;
214
- float: left;
237
+ display: flex;
238
+ flex-wrap: nowrap;
215
239
  line-height: $header-height - 1;
216
240
  color: #444;
217
241
  border-right: $default-border;
218
242
  border-bottom: $default-border;
219
- padding-right: 24px;
220
243
 
221
244
  form {
222
- position: absolute;
223
- right: $default-padding;
224
- top: 7px;
245
+ align-self: center;
246
+ justify-self: flex-end;
247
+ margin-right: $default-padding;
225
248
  line-height: 1;
226
249
  }
227
250
 
@@ -231,6 +254,7 @@
231
254
  }
232
255
 
233
256
  > a {
257
+ display: flex;
234
258
  cursor: pointer;
235
259
  padding: 0px 8px;
236
260
 
@@ -16,5 +16,9 @@
16
16
 
17
17
  .collapsed-menu.elements-window-visible & {
18
18
  width: calc(100vw - #{$collapsed-main-menu-width - $default-border-width} - #{$elements-window-width});
19
+
20
+ @media screen and (min-width: $large-screen-break-point) {
21
+ max-width: calc(100vw - #{$collapsed-main-menu-width - $default-border-width} - #{$elements-window-min-width});
22
+ }
19
23
  }
20
24
  }
@@ -7,7 +7,7 @@
7
7
  line-height: 1;
8
8
 
9
9
  path {
10
- fill: $text-color;
10
+ fill: currentColor;
11
11
  opacity: 1;
12
12
  animation: spinner 1s infinite ease-in-out both;
13
13
  }
@@ -155,10 +155,10 @@ module Alchemy
155
155
  end
156
156
 
157
157
  def set_expiration_headers
158
- if @page.cache_page?
159
- expires_in @page.expiration_time, public: !@page.restricted, must_revalidate: true
160
- else
158
+ if must_not_cache?
161
159
  expires_now
160
+ else
161
+ expires_in @page.expiration_time, public: !@page.restricted, must_revalidate: true
162
162
  end
163
163
  end
164
164
 
@@ -190,12 +190,17 @@ module Alchemy
190
190
  # or the cache is stale, because it's been republished by the user.
191
191
  #
192
192
  def render_fresh_page?
193
- !@page.cache_page? || stale?(etag: page_etag,
193
+ must_not_cache? || stale?(etag: page_etag,
194
194
  last_modified: @page.published_at,
195
195
  public: !@page.restricted,
196
196
  template: 'pages/show')
197
197
  end
198
198
 
199
+ # don't cache pages if we have flash message to display or the page has caching disabled
200
+ def must_not_cache?
201
+ flash.present? || !@page.cache_page?
202
+ end
203
+
199
204
  def page_not_found!
200
205
  not_found_error!("Alchemy::Page not found \"#{request.fullpath}\"")
201
206
  end
@@ -11,12 +11,11 @@ module Alchemy
11
11
  # Logs a message in the Rails logger (warn level)
12
12
  # and optionally displays an error message to the user.
13
13
  def warning(message, text = nil)
14
- Logger.warn(message, caller(0..0))
14
+ Logger.warn(message, caller(1..1))
15
15
  unless text.nil?
16
- warning = content_tag('p', class: 'content_editor_error') do
17
- render_icon('warning') + text
16
+ render_message(:warning) do
17
+ text.html_safe
18
18
  end
19
- return warning
20
19
  end
21
20
  end
22
21
 
@@ -98,7 +98,7 @@ module Alchemy
98
98
  scope :not_restricted, -> { joins(:page).merge(Page.not_restricted) }
99
99
  scope :available, -> { published.not_trashed }
100
100
  scope :named, ->(names) { where(name: names) }
101
- scope :excluded, ->(names) { where(arel_table[:name].not_in(names)) }
101
+ scope :excluded, ->(names) { where.not(name: names) }
102
102
  scope :fixed, -> { where(fixed: true) }
103
103
  scope :unfixed, -> { where(fixed: false) }
104
104
  scope :from_current_site, -> { where(Language.table_name => {site_id: Site.current || Site.default}).joins(page: 'language') }
@@ -66,7 +66,7 @@ module Alchemy
66
66
  # Returns all content pages.
67
67
  #
68
68
  scope :contentpages, -> {
69
- where(layoutpage: [false, nil]).where(Page.arel_table[:parent_id].not_eq(nil))
69
+ where(layoutpage: [false, nil]).where.not(parent_id: nil)
70
70
  }
71
71
 
72
72
  # Returns all public contentpages that are not locked.
@@ -7,7 +7,7 @@
7
7
  title: Alchemy.t(:info),
8
8
  dialog_options: {
9
9
  title: Alchemy.t(:info),
10
- size: "420x380"
10
+ size: "420x435"
11
11
  },
12
12
  if_permitted_to: [:info, :alchemy_admin_dashboard],
13
13
  hotkey: 'alt+i'
@@ -1,6 +1,11 @@
1
- <p class="center"><%= image_tag('alchemy/alchemy-logo.svg') %></p>
1
+ <p class="center">
2
+ <%= image_tag('alchemy/alchemy-logo.svg', width: 267, height: 91) %>
3
+ </p>
2
4
  <h2 class="center">
3
- <%= Alchemy.t("Version") %>: <%= @alchemy_version %>
5
+ v<%= @alchemy_version %><br>
6
+ <small>
7
+ Ruby v<%= RUBY_VERSION %>, Rails v<%= Rails.version %>
8
+ </small>
4
9
  </h2>
5
10
  <% if can? :update_check, :alchemy_admin_dashboard %>
6
11
  <p class="center" id="update_check">
@@ -17,6 +22,22 @@
17
22
  <%= Alchemy.t 'Update status unavailable' %>
18
23
  </span>
19
24
  </p>
25
+ <script type="text/javascript">
26
+ var el = $('#update_check');
27
+ var spinner = new Alchemy.Spinner('small')
28
+ spinner.spin(el[0])
29
+ $.get('<%= alchemy.update_check_path %>', function(data, textStatus, jqXHR) {
30
+ if (data == 'true') {
31
+ $('#update_available').show()
32
+ } else {
33
+ $('#up_to_date').show()
34
+ }
35
+ }).fail(function(jqXHR, textStatus, errorThrown) {
36
+ $('#error').show()
37
+ }).always(function() {
38
+ spinner.stop()
39
+ })
40
+ </script>
20
41
  <% end %>
21
42
  <%= render_message do %>
22
43
  <p><%= Alchemy.t('Alchemy is open software and itself uses open software and free resources:') %></p>
@@ -41,23 +62,3 @@
41
62
  </li>
42
63
  </ul>
43
64
  <% end %>
44
- <% if can? :update_check, :alchemy_admin_dashboard %>
45
- <script type="text/javascript">
46
- (function() {
47
- var el = $('#update_check');
48
- var spinner = new Alchemy.Spinner('small');
49
- spinner.spin(el[0]);
50
- $.get('<%= alchemy.update_check_path %>', function(data, textStatus, jqXHR) {
51
- if (data == 'true') {
52
- $('#update_available').show();
53
- } else {
54
- $('#up_to_date').show();
55
- }
56
- }).fail(function(jqXHR, textStatus, errorThrown) {
57
- $('#error').show();
58
- }).always(function() {
59
- spinner.stop();
60
- });
61
- })();
62
- </script>
63
- <% end %>
@@ -13,16 +13,6 @@
13
13
  <% end %>
14
14
  </div>
15
15
  <div class="toolbar_spacer"></div>
16
- <% unless @page.layoutpage? %>
17
- <div class="button_with_label">
18
- <%= form_tag alchemy.visit_admin_page_path(@page), id: 'visit_page_form' do %>
19
- <button class="icon_button" title="<%= Alchemy.t('Visit page') %>">
20
- <%= render_icon('external-link-alt') %>
21
- </button>
22
- <label><%= Alchemy.t("Visit page") %></label>
23
- <% end %>
24
- </div>
25
- <% end %>
26
16
  <div class="button_with_label">
27
17
  <%= link_to_dialog(
28
18
  render_icon('info-circle'),
@@ -76,6 +66,16 @@
76
66
  <% end %>
77
67
  </div>
78
68
  <% end %>
69
+ <% unless @page.layoutpage? %>
70
+ <div class="button_with_label">
71
+ <%= form_tag alchemy.visit_admin_page_path(@page), id: 'visit_page_form' do %>
72
+ <%= button_tag class: 'icon_button', disabled: !@page.public? do %>
73
+ <%= render_icon('external-link-alt') %>
74
+ <% end %>
75
+ <label><%= Alchemy.t("Visit page") %></label>
76
+ <% end %>
77
+ </div>
78
+ <% end %>
79
79
  <% if @page.has_hint? %>
80
80
  <div class="toolbar_spacer"></div>
81
81
  <%= render_hint_for(@page) %>
@@ -5,14 +5,6 @@
5
5
  label_method: relation[:attr_method],
6
6
  include_blank: Alchemy.t(:blank, scope: 'resources.relation_select'),
7
7
  input_html: {class: 'alchemy_selectbox'} %>
8
- <% elsif attribute[:type] == :datetime || attribute[:type] == :time %>
9
- <div class="input <%= attribute[:type] %>">
10
- <label class="control-label">
11
- <%= f.object.class.human_attribute_name(attribute[:name]) %>
12
- </label>
13
- <%= alchemy_datepicker resource_instance_variable, attribute[:name],
14
- type: attribute[:type] %>
15
- </div>
16
8
  <% else %>
17
9
  <%= f.input attribute[:name], resource_attribute_field_options(attribute) %>
18
10
  <% end %>
@@ -1,5 +1,6 @@
1
1
  <div class="resources-header">
2
2
  <h2>
3
- <%= pluralize resources_instance_variable.total_count, resource_model.model_name.human %>
3
+ <%= resources_instance_variable.total_count %>
4
+ <%= resource_model.model_name.human(count: resources_instance_variable.total_count) %>
4
5
  </h2>
5
6
  </div>
@@ -11,7 +11,7 @@
11
11
  <%= content_label(content) %>
12
12
 
13
13
  <% if select_values.nil? %>
14
- <%== warning(':select_values is nil',
14
+ <%= warning(':select_values is nil',
15
15
  "<strong>No select values given.</strong>
16
16
  <br>Please provide :<code>select_values</code> either as argument to
17
17
  <code>render_essence_editor</code> helper or as setting on the content definition in
@@ -280,7 +280,6 @@ en:
280
280
  "User deleted": "%{name} User deleted"
281
281
  "User updated": "%{name} User updated"
282
282
  "Validation failed": "Validation failed"
283
- "Version": "Version"
284
283
  "View File": "View File"
285
284
  "Attachment Preview": "Attachment Preview"
286
285
  "Visit page": "Visit page"
@@ -612,6 +611,12 @@ en:
612
611
  error_notification:
613
612
  default_message: "Please review the problems below:"
614
613
 
614
+ # Alchemy date formats
615
+ date:
616
+ formats:
617
+ alchemy:
618
+ default: "%Y-%m-%d"
619
+
615
620
  # Alchemy time formats
616
621
  time:
617
622
  formats:
@@ -47,7 +47,7 @@ module Alchemy
47
47
  # Registers a callback for given page layout
48
48
  def self.register_callback(page_layout, callback)
49
49
  @callbacks ||= {}
50
- @callbacks[page_layout] ||= []
50
+ @callbacks[page_layout] ||= Set.new
51
51
  @callbacks[page_layout] << callback
52
52
  end
53
53
 
@@ -64,8 +64,7 @@ module Alchemy
64
64
 
65
65
  # Resources
66
66
  can [:show, :download], Alchemy::Attachment
67
- can :read, Alchemy::Page, Alchemy::Page.published, &:public?
68
- can :see, Alchemy::Page, restricted: true, visible: true
67
+ can :see, Alchemy::Page, restricted: true, visible: true
69
68
 
70
69
  can :read, Alchemy::Content, Alchemy::Content.available do |c|
71
70
  c.public? && !c.trashed?
@@ -74,6 +73,10 @@ module Alchemy
74
73
  can :read, Alchemy::Element, Alchemy::Element.available do |e|
75
74
  e.public? && !e.trashed?
76
75
  end
76
+
77
+ can :read, Alchemy::Page, Alchemy::Page.published do |p|
78
+ p.public?
79
+ end
77
80
  end
78
81
  end
79
82
 
@@ -78,9 +78,11 @@ module Alchemy
78
78
  if attribute[:relation]
79
79
  record = resource.send(attribute[:relation][:name])
80
80
  value = record.present? ? record.send(attribute[:relation][:attr_method]) : Alchemy.t(:not_found)
81
- elsif attribute_value && (attribute[:type] == :datetime || attribute[:type] == :time)
81
+ elsif attribute_value && attribute[:type].to_s =~ /(date|time)/
82
82
  localization_format = if attribute[:type] == :datetime
83
83
  options[:datetime_format] || :'alchemy.default'
84
+ elsif attribute[:type] == :date
85
+ options[:date_format] || :'alchemy.default'
84
86
  else
85
87
  options[:time_format] || :'alchemy.time'
86
88
  end
@@ -100,19 +102,19 @@ module Alchemy
100
102
  # Returns a options hash for simple_form input fields.
101
103
  def resource_attribute_field_options(attribute)
102
104
  options = {hint: resource_handler.help_text_for(attribute)}
103
- case attribute[:type].to_s
105
+ input_type = attribute[:type].to_s
106
+ case input_type
104
107
  when 'boolean'
105
108
  options
106
- when 'date', 'datetime'
107
- options.merge as: 'string',
109
+ when 'date', 'time', 'datetime'
110
+ date = resource_instance_variable.send(attribute[:name]) || Time.current
111
+ options.merge(
112
+ as: 'string',
108
113
  input_html: {
109
- type: attribute[:type].to_s,
110
- value: l(resource_instance_variable.send(attribute[:name]) || Time.current,
111
- format: "#{attribute[:type]}picker".to_sym
112
- )
114
+ 'data-datepicker-type' => input_type,
115
+ value: date ? date.iso8601 : nil
113
116
  }
114
- when 'time'
115
- options.merge(as: 'time')
117
+ )
116
118
  when 'text'
117
119
  options.merge(as: 'text', input_html: {rows: 4})
118
120
  else