alchemy_cms 5.1.0.beta1 → 5.1.2

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 (51) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/ci.yml +126 -0
  3. data/CHANGELOG.md +29 -1
  4. data/Gemfile +2 -2
  5. data/README.md +1 -1
  6. data/alchemy_cms.gemspec +1 -1
  7. data/app/assets/javascripts/alchemy/admin.js +0 -1
  8. data/app/assets/javascripts/alchemy/alchemy.element_editors.js.coffee +1 -4
  9. data/app/assets/javascripts/alchemy/alchemy.preview.js.coffee +0 -3
  10. data/app/assets/javascripts/alchemy/alchemy.preview_window.js.coffee +29 -4
  11. data/app/assets/stylesheets/alchemy/_variables.scss +7 -0
  12. data/app/assets/stylesheets/alchemy/admin.scss +0 -1
  13. data/app/assets/stylesheets/alchemy/buttons.scss +26 -15
  14. data/app/assets/stylesheets/alchemy/elements.scss +58 -19
  15. data/app/assets/stylesheets/alchemy/frame.scss +0 -1
  16. data/app/assets/stylesheets/alchemy/hints.scss +2 -1
  17. data/app/assets/stylesheets/alchemy/search.scss +1 -1
  18. data/app/assets/stylesheets/alchemy/selects.scss +26 -20
  19. data/app/assets/stylesheets/alchemy/tables.scss +38 -9
  20. data/app/controllers/alchemy/admin/pages_controller.rb +58 -8
  21. data/app/decorators/alchemy/element_editor.rb +1 -0
  22. data/app/models/alchemy/legacy_page_url.rb +1 -1
  23. data/app/models/alchemy/page.rb +8 -0
  24. data/app/models/alchemy/site/layout.rb +30 -2
  25. data/app/serializers/alchemy/page_tree_serializer.rb +4 -4
  26. data/app/views/alchemy/admin/elements/_element_toolbar.html.erb +1 -1
  27. data/app/views/alchemy/admin/elements/publish.js.erb +1 -0
  28. data/app/views/alchemy/admin/pages/_create_language_form.html.erb +19 -29
  29. data/app/views/alchemy/admin/pages/_new_page_form.html.erb +10 -1
  30. data/app/views/alchemy/admin/pages/_page_layout_filter.html.erb +29 -0
  31. data/app/views/alchemy/admin/pages/_table.html.erb +27 -0
  32. data/app/views/alchemy/admin/pages/_table_row.html.erb +107 -0
  33. data/app/views/alchemy/admin/pages/_toolbar.html.erb +77 -0
  34. data/app/views/alchemy/admin/pages/edit.html.erb +9 -1
  35. data/app/views/alchemy/admin/pages/index.html.erb +41 -74
  36. data/app/views/alchemy/admin/pages/list/_table.html.erb +31 -0
  37. data/app/views/alchemy/admin/pages/unlock.js.erb +2 -2
  38. data/app/views/alchemy/admin/pages/update.js.erb +19 -10
  39. data/app/views/alchemy/admin/resources/_filter_bar.html.erb +13 -11
  40. data/config/locales/alchemy.en.yml +6 -4
  41. data/lib/alchemy.rb +66 -0
  42. data/lib/alchemy/admin/preview_url.rb +2 -0
  43. data/lib/alchemy/engine.rb +0 -4
  44. data/lib/alchemy/permissions.rb +1 -0
  45. data/lib/alchemy/test_support/integration_helpers.rb +0 -7
  46. data/lib/alchemy/version.rb +1 -1
  47. data/lib/alchemy_cms.rb +2 -4
  48. data/vendor/assets/javascripts/jquery_plugins/select2.js +3729 -0
  49. data/vendor/assets/stylesheets/alchemy_admin/select2.scss +740 -0
  50. metadata +30 -29
  51. data/.travis.yml +0 -48
@@ -3,81 +3,48 @@
3
3
  <% end %>
4
4
 
5
5
  <% content_for :toolbar do %>
