camaleon_cms 2.7.4 → 2.7.5

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 camaleon_cms might be problematic. Click here for more details.

checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: db5860768212ca0703d01e3d2b16a691d5972917905bab1740cf16a1fb79ab58
4
- data.tar.gz: cd0b90107912e5281d49d4a0070734a9db641deadb27f9671d27a13a297c8c71
3
+ metadata.gz: db43005ef92c135dec7d3ef6871aeac761dd01d904e3f5707bee706f2dd65aa3
4
+ data.tar.gz: e99b9643fba7ab169ede5758bedfd1b8c7c569c7972363782f79ec91770da3da
5
5
  SHA512:
6
- metadata.gz: bfc9b376433413768f22e2b191f0f977697c8789004ef91eab78edd683ae22914a4a194c1e8568fa4d074a3eb5f7773b1d492298121b8afb64731c87bd390f8b
7
- data.tar.gz: 99f90a12596bc5d24a8dd73555cba291d3140f84f4c5373931b6b62a8a79301126020c7de1a82b4fea7b4c5019062bbfc3573ad5e614584ab8777fc7e6767a2a
6
+ metadata.gz: 7e4d609bf25dfbfe2c994f1393dbfe7bc9aa060f07e10bd6446f3273fbecc1646868a1efcae9b14dd32dd206c64a986e0210ea1199c3f1e2cda680953824ada2
7
+ data.tar.gz: fb39191112e373c198578073e5b317bd94749b4ef2956afd2e3baa07a98e91d3a30af9c1f5c9e98822f9338a8ab5f86aea6be405335a71794c55317bc68e285e
@@ -9,18 +9,18 @@ window.cama_init_posttype_form = function() {
9
9
 
10
10
  item.parent().siblings().find('input').prop('disabled', $(this).is(':checked'))
11
11
  if ($(this).is(':checked'))
12
- return item.prop('checked', true).prop('disabled', false)
12
+ item.prop('checked', true).prop('disabled', false)
13
13
  else
14
- return item.prop('disabled', true)
14
+ item.prop('disabled', true)
15
15
  }).trigger('change')
16
16
 
17
17
  form.find('[name="meta[has_picture]"]').change(function() {
18
18
  const items = form.find('.picture_settings input')
19
19
 
20
20
  if ($(this).is(':checked'))
21
- return items.prop('disabled', false)
21
+ items.prop('disabled', false)
22
22
  else
23
- return items.prop('disabled', true)
23
+ items.prop('disabled', true)
24
24
  }).trigger('change')
25
25
 
26
26
  // toggle single and multiple categories checkbox
@@ -28,9 +28,10 @@ window.cama_init_posttype_form = function() {
28
28
  'input:checkbox[name="meta[has_category]"], input:checkbox[name="meta[has_single_category]"]'
29
29
  )
30
30
 
31
- return catChecks.change(
31
+ catChecks.change(
32
32
  function() {
33
- if ($(this).is(':checked')) return catChecks.not(this).prop('checked', false)
33
+ if ($(this).is(':checked'))
34
+ catChecks.not(this).prop('checked', false)
34
35
  }
35
36
  ).filter(':checked').trigger('change')
36
37
  }
@@ -1,9 +1,3 @@
1
- /*
2
- * decaffeinate suggestions:
3
- * DS102: Remove unnecessary code created because of implicit returns
4
- * Full docs: https://github.com/decaffeinate/decaffeinate/blob/main/docs/suggestions.md
5
- */
6
-
7
1
  /* eslint-env jquery */
