alchemy_cms 6.0.0.pre.b6 → 6.0.0.pre.rc4

Sign up to get free protection for your applications and to get access to all the features.
Files changed (37) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/brakeman-analysis.yml +46 -0
  3. data/.github/workflows/ci.yml +4 -3
  4. data/CHANGELOG.md +35 -0
  5. data/Gemfile +6 -1
  6. data/README.md +1 -1
  7. data/SECURITY.md +13 -0
  8. data/alchemy_cms.gemspec +3 -3
  9. data/app/assets/javascripts/alchemy/admin.js +0 -1
  10. data/app/assets/javascripts/alchemy/alchemy.gui.js.coffee +2 -2
  11. data/app/assets/javascripts/alchemy/alchemy.link_dialog.js.coffee +7 -6
  12. data/app/assets/stylesheets/alchemy/_extends.scss +4 -4
  13. data/app/assets/stylesheets/alchemy/flatpickr.scss +182 -232
  14. data/app/assets/stylesheets/tinymce/skins/alchemy/content.min.css.scss +3 -3
  15. data/app/assets/stylesheets/tinymce/skins/alchemy/skin.min.css.scss +7 -7
  16. data/app/controllers/alchemy/admin/base_controller.rb +9 -3
  17. data/app/models/alchemy/attachment.rb +1 -1
  18. data/app/models/alchemy/element.rb +1 -1
  19. data/app/models/alchemy/node.rb +1 -1
  20. data/app/models/alchemy/page.rb +1 -1
  21. data/app/models/alchemy/picture.rb +1 -1
  22. data/app/views/alchemy/ingredients/_picture_editor.html.erb +2 -2
  23. data/config/brakeman.ignore +25 -5
  24. data/lib/alchemy/engine.rb +8 -1
  25. data/lib/alchemy/error_tracking/airbrake_handler.rb +13 -0
  26. data/lib/alchemy/error_tracking.rb +14 -0
  27. data/lib/alchemy/taggable.rb +11 -4
  28. data/lib/alchemy/upgrader.rb +6 -0
  29. data/lib/alchemy/version.rb +1 -1
  30. data/lib/alchemy_cms.rb +1 -0
  31. data/lib/generators/alchemy/install/install_generator.rb +2 -1
  32. data/lib/tasks/alchemy/upgrade.rake +6 -0
  33. data/package/admin.js +3 -1
  34. data/package/src/datepicker.js +39 -0
  35. data/package.json +2 -1
  36. metadata +44 -39
  37. data/app/assets/javascripts/alchemy/alchemy.datepicker.js.coffee +0 -29