6
- <div class="toolbar_buttons">
7
- <%= render 'alchemy/admin/partials/site_select' %>
8
- <%= render 'alchemy/admin/partials/language_tree_select' %>
9
- <% if can?(:flush, Alchemy::Page) %>
10
- <div class="button_with_label">
11
- <%= link_to(
12
- render_icon(:eraser),
13
- alchemy.flush_admin_pages_path,
14
- :remote => true,
15
- :method => :post,
16
- :class => 'icon_button please_wait',
17
- :title => Alchemy.t('Flush page cache')
18
- ) %>
19
- <label><%= Alchemy.t('Flush page cache') %></label>
20
- </div>
21
- <% end %>
22
- <% if can?(:sort, Alchemy::Page) %>
23
- <div class="button_with_label">
24
- <%= link_to(
25
- render_icon(:random),
26
- alchemy.sort_admin_pages_path,
27
- method: :get,
28
- class: 'icon_button',
29
- title: Alchemy.t('Sort pages')
30
- ) %>
31
- <label><%= Alchemy.t('Sort pages') %></label>
32
- </div>
33
- <% end %>
34
- <div class="button_with_label" id="clipboard_button">
35
- <%= link_to_dialog(
36
- render_icon(clipboard_empty?('pages') ? :clipboard : :paste),
37
- alchemy.admin_clipboard_path(:remarkable_type => 'pages'),
38
- {
39
- :title => Alchemy.t('Clipboard'),
40
- :size => '380x305'
41
- },
42
- :class => 'icon_button',
43
- :title => Alchemy.t('Show clipboard')
44
- ) %>
45
- <label><%= Alchemy.t('Show clipboard') %></label>
46
- </div>
47
- </div>
48
- <div class="search_form">
49
- <div class="search_field">
50
- <label>
51
- <%= text_field_tag 'filter', '',
52
- class: 'search_input_field',
53
- placeholder: Alchemy.t(:search),
54
- id: nil %>
55
- <%= render_icon :search %>
56
- </label>
57
- <%= link_to(render_icon(:times, size: 'xs'), '#', {
58
- class: "search_field_clear",
59
- title: Alchemy.t(:click_to_show_all)
60
- }) %>
61
- </div>
62
- </div>
6
+ <%= render "alchemy/admin/pages/toolbar" %>
63
7
  <% end %>
64
8
 
65
- <div id="archive_all">
66
- <% if @page_root %>
67
- <h2 id="page_filter_result"></h2>
68
-
69
- <%= render 'sitemap', page_partial: 'page', full: !!@sorting %>
70
-
71
- <% elsif can?(:create, Alchemy::Page) %>
72
-
73
- <%= render partial: 'create_language_form' %>
74
-
75
- <% else %>
76
-
77
- <%= render_message :warn do %>
78
- <h2>No language root page found.</h2>
79
- <p>Please ask the admin to create one.</p>
9
+ <%= content_tag :div,
10
+ id: "archive_all",
11
+ class: [@view == "list" && "resources-table-wrapper with_tag_filter"] do %>
12
+ <% if @view == "list" %>
13
+ <%= render "alchemy/admin/resources/table_header", resources_instance_variable: @pages %>
14
+ <% if @pages.any? %>
15
+ <%= render "table" %>
16
+ <% elsif search_filter_params.present? %>
17
+ <%= render_message do %>
18
+ <%= Alchemy.t("No pages found") %>
19
+ <% end %>
20
+ <% elsif can?(:create, Alchemy::Page) %>
21
+ <%= render partial: "create_language_form" %>
22
+ <% end %>
23
+
24
+ <%= paginate @pages, scope: alchemy, theme: "alchemy" %>
25
+
26
+ <div id="library_sidebar">
27
+ <%= render "page_layout_filter" %>
28
+
29
+ <%= render "filter_bar",
30
+ label: Alchemy::Page.human_attribute_name(:status),
31
+ url: alchemy.admin_pages_path(search_filter_params.except(:filter, :page).merge(view: "list")) %>
32
+
33
+ <% if resource_has_tags %>
34
+ <%= render "tag_list" %>
35
+ <% end %>
36
+ </div>
37
+ <% else %>
38
+ <% if @page_root %>
39
+ <h2 id="page_filter_result"></h2>
40
+ <%= render "sitemap", page_partial: "page", full: !!@sorting %>
41
+ <% elsif can?(:create, Alchemy::Page) %>
42
+ <%= render partial: "create_language_form" %>
43
+ <% else %>
44
+ <%= render_message :warn do %>
45
+ <h2>No language root page found.</h2>
46
+ <p>Please ask the admin to create one.</p>
47
+ <% end %>
48
+ <% end %>
80
49
  <% end %>
81
-
82
50
  <% end %>
