alchemy_cms 6.0.0.pre.rc4 → 6.0.0.pre.rc7

Sign up to get free protection for your applications and to get access to all the features.
Files changed (70) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/ci.yml +8 -15
  3. data/.github/workflows/stale.yml +21 -7
  4. data/.gitignore +0 -1
  5. data/.rspec +1 -0
  6. data/CHANGELOG.md +87 -0
  7. data/Gemfile +7 -6
  8. data/Rakefile +5 -1
  9. data/alchemy_cms.gemspec +2 -2
  10. data/app/assets/javascripts/alchemy/admin.js +0 -2
  11. data/app/assets/javascripts/alchemy/alchemy.dialog.js.coffee +6 -1
  12. data/app/assets/javascripts/alchemy/page_select.js +13 -8
  13. data/app/assets/javascripts/alchemy/templates/index.js +1 -0
  14. data/app/assets/javascripts/alchemy/templates/page.hbs +17 -7
  15. data/app/assets/javascripts/alchemy/templates/page_folder.hbs +3 -0
  16. data/app/assets/stylesheets/alchemy/archive.scss +4 -0
  17. data/app/assets/stylesheets/alchemy/page-select.scss +29 -4
  18. data/app/assets/stylesheets/alchemy/sitemap.scss +9 -7
  19. data/app/controllers/alchemy/admin/pages_controller.rb +10 -15
  20. data/app/controllers/alchemy/api/pages_controller.rb +14 -4
  21. data/app/models/alchemy/ingredient.rb +6 -1
  22. data/app/models/alchemy/page.rb +9 -3
  23. data/app/serializers/alchemy/page_serializer.rb +7 -1
  24. data/app/serializers/alchemy/page_tree_serializer.rb +3 -3
  25. data/app/services/alchemy/tag_validations.rb +21 -0
  26. data/app/views/alchemy/admin/pages/_form.html.erb +19 -0
  27. data/app/views/alchemy/admin/pages/_new_page_form.html.erb +16 -5
  28. data/app/views/alchemy/admin/pages/_page.html.erb +111 -133
  29. data/app/views/alchemy/admin/pages/_sitemap.html.erb +10 -16
  30. data/app/views/alchemy/admin/pages/_toolbar.html.erb +0 -12
  31. data/app/views/alchemy/admin/pages/edit.html.erb +1 -1
  32. data/app/views/alchemy/admin/pages/index.html.erb +1 -1
  33. data/app/views/alchemy/admin/pages/update.js.erb +7 -5
  34. data/app/views/alchemy/admin/partials/_routes.html.erb +8 -1
  35. data/app/views/alchemy/essences/_essence_page_editor.html.erb +1 -1
  36. data/config/locales/alchemy.en.yml +0 -4
  37. data/config/routes.rb +4 -3
  38. data/db/migrate/20200226213334_alchemy_four_point_four.rb +30 -30
  39. data/db/migrate/20200423073425_create_alchemy_essence_nodes.rb +1 -1
  40. data/db/migrate/20200504210159_remove_site_id_from_nodes.rb +1 -1
  41. data/db/migrate/20200505215518_add_language_id_foreign_key_to_alchemy_pages.rb +1 -1
  42. data/db/migrate/20200511113603_add_menu_type_to_alchemy_nodes.rb +1 -1
  43. data/db/migrate/20200514091507_make_page_layoutpage_null_false.rb +1 -1
  44. data/db/migrate/20200519073500_remove_visible_from_alchemy_pages.rb +1 -1
  45. data/db/migrate/20200617110713_create_alchemy_picture_thumbs.rb +1 -1
  46. data/db/migrate/20200907111332_remove_tri_state_booleans.rb +1 -1
  47. data/db/migrate/20201207131309_create_page_versions.rb +1 -1
  48. data/db/migrate/20201207135820_add_page_version_id_to_alchemy_elements.rb +1 -1
  49. data/lib/alchemy/engine.rb +5 -11
  50. data/lib/alchemy/permissions.rb +0 -1
  51. data/lib/alchemy/test_support/shared_ingredient_examples.rb +4 -2
  52. data/lib/alchemy/version.rb +1 -1
  53. data/lib/generators/alchemy/install/install_generator.rb +6 -1
  54. data/package/admin.js +5 -1
  55. data/package/src/image_loader.js +4 -2
  56. data/package/src/node_tree.js +13 -6
  57. data/package/src/page_publication_fields.js +28 -0
  58. data/package/src/page_sorter.js +62 -0
  59. data/package/src/picture_editors.js +4 -4
  60. data/package/src/sitemap.js +148 -0
  61. data/package/src/utils/__tests__/ajax.spec.js +52 -16
  62. data/package/src/utils/ajax.js +12 -0
  63. data/package.json +1 -1
  64. metadata +24 -24
  65. data/app/assets/javascripts/alchemy/alchemy.page_sorter.js +0 -24
  66. data/app/assets/javascripts/alchemy/alchemy.sitemap.js.coffee +0 -119
  67. data/app/views/alchemy/admin/pages/fold.js.erb +0 -2
  68. data/app/views/alchemy/admin/pages/sort.html.erb +0 -19
  69. data/lib/alchemy/error_tracking/airbrake_handler.rb +0 -13
  70. data/vendor/assets/javascripts/jquery_plugins/jquery.ui.nestedSortable.js +0 -434