@@ -30,7 +30,7 @@ td,th {
30
30
 
31
31
  .mce-object {
32
32
  border: 1px dotted #3a3a3a;
33
- background: #d5d5d5 url(img/object.gif) no-repeat center;
33
+ background: #d5d5d5 url('tinymce/skins/alchemy/fonts/img/object.gif') no-repeat center;
34
34
  }
35
35
 
36
36
  .mce-pagebreak {
@@ -55,7 +55,7 @@ td,th {
55
55
  width: 9px!important;
56
56
  height: 9px!important;
57
57
  border: 1px dotted #3a3a3a;
58
- background: #d5d5d5 url(img/anchor.gif) no-repeat center;
58
+ background: #d5d5d5 url('tinymce/skins/alchemy/fonts/img/anchor.gif') no-repeat center;
59
59
  }
60
60
 
61
61
  .mce-nbsp {
@@ -77,7 +77,7 @@ hr {
77
77
  }
78
78
 
79
79
  .mce-spellchecker-word {
80
- background: url(img/wline.gif) repeat-x bottom left;
80
+ background: url('tinymce/skins/alchemy/fonts/img/wline.gif') repeat-x bottom left;
81
81
  cursor: default;
82
82
  }
83
83
 
@@ -1561,23 +1561,23 @@ i.mce-i-resize {
1561
1561
  opacity: 0.6;
1562
1562
  filter: alpha(opacity=60);
1563
1563
  zoom: 1;
1564
- background: #fff url('img/loader.gif') no-repeat center center;
1564
+ background: #fff url('tinymce/skins/alchemy/fonts/img/loader.gif') no-repeat center center;
1565
1565
  }
1566
1566
 
1567
1567
  @font-face {
1568
1568
  font-family: 'tinymce';
1569
- src: url('fonts/tinymce.woff') format('woff'),
1570
- url('fonts/tinymce.ttf') format('truetype'),
1571
- url('fonts/tinymce.svg#tinymce') format('svg');
1569
+ src: url('tinymce/skins/alchemy/fonts/tinymce.woff') format('woff'),
1570
+ url('tinymce/skins/alchemy/fonts/tinymce.ttf') format('truetype'),
1571
+ url('tinymce/skins/alchemy/fonts/tinymce.svg#tinymce') format('svg');
1572
1572
  font-weight: normal;
1573
1573
  font-style: normal;
1574
1574
  }
1575
1575
 
1576
1576
  @font-face {
1577
1577
  font-family: 'tinymce-small';
1578
- src: url('fonts/tinymce-small.woff') format('woff'),
1579
- url('fonts/tinymce-small.ttf') format('truetype'),
1580
- url('fonts/tinymce-small.svg#tinymce') format('svg');
1578
+ src: url('tinymce/skins/alchemy/fonts/tinymce-small.woff') format('woff'),
1579
+ url('tinymce/skins/alchemy/fonts/tinymce-small.ttf') format('truetype'),
1580
+ url('tinymce/skins/alchemy/fonts/tinymce-small.svg#tinymce') format('svg');
1581
1581
  font-weight: normal;
1582
1582
  font-style: normal;
1583
1583
  }
@@ -40,9 +40,7 @@ module Alchemy
40
40
  def exception_handler(error)
41
41
  exception_logger(error)
42
42
  show_error_notice(error)
43
- if defined?(Airbrake)
44
- notify_airbrake(error) unless Rails.env.development? || Rails.env.test?
45
- end
43
+ notify_error_tracker(error)
46
44
  end
47
45
 
48
46
  # Displays an error notice in the Alchemy backend.
@@ -146,6 +144,14 @@ module Alchemy
146
144
  site
147
145
  end
148
146
  end
147
+
148
+ def notify_error_tracker(exception)
149
+ if ::Alchemy::ErrorTracking.notification_handler.respond_to?(:call)
150
+ ::Alchemy::ErrorTracking.notification_handler.call(exception)
151
+ else
152
+ Rails.logger.warn("To use the Alchemy::ErrorTracking.notification_handler, it must respond to #call.")
153
+ end
154
+ end
149
155
  end
150
156
  end
151
157
  end
@@ -28,7 +28,7 @@ module Alchemy
28
28
  after_assign { |f| write_attribute(:file_mime_type, f.mime_type) }
29
29
  end
30
30
 
31
- stampable stamper_class_name: Alchemy.user_class_name
31
+ stampable stamper_class_name: Alchemy.user_class.name
32
32
 
33
33
  has_many :essence_files, class_name: "Alchemy::EssenceFile", foreign_key: "attachment_id"
34
34
  has_many :contents, through: :essence_files
@@ -57,7 +57,7 @@ module Alchemy
57
57
  #
58
58
  acts_as_list scope: [:page_version_id, :fixed, :parent_element_id]
59
59
 
60
- stampable stamper_class_name: Alchemy.user_class_name
60
+ stampable stamper_class_name: Alchemy.user_class.name
61
61
 
62
62
  has_many :contents, dependent: :destroy, inverse_of: :element
63
63
 
@@ -7,7 +7,7 @@ module Alchemy
7
7
  before_destroy :check_if_related_essence_nodes_present
8
8
 
9
9
  acts_as_nested_set scope: "language_id", touch: true
10
- stampable stamper_class_name: Alchemy.user_class_name
10
+ stampable stamper_class_name: Alchemy.user_class.name
11
11
 
12
12
  belongs_to :language, class_name: "Alchemy::Language"
13
13
  belongs_to :page, class_name: "Alchemy::Page", optional: true, inverse_of: :nodes
@@ -88,7 +88,7 @@ module Alchemy
88
88
 
89
89
  acts_as_nested_set(dependent: :destroy, scope: [:layoutpage, :language_id])
90
90
 
91
- stampable stamper_class_name: Alchemy.user_class_name
91
+ stampable stamper_class_name: Alchemy.user_class.name
92
92
 
93
93
  belongs_to :language
94
94
 
@@ -110,7 +110,7 @@ module Alchemy
110
110
  case_sensitive: false,
111
111
  message: Alchemy.t("not a valid image")
112
112
 
113
- stampable stamper_class_name: Alchemy.user_class_name
113
+ stampable stamper_class_name: Alchemy.user_class.name
114
114
 
115
115
  scope :named, ->(name) { where("#{table_name}.name LIKE ?", "%#{name}%") }
116
116
  scope :recent, -> { where("#{table_name}.created_at > ?", Time.current - 24.hours).order(:created_at) }
@@ -54,7 +54,7 @@
54
54
  <%= f.hidden_field :link_title, data: { link_title: true }, id: nil %>
55
55
  <%= f.hidden_field :link_class_name, data: { link_class: true }, id: nil %>
56
56
  <%= f.hidden_field :link_target, data: { link_target: true }, id: nil %>
57
- <%= f.hidden_field :crop_from, data: { crop_from: true }, id: nil %>
58
- <%= f.hidden_field :crop_size, data: { crop_size: true }, id: nil %>
57
+ <%= f.hidden_field :crop_from, data: { crop_from: true }, id: picture_editor.form_field_id(:crop_from) %>
58
+ <%= f.hidden_field :crop_size, data: { crop_size: true }, id: picture_editor.form_field_id(:crop_size) %>
59
59
  <% end %>
60
60
  <% end %>
@@ -58,7 +58,7 @@
58
58
  "check_name": "MassAssignment",
59
59
  "message": "Specify exact keys allowed for mass assignment instead of using `permit!` which allows any keys",
60
60
  "file": "app/controllers/alchemy/admin/resources_controller.rb",
61
- "line": 136,
61
+ "line": 209,
62
62
  "link": "https://brakemanscanner.org/docs/warning_types/mass_assignment/",
63
63
  "code": "params.require(resource_handler.namespaced_resource_name).permit!",
64
64
  "render_path": null,
@@ -86,7 +86,7 @@
86
86
  "type": "controller",
87
87
  "class": "Alchemy::Admin::ElementsController",
88
88
  "method": "fold",
89
- "line": 97,
89
+ "line": 102,
90
90
  "file": "app/controllers/alchemy/admin/elements_controller.rb",
91
91
  "rendered": {
92
92
  "name": "alchemy/admin/elements/fold",
@@ -109,7 +109,7 @@
109
109
  "check_name": "MassAssignment",
110
110
  "message": "Specify exact keys allowed for mass assignment instead of using `permit!` which allows any keys",
111
111
  "file": "app/controllers/alchemy/admin/elements_controller.rb",
112
- "line": 150,
112
+ "line": 155,
113
113
  "link": "https://brakemanscanner.org/docs/warning_types/mass_assignment/",
114
114
  "code": "params.fetch(:contents, {}).permit!",
115
115
  "render_path": null,
@@ -122,6 +122,26 @@
122
122
  "confidence": "Medium",
123
123
  "note": "`Alchemy::Content` is a polymorphic association of any kind of model extending `Alchemy::Essence`. Since we can't know the attributes of all potential essences we need to permit all attributes. As this all happens inside the password protected /admin namespace this can be considered a false positive."
124
124
  },
125
+ {
126
+ "warning_type": "Command Injection",
127
+ "warning_code": 14,
128
+ "fingerprint": "6addfcb9d23d2d6f699f2f3542169744ff749dc4d0a97f8ac783ab92593e1d84",
129
+ "check_name": "Execute",
130
+ "message": "Possible command injection",
131
+ "file": "lib/alchemy/upgrader.rb",
132
+ "line": 30,
133
+ "link": "https://brakemanscanner.org/docs/warning_types/command_injection/",
134
+ "code": "`yarn add @alchemy_cms/admin@~#{Alchemy.version}`",
135
+ "render_path": null,
136
+ "location": {
137
+ "type": "method",
138
+ "class": "Alchemy::Upgrader",
139
+ "method": "update_npm_package"
140
+ },
141
+ "user_input": "Alchemy.version",
142
+ "confidence": "Medium",
143
+ "note": "The alchemy version is safe"
144
+ },
125
145
  {
126
146
  "warning_type": "Cross-Site Scripting",
127
147
  "warning_code": 4,
@@ -255,6 +275,6 @@
255
275
  "note": ""
256
276
  }
257
277
  ],
258
- "updated": "2021-06-29 20:56:10 +0200",
259
- "brakeman_version": "5.0.1"
278
+ "updated": "2021-10-26 21:44:59 +0200",
279
+ "brakeman_version": "5.1.1"
260
280
  }
@@ -40,9 +40,16 @@ module Alchemy
40
40
  if Alchemy.user_class
41
41
  ActiveSupport.on_load(:active_record) do
42
42
  Alchemy.user_class.model_stamper
43
- Alchemy.user_class.stampable(stamper_class_name: Alchemy.user_class_name)
43
+ Alchemy.user_class.stampable(stamper_class_name: Alchemy.user_class.name)
44
44
  end
45
45
  end
46
46
  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
47
54
  end
48
55
  end
@@ -0,0 +1,13 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Alchemy
4
+ module ErrorTracking
5
+ class AirbrakeHandler < BaseHandler
6
+ def self.call(exception)
7
+ return if ["development", "test"].include?(Rails.env)
8
+
9
+ notify_airbrake(exception)
10
+ end
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,14 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Alchemy
4
+ module ErrorTracking
5
+ class BaseHandler
6
+ def self.call(exception)
7
+ # implement your own notification method
8
+ end
9
+ end
10
+
11
+ mattr_accessor :notification_handler
12
+ @@notification_handler = BaseHandler
13
+ end
14
+ end
@@ -22,13 +22,20 @@ module Alchemy
22
22
  end
23
23
 
24
24
  module ClassMethods
25
- # Find all records matching all of the given tags.
26
- # Separate multiple tags by comma.
27
- def tagged_with(names)
25
+ def tagged_with(names = [], **args)
28
26
  if names.is_a? String
29
27
  names = names.split(/,\s*/)
30
28
  end
31
- super(names: names, match: :all)
29
+
30
+ unless args[:match]
31
+ args.merge!(match: :all)
32
+ end
33
+
34
+ if names.any?
35
+ args.merge!(names: names)
36
+ end
37
+
38
+ super(args)
32
39
  end
33
40
 
34
41
  # Returns all unique tags
@@ -24,6 +24,12 @@ module Alchemy
24
24
  todo "Check the default configuration file (./config/alchemy/config.yml.defaults) for new configuration options and insert them into your config file.", "Configuration has changed"
25
25
  end
26
26
  end
27
+
28
+ def update_npm_package
29
+ desc "Install new npm package."
30
+ `yarn add @alchemy_cms/admin@~#{Alchemy.version}`
31
+ log "Installed new npm package."
32
+ end
27
33
  end
28
34
  end
29
35
  end
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Alchemy
4
- VERSION = "6.0.0-b6"
4
+ VERSION = "6.0.0-rc4"
5
5
 
6
6
  def self.version
7
7
  VERSION
data/lib/alchemy_cms.rb CHANGED
@@ -37,6 +37,7 @@ require_relative "alchemy/controller_actions"
37
37
  require_relative "alchemy/deprecation"
38
38
  require_relative "alchemy/element_definition"
39
39
  require_relative "alchemy/elements_finder"
40
+ require_relative "alchemy/error_tracking"
40
41
  require_relative "alchemy/errors"
41
42
  require_relative "alchemy/essence"
42
43
  require_relative "alchemy/filetypes"
@@ -1,6 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
  require "rails/generators"
3
3
  require "alchemy/install/tasks"
4
+ require "alchemy/version"
4
5
 
5
6
  module Alchemy
6
7
  module Generators
@@ -88,7 +89,7 @@ module Alchemy
88
89
  end
89
90
 
90
91
  def add_npm_package
91
- run "yarn add @alchemy_cms/admin"
92
+ run "yarn add @alchemy_cms/admin@~#{Alchemy.version}"
92
93
  end
93
94
 
94
95
  def copy_alchemy_entry_point
@@ -17,6 +17,7 @@ namespace :alchemy do
17
17
  task prepare: [
18
18
  "alchemy:upgrade:database",
19
19
  "alchemy:upgrade:config",
20
+ "alchemy:upgrade:package",
20
21
  ]
21
22
 
22
23
  desc "Alchemy Upgrader: Prepares the database."
@@ -30,6 +31,11 @@ namespace :alchemy do
30
31
  Alchemy::Upgrader.copy_new_config_file
31
32
  end
32
33
 
34
+ desc "Alchemy Upgrader: Install new Node package."
35
+ task package: [:environment] do
36
+ Alchemy::Upgrader.update_npm_package
37
+ end
38
+
33
39
  desc "Upgrade Alchemy to v5.0"
34
40
  task "5.0" => [
35
41
  "alchemy:upgrade:prepare",
data/package/admin.js CHANGED
@@ -5,6 +5,7 @@ import fileEditors from "./src/file_editors"
5
5
  import pictureEditors from "./src/picture_editors"
6
6
  import ImageLoader from "./src/image_loader"
7
7
  import ImageCropper from "./src/image_cropper"
8
+ import Datepicker from "./src/datepicker"
8
9
 
9
10
  // Global Alchemy object
10
11
  if (typeof window.Alchemy === "undefined") {
@@ -20,5 +21,6 @@ Object.assign(Alchemy, {
20
21
  fileEditors,
21
22
  pictureEditors,
22
23
  ImageLoader: ImageLoader.init,
23
- ImageCropper
24
+ ImageCropper,
25
+ Datepicker
24
26
  })
@@ -0,0 +1,39 @@
1
+ import flatpickr from "flatpickr"
2
+
3
+ export default function Datepicker(scope = document) {
4
+ if (scope === "") {
5
+ scope = document
6
+ } else if (scope instanceof String) {
7
+ scope = document.querySelectorAll(scope)
8
+ }
9
+
10
+ const datepickerInputs = scope.querySelectorAll("input[data-datepicker-type]")
11
+
12
+ // Initializes the datepickers on the text inputs and sets the proper type
13
+ // to enable browsers default datepicker if the current OS is iOS.
14
+ if (Alchemy.isiOS) {
15
+ datepickerInputs.forEach((input) => {
16
+ input.attributes.type = input.dataset.datepickerType
17
+ })
18
+ } else {
19
+ datepickerInputs.forEach((input) => {
20
+ const type = input.dataset.datepickerType
21
+ const options = {
22
+ // alchemy_i18n supports `zh_CN` etc., but flatpickr only has two-letter codes (`zh`)
23
+ locale: Alchemy.locale.slice(0, 2),
24
+ altInput: true,
25
+ altFormat: Alchemy.t(`formats.${type}`),
26
+ altInputClass: "flatpickr-input",
27
+ enableTime: /time/.test(type),
28
+ noCalendar: type === "time",
29
+ time_24hr: Alchemy.t("formats.time_24hr"),
30
+ onValueUpdate(_selectedDates, _dateStr, instance) {
31
+ return Alchemy.setElementDirty(
32
+ instance.element.closest(".element-editor")
33
+ )
34
+ }
35
+ }
36
+ flatpickr(input, options)
37
+ })
38
+ }
39
+ }
data/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@alchemy_cms/admin",
3
- "version": "6.0.0-b6",
3
+ "version": "6.0.0-rc4",
4
4
  "description": "AlchemyCMS",
5
5
  "browser": "package/admin.js",
6
6
  "files": [
@@ -24,6 +24,7 @@
24
24
  },
25
25
  "homepage": "https://github.com/AlchemyCMS/alchemy_cms#readme",
26
26
  "dependencies": {
27
+ "flatpickr": "^4.6.9",
27
28
  "lodash-es": "^4.17.21",
28
29
  "sortablejs": "^1.10.2"
29
30
  },