83
- </div>
@@ -0,0 +1,31 @@
1
+ <% if @pages.any? %>
2
+ <table class="list">
3
+ <thead>
4
+ <tr>
5
+ <th class="icon"></th>
6
+ <th class="string name">
7
+ <%= sort_link [:alchemy, @query],
8
+ "name",
9
+ Alchemy::Page.human_attribute_name(:name),
10
+ default_order: "asc" %>
11
+ </th>
12
+ <th><%= Alchemy::Page.human_attribute_name(:urlname) %></th>
13
+ <th><%= Alchemy::Page.human_attribute_name(:page_type) %></th>
14
+ <th><%= Alchemy::Page.human_attribute_name(:tag_list) %></th>
15
+ <th class="status center"><%= Alchemy::Page.human_attribute_name(:status) %></th>
16
+ <th class="tools"></th>
17
+ </tr>
18
+ </thead>
19
+ <tbody>
20
+ <%= render partial: "page", collection: @pages %>
21
+ </tbody>
22
+ </table>
23
+ <% elsif search_filter_params.present? %>
24
+ <%= render_message do %>
25
+ <%= Alchemy.t('No pages found') %>
26
+ <% end %>
27
+ <% else %>
28
+ <%= render partial: 'alchemy/admin/pages/create_language_form' %>
29
+ <% end %>
30
+
31
+ <%= paginate @pages, scope: alchemy, theme: 'alchemy' %>
@@ -2,12 +2,12 @@
2
2
  var locked_page_tab = document.querySelector('#locked_page_<%= @page.id -%>')
3
3
  var locked_page_icon = document.querySelector(
4
4
  '#page_<%= @page.id -%> > .sitemap_page > .sitemap_left_images .with-hint'
5
- )
5
+ ) || document.querySelector('[data-page-id="<%= @page.id -%>"] > .icon')
6
6
  if (locked_page_tab) {
7
7
  locked_page_tab.remove()
8
8
  }
9
9
  if (locked_page_icon) {
10
- locked_page_icon.innerHTML = '<%= j render_icon(:file, style: 'regular', size: 'lg') %>'
10
+ locked_page_icon.innerHTML = '<i class="icon far fa-file fa-lg"></i>'
11
11
  }
12
12
  Alchemy.growl('<%= flash[:notice] -%>')
13
13
  })()
@@ -1,5 +1,5 @@
1
1
  (function() {
2
- var $page;
2
+ var page = document.querySelector('#page_<%= @page.id %>');
3
3
 
4
4
  <% if @old_page_layout != @page.page_layout -%>
5
5
  Alchemy.ElementsWindow.reload();
@@ -9,18 +9,27 @@
9
9
  <% if @while_page_edit -%>
10
10
 
11
11
  Alchemy.reloadPreview();
12
- $('#page_<%= @page.id %>_status').replaceWith('<%= j render("current_page", current_page: @page) %>');
12
+ document.querySelector('#page_<%= @page.id %>_status').outerHTML = '<%= j render("current_page", current_page: @page) %>';
13
+ Alchemy.growl("<%= j @notice %>");
14
+ Alchemy.closeCurrentDialog();
13
15
 
14
16
  <% else -%>
15
17
 
16
- var page_html = "<%= j render('page', page: @page) %>";
17
- var compiler = Handlebars.compile(page_html);
18
- var tree = <%== @tree.to_json %>;
19
- var html = compiler(tree.pages[0]);
20
- $('#page_<%= @page.id %>').replaceWith(html);
18
+ if (page) {
19
+ var page_html = "<%= j render('page', page: @page) %>";
20
+ var compiler = Handlebars.compile(page_html);
21
+ var tree = <%== @tree.to_json %>;
22
+ page.outerHTML = compiler(tree.pages[0]);
23
+ Alchemy.growl("<%= j @notice %>");
24
+ Alchemy.closeCurrentDialog();
25
+ } else {
26
+ document.addEventListener('turbolinks:load', function () {
27
+ Alchemy.growl("<%= j @notice %>");
28
+ }, { once: true })
29
+ Alchemy.closeCurrentDialog(function() {
30
+ Turbolinks.visit(location.toString(), { action: "replace" });
31
+ });
32
+ }
21
33
 
22
34
  <% end -%>
23
-
24
- Alchemy.growl("<%= j @notice %>");
25
- Alchemy.closeCurrentDialog();
26
35
  })()
@@ -1,21 +1,23 @@
1
1
  <div id="filter_bar">