@@ -1,23 +1,23 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- class AlchemyFourPointFour < ActiveRecord::Migration[5.2]
3
+ class AlchemyFourPointFour < ActiveRecord::Migration[6.0]
4
4
  def up
5
5
  unless table_exists?("alchemy_attachments")
6
- create_table "alchemy_attachments", force: :cascade do |t|
6
+ create_table "alchemy_attachments" do |t|
7
7
  t.string "name"
8
8
  t.string "file_name"
9
9
  t.string "file_mime_type"
10
10
  t.integer "file_size"
11
11
  t.references "creator"
12
12
  t.references "updater"
13
- t.timestamps null: false
13
+ t.timestamps
14
14
  t.string "file_uid"
15
15
  t.index ["file_uid"], name: "index_alchemy_attachments_on_file_uid"
16
16
  end
17
17
  end
18
18
 
19
19
  unless table_exists?("alchemy_contents")
20
- create_table "alchemy_contents", force: :cascade do |t|
20
+ create_table "alchemy_contents" do |t|
21
21
  t.string "name"
22
22
  t.references "essence", null: false, polymorphic: true, index: { unique: true }
23
23
  t.references "element", null: false
@@ -25,14 +25,14 @@ class AlchemyFourPointFour < ActiveRecord::Migration[5.2]
25
25
  end
26
26
 
27
27
  unless table_exists?("alchemy_elements")
28
- create_table "alchemy_elements", force: :cascade do |t|
28
+ create_table "alchemy_elements" do |t|
29
29
  t.string "name"
30
30
  t.integer "position"
31
31
  t.references "page", null: false, index: false
32
32
  t.boolean "public", default: true
33
33
  t.boolean "folded", default: false
34
34
  t.boolean "unique", default: false
35
- t.timestamps null: false
35
+ t.timestamps
36
36
  t.references "creator"
37
37
  t.references "updater"
38
38
  t.references "parent_element", index: false
@@ -44,27 +44,27 @@ class AlchemyFourPointFour < ActiveRecord::Migration[5.2]
44
44
  end
45
45
 
46
46
  unless table_exists?("alchemy_elements_alchemy_pages")
47
- create_table "alchemy_elements_alchemy_pages", id: false, force: :cascade do |t|
47
+ create_table "alchemy_elements_alchemy_pages", id: false do |t|
48
48
  t.references "element"
49
49
  t.references "page"
50
50
  end