8
2
  $(function() {
9
3
  const panel = $('#menu_content')
@@ -21,11 +15,11 @@ $(function() {
21
15
  return
22
16
 
23
17
  panel.find('#menu_reoreder_loading').show()
24
- return $.post(listPanel.attr('data-reorder_url'), data, function(res) {
18
+ $.post(listPanel.attr('data-reorder_url'), data, function(res) {
25
19
  lastData = data
26
20
  panel.find('#menu_reoreder_loading').hide()
27
21
  if (res)
28
- return alert(res)
22
+ alert(res)
29
23
  })
30
24
  })
31
25
 
@@ -33,13 +27,10 @@ $(function() {
33
27
  const saveMenu = function(data) {
34
28
  showLoading()
35
29
 
36
- return $.post(
37
- listPanel.attr('data-url'),
38
- data,
39
- function(res) {
40
- listPanel.children('.dd-list').append($(res).children())
41
- return hideLoading()
42
- })
30
+ $.post(listPanel.attr('data-url'), data, function(res) {
31
+ listPanel.children('.dd-list').append($(res).children())
32
+ hideLoading()
33
+ })
43
34
  }
44
35
 
45
36
  // add menu items (non-external)
@@ -48,7 +39,7 @@ $(function() {
48
39
  let flag = false
49
40
  $(this).closest('.panel').find('input:checkbox:checked').each(function() {
50
41
  flag = true
51
- return data.items.push({ id: $(this).val(), kind: $(this).closest('.class_type').attr('data-type') })
42
+ data.items.push({ id: $(this).val(), kind: $(this).closest('.class_type').attr('data-type') })
52
43
  }).prop('checked', false)
53
44
 
54
45
  if (!flag)
@@ -64,7 +55,7 @@ $(function() {
64
55
  let flag = false
65
56
  $(this).closest('.panel').find('input:checkbox:checked').each(function() {
66
57
  flag = true
67
- return data.custom_items.push({ url: $(this).val(), kind: $(this).attr('data-kind'), label: $(this).attr('data-label') })
58
+ data.custom_items.push({ url: $(this).val(), kind: $(this).attr('data-kind'), label: $(this).attr('data-label') })
68
59
  }).prop('checked', false)
69
60
 
70
61
  if (!flag)
@@ -93,10 +84,10 @@ $(function() {
93
84
  title: link.attr('data-original-title') || link.attr('title'),
94
85
  url: link.attr('href'),
95
86
  mode: 'ajax',
96
- callback(modal) {
87
+ callback: (modal) => {
97
88
  const form = modal.find('form')
98
89
  init_form_validations(form)
99
- return form.submit(function() {
90
+ form.submit(function() {
100
91
  if (!form.valid())
101
92
  return false
102
93
 
@@ -104,7 +95,7 @@ $(function() {
104
95
  $.post(form.attr('action'), form.serialize(), function(res) {
105
96
  link.closest('li').replaceWith($(res).html())
106
97
  modal.modal('hide')
107
- return hideLoading()
98
+ hideLoading()
108
99
  })
109
100
  return false
110
101
  })
@@ -122,39 +113,39 @@ $(function() {
122
113
  showLoading()
123
114
  $.get(link.attr('href'), function() {
124
115
  link.closest('.dd-item').remove()
125
- return hideLoading()
116
+ hideLoading()
126
117
  })
127
118
  return false
128
119
  })
129
120
 
130
121
  // new menu
131
122
  panel.find('.new_menu_link, .edit_menu_link').ajax_modal({
132
- callback(modal) {
123
+ callback: (modal) => {
133
124
  const form = modal.find('form')
134
125
 
135
- return setTimeout(() => init_form_validations(form), 1000)
126
+ setTimeout(() => init_form_validations(form), 1000)
136
127
  }
137
128
  })
138
129
 
139
130
  // menus list - change dropdown
140
- panel.find('#menu_items #switch_nav_menuForm select').change(function() {
131
+ panel.find('#menu_items #switch_nav_menu_form select').change(function() {
141
132
  if (!$(this).val())
142
133
  return
143
134
 
144
- return $(this).closest('form').submit()
135
+ $(this).closest('form').submit()
145
136
  })
146
137
 
147
138
  // custom fields
148
- return listPanel.on('click', '.custom_settings_link', function() {
139
+ listPanel.on('click', '.custom_settings_link', function() {
149
140
  const link = $(this)
150
141
  open_modal({
151
142
  title: link.attr('data-original-title') || link.attr('title'),
152
143
  url: link.attr('href'),
153
144
  mode: 'ajax',
154
- callback(modal) {
145
+ callback: (modal) => {
155
146
  const form = modal.find('form')
156
147
  init_form_validations(form)
157
- return form.submit(function() {
148
+ form.submit(function() {
158
149
  if (!form.valid())
159
150
  return false
160
151
 
@@ -164,7 +155,7 @@ $(function() {
164
155
  alert(res)
165
156
 
166
157
  modal.modal('hide')
167
- return hideLoading()
158
+ hideLoading()
168
159
  })
169
160
  return false
170
161
  })
@@ -6,15 +6,6 @@ module CamaleonCms
6
6
  skip_before_action :verify_authenticity_token, only: :upload, raise: false
7
7
  before_action :init_media_vars, except: :download_private_file
8
8
 
9
- LOCALHOST_DOMAIN_MATCHER = /
10
- localhost|
11
- 127\.0\.0\.1|
12
- 0\.0\.0\.0|
13
- 0x7f\.0x0\.0x0\.0x1| # hex encoding
14
- 0177\.0\.0\.01| # octal encoding
15
- 2130706433 # dword encoding
16
- /x.freeze
17
-
18
9
  # render media section
19
10
  def index
20
11
  authorize! :manage, :media
@@ -71,13 +62,13 @@ module CamaleonCms
71
62
  cama_uploader.delete_file(params[:folder].gsub('//', '/'))
72
63
  render plain: ''
73
64
  when 'crop_url'
74
- unless params[:url].start_with?('data:')
75
- params[:url] = (params[:url].start_with?('http') ? '' : current_site.the_url(locale: nil)) + params[:url]
76
- end
77
- r = if local_url?(params[:url])
78
- { error: t('camaleon_cms.admin.media.local_upload_denied') }
65
+ user_url = params[:url].to_s
66
+ user_url = "#{current_site.the_url(locale: nil)}#{user_url}" unless user_url.start_with?('data:', 'http')
67
+ url_validation_result = UserUrlValidator.validate(user_url)
68
+ r = if url_validation_result.is_a?(Array)
69
+ { error: url_validation_result.join(', ') }
79
70
  else
80
- cama_tmp_upload(params[:url], formats: params[:formats], name: params[:name])
71
+ cama_tmp_upload(user_url, formats: params[:formats], name: params[:name])
81
72
  end
82
73
  if r[:error].present?
83
74
  render plain: helpers.sanitize(r[:error])
@@ -91,10 +82,6 @@ module CamaleonCms
91
82
  end
92
83
  end
93
84
 
94
- def local_url?(url)
95
- url.try :match?, LOCALHOST_DOMAIN_MATCHER
96
- end
97
-
98
85
  # upload files from media uploader
99
86
  def upload(settings = {})
100
87
  params[:dimension] = nil if params[:skip_auto_crop].present?
@@ -70,7 +70,8 @@ module CamaleonCms
70
70
 
71
71
  # send email test
72
72
  def test_email
73
- CamaleonCms::HtmlMailer.sender(params[:email], 'Test', { content: 'Test content' }).deliver_now
73
+ data = { content: 'Test content', current_site: current_site, url_base: cama_root_url }
74
+ CamaleonCms::HtmlMailer.sender(params[:email], 'Test', data).deliver_now
74
75
  head :ok
75
76
  rescue StandardError => e
76
77
  render inline: e.message, status: 502
@@ -29,7 +29,8 @@ module CamaleonCms
29
29
  # return a child category from this category with id (integer) or by slug (string)
30
30
  def the_category(slug_or_id)
31
31
  return object.categories.where(id: slug_or_id).first if slug_or_id.is_a?(Integer)
32
- return object.categories.find_by_slug(slug_or_id) if slug_or_id.is_a?(String)
32
+
33
+ object.categories.find_by_slug(slug_or_id) if slug_or_id.is_a?(String)
33
34
  end
34
35
 
35
36
  # ---------------------
@@ -70,7 +70,8 @@ module CamaleonCms
70
70
  # return a category from this post_type with id (integer) or by slug (string)
71
71
  def the_category(slug_or_id)
72
72
  return the_categories.where(id: slug_or_id).first if slug_or_id.is_a?(Integer)
73
- return the_categories.find_by_slug(slug_or_id) if slug_or_id.is_a?(String)
73
+
74
+ the_categories.find_by_slug(slug_or_id) if slug_or_id.is_a?(String)
74
75
  end
75
76
 
76
77
  # return all post_tags for the post_type (active_record) filtered by permissions + hidden posts + roles + etc...
@@ -80,7 +80,8 @@ module CamaleonCms
80
80
  # slug_or_id: string => return all main categories of the post_type with slug = slug_or_id
81
81
  def the_categories(slug_or_id = nil)
82
82
  return the_post_type(slug_or_id).the_categories if slug_or_id.present?
83
- return object.categories unless slug_or_id.present?
83
+
84
+ object.categories unless slug_or_id.present?
84
85
  end
85
86
 
86
87
  # return the category object with id or slug = slug_or_id from this site
@@ -58,8 +58,11 @@ module CamaleonCms
58
58
  prepend_view_path(Rails.root.join(views_dir).to_s)
59
59
 
60
60
  theme = @current_site.get_theme
61
- lookup_context.prefixes.prepend("themes/#{theme.slug}") if theme.settings['gem_mode']
62
- lookup_context.prefixes.prepend("themes/#{theme.slug}/views") unless theme.settings['gem_mode']
61
+ if theme.settings && theme.settings['gem_mode']
62
+ lookup_context.prefixes.prepend("themes/#{theme.slug}")
63
+ else
64
+ lookup_context.prefixes.prepend("themes/#{theme.slug}/views")
65
+ end
63
66
  lookup_context.use_camaleon_partial_prefixes = true
64
67
  ((data[:files] || []) + (data[:attachments] || [])).each do |attach|
65
68
  if File.exist?(attach) && !File.directory?(attach)
@@ -8,9 +8,10 @@ module CamaleonCms
8
8
  return unless ptype.present? # only for posts that belongs to a post type model
9
9
 
10
10
  posts = ptype.site.posts
11
- .where("(#{slug_array.map do |s|
12
- "#{CamaleonCms::Post.table_name}.slug LIKE '%-->#{s}<!--%'"
13
- end.join(' OR ')} ) OR #{CamaleonCms::Post.table_name}.slug = ?", record.slug)
11
+ .where(
12
+ "(#{slug_array.map { |s| "#{CamaleonCms::Post.table_name}.slug LIKE '%-->#{s}<!--%'" }
13
+ .join(' OR ')} ) OR #{CamaleonCms::Post.table_name}.slug = ?", record.slug
14
+ )
14
15
  .where.not(id: record.id)
15
16
  .where.not(status: %i[draft draft_child trash])
16
17
  if posts.size.positive?
@@ -0,0 +1,207 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Copyright (c) 2011-present GitLab B.V.
4
+ #
5
+ # See https://gitlab.com/gitlab-org/gitlab/-/blob/master/lib/gitlab/url_blocker.rb
6
+ #
7
+ # Portions of this software are licensed under the "MIT Expat" license as defined below.
8
+ #
9
+ # Permission is hereby granted, free of charge, to any person obtaining a copy
10
+ # of this software and associated documentation files (the "Software"), to deal
11
+ # in the Software without restriction, including without limitation the rights
12
+ # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
13
+ # copies of the Software, and to permit persons to whom the Software is
14
+ # furnished to do so, subject to the following conditions:
15
+ #
16
+ # The above copyright notice and this permission notice shall be included in all
17
+ # copies or substantial portions of the Software.
18
+ #
19
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
20
+ # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
21
+ # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
22
+ # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
23
+ # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
24
+ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
25
+ # SOFTWARE.
26
+
27
+ # require 'resolv'
28
+ require 'ipaddress'
29
+ require 'addressable/uri'
30
+
31
+ module CamaleonCms
32
+ class UserUrlValidator
33
+ LOCAL_IPS = %w[0.0.0.0 ::].freeze
34
+
35
+ def self.validate(...)
36
+ new.validate(...)
37
+ end
38
+
39
+ def initialize
40
+ @errors = []
41
+ end
42
+
43
+ # Validates the given url according to the constraints specified by the received arguments.
44
+ #
45
+ # allow_localhost - Registers error if URL resolves to a localhost IP address and argument is false.
46
+ # allow_local_network - Registers error if URL resolves to a link-local address and argument is false.
47
+ # enforce_user - Registers error if URL user doesn't start with alphanumeric characters and argument is true.
48
+ # enforce_sanitizing - Registers error if URL includes any HTML/CSS/JS tags and argument is true.
49
+ #
50
+ # Returns an array with [<uri>, <original-hostname>].
51
+ def validate(url, allow_localhost: false, allow_local_network: false, enforce_user: true, enforce_sanitizing: true)
52
+ return invalid_url unless url.present?
53
+
54
+ # Param url can be a string, URI or Addressable::URI
55
+ return invalid_url unless (uri = parse_url(url))
56
+
57
+ validate_uri(uri: uri, enforce_sanitizing: enforce_sanitizing, enforce_user: enforce_user)
58
+ return @errors if @errors.any?
59
+
60
+ address_info = get_address_info(uri)
61
+ return @errors if @errors.any?
62
+
63
+ validate_local_request(
64
+ address_info: address_info,
65
+ allow_localhost: allow_localhost,
66
+ allow_local_network: allow_local_network
67
+ )
68
+
69
+ @errors.empty? || @errors
70
+ end
71
+
72
+ private
73
+
74
+ def validate_uri(uri:, enforce_sanitizing:, enforce_user:)
75
+ validate_html_tags(uri) if enforce_sanitizing
76
+
77
+ validate_user(uri.user) if enforce_user
78
+ validate_hostname(uri.hostname)
79
+ end
80
+
81
+ # @param uri [Addressable::URI]
82
+ # @return [Array<Addrinfo>] addrinfo object for the URI
83
+ def get_address_info(uri)
84
+ Addrinfo.getaddrinfo(uri.hostname, get_port(uri), nil, :STREAM).map do |addr|
85
+ addr.ipv6_v4mapped? ? addr.ipv6_to_ipv4 : addr
86
+ end
87
+ rescue ArgumentError => e
88
+ # Addrinfo.getaddrinfo errors if the domain exceeds 1024 characters.
89
+ @errors << I18n.t('camaleon_cms.admin.validate.hostname_long') if e.message.include?('hostname too long')
90
+
91
+ @errors << "#{e.message}: #{I18n.t('camaleon_cms.admin.validate.url')}" if @errors.blank?
92
+ rescue SocketError
93
+ @errors << I18n.t('camaleon_cms.admin.validate.host_invalid')
94
+ end
95
+
96
+ def validate_local_request(address_info:, allow_localhost:, allow_local_network:)
97
+ return if allow_local_network && allow_localhost
98
+
99
+ unless allow_localhost
100
+ validate_localhost(address_info)
101
+ validate_loopback(address_info)
102
+ end
103
+
104
+ return if allow_local_network
105
+
106
+ validate_local_network(address_info)
107
+ validate_link_local(address_info)
108
+ validate_shared_address(address_info)
109
+ validate_limited_broadcast_address(address_info)
110
+ end
111
+
112
+ def get_port(uri)
113
+ uri.port || uri.default_port
114
+ end
115
+
116
+ def validate_html_tags(uri)
117
+ uri_str = uri.to_s
118
+ sanitized_uri = ActionController::Base.helpers.sanitize(uri_str, tags: [])
119
+ @errors << I18n.t('camaleon_cms.admin.validate.html_tags') unless sanitized_uri == uri_str
120
+ end
121
+
122
+ # @param [String, Addressable::URI, #to_str] url The URL string to parse
123
+ # @return [Addressable::URI, nil] URI object based on the parsed string, or `nil` if the `url` is invalid
124
+ def parse_url(url)
125
+ invalid = nil
126
+ uri = Addressable::URI.parse(url).tap do |parsed_url|
127
+ invalid = true if multiline_blocked?(parsed_url)
128
+ end
129
+ return if invalid
130
+
131
+ uri
132
+ rescue Addressable::URI::InvalidURIError, URI::InvalidURIError
133
+ nil
134
+ end
135
+
136
+ def multiline_blocked?(parsed_url)
137
+ url = parsed_url.to_s
138
+
139
+ return true if url =~ /[\n\r]/
140
+ # Google Cloud Storage uses a multi-line, encoded Signature query string
141
+ return false if %w[http https].include?(parsed_url.scheme&.downcase)
142
+
143
+ CGI.unescape(url) =~ /[\n\r]/
144
+ end
145
+
146
+ def validate_user(value)
147
+ return if value.blank?
148
+ return if value =~ /\A\p{Alnum}/
149
+
150
+ @errors << I18n.t('camaleon_cms.admin.validate.username_alphanumeric')
151
+ end
152
+
153
+ def validate_hostname(value)
154
+ return if value.blank?
155
+ return if IPAddress.valid?(value)
156
+ return if value =~ /\A\p{Alnum}/
157
+
158
+ @errors << I18n.t('camaleon_cms.admin.validate.host_or_ip_invalid')
159
+ end
160
+
161
+ def validate_localhost(addrs_info)
162
+ return if (Socket.ip_address_list.map(&:ip_address).concat(LOCAL_IPS) & addrs_info.map(&:ip_address)).empty?
163
+
164
+ @errors << I18n.t('camaleon_cms.admin.validate.no_localhost_requests')
165
+ end
166
+
167
+ def validate_loopback(addrs_info)
168
+ return unless addrs_info.any? { |addr| addr.ipv4_loopback? || addr.ipv6_loopback? }
169
+
170
+ @errors << I18n.t('camaleon_cms.admin.validate.no_loopback_requests')
171
+ end
172
+
173
+ def validate_local_network(addrs_info)
174
+ return unless addrs_info.any? { |addr| addr.ipv4_private? || addr.ipv6_sitelocal? || addr.ipv6_unique_local? }
175
+
176
+ @errors << I18n.t('camaleon_cms.admin.validate.no_local_net_requests')
177
+ end
178
+
179
+ def validate_link_local(addrs_info)
180
+ netmask = IPAddr.new('169.254.0.0/16')
181
+ return unless addrs_info.any? { |addr| addr.ipv6_linklocal? || netmask.include?(addr.ip_address) }
182
+
183
+ @errors << I18n.t('camaleon_cms.admin.validate.no_link_local_net_requests')
184
+ end
185
+
186
+ def validate_shared_address(addrs_info)
187
+ netmask = IPAddr.new('100.64.0.0/10')
188
+ return unless addrs_info.any? { |addr| netmask.include?(addr.ip_address) }
189
+
190
+ @errors << I18n.t('camaleon_cms.admin.validate.no_shared_address_requests')
191
+ end
192
+
193
+ # Registers an error if any IP in `addrs_info` is the limited broadcast address.
194
+ # https://datatracker.ietf.org/doc/html/rfc919#section-7
195
+ def validate_limited_broadcast_address(addrs_info)
196
+ blocked_ips = ['255.255.255.255']
197
+
198
+ return if (blocked_ips & addrs_info.map(&:ip_address)).empty?
199
+
200
+ @errors << I18n.t('camaleon_cms.admin.validate.no_limited_broadcast_address_requests')
201
+ end
202
+
203
+ def invalid_url
204
+ @errors << I18n.t('camaleon_cms.admin.validate.url')
205
+ end
206
+ end
207
+ end
@@ -212,7 +212,6 @@ en:
212
212
  reload: 'Reload'
213
213
  clear_cache: 'Clear Cache'
214
214
  name_required: 'File name is required'
215
- local_upload_denied: 'Cannot upload from localhost'
216
215
  menus:
217
216
  menus: Menus
218
217
  link_url: 'Link URL'
@@ -684,6 +683,16 @@ en:
684
683
  required: 'This field is required.'
685
684
  remote: 'Please fix this field.'
686
685
  email: 'Please enter a valid email address.'
686
+ host_invalid: 'Host cannot be resolved or invalid'
687
+ host_or_ip_invalid: 'Hostname or IP address invalid'
688
+ hostname_long: 'Host is too long (maximum is 1024 characters)'
689
+ html_tags: 'HTML/CSS/JS tags are not allowed'
690
+ no_localhost_requests: 'Requests to localhost are not allowed'
691
+ no_loopback_requests: 'Requests to loopback addresses are not allowed'
692
+ no_local_net_requests: 'Requests to the local network are not allowed'
693
+ no_link_local_net_requests: 'Requests to the link local network are not allowed'
694
+ no_shared_address_requests: 'Requests to the shared address space are not allowed'
695
+ no_limited_broadcast_address_requests: 'Requests to the limited broadcast address are not allowed'
687
696
  url: 'Please enter a valid URL.'
688
697
  date: 'Please enter a valid date.'
689
698
  dateiso: 'Please enter a valid date ( ISO ).'
@@ -697,6 +706,7 @@ en:
697
706
  range: 'Please enter a value between {0} and {1}.'
698
707
  max: 'Please enter a value less than or equal to {0}.'
699
708
  min: 'Please enter a value greater than or equal to {0}.'
709
+ username_alphanumeric: 'Username needs to start with an alphanumeric character'
700
710
  widgets:
701
711
  create_widget: 'Create Widget'
702
712
  create_sidebar: 'Create Sidebar'
@@ -1,3 +1,3 @@
1
1
  module CamaleonCms
2
- VERSION = '2.7.4'.freeze
2
+ VERSION = '2.7.5'.freeze
3
3
  end
metadata CHANGED
@@ -1,15 +1,29 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: camaleon_cms
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.7.4
4
+ version: 2.7.5
5
5
  platform: ruby
6
6
  authors:
7
7
  - Owen Peredo Diaz
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2023-04-11 00:00:00.000000000 Z
11
+ date: 2023-12-22 00:00:00.000000000 Z
12
12
  dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: addressable
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: '0'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: '0'
13
27
  - !ruby/object:Gem::Dependency
14
28
  name: bcrypt
15
29
  requirement: !ruby/object:Gem::Requirement
@@ -86,6 +100,20 @@ dependencies:
86
100
  - - ">="
87
101
  - !ruby/object:Gem::Version
88
102
  version: '0'
103
+ - !ruby/object:Gem::Dependency
104
+ name: ipaddress
105
+ requirement: !ruby/object:Gem::Requirement
106
+ requirements:
107
+ - - ">="
108
+ - !ruby/object:Gem::Version
109
+ version: '0'
110
+ type: :runtime
111
+ prerelease: false
112
+ version_requirements: !ruby/object:Gem::Requirement
113
+ requirements:
114
+ - - ">="
115
+ - !ruby/object:Gem::Version
116
+ version: '0'
89
117
  - !ruby/object:Gem::Dependency
90
118
  name: jquery-rails
91
119
  requirement: !ruby/object:Gem::Requirement
@@ -766,6 +794,7 @@ files:
766
794
  - app/uploaders/camaleon_cms_uploader.rb
767
795
  - app/validators/camaleon_cms/post_uniq_validator.rb
768
796
  - app/validators/camaleon_cms/uniq_validator.rb
797
+ - app/validators/camaleon_cms/user_url_validator.rb
769
798
  - app/views/camaleon_cms/404.html.erb
770
799
  - app/views/camaleon_cms/500.html.erb
771
800
  - app/views/camaleon_cms/_flash_messages.html.erb
@@ -1103,7 +1132,7 @@ requirements:
1103
1132
  - rails >= 6.0
1104
1133
  - ruby >= 2.7
1105
1134
  - imagemagick
1106
- rubygems_version: 3.4.10
1135
+ rubygems_version: 3.5.1
1107
1136
  signing_key:
1108
1137
  specification_version: 4
1109
1138
  summary: Camaleon is a CMS for Ruby on Rails as an alternative to Wordpress.