2
- <h3><%= Alchemy.t('Filter') %></h3>
3
- <%= select_tag(
4
- 'resource_filter',
5
- options_for_select(
6
- resource_filter_select, search_filter_params[:filter]
7
- ),
8
- include_blank: Alchemy.t(:all, scope: ['resources', resource_name, 'filters']),
9
- data: { remote: !!request.xhr? },
10
- class: 'alchemy_selectbox'
11
- ) %>
2
+ <label>
3
+ <h3><%= local_assigns[:label] || Alchemy.t('Filter') %></h3>
4
+ <%= select_tag(
5
+ 'resource_filter',
6
+ options_for_select(
7
+ resource_filter_select, search_filter_params[:filter]
8
+ ),
9
+ include_blank: Alchemy.t(:all, scope: ['resources', resource_name, 'filters']),
10
+ data: { remote: !!request.xhr? },
11
+ class: 'alchemy_selectbox'
12
+ ) %>
13
+ </label>
12
14
  </div>
13
15
 
14
16
  <script type="text/javascript">
15
17
  $(function() {
16
18
  $('#resource_filter').on('change', function(e) {
17
19
  var $this = $(this);
18
- var url = '<%= resources_path(resource_handler.namespaced_resources_name, search_filter_params.except(:filter).to_h) %>';
20
+ var url = '<%= local_assigns[:url] || resources_path(resource_handler.namespaced_resources_name, search_filter_params.except(:filter).to_h) %>';
19
21
  if ($this.data('remote') === true) {
20
22
  $.get(url, {filter: $this.val()}, null, 'script');
21
23
  } else {
@@ -171,8 +171,8 @@ en:
171
171
  anchor_link_headline: "You can link to an element anchor from the actual page."
172
172
  attribute_fixed: Value can't be changed for this page type
173
173
  back: 'back'
174
- create_tree_as_new_language: "Create %{language} as a new language tree"
175
174
  locked_pages: "Active pages"
175
+ "Add a page": "Add a page"
176
176
  "Add global page": "Add global page"
177
177
  "Add page link": "Add page link"
178
178
  "Alchemy is open software and itself uses open software and free resources:": "Alchemy is open software and itself uses open software and free resources:"
@@ -339,13 +339,13 @@ en:
339
339
  copy_element: "Copy this element"
340
340
  copy_page: "Copy page"
341
341
  "Could not delete Pictures": "Could not delete Pictures"
342
- copy_language_tree_heading: "Copy page tree"
342
+ copy_language_tree_heading: "Copy pages"
343
343
  country_code_placeholder: 'i.e. US (optional)'
344
344
  country_code_foot_note: "You only need to set a country code if you want to support multiple countries with the same language."
345
345
  create: "create"
346
346
  "Create language": "Create a new language"
347
347
  "Create site": "Create a new site"
348
- create_language_tree_heading: "Create empty language tree"
348
+ create_language_tree_heading: "Create new homepage"
349
349
  create_menu: "Add a menu"
350
350
  create_node: "Add a menu node"
351
351
  create_page: "Create a new subpage"
@@ -402,6 +402,7 @@ en:
402
402
  "Open upload form": "Open upload form"
403
403
  "Select all pictures": "Select all pictures"
404
404
  hide_element_content: "Hide this elements content."
405
+ homepage_does_not_exist: "This language has no homepage yet"
405
406
  dashboard: "Dashboard"
406
407
  image_alt_tag: "Alt-tag"
407
408
  image_caption: "Caption"
@@ -415,7 +416,6 @@ en:
415
416
  javascript_disabled_headline: "Javascript is disabled!"
416
417
  javascript_disabled_text: "Alchemy needs Javascript to run smoothly. Please enable it in your browser settings."
417
418
  language_code_placeholder: 'i.e. en'
418
- language_does_not_exist: "This language tree does not exist"
419
419
  language_pages_copied: "Language tree successfully copied."
420
420
  last_upload_only: "Last upload only"
421
421
  left: "left"
@@ -521,6 +521,7 @@ en:
521
521
  '768': '768px (iPad - Portrait)'
522
522
  '1024': '1024px (iPad - Landscape)'
523
523
  '1280': '1280px (Desktop)'
524
+ preview_url: Preview
524
525
  recently_uploaded_only: 'Recently uploaded only'
525
526
  "regular method": "Regular method"
526
527
  remove: "Remove"
@@ -690,6 +691,7 @@ en:
690
691
  alchemy/message:
691
692
  one: Message
692
693
  other: Messages
694
+ alchemy/admin/preview_url: Internal
693
695
  attributes:
694
696
  alchemy/message:
695
697
  salutation: 'Salutation'
@@ -0,0 +1,66 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "alchemy/admin/preview_url"
4
+
5
+ module Alchemy
6
+ YAML_WHITELIST_CLASSES = %w(Symbol Date Regexp)
7
+
8
+ # Define page preview sources
9
+ #
10
+ # A preview source is a Ruby class returning an URL
11
+ # that is used as source for the preview frame in the
12
+ # admin UI.
13
+ #
14
+ # == Example
15
+ #
16
+ # # lib/acme/preview_source.rb
17
+ # class Acme::PreviewSource < Alchemy::Admin::PreviewUrl
18
+ # def url_for(page)
19
+ # if page.site.name == "Next"
20
+ # "https://user:#{ENV['PREVIEW_HTTP_PASS']}@next.acme.com"
21
+ # else
22
+ # "https://www.acme.com"
23
+ # end
24
+ # end
25
+ # end
26
+ #
27
+ # # config/initializers/alchemy.rb
28
+ # require "acme/preview_source"
29
+ # Alchemy.preview_sources << Acme::PreviewSource
30
+ #
31
+ # # config/locales/de.yml
32
+ # de:
33
+ # activemodel:
34
+ # models:
35
+ # acme/preview_source: Acme Vorschau
36
+ #
37
+ def self.preview_sources
38
+ @_preview_sources ||= begin
39
+ Set.new << Alchemy::Admin::PreviewUrl
40
+ end
41
+ end
42
+
43
+ # Define page publish targets
44
+ #
45
+ # A publish target is a ActiveJob that gets performed
46
+ # whenever a user clicks the publish page button.
47
+ #
48
+ # Use this to trigger deployment hooks of external
49
+ # services in an asychronous way.
50
+ #
51
+ # == Example
52
+ #
53
+ # # app/jobs/publish_job.rb
54
+ # class PublishJob < ApplicationJob
55
+ # def perform(page)
56
+ # RestClient.post(ENV['BUILD_HOOK_URL'])
57
+ # end
58
+ # end
59
+ #
60
+ # # config/initializers/alchemy.rb
61
+ # Alchemy.publish_targets << PublishJob
62
+ #
63
+ def self.publish_targets
64
+ @_publish_targets ||= Set.new
65
+ end
66
+ end
@@ -31,6 +31,8 @@ module Alchemy
31
31
  # password: <%= ENV["BASIC_AUTH_PASSWORD"] %>
32
32
  #
33
33
  class PreviewUrl
34
+ extend ActiveModel::Translation
35
+
34
36
  class MissingProtocolError < StandardError; end
35
37
 
36
38
  def initialize(routes:)
@@ -9,10 +9,6 @@ module Alchemy
9
9
  Alchemy::LOOKUP_CONTEXT = ActionView::LookupContext.new(Rails.root.join("app", "views", "alchemy"))
10
10
  end
11
11
 
12
- initializer "alchemy.admin.preview_url" do
13
- Alchemy::Admin::PREVIEW_URL = Alchemy::Admin::PreviewUrl.new(routes: Alchemy::Engine.routes)
14
- end
15
-
16
12
  initializer "alchemy.dependency_tracker" do
17
13
  [:erb, :slim, :haml].each do |handler|
18
14
  ActionView::DependencyTracker.register_tracker(handler, CacheDigests::TemplateTracker)
@@ -220,6 +220,7 @@ module Alchemy
220
220
  :update,
221
221
  :unlock,
222
222
  :visit,
223
+ :tree,
223
224
  to: :edit_content
224
225
  end
225
226
 
@@ -17,15 +17,8 @@ module Alchemy
17
17
  else
18
18
  user = build(:alchemy_dummy_user, user_or_role)
19
19
  end
20
- set_phantomjs_browser_language("en")
21
20
  allow_any_instance_of(ApplicationController).to receive(:current_user).and_return(user)
22
21
  end
23
-
24
- def set_phantomjs_browser_language(lang = nil)
25
- if Capybara.current_driver == :poltergeist
26
- page.driver.headers = {"Accept-Language" => lang}
27
- end
28
- end
29
22
  end
30
23
  end
31
24
  end
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Alchemy
4
- VERSION = "5.1.0.beta1"
4
+ VERSION = "5.1.2"
5
5
 
6
6
  def self.version
7
7
  VERSION
@@ -1,8 +1,7 @@
1
1
  # frozen_string_literal: true
2
+
2
3
  # Instantiate the global Alchemy namespace
3
- module Alchemy
4
- YAML_WHITELIST_CLASSES = %w(Symbol Date Regexp)
5
- end
4
+ require "alchemy"
6
5
 
7
6
  # Require globally used external libraries
8
7
  require "acts_as_list"
@@ -22,7 +21,6 @@ require "request_store"
22
21
  require "responders"
23
22
  require "sassc-rails"
24
23
  require "simple_form"
25
- require "select2-rails"
26
24
  require "turbolinks"
27
25
  require "userstamp"
28
26
  require "webpacker"