51
51
  end
52
52
 
53
53
  unless table_exists?("alchemy_essence_booleans")
54
- create_table "alchemy_essence_booleans", force: :cascade do |t|
54
+ create_table "alchemy_essence_booleans" do |t|
55
55
  t.boolean "value"
56
56
  t.index ["value"], name: "index_alchemy_essence_booleans_on_value"
57
57
  end
58
58
  end
59
59
 
60
60
  unless table_exists?("alchemy_essence_dates")
61
- create_table "alchemy_essence_dates", force: :cascade do |t|
61
+ create_table "alchemy_essence_dates" do |t|
62
62
  t.datetime "date"
63
63
  end
64
64
  end
65
65
 
66
66
  unless table_exists?("alchemy_essence_files")
67
- create_table "alchemy_essence_files", force: :cascade do |t|
67
+ create_table "alchemy_essence_files" do |t|
68
68
  t.references "attachment"
69
69
  t.string "title"
70
70
  t.string "css_class"
@@ -73,13 +73,13 @@ class AlchemyFourPointFour < ActiveRecord::Migration[5.2]
73
73
  end
74
74
 
75
75
  unless table_exists?("alchemy_essence_htmls")
76
- create_table "alchemy_essence_htmls", force: :cascade do |t|
76
+ create_table "alchemy_essence_htmls" do |t|
77
77
  t.text "source"
78
78
  end
79
79
  end
80
80
 
81
81
  unless table_exists?("alchemy_essence_links")
82
- create_table "alchemy_essence_links", force: :cascade do |t|
82
+ create_table "alchemy_essence_links" do |t|
83
83
  t.string "link"
84
84
  t.string "link_title"
85
85
  t.string "link_target"
@@ -88,13 +88,13 @@ class AlchemyFourPointFour < ActiveRecord::Migration[5.2]
88
88
  end
89
89
 
90
90
  unless table_exists?("alchemy_essence_pages")
91
- create_table "alchemy_essence_pages", force: :cascade do |t|
91
+ create_table "alchemy_essence_pages" do |t|
92
92
  t.references "page"
93
93
  end
94
94
  end
95
95
 
96
96
  unless table_exists?("alchemy_essence_pictures")
97
- create_table "alchemy_essence_pictures", force: :cascade do |t|
97
+ create_table "alchemy_essence_pictures" do |t|
98
98
  t.references "picture"
99
99
  t.string "caption"
100
100
  t.string "title"
@@ -111,7 +111,7 @@ class AlchemyFourPointFour < ActiveRecord::Migration[5.2]
111
111
  end
112
112
 
113
113
  unless table_exists?("alchemy_essence_richtexts")
114
- create_table "alchemy_essence_richtexts", force: :cascade do |t|
114
+ create_table "alchemy_essence_richtexts" do |t|
115
115
  t.text "body"
116
116
  t.text "stripped_body"
117
117
  t.boolean "public"
@@ -119,14 +119,14 @@ class AlchemyFourPointFour < ActiveRecord::Migration[5.2]
119
119
  end
120
120
 
121
121
  unless table_exists?("alchemy_essence_selects")
122
- create_table "alchemy_essence_selects", force: :cascade do |t|
122
+ create_table "alchemy_essence_selects" do |t|
123
123
  t.string "value"
124
124
  t.index ["value"], name: "index_alchemy_essence_selects_on_value"
125
125
  end
126
126
  end
127
127
 
128
128
  unless table_exists?("alchemy_essence_texts")
129
- create_table "alchemy_essence_texts", force: :cascade do |t|
129
+ create_table "alchemy_essence_texts" do |t|
130
130
  t.text "body"
131
131
  t.string "link"
132
132
  t.string "link_title"
@@ -137,7 +137,7 @@ class AlchemyFourPointFour < ActiveRecord::Migration[5.2]
137
137
  end
138
138
 
