alchemy_cms 5.1.0.beta2 → 5.1.0.rc1

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.

checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 9e12f510dae37d181a93e110634f158c8f3fac7231c489579d61118528c1174a
4
- data.tar.gz: 467204837b948c660077c45e0dab282d23fff3c79a1fea854658d05098a3b8b7
3
+ metadata.gz: de384cea86fc5b57fd32980a289b81cc172a08bd6d9bea7f2fce58764247d5af
4
+ data.tar.gz: d82439febbc4a16884d31dcf84bf206ee7d886c2793ffa35d634611a522966ea
5
5
  SHA512:
6
- metadata.gz: 3037cd0a408e63b6edde1c58bc398ec4d377d6a9452caf0e6d8b418b438eb8e7176ca8336f5987089caf355c90e68fbca04ab7df3ac67fca76654efcf00d2cab
7
- data.tar.gz: d5a363910f6e4bfb221094c15f33c550dc3650c5eae35a375ea187e92bdac102e31c155168f336c1fa8f11a22235ff1558cc5433ad34fadc79cf59d28e0d883e
6
+ metadata.gz: cf29a07639284f89bac148065a3d2fdac90f8b24fba245d1af97e7dbb045bf7ee59f88b56a16c7f2211d99c9160605bd9be24ba7a333771b08a96d9c53450d60
7
+ data.tar.gz: 59d1b77141ab07bd6faa3733e730fb03e47bafa60ea855b339ca202239c99052c968b9419a6fd6bd7378250335d378fb883d2b1de638c7f14bc68204c32cac9c
@@ -8,6 +8,7 @@
8
8
  - Store current pictures size in session [#1927](https://github.com/AlchemyCMS/alchemy_cms/pull/1927) ([tvdeyen](https://github.com/tvdeyen))
9
9
  - Add support for custom mount points in Page::UrlPath [#1921](https://github.com/AlchemyCMS/alchemy_cms/pull/1921) ([tvdeyen](https://github.com/tvdeyen))
10
10
  - Allow to set a custom Page::UrlPath class [#1919](https://github.com/AlchemyCMS/alchemy_cms/pull/1919) ([tvdeyen](https://github.com/tvdeyen))
11
+ - Introduce a pages list view [#1906](https://github.com/AlchemyCMS/alchemy_cms/pull/1906) ([tvdeyen](https://github.com/tvdeyen))
11
12
 
12
13
  ### Changes
13
14
 
@@ -54,6 +54,7 @@ Gem::Specification.new do |gem|
54
54
  gem.add_development_dependency 'webdrivers', ['~> 4.0']
55
55
  gem.add_development_dependency 'webmock', ['~> 3.3']
56
56
  gem.add_development_dependency 'shoulda-matchers', ['~> 4.0']
57
+ gem.add_development_dependency 'timecop', ['~> 0.9']
57
58
 
58
59
  gem.post_install_message = <<-MSG
59
60
  -------------------------------------------------------------
@@ -1,4 +1,7 @@
1
- button, input[type="submit"], a.button, input.button {
1
+ button,
2
+ input[type="submit"],
3
+ a.button,
4
+ input.button {
2
5
  @include button-defaults;
3
6
  position: relative;
4
7
 
@@ -14,7 +17,8 @@ button, input[type="submit"], a.button, input.button {
14
17
  );
15
18
  }
16
19
 
17
- &:active, &:active:focus {
20
+ &:active,
21
+ &:active:focus {
18
22
  border-color: $button-hover-border-color;
19
23
  box-shadow: none;
20
24
  }
@@ -23,7 +27,7 @@ button, input[type="submit"], a.button, input.button {
23
27
  &.small {
24
28
  padding: $small-button-padding;
25
29
  vertical-align: inherit;
26
- line-height: 4*$default-padding;
30
+ line-height: 4 * $default-padding;
27
31
  font-size: inherit;
28
32
  }
29
33
 
@@ -83,13 +87,14 @@ button, input[type="submit"], a.button, input.button {
83
87
  border: $default-border-width $default-border-style $icon-color;
84
88
  color: $icon-color;
85
89
 
86
- .icon { color: inherit }
90
+ .icon {
91
+ color: inherit;
92
+ }
87
93
  }
88
94
  }
89
95
 
90
96
  &.disabled,
91
97
  &[disabled] {
92
-
93
98
  span {
94
99
  opacity: 0.3;
95
100
  cursor: not-allowed;
@@ -118,7 +123,8 @@ button.icon_button {
118
123
  border: 0 none;
119
124
  box-shadow: none;
120
125
 
121
- &:disabled, &.disabled {
126
+ &:disabled,
127
+ &.disabled {
122
128
  background: transparent;
123
129
  }
124
130
  }
@@ -131,10 +137,15 @@ button.icon_button {
131
137
  position: relative;
132
138
  display: inline-block;
133
139
  text-align: center;
134
- margin: 0 2*$default-margin;
135
140
 
136
- &.active, &:active, &:hover {
137
- .icon_button:not([disabled]) {
141
+ .toolbar_buttons & {
142
+ margin: 0 2 * $default-margin;
143
+ }
144
+
145
+ &.active,
146
+ &:active,
147
+ &:hover {
148
+ .icon_button:not([disabled]) {
138
149
  background-color: $default-border-color;
139
150
  cursor: pointer;
140
151
  }
@@ -161,8 +172,8 @@ button.icon_button {
161
172
  }
162
173
  }
163
174
 
164
- .button_with_label, .button_group {
165
-
175
+ .button_with_label,
176
+ .button_group {
166
177
  .icon_button {
167
178
  width: 29px;
168
179
  height: 29px;
@@ -179,16 +190,16 @@ button.icon_button {
179
190
  z-index: 30;
180
191
  background-color: $tooltip-background-color;
181
192
  color: $white;
182
- padding: $default-padding 2*$default-padding 1.5*$default-padding;
193
+ padding: $default-padding 2 * $default-padding 1.5 * $default-padding;
183
194
  line-height: 1;
184
195
  box-shadow: 0 0 4px $default-border-color;
185
196
  white-space: nowrap;
186
197
  pointer-events: none;
187
198
  opacity: 0;
188
- transition: .3s;
199
+ transition: 0.3s;
189
200
 
190
201
  &:before {
191
- content: '';
202
+ content: "";
192
203
  position: absolute;
193
204
  bottom: -10px;
194
205
  left: 8px;
@@ -238,6 +249,6 @@ button.icon_button {
238
249
  visibility: visible;
239
250
  opacity: 1;
240
251
  top: -25px;
241
- transition-delay: .2s;
252
+ transition-delay: 0.2s;
242
253
  }
243
254
  }
@@ -6,7 +6,8 @@ table {
6
6
  width: 100%;
7
7
 
8
8
  &.list .tools {
9
- button, a {
9
+ button,
10
+ a {
10
11
  display: inline-block;
11
12
  width: 18px;
12
13
  height: 18px;
@@ -22,8 +23,9 @@ table {
22
23
  }
23
24
  }
24
25
 
25
- .list td, .list th {
26
- padding: 2*$default-padding;
26
+ .list td,
27
+ .list th {
28
+ padding: 2 * $default-padding;
27
29
  vertical-align: top;
28
30
  line-height: 18px;
29
31
  border-right: 1px solid $medium-gray;
@@ -93,9 +95,10 @@ td.heading {
93
95
  background-color: $table-row-hover-color;
94
96
  }
95
97
 
96
- td, th {
97
-
98
- &.center, &.boolean {
98
+ td,
99
+ th {
100
+ &.center,
101
+ &.boolean {
99
102
  text-align: center;
100
103
  }
101
104
 
@@ -105,7 +108,6 @@ td, th {
105
108
  }
106
109
 
107
110
  td {
108
-
109
111
  &.file_name {
110
112
  word-break: break-all;
111
113
  }
@@ -118,7 +120,8 @@ td {
118
120
  width: 80px;
119
121
  }
120
122
 
121
- &.date, &.datetime {
123
+ &.date,
124
+ &.datetime {
122
125
  width: 150px;
123
126
  }
124
127
 
@@ -137,9 +140,35 @@ td {
137
140
  &.rights {
138
141
  width: 60px;
139
142
  }
143
+
144
+ &.page_layout {
145
+ width: 160px;
146
+ }
147
+
148
+ &.status {
149
+ width: 80px;
150
+
151
+ .page_status {
152
+ margin: 0 $default-margin;
153
+ }
154
+ }
155
+
156
+ &.tags {
157
+ width: 180px;
158
+
159
+ .tag {
160
+ margin: 0;
161
+ padding: 0 2 + $default-padding;
162
+
163
+ &:before {
164
+ padding-right: $default-padding;
165
+ }
166
+ }
167
+ }
140
168
  }
141
169
 
142
- th.count, td.count {
170
+ th.count,
171
+ td.count {
143
172
  width: 120px;
144
173
  text-align: right;
145
174
  padding-right: 16px;
@@ -2,12 +2,12 @@
2
2
 
3
3
  module Alchemy
4
4
  module Admin
5
- class PagesController < Alchemy::Admin::BaseController
5
+ class PagesController < ResourcesController
6
6
  include OnPageLayout::CallbacksRunner
7
7
 
8
8
  helper "alchemy/pages"
9
9
 
10
- before_action :load_page, except: [:index, :flush, :new, :order, :create, :copy_language_tree, :link, :sort]
10
+ before_action :load_resource, except: [:index, :flush, :new, :order, :create, :copy_language_tree, :link, :sort]
11
11
 
12
12
  authorize_resource class: Alchemy::Page, except: [:index, :tree]
13
13
 
@@ -27,11 +27,33 @@ module Alchemy
27
27
  if: :run_on_page_layout_callbacks?,
28
28
  only: [:show]
29
29
 
30
+ before_action :load_languages_and_layouts,
31
+ unless: -> { @page_root },
32
+ only: [:index]
33
+
34
+ before_action :set_view, only: [:index]
35
+
30
36
  def index
31
- if !@page_root
32
- @language = @current_language
33
- @languages_with_page_tree = Language.on_current_site.with_root_page
34
- @page_layouts = PageLayout.layouts_for_select(@language.id)
37
+ @query = @current_language.pages.contentpages.ransack(search_filter_params[:q])
38
+
39
+ if @view == "list"
40
+ @query.sorts = default_sort_order if @query.sorts.empty?
41
+ items = @query.result
42
+
43
+ if search_filter_params[:tagged_with].present?
44
+ items = items.tagged_with(search_filter_params[:tagged_with])
45
+ end
46
+
47
+ if search_filter_params[:filter].present?
48
+ items = items.public_send(sanitized_filter_params)
49
+ end
50
+
51
+ if search_filter_params[:page_layout].present?
52
+ items = items.where(page_layout: search_filter_params[:page_layout])
53
+ end
54
+
55
+ items = items.page(params[:page] || 1).per(items_per_page)
56
+ @pages = items
35
57
  end
36
58
  end
37
59
 
@@ -229,6 +251,19 @@ module Alchemy
229
251
 
230
252
  private
231
253
 
254
+ def resource_handler
255
+ @_resource_handler ||= Alchemy::Resource.new(controller_path, alchemy_module, Alchemy::Page)
256
+ end
257
+
258
+ def common_search_filter_includes
259
+ super.push(:page_layout, :view)
260
+ end
261
+
262
+ def set_view
263
+ @view = params[:view] || session[:alchemy_pages_view] || "tree"
264
+ session[:alchemy_pages_view] = @view
265
+ end
266
+
232
267
  def copy_of_language_root
233
268
  Page.copy(
234
269
  language_root_to_copy_from,
@@ -310,7 +345,7 @@ module Alchemy
310
345
  { my_urlname: default_urlname, children_path: default_urlname }
311
346
  end
312
347
 
313
- def load_page
348
+ def load_resource
314
349
  @page = Page.find(params[:id])
315
350
  end
316
351
 
@@ -373,6 +408,12 @@ module Alchemy
373
408
  user: current_alchemy_user,
374
409
  full: params[:full] == "true")
375
410
  end
411
+
412
+ def load_languages_and_layouts
413
+ @language = @current_language
414
+ @languages_with_page_tree = Language.on_current_site.with_root_page
415
+ @page_layouts = PageLayout.layouts_for_select(@language.id)
416
+ end
376
417
  end
377
418
  end
378
419
  end
@@ -164,6 +164,14 @@ module Alchemy
164
164
  @_url_path_class = klass
165
165
  end
166
166
 
167
+ def alchemy_resource_filters
168
+ %w[published not_public restricted]
169
+ end
170
+
171
+ def searchable_alchemy_resource_attributes
172
+ %w[name urlname title]
173
+ end
174
+
167
175
  # Used to store the current page previewed in the edit page template.
168
176
  #
169
177
  def current_preview=(page)
@@ -25,10 +25,38 @@ module Alchemy
25
25
  end
26
26
  end
27
27
 
28
- # Returns site's layout definition
28
+ # Returns sites layout definition
29
29
  #
30
30
  def definition
31
- self.class.definitions.detect { |l| l["name"] == partial_name }
31
+ self.class.definitions.detect { |l| l["name"] == partial_name } || {}
32
+ end
33
+
34
+ # Returns sites page layout names
35
+ #
36
+ # If no site layout file is defined all page layouts are returned
37
+ #
38
+ # @param [Boolean] layoutpages Return layout pages only (default false)
39
+ #
40
+ # @return [Array<String>] Array of page layout names
41
+ #
42
+ def page_layout_names(layoutpages: false)
43
+ page_layout_definitions.select do |layout|
44
+ !!layout["layoutpage"] && layoutpages || !layout["layoutpage"] && !layoutpages
45
+ end.collect { |layout| layout["name"] }
46
+ end
47
+
48
+ # Returns sites page layout definitions
49
+ #
50
+ # If no site layout file is defined all page layouts are returned
51
+ #
52
+ def page_layout_definitions
53
+ if definition["page_layouts"].presence
54
+ Alchemy::PageLayout.all.select do |layout|
55
+ layout["name"].in?(definition["page_layouts"])
56
+ end
57
+ else
58
+ Alchemy::PageLayout.all
59
+ end
32
60
  end
33
61
 
34
62
  # Returns the name for the layout partial
@@ -1,8 +1,7 @@
1
1
  <div class="panel">
2
2
  <%= render_message do %>
3
- <p><%= Alchemy.t(:language_does_not_exist) %></p>
3
+ <p><%= Alchemy.t(:homepage_does_not_exist) %></p>
4
4
  <% end %>
5
- <%- if @language -%>
6
5
 
7
6
  <%- if @languages_with_page_tree.size >= 1 -%>
8
7
  <%= form_tag(alchemy.copy_language_tree_admin_pages_path) do %>
@@ -19,32 +18,23 @@
19
18
  <% end %>
20
19
  <%- end -%>
21
20
 
22
- <%- if params[:action] == "index" -%>
23
- <%= alchemy_form_for([:admin, Alchemy::Page.new], id: 'create_language_tree') do |form| %>
24
- <% if @languages_with_page_tree.size >= 1 %>
25
- <h3><%= Alchemy.t(:create_language_tree_heading) %></h3>
26
- <p><%= Alchemy.t(:want_to_create_new_language) %></p>
27
- <% end %>
28
- <%= form.input :name, input_html: {value: @language.frontpage_name} %>
29
- <%= form.input :page_layout,
30
- collection: @page_layouts,
31
- selected: @language.page_layout,
32
- label: Alchemy.t(:page_type),
33
- include_blank: Alchemy.t('Please choose'),
34
- required: true,
35
- input_html: {class: 'alchemy_selectbox'} %>
36
- <%= form.hidden_field :language_id, value: @language.id %>
37
- <%= form.hidden_field :language_code, value: @language.code %>
38
- <%= form.hidden_field :language_root, value: true %>
39
- <%= form.hidden_field :public, value: Alchemy::Language.all.size == 1 %>
40
- <%= form.submit Alchemy.t("create_tree_as_new_language", language: @language.name), autofocus: true %>
21
+ <%= alchemy_form_for([:admin, Alchemy::Page.new], id: 'create_language_tree') do |form| %>
22
+ <% if @languages_with_page_tree.size >= 1 %>
23
+ <h3><%= Alchemy.t(:create_language_tree_heading) %></h3>
24
+ <p><%= Alchemy.t(:want_to_create_new_language) %></p>
41
25
  <% end %>
42
- <%- end -%>
43
-
44
- <%- else -%>
45
-
46
- <p><%= Alchemy.t("Actually this language does not exist. Please create this language first.") %></p>
47
-
48
- <%- end -%>
49
-
26
+ <%= form.input :name, input_html: {value: @language.frontpage_name} %>
27
+ <%= form.input :page_layout,
28
+ collection: @page_layouts,
29
+ selected: @language.page_layout,
30
+ label: Alchemy.t(:page_type),
31
+ include_blank: Alchemy.t('Please choose'),
32
+ required: true,
33
+ input_html: {class: 'alchemy_selectbox'} %>
34
+ <%= form.hidden_field :language_id, value: @language.id %>
35
+ <%= form.hidden_field :language_code, value: @language.code %>
36
+ <%= form.hidden_field :language_root, value: true %>
37
+ <%= form.hidden_field :public, value: Alchemy::Language.all.size == 1 %>
38
+ <%= form.submit Alchemy.t(:create), autofocus: true %>
39
+ <% end %>
50
40
  </div>
@@ -1,5 +1,14 @@
1
1
  <%= alchemy_form_for([:admin, @page]) do |f| %>
2
- <%= f.hidden_field(:parent_id) %>
2
+ <% if @page.parent_id || @page.layoutpage %>
3
+ <%= f.hidden_field(:parent_id) %>
4
+ <% else %>
5
+ <% @page.parent = @current_language.root_page %>
6
+ <%= f.input :parent_id,
7
+ collection: @current_language.pages.contentpages,
8
+ label_method: :name,
9
+ value_method: :id,
10
+ input_html: { class: "alchemy_selectbox" } %>
11
+ <% end %>
3
12
  <%= f.hidden_field(:language_id) %>
4
13
  <%= f.hidden_field(:layoutpage) %>
5
14
  <%= f.input :page_layout,
@@ -0,0 +1,29 @@
1
+ <div id="page_layout_filter">
2
+ <label>
3
+ <h3><%= Alchemy::Page.human_attribute_name(:page_layout) %></h3>
4
+ <%= select_tag(
5
+ "page_layout",
6
+ options_for_select(
7
+ @current_language.site.page_layout_names.map do |layout|
8
+ [
9
+ Alchemy::PageLayout.human_layout_name(layout),
10
+ layout
11
+ ]
12
+ end,
13
+ search_filter_params[:page_layout]
14
+ ),
15
+ include_blank: Alchemy.t(:all, scope: ["resources", "page", "filters"]),
16
+ class: "alchemy_selectbox full_width"
17
+ ) %>
18
+ </label>
19
+ </div>
20
+
21
+ <script type="text/javascript">
22
+ $(function() {
23
+ $("#page_layout").on("change", function(e) {
24
+ var url = "<%= alchemy.admin_pages_path(search_filter_params.except(:page_layout, :page).merge(view: "list")) %>";
25
+ delimiter = url.match(/\?/) ? "&" : "?";
26
+ Turbolinks.visit(url + delimiter + "page_layout=" + encodeURIComponent($(this).val()));
27
+ });
28
+ });
29
+ </script>
@@ -0,0 +1,27 @@
1
+ <table class="list">
2
+ <thead>
3
+ <tr>
4
+ <th class="icon"></th>
5
+ <th class="string name">
6
+ <%= sort_link [:alchemy, @query],
7
+ "name",
8
+ Alchemy::Page.human_attribute_name(:name),
9
+ default_order: "asc" %>
10
+ </th>
11
+ <th><%= Alchemy::Page.human_attribute_name(:urlname) %></th>
12
+ <th><%= Alchemy::Page.human_attribute_name(:page_type) %></th>
13
+ <th><%= Alchemy::Page.human_attribute_name(:tag_list) %></th>
14
+ <th>
15
+ <%= sort_link [:alchemy, @query],
16
+ :updated_at,
17
+ Alchemy::Page.human_attribute_name(:updated_at),
18
+ default_order: "desc" %>
19
+ </th>
20
+ <th class="status center"><%= Alchemy::Page.human_attribute_name(:status) %></th>
21
+ <th class="tools"></th>
22
+ </tr>
23
+ </thead>
24
+ <tbody>
25
+ <%= render partial: "table_row", collection: @pages, as: "page" %>
26
+ </tbody>
27
+ </table>
@@ -0,0 +1,107 @@
1
+ <tr class="<%= cycle(:even, :odd) %>" data-page-id="<%= page.id %>">
2
+ <td class="icon">
3
+ <% if can?(:edit_content, page) %>
4
+ <% if page.locked? %>
5
+ <span class="with-hint">
6
+ <i class="icon fas fa-edit fa-fw"></i>
7
+ <span class="hint-bubble">
8
+ <%= Alchemy.t("This page is locked", name: page.locker_name) %>
9
+ </span>
10
+ </span>
11
+ <% else %>
12
+ <i class="icon far fa-file fa-lg"></i>
13
+ <% end %>
14
+ <% else %>
15
+ <span class="with-hint">
16
+ <i class="icon fas fa-ban fa-fw"></i>
17
+ <span class="hint-bubble">
18
+ <%= Alchemy.t("Your user role does not allow you to edit this page") %>
19
+ </span>
20
+ </span>
21
+ <% end %>
22
+ </td>
23
+ <td class="string name">
24
+ <%= link_to_if(
25
+ can?(:edit_content, page),
26
+ page.name,
27
+ alchemy.edit_admin_page_path(page),
28
+ title: Alchemy.t(:edit_page),
29
+ ) { content_tag(:span, page.name) } -%>
30
+ </td>
31
+ <td class="url">
32
+ <%= page.url_path %>
33
+ </td>
34
+ <td class="page_layout">
35
+ <%= Alchemy.t(page.page_layout, scope: "page_layout_names", default: page.page_layout.to_s.humanize) %>
36
+ </td>
37
+ <td class="tags">
38
+ <% page.tag_list.each do |tag| %>
39
+ <%= content_tag(:span, tag, class: "tag") %>
40
+ <% end %>
41
+ </td>
42
+ <td class="date">
43
+ <%= l(page.updated_at, format: :"alchemy.default") %>
44
+ </td>
45
+ <td class="status center">
46
+ <span class="page_status with-hint">
47
+ <i class="icon fas fa-fw fa-compass <% unless page.public? %>disabled<% end %>" data-fa-transform="shrink-2"></i>
48
+ <span class="hint-bubble"><%= page.status_title(:public) %></span>
49
+ </span>
50
+ <span class="page_status with-hint">
51
+ <i class="icon fas fa-fw fa-lock <% unless page.restricted? %>disabled<% end %>" data-fa-transform="shrink-2"></i>
52
+ <span class="hint-bubble"><%= page.status_title(:restricted) %></span>
53
+ </span>
54
+ </td>
55
+ <td class="tools">
56
+ <% if can?(:info, page) %>
57
+ <div class="button_with_label">
58
+ <%= link_to_dialog(
59
+ render_icon('info-circle'),
60
+ alchemy.info_admin_page_path(page),
61
+ {
62
+ title: Alchemy.t(:page_infos),
63
+ size: '520x290'
64
+ }
65
+ ) %>
66
+ <label class="center"><%= Alchemy.t(:page_infos) %></label>
67
+ </div>
68
+ <% end %>
69
+ <% if can?(:configure, page) %>
70
+ <div class="button_with_label sitemap_tool">
71
+ <%= link_to_dialog(
72
+ render_icon(:cog),
73
+ alchemy.configure_admin_page_path(page),
74
+ {
75
+ title: Alchemy.t(:edit_page_properties),
76
+ size: '450x680'
77
+ }
78
+ ) -%>
79
+ <label class="center"><%= Alchemy.t(:edit_page_properties) %></label>
80
+ </div>
81
+ <% end %>
82
+ <% if can?(:copy, page) %>
83
+ <div class="button_with_label sitemap_tool">
84
+ <%= link_to(
85
+ render_icon(:copy),
86
+ alchemy.insert_admin_clipboard_path(
87
+ remarkable_type: :pages,
88
+ remarkable_id: page.id,
89
+ ),
90
+ remote: true,
91
+ method: :post
92
+ ) %>
93
+ <label class="center"><%= Alchemy.t(:copy_page) %></label>
94
+ </div>
95
+ <% end %>
96
+ <% if can?(:destroy, page) %>
97
+ <div class="button_with_label">
98
+ <%= link_to_confirm_dialog(
99
+ render_icon(:minus),
100
+ Alchemy.t(:confirm_to_delete_page),
101
+ alchemy.admin_page_path(page)
102
+ ) -%>
103
+ <label class="center"><%= Alchemy.t(:delete_page) %></label>
104
+ </div>
105
+ <% end %>
106
+ </td>
107
+ </tr>
@@ -0,0 +1,77 @@
1
+ <div class="toolbar_buttons">
2
+ <%= render "alchemy/admin/partials/site_select" %>
3
+ <%= render "alchemy/admin/partials/language_tree_select" %>
4
+ <%= toolbar_button(
5
+ icon: :plus,
6
+ url: alchemy.new_admin_page_path(language: @current_language),
7
+ hotkey: 'alt+n',
8
+ dialog_options: {
9
+ title: Alchemy.t('Add a page'),
10
+ size: '340x215',
11
+ overflow: true
12
+ },
13
+ title: Alchemy.t('Add a page'),
14
+ label: Alchemy.t('Add a page'),
15
+ if_permitted_to: [:create, Alchemy::Page]
16
+ ) %>
17
+ <div class="toolbar_spacer"></div>
18
+ <% if can?(:flush, Alchemy::Page) %>
19
+ <div class="button_with_label">
20
+ <%= link_to(
21
+ render_icon(:eraser),
22
+ alchemy.flush_admin_pages_path,
23
+ remote: true,
24
+ method: :post,
25
+ class: "icon_button please_wait",
26
+ title: Alchemy.t("Flush page cache")
27
+ ) %>
28
+ <label><%= Alchemy.t("Flush page cache") %></label>
29
+ </div>
30
+ <% end %>
31
+ <% if can?(:sort, Alchemy::Page) %>
32
+ <div class="button_with_label">
33
+ <%= link_to(
34
+ render_icon(:random),
35
+ alchemy.sort_admin_pages_path,
36
+ method: :get,
37
+ class: "icon_button",
38
+ title: Alchemy.t("Sort pages")
39
+ ) %>
40
+ <label><%= Alchemy.t("Sort pages") %></label>
41
+ </div>
42
+ <% end %>
43
+ <div class="button_with_label" id="clipboard_button">
44
+ <%= link_to_dialog(
45
+ render_icon(clipboard_empty?("pages") ? :clipboard : :paste),
46
+ alchemy.admin_clipboard_path(remarkable_type: "pages"),
47
+ {
48
+ title: Alchemy.t("Clipboard"),
49
+ size: "380x305"
50
+ },
51
+ class: "icon_button",
52
+ title: Alchemy.t("Show clipboard")
53
+ ) %>
54
+ <label><%= Alchemy.t("Show clipboard") %></label>
55
+ </div>
56
+ <div class="toolbar_spacer"></div>
57
+ <div class="button_with_label<%= @view != "list" ? " active" : nil %>">
58
+ <%= link_to(
59
+ render_icon(:stream),
60
+ alchemy.admin_pages_path(view: "tree"),
61
+ class: "icon_button"
62
+ ) %>
63
+ <label><%= Alchemy.t("Hierarchical") %></label>
64
+ </div>
65
+ <div class="button_with_label<%= @view == "list" ? " active" : nil %>">
66
+ <%= link_to(
67
+ render_icon("sort-amount-down-alt"),
68
+ alchemy.admin_pages_path(view: "list"),
69
+ class: "icon_button"
70
+ ) %>
71
+ <label><%= Alchemy.t("Sortable List") %></label>
72
+ </div>
73
+ </div>
74
+ <% search_filter_params[:view] = "list" %>
75
+ <%= render "alchemy/admin/partials/search_form",
76
+ url: alchemy.admin_pages_path(search_filter_params.except(:q, :page)),
77
+ additional_params: [:view, :page_layout, :tagged_with, :filter] %>
@@ -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"
@@ -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
 
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Alchemy
4
- VERSION = "5.1.0.beta2"
4
+ VERSION = "5.1.0.rc1"
5
5
 
6
6
  def self.version
7
7
  VERSION
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: alchemy_cms
3
3
  version: !ruby/object:Gem::Version
4
- version: 5.1.0.beta2
4
+ version: 5.1.0.rc1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Thomas von Deyen
@@ -583,6 +583,20 @@ dependencies:
583
583
  - - "~>"
584
584
  - !ruby/object:Gem::Version
585
585
  version: '4.0'
586
+ - !ruby/object:Gem::Dependency
587
+ name: timecop
588
+ requirement: !ruby/object:Gem::Requirement
589
+ requirements:
590
+ - - "~>"
591
+ - !ruby/object:Gem::Version
592
+ version: '0.9'
593
+ type: :development
594
+ prerelease: false
595
+ version_requirements: !ruby/object:Gem::Requirement
596
+ requirements:
597
+ - - "~>"
598
+ - !ruby/object:Gem::Version
599
+ version: '0.9'
586
600
  description: Alchemy is a powerful, userfriendly and flexible Rails CMS.
587
601
  email:
588
602
  - hello@alchemy-cms.com
@@ -928,10 +942,14 @@ files:
928
942
  - app/views/alchemy/admin/pages/_new_page_form.html.erb
929
943
  - app/views/alchemy/admin/pages/_page.html.erb
930
944
  - app/views/alchemy/admin/pages/_page_infos.html.erb
945
+ - app/views/alchemy/admin/pages/_page_layout_filter.html.erb
931
946
  - app/views/alchemy/admin/pages/_page_status.html.erb
932
947
  - app/views/alchemy/admin/pages/_publication_fields.html.erb
933
948
  - app/views/alchemy/admin/pages/_sitemap.html.erb
949
+ - app/views/alchemy/admin/pages/_table.html.erb
950
+ - app/views/alchemy/admin/pages/_table_row.html.erb
934
951
  - app/views/alchemy/admin/pages/_tinymce_custom_config.html.erb
952
+ - app/views/alchemy/admin/pages/_toolbar.html.erb
935
953
  - app/views/alchemy/admin/pages/configure.html.erb
936
954
  - app/views/alchemy/admin/pages/edit.html.erb
937
955
  - app/views/alchemy/admin/pages/flush.js.erb
@@ -939,6 +957,7 @@ files:
939
957
  - app/views/alchemy/admin/pages/index.html.erb
940
958
  - app/views/alchemy/admin/pages/info.html.erb
941
959
  - app/views/alchemy/admin/pages/link.html.erb
960
+ - app/views/alchemy/admin/pages/list/_table.html.erb
942
961
  - app/views/alchemy/admin/pages/locked.html.erb
943
962
  - app/views/alchemy/admin/pages/new.html.erb
944
963
  - app/views/alchemy/admin/pages/show.html.erb