alchemy_cms 5.1.0.beta1 → 5.1.2

Sign up to get free protection for your applications and to get access to all the features.
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"