139
139
  unless table_exists?("alchemy_folded_pages")
140
- create_table "alchemy_folded_pages", force: :cascade do |t|
140
+ create_table "alchemy_folded_pages" do |t|
141
141
  t.references "page", null: false, index: false
142
142
  t.references "user", null: false, index: false
143
143
  t.boolean "folded", default: false
@@ -146,13 +146,13 @@ class AlchemyFourPointFour < ActiveRecord::Migration[5.2]
146
146
  end
147
147
 
148
148
  unless table_exists?("alchemy_languages")
149
- create_table "alchemy_languages", force: :cascade do |t|
149
+ create_table "alchemy_languages" do |t|
150
150
  t.string "name"
151
151
  t.string "language_code"
152
152
  t.string "frontpage_name"
153
153
  t.string "page_layout", default: "intro"
154
154
  t.boolean "public", default: false
155
- t.timestamps null: false
155
+ t.timestamps
156
156
  t.references "creator"
157
157
  t.references "updater"
158
158
  t.boolean "default", default: false
@@ -165,16 +165,16 @@ class AlchemyFourPointFour < ActiveRecord::Migration[5.2]
165
165
  end
166
166
 
167
167
  unless table_exists?("alchemy_legacy_page_urls")
168
- create_table "alchemy_legacy_page_urls", force: :cascade do |t|
168
+ create_table "alchemy_legacy_page_urls" do |t|
169
169
  t.string "urlname", null: false
170
170
  t.references "page", null: false
171
- t.timestamps null: false
171
+ t.timestamps
172
172
  t.index ["urlname"], name: "index_alchemy_legacy_page_urls_on_urlname"
173
173
  end
174
174
  end
175
175
 
176
176
  unless table_exists?("alchemy_nodes")
177
- create_table "alchemy_nodes", force: :cascade do |t|
177
+ create_table "alchemy_nodes" do |t|
178
178
  t.string "name"
179
179
  t.string "title"
180
180
  t.string "url"
@@ -189,7 +189,7 @@ class AlchemyFourPointFour < ActiveRecord::Migration[5.2]
189
189
  t.references "language", null: false
190
190
  t.references "creator"
191
191
  t.references "updater"
192
- t.timestamps null: false
192
+ t.timestamps
193
193
  t.references "site", null: false
194
194
  t.index ["lft"], name: "index_alchemy_nodes_on_lft"
195
195
  t.index ["rgt"], name: "index_alchemy_nodes_on_rgt"
@@ -197,7 +197,7 @@ class AlchemyFourPointFour < ActiveRecord::Migration[5.2]
197
197
  end
198
198
 
199
199
  unless table_exists?("alchemy_pages")
200
- create_table "alchemy_pages", force: :cascade do |t|
200
+ create_table "alchemy_pages" do |t|
201
201
  t.string "name"
202
202
  t.string "urlname"
203
203
  t.string "title"
@@ -217,7 +217,7 @@ class AlchemyFourPointFour < ActiveRecord::Migration[5.2]
217
217
  t.boolean "robot_follow", default: true
218
218
  t.boolean "sitemap", default: true
219
219
  t.boolean "layoutpage", default: false
220
- t.timestamps null: false
220
+ t.timestamps
221
221
  t.references "creator"
222
222
  t.references "updater"
223
223
  t.references "language"
@@ -234,12 +234,12 @@ class AlchemyFourPointFour < ActiveRecord::Migration[5.2]
234
234
  end
235
235
 
236
236
  unless table_exists?("alchemy_pictures")
237
- create_table "alchemy_pictures", force: :cascade do |t|
237
+ create_table "alchemy_pictures" do |t|
238
238
  t.string "name"
239
239
  t.string "image_file_name"
240
240
  t.integer "image_file_width"
241
241
  t.integer "image_file_height"
242
- t.timestamps null: false
242
+ t.timestamps
243
243
  t.references "creator"
244
244
  t.references "updater"
245
245
  t.string "upload_hash"
@@ -250,10 +250,10 @@ class AlchemyFourPointFour < ActiveRecord::Migration[5.2]
250
250
  end
251
251
 
252
252
  unless table_exists?("alchemy_sites")
253
- create_table "alchemy_sites", force: :cascade do |t|
253
+ create_table "alchemy_sites" do |t|
254
254
  t.string "host"
255
255
  t.string "name"
256
- t.timestamps null: false
256
+ t.timestamps
257
257
  t.boolean "public", default: false
258
258
  t.text "aliases"
259
259
  t.boolean "redirect_to_primary_host"
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- class CreateAlchemyEssenceNodes < ActiveRecord::Migration[5.2]
3
+ class CreateAlchemyEssenceNodes < ActiveRecord::Migration[6.0]
4
4
  def change
5
5
  create_table :alchemy_essence_nodes do |t|
6
6
  t.references "node"
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
- class RemoveSiteIdFromNodes < ActiveRecord::Migration[5.2]
2
+ class RemoveSiteIdFromNodes < ActiveRecord::Migration[6.0]
3
3
  def up
4
4
  remove_foreign_key :alchemy_nodes, :alchemy_sites
5
5
  remove_index :alchemy_nodes, :site_id
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- class AddLanguageIdForeignKeyToAlchemyPages < ActiveRecord::Migration[5.2]
3
+ class AddLanguageIdForeignKeyToAlchemyPages < ActiveRecord::Migration[6.0]
4
4
  def change
5
5
  add_foreign_key :alchemy_pages, :alchemy_languages, column: :language_id
6
6
  change_column_null :alchemy_pages, :language_id, false, Alchemy::Language.default&.id
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
- class AddMenuTypeToAlchemyNodes < ActiveRecord::Migration[5.2]
2
+ class AddMenuTypeToAlchemyNodes < ActiveRecord::Migration[6.0]
3
3
  class LocalNode < ActiveRecord::Base
4
4
  self.table_name = :alchemy_nodes
5
5
  acts_as_nested_set scope: :language_id
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
- class MakePageLayoutpageNullFalse < ActiveRecord::Migration[5.2]
2
+ class MakePageLayoutpageNullFalse < ActiveRecord::Migration[6.0]
3
3
  def change
4
4
  change_column_null :alchemy_pages, :layoutpage, false, false
5
5
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
- class RemoveVisibleFromAlchemyPages < ActiveRecord::Migration[5.2]
2
+ class RemoveVisibleFromAlchemyPages < ActiveRecord::Migration[6.0]
3
3
  class LocalPage < ActiveRecord::Base
4
4
  self.table_name = "alchemy_pages"
5
5
 
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- class CreateAlchemyPictureThumbs < ActiveRecord::Migration[5.2]
3
+ class CreateAlchemyPictureThumbs < ActiveRecord::Migration[6.0]
4
4
  def up
5
5
  return if table_exists?(:alchemy_picture_thumbs)
6
6
 
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- class RemoveTriStateBooleans < ActiveRecord::Migration[5.2]
3
+ class RemoveTriStateBooleans < ActiveRecord::Migration[6.0]
4
4
  def change
5
5
  change_column_null :alchemy_elements, :public, false, false
6
6
  change_column_default :alchemy_elements, :public, true
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- class CreatePageVersions < ActiveRecord::Migration[5.2]
3
+ class CreatePageVersions < ActiveRecord::Migration[6.0]
4
4
  def change
5
5
  create_table :alchemy_page_versions do |t|
6
6
  t.references :page,
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- class AddPageVersionIdToAlchemyElements < ActiveRecord::Migration[5.2]
3
+ class AddPageVersionIdToAlchemyElements < ActiveRecord::Migration[6.0]
4
4
  class LocalPage < ActiveRecord::Base
5
5
  self.table_name = :alchemy_pages
6
6
  has_many :elements, class_name: "LocalElement", inverse_of: :page
@@ -19,11 +19,12 @@ module Alchemy
19
19
  NonStupidDigestAssets.whitelist += [/^tinymce\//]
20
20
  end
21
21
 
22
- # Gutentag downcases all tgas before save.
23
- # We support having tags with uppercase characters.
24
- # The Gutentag search is case insensitive.
25
- initializer "alchemy.gutentag_normalizer" do
22
+ # Gutentag downcases all tags before save
23
+ # and Gutentag validations are not case sensitive.
24
+ # But we support having tags with uppercase characters.
25
+ config.to_prepare do
26
26
  Gutentag.normaliser = ->(value) { value.to_s }
27
+ Gutentag.tag_validations = Alchemy::TagValidations
27
28
  end
28
29
 
29
30
  # Custom Ransack sort arrows
@@ -44,12 +45,5 @@ module Alchemy
44
45
  end
45
46
  end
46
47
  end
47
-
48
- initializer "alchemy.error_tracking" do
49
- if defined?(Airbrake)
50
- require_relative "error_tracking/airbrake_handler"
51
- Alchemy::ErrorTracking.notification_handler = Alchemy::ErrorTracking::AirbrakeHandler
52
- end
53
- end
54
48
  end
55
49
  end
@@ -147,7 +147,6 @@ module Alchemy
147
147
  :copy_language_tree,
148
148
  :flush,
149
149
  :order,
150
- :sort,
151
150
  :switch_language,
152
151
  ], Alchemy::Page
153
152
 
@@ -28,7 +28,7 @@ RSpec.shared_examples_for "an alchemy ingredient" do
28
28
 
29
29
  context "with element" do
30
30
  before do
31
- expect(element).to receive(:ingredient_definition_for) do
31
+ expect(element).to receive(:ingredient_definition_for).at_least(:once) do
32
32
  {
33
33
  settings: {
34
34
  linkable: true,
@@ -63,7 +63,9 @@ RSpec.shared_examples_for "an alchemy ingredient" do
63
63
  end
64
64
 
65
65
  before do
66
- expect(element).to receive(:ingredient_definition_for) { definition }
66
+ expect(element).to receive(:ingredient_definition_for).at_least(:once) do
67
+ definition
68
+ end
67
69
  end
68
70
 
69
71
  it "returns ingredient definition" do
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Alchemy
4
- VERSION = "6.0.0-rc4"
4
+ VERSION = "6.0.0-rc7"
5
5
 
6
6
  def self.version
7
7
  VERSION
@@ -23,6 +23,11 @@ module Alchemy
23
23
  default: false,
24
24
  desc: "Skip running the webpacker installer."
25
25
 
26
+ class_option :skip_db_create,
27
+ type: :boolean,
28
+ default: false,
29
+ desc: "Skip creting the database during install."
30
+
26
31
  source_root File.expand_path("files", __dir__)
27
32
 
28
33
  def setup
@@ -104,7 +109,7 @@ module Alchemy
104
109
  end
105
110
 
106
111
  def setup_database
107
- rake("db:create", abort_on_failure: true)
112
+ rake("db:create", abort_on_failure: true) unless options[:skip_db_create]
108
113
  # We can't invoke this rake task, because Rails will use wrong engine names otherwise
109
114
  rake("railties:install:migrations", abort_on_failure: true)
110
115
  rake("db:migrate", abort_on_failure: true)
data/package/admin.js CHANGED
@@ -6,6 +6,8 @@ import pictureEditors from "./src/picture_editors"
6
6
  import ImageLoader from "./src/image_loader"
7
7
  import ImageCropper from "./src/image_cropper"
8
8
  import Datepicker from "./src/datepicker"
9
+ import Sitemap from "./src/sitemap"
10
+ import PagePublicationFields from "./src/page_publication_fields.js"
9
11
 
10
12
  // Global Alchemy object
11
13
  if (typeof window.Alchemy === "undefined") {
@@ -22,5 +24,7 @@ Object.assign(Alchemy, {
22
24
  pictureEditors,
23
25
  ImageLoader: ImageLoader.init,
24
26
  ImageCropper,
25
- Datepicker
27
+ Datepicker,
28
+ Sitemap,
29
+ PagePublicationFields
26
30
  })
@@ -39,9 +39,11 @@ export default class ImageLoader {
39
39
  this.unbind()
40
40
  }
41
41
 
42
- onError() {
42
+ onError(evt) {
43
+ const message = `Could not load ${this.image.src}`
43
44
  this.removeSpinner()
44
- this.parent.innerHtml = '<span class="icon warn"></span>'
45
+ this.parent.innerHTML = `<span class="icon fas fa-exclamation-triangle" title="${message}" />`
46
+ console.error(message, evt)
45
47
  this.unbind()
46
48
  }
47
49
 
@@ -1,12 +1,16 @@
1
1
  import Sortable from "sortablejs"
2
- import ajax from "./utils/ajax"
2
+ import { patch } from "./utils/ajax"
3
3
  import { on } from "./utils/events"
4
4
 
5
5
  function displayNodeFolders() {
6
6
  document.querySelectorAll("li.menu-item").forEach((el) => {
7
7
  const leftIconArea = el.querySelector(".nodes_tree-left_images")
8
8
  const list = el.querySelector(".children")
9
- const node = { folded: el.dataset.folded === "true", id: el.dataset.id, type: el.dataset.type }
9
+ const node = {
10
+ folded: el.dataset.folded === "true",
11
+ id: el.dataset.id,
12
+ type: el.dataset.type
13
+ }
10
14
 
11
15
  if (list.children.length > 0 || node.folded) {
12
16
  leftIconArea.innerHTML = HandlebarsTemplates.node_folder({ node: node })
@@ -17,13 +21,15 @@ function displayNodeFolders() {
17
21
  }
18
22
 
19
23
  function onFinishDragging(evt) {
20
- const url = Alchemy.routes[evt.item.dataset.type].move_api_path(evt.item.dataset.id)
24
+ const url = Alchemy.routes[evt.item.dataset.type].move_api_path(
25
+ evt.item.dataset.id
26
+ )
21
27
  const data = {
22
28
  target_parent_id: evt.to.dataset.recordId,
23
29
  new_position: evt.newIndex
24
30
  }
25
31
 
26
- ajax("PATCH", url, data)
32
+ patch(url, data)
27
33
  .then(() => {
28
34
  const message = Alchemy.t("Successfully moved menu item")
29
35
  Alchemy.growl(message)
@@ -38,10 +44,11 @@ function handleNodeFolders() {
38
44
  on("click", ".nodes_tree", ".node_folder", function () {
39
45
  const nodeId = this.dataset.recordId
40
46
  const menu_item = this.closest("li.menu-item")
41
- const url = Alchemy.routes[this.dataset.recordType].toggle_folded_api_path(nodeId)
47
+ const url =
48
+ Alchemy.routes[this.dataset.recordType].toggle_folded_api_path(nodeId)
42
49
  const list = menu_item.querySelector(".children")
43
50
 
44
- ajax("PATCH", url)
51
+ patch(url)
45
52
  .then(() => {
46
53
  list.classList.toggle("folded")
47
54
  menu_item.dataset.folded =
@@ -0,0 +1,28 @@
1
+ // Handles the page publication date fields
2
+ export default function () {
3
+ document.addEventListener("DialogReady.Alchemy", function (evt) {
4
+ const dialog = evt.detail.body
5
+ const public_on_field = dialog.querySelector("#page_public_on")
6
+ const public_until_field = dialog.querySelector("#page_public_until")
7
+ const publication_date_fields = dialog.querySelector(
8
+ ".page-publication-date-fields"
9
+ )
10
+ const public_field = dialog.querySelector("#page_public")
11
+
12
+ if(!public_field) return
13
+
14
+ public_field.addEventListener("click", function (evt) {
15
+ const checkbox = evt.target
16
+ const now = new Date()
17
+
18
+ if (checkbox.checked) {
19
+ publication_date_fields.classList.remove("hidden")
20
+ public_on_field._flatpickr.setDate(now)
21
+ } else {
22
+ publication_date_fields.classList.add("hidden")
23
+ public_on_field.value = ""
24
+ }
25
+ public_until_field.value = ""
26
+ })
27
+ })
28
+ }
@@ -0,0 +1,62 @@
1
+ import Sortable from "sortablejs"
2
+ import { patch } from "./utils/ajax"
3
+
4
+ function onFinishDragging(evt) {
5
+ const pageId = evt.item.dataset.pageId
6
+ const url = Alchemy.routes.move_admin_page_path(pageId)
7
+ const data = {
8
+ target_parent_id: evt.to.dataset.parentId,
9
+ new_position: evt.newIndex
10
+ }
11
+
12
+ patch(url, data)
13
+ .then(async (response) => {
14
+ const pageData = await response.data
15
+ const pageEl = document.getElementById(`page_${pageId}`)
16
+ const urlPathEl = pageEl.querySelector(".sitemap_url")
17
+
18
+ Alchemy.growl(Alchemy.t("Successfully moved page"))
19
+ urlPathEl.textContent = pageData.url_path
20
+ displayPageFolders()
21
+ })
22
+ .catch((error) => {
23
+ Alchemy.growl(error.message || error, "error")
24
+ })
25
+ }
26
+
27
+ export function displayPageFolders() {
28
+ document.querySelectorAll("li.sitemap-item").forEach((el) => {
29
+ const pageFolderEl = el.querySelector(".page_folder")
30
+ const list = el.querySelector(".children")
31
+ const page = {
32
+ folded: el.dataset.folded === "true",
33
+ id: el.dataset.pageId,
34
+ type: el.dataset.type
35
+ }
36
+
37
+ if (list.children.length > 0 || page.folded) {
38
+ pageFolderEl.outerHTML = HandlebarsTemplates.page_folder({ page })
39
+ } else {
40
+ pageFolderEl.innerHTML = ""
41
+ }
42
+ })
43
+ }
44
+
45
+ export function createSortables(sortables) {
46
+ sortables.forEach((el) => {
47
+ new Sortable(el, {
48
+ group: "pages",
49
+ animation: 150,
50
+ fallbackOnBody: true,
51
+ swapThreshold: 0.65,
52
+ handle: ".handle",
53
+ onEnd: onFinishDragging
54
+ })
55
+ })
56
+ }
57
+
58
+ export default function () {
59
+ const sortables = document.querySelectorAll("ul.children")
60
+ displayPageFolders()
61
+ createSortables(sortables)
62
+ }
@@ -1,6 +1,6 @@
1
- import debounce from "lodash/debounce"
2
- import max from "lodash/max"
3
- import ajax from "./utils/ajax"
1
+ import debounce from "lodash-es/debounce"
2
+ import max from "lodash-es/max"
3
+ import { get } from "./utils/ajax"
4
4
  import ImageLoader from "./image_loader"
5
5
 
6
6
  const UPDATE_DELAY = 125
@@ -62,7 +62,7 @@ class PictureEditor {
62
62
  this.image.removeAttribute("alt")
63
63
  this.image.removeAttribute("src")
64
64
  this.imageLoader.load(true)
65
- ajax("GET", `/admin/pictures/${this.pictureId}/url`, {
65
+ get(`/admin/pictures/${this.pictureId}/url`, {
66
66
  crop: this.imageCropperEnabled,
67
67
  crop_from: this.cropFrom,
68
68
  crop_size: this.cropSize,