diff --git a/CHANGELOG.md b/CHANGELOG.md
index c0dd5ce4e..66a11145f 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,6 +1,17 @@
CHANGELOG
=========
+v0.8.4
+------
+
+Compatibility:
+ - Decidim v0.26.x
+ - Decidim v0.25.x
+
+Features:
+ - Feature: Override validation rules for title and body in proposals, with constrains available
+ - Improve loading process to facilitate development
+
v0.8.3
------
diff --git a/Gemfile.lock b/Gemfile.lock
index cbd37b769..c37c9f548 100644
--- a/Gemfile.lock
+++ b/Gemfile.lock
@@ -1,7 +1,7 @@
PATH
remote: .
specs:
- decidim-decidim_awesome (0.8.3)
+ decidim-decidim_awesome (0.8.4)
decidim-admin (>= 0.25.0, < 0.27)
decidim-core (>= 0.25.0, < 0.27)
sassc (~> 2.3)
diff --git a/README.md b/README.md
index 6851642be..55718732e 100644
--- a/README.md
+++ b/README.md
@@ -143,6 +143,19 @@ Using a link with a query string (ie: `/take-me-somewhere?locale=es`) that will
![Custom redirections screenshot](examples/custom-redirections.png)
+#### 14. Custom validation rules for title and body in proposals
+
+Configure as you wish how the fields "title" and "body" are validated in proposals creation.
+
+Rules available:
+
+* Minimum title and body length (defaults to 15 chars).
+* Maximum percentage of capital letters for title and body (defaults to 25%).
+* Maximum number of "marks" (aka: exclamation and interrogation signs) that can be consective in the title or the body (defaults to 1).
+* Enable/disable forcing to start the title or the body with a capital letter (defaults to "enabled").
+
+![Custom validations](examples/custom_validations.png)
+
#### To be continued...
We're not done! Please check the [issues](/Platoniq/decidim-module-decidim_awesome/issues) (and participate) to see what's on our mind
diff --git a/app/forms/decidim/decidim_awesome/admin/config_form.rb b/app/forms/decidim/decidim_awesome/admin/config_form.rb
index c5b962aff..57ed59307 100644
--- a/app/forms/decidim/decidim_awesome/admin/config_form.rb
+++ b/app/forms/decidim/decidim_awesome/admin/config_form.rb
@@ -22,12 +22,26 @@ class ConfigForm < Decidim::Form
attribute :intergram_for_admins_settings, IntergramForm
attribute :intergram_for_public, Boolean
attribute :intergram_for_public_settings, IntergramForm
+ attribute :validate_title_min_length, Integer, default: 15
+ attribute :validate_title_max_caps_percent, Integer, default: 25
+ attribute :validate_title_max_marks_together, Integer, default: 1
+ attribute :validate_title_start_with_caps, Boolean, default: true
+ attribute :validate_body_min_length, Integer, default: 15
+ attribute :validate_body_max_caps_percent, Integer, default: 25
+ attribute :validate_body_max_marks_together, Integer, default: 1
+ attribute :validate_body_start_with_caps, Boolean, default: true
# collect all keys anything not specified in the params (UpdateConfig command ignores it)
attr_accessor :valid_keys
validate :css_syntax, if: ->(form) { form.scoped_styles.present? }
validate :json_syntax, if: ->(form) { form.proposal_custom_fields.present? }
+ validates :validate_title_min_length, presence: true, numericality: { greater_than_or_equal_to: 1, less_than_or_equal_to: 100 }
+ validates :validate_title_max_caps_percent, presence: true, numericality: { greater_than_or_equal_to: 0, less_than_or_equal_to: 100 }
+ validates :validate_title_max_marks_together, presence: true, numericality: { greater_than_or_equal_to: 1 }
+ validates :validate_body_min_length, presence: true, numericality: { greater_than_or_equal_to: 0 }
+ validates :validate_body_max_caps_percent, presence: true, numericality: { greater_than_or_equal_to: 0, less_than_or_equal_to: 100 }
+ validates :validate_body_max_marks_together, presence: true, numericality: { greater_than_or_equal_to: 1 }
# TODO: validate non general admins are here
diff --git a/app/forms/decidim/decidim_awesome/proposals/proposal_wizard_create_step_form_override.rb b/app/forms/decidim/decidim_awesome/proposals/proposal_wizard_create_step_form_override.rb
index 98b70a627..91bf5113c 100644
--- a/app/forms/decidim/decidim_awesome/proposals/proposal_wizard_create_step_form_override.rb
+++ b/app/forms/decidim/decidim_awesome/proposals/proposal_wizard_create_step_form_override.rb
@@ -11,11 +11,15 @@ module ProposalWizardCreateStepFormOverride
clear_validators!
validates :title, presence: true, etiquette: true
- validates :title, length: { in: 15..150 }
- validates :body, presence: true, etiquette: true, unless: ->(form) { form.override_validations? }
+ validates :title, proposal_length: {
+ minimum: ->(form) { form.minimum_title_length },
+ maximum: 150
+ }
+ validates :body, presence: true, unless: ->(form) { form.override_validations? || form.minimum_body_length.zero? }
+ validates :body, etiquette: true, unless: ->(form) { form.override_validations? }
validates :body, proposal_length: {
- minimum: 15,
- maximum: ->(record) { record.override_validations? ? 0 : record.component.settings.proposal_length }
+ minimum: ->(form) { form.minimum_body_length },
+ maximum: ->(form) { form.override_validations? ? 0 : form.component.settings.proposal_length }
}
validate :body_is_not_bare_template, unless: ->(form) { form.override_validations? }
@@ -26,10 +30,24 @@ def override_validations?
custom_fields.present?
end
+ def minimum_title_length
+ awesome_config.config[:validate_title_min_length].to_i
+ end
+
+ def minimum_body_length
+ awesome_config.config[:validate_body_min_length].to_i
+ end
+
def custom_fields
- awesome_config = Decidim::DecidimAwesome::Config.new(context.current_organization)
- awesome_config.context_from_component(context.current_component)
- awesome_config.collect_sub_configs_values("proposal_custom_field")
+ @custom_fields ||= awesome_config.collect_sub_configs_values("proposal_custom_field")
+ end
+
+ def awesome_config
+ @awesome_config ||= begin
+ conf = Decidim::DecidimAwesome::Config.new(context.current_organization)
+ conf.context_from_component(context.current_component)
+ conf
+ end
end
end
end
diff --git a/app/helpers/decidim/decidim_awesome/admin/config_constraints_helpers.rb b/app/helpers/decidim/decidim_awesome/admin/config_constraints_helpers.rb
index b3bda30c4..8b514e9d2 100644
--- a/app/helpers/decidim/decidim_awesome/admin/config_constraints_helpers.rb
+++ b/app/helpers/decidim/decidim_awesome/admin/config_constraints_helpers.rb
@@ -15,7 +15,11 @@ def check(status)
def menus
@menus ||= {
editors: config_enabled?([:allow_images_in_full_editor, :allow_images_in_small_editor, :use_markdown_editor, :allow_images_in_markdown_editor]),
- proposals: config_enabled?(:allow_images_in_proposals),
+ proposals: config_enabled?([:allow_images_in_proposals,
+ :validate_title_min_length, :validate_title_max_caps_percent,
+ :validate_title_max_marks_together, :validate_title_start_with_caps,
+ :validate_body_min_length, :validate_body_max_caps_percent,
+ :validate_body_max_marks_together, :validate_body_start_with_caps]),
surveys: config_enabled?(:auto_save_forms),
styles: config_enabled?(:scoped_styles),
proposal_custom_fields: config_enabled?(:proposal_custom_fields),
diff --git a/app/validators/concerns/decidim/decidim_awesome/etiquette_validator_override.rb b/app/validators/concerns/decidim/decidim_awesome/etiquette_validator_override.rb
new file mode 100644
index 000000000..4e276892e
--- /dev/null
+++ b/app/validators/concerns/decidim/decidim_awesome/etiquette_validator_override.rb
@@ -0,0 +1,41 @@
+# frozen_string_literal: true
+
+module Decidim
+ module DecidimAwesome
+ module EtiquetteValidatorOverride
+ extend ActiveSupport::Concern
+
+ included do
+ private
+
+ def validate_caps(record, attribute, value)
+ percent = awesome_config(record, "validate_#{attribute}_max_caps_percent").to_f
+ return if value.scan(/[[:upper:]]/).length < value.length * percent / 100
+
+ record.errors.add(attribute, options[:message] || I18n.t("too_much_caps", scope: "decidim.decidim_awesome.validators", percent: percent.round))
+ end
+
+ def validate_marks(record, attribute, value)
+ marks = awesome_config(record, "validate_#{attribute}_max_marks_together").to_i + 1
+ return if value.scan(/[!?¡¿]{#{marks},}/).empty?
+
+ record.errors.add(attribute, options[:message] || :too_many_marks)
+ end
+
+ def validate_caps_first(record, attribute, value)
+ return unless awesome_config(record, "validate_#{attribute}_start_with_caps")
+ return if value.scan(/\A[[:lower:]]{1}/).empty?
+
+ record.errors.add(attribute, options[:message] || :must_start_with_caps)
+ end
+
+ def awesome_config(record, var)
+ config = record.try(:awesome_config)&.config
+ return unless config.is_a?(Hash)
+
+ config[var.to_sym]
+ end
+ end
+ end
+ end
+end
diff --git a/app/views/decidim/decidim_awesome/admin/config/_form_proposals.html.erb b/app/views/decidim/decidim_awesome/admin/config/_form_proposals.html.erb
index 61ad49b8f..b563aca1f 100644
--- a/app/views/decidim/decidim_awesome/admin/config/_form_proposals.html.erb
+++ b/app/views/decidim/decidim_awesome/admin/config/_form_proposals.html.erb
@@ -2,9 +2,89 @@
<% if config_enabled? :allow_images_in_proposals %>
<%= t("rich_text_editor_in_public_views", scope: "decidim.decidim_awesome.admin.config") if current_organization.rich_text_editor_in_public_views %>
- <%= form.check_box :allow_images_in_proposals %>
+ <%= form.check_box :allow_images_in_proposals, disabled: current_organization.rich_text_editor_in_public_views %>
<%= t("help.allow_images_in_proposals", scope: "decidim.decidim_awesome.admin.config.form") %>
- <%= render(partial: "decidim/decidim_awesome/admin/config/constraints", locals: { key: :allow_images_in_proposals, constraints: constraints_for(:allow_images_in_proposals) }) %>
+ <% unless current_organization.rich_text_editor_in_public_views %>
+ <%= render(partial: "decidim/decidim_awesome/admin/config/constraints", locals: { key: :allow_images_in_proposals, constraints: constraints_for(:allow_images_in_proposals) }) %>
+ <% end %>
<% end %>
+
+<% if config_enabled? %i(validate_title_min_length validate_title_max_caps_percent validate_title_max_marks_together validate_title_start_with_caps) %>
+
+
+
+
+
+
<%= t("validators.body", scope: "decidim.decidim_awesome.admin.config.form") %>
+
+
+ <% if config_enabled? :validate_body_start_with_caps %>
+ <%= form.check_box :validate_body_start_with_caps %>
+
+ <%= render(partial: "decidim/decidim_awesome/admin/config/constraints", locals: { key: :validate_body_start_with_caps, constraints: constraints_for(:validate_body_start_with_caps) }) %>
+ <% end %>
+
+ <% if config_enabled? :validate_body_min_length %>
+ <%= form.number_field :validate_body_min_length %>
+
<%= t("help.validate_body_min_length", scope: "decidim.decidim_awesome.admin.config.form") %>
+
+ <%= render(partial: "decidim/decidim_awesome/admin/config/constraints", locals: { key: :validate_body_min_length, constraints: constraints_for(:validate_body_min_length) }) %>
+ <% end %>
+
+ <% if config_enabled? :validate_body_max_caps_percent %>
+ <%= form.number_field :validate_body_max_caps_percent %>
+
<%= t("help.validate_body_max_caps_percent", scope: "decidim.decidim_awesome.admin.config.form") %>
+
+ <%= render(partial: "decidim/decidim_awesome/admin/config/constraints", locals: { key: :validate_body_max_caps_percent, constraints: constraints_for(:validate_body_max_caps_percent) }) %>
+ <% end %>
+
+ <% if config_enabled? :validate_body_max_marks_together %>
+ <%= form.number_field :validate_body_max_marks_together %>
+
<%= t("help.validate_body_max_marks_together", scope: "decidim.decidim_awesome.admin.config.form") %>
+
+ <%= render(partial: "decidim/decidim_awesome/admin/config/constraints", locals: { key: :validate_body_max_marks_together, constraints: constraints_for(:validate_body_max_marks_together) }) %>
+ <% end %>
+
+<% end %>
diff --git a/config/locales/en.yml b/config/locales/en.yml
index 7dbab1f18..faa6a3d56 100644
--- a/config/locales/en.yml
+++ b/config/locales/en.yml
@@ -26,6 +26,18 @@ en:
scoped_admins: Scoped admins group %{id}
scoped_styles: Custom styles %{id}
use_markdown_editor: Use a Markdown editor instead of the HTML editor
+ validate_body_max_caps_percent: Maximum allowed percentage of capital letters
+ for the body
+ validate_body_max_marks_together: Maximum consecutive marks symbols allowed
+ in the body
+ validate_body_min_length: Minimum required characters for the body
+ validate_body_start_with_caps: Force the body to start with a capital letter
+ validate_title_max_caps_percent: Maximum allowed percentage of capital letters
+ for the title
+ validate_title_max_marks_together: Maximum consecutive marks symbols allowed
+ in the title
+ validate_title_min_length: Minimum required characters for the title
+ validate_title_start_with_caps: Force the title to start with a capital letter
constraint:
component_id: or specifically in
component_manifest: Only in components of type
@@ -199,6 +211,23 @@ en:
use_markdown_editor: This will substitute the Quill WYSIWYG editor,
to use a Markdown editor instead. Text will be converted and saved
as HTML in the database.
+ validate_body_max_caps_percent: Zero won't allow any capital letter,
+ 100 will force to write everything in capital letters
+ validate_body_max_marks_together: 'Limit the number of question and
+ exclamation marks that can be written together. Ie: if it is 2, then
+ ''!!!'' won''t be allowed in the text'
+ validate_body_min_length: This number can be zero, this will effectively
+ make this field non-mandatory
+ validate_title_max_caps_percent: Zero won't allow any capital letter,
+ 100 will force to write everything in capital letters
+ validate_title_max_marks_together: 'Limit the number of question and
+ exclamation marks that can be written together. Ie: if it is 2, then
+ ''!!!'' won''t be allowed in the text'
+ validate_title_min_length: Title is always mandatory and this number
+ cannot be zero
+ validators:
+ body: User input validations for the "body" field
+ title: User input validations for the "title" field
form_proposal_custom_fields:
new: Add a new "custom fields" box
remove: Remove this "custom fields" box
@@ -388,6 +417,9 @@ en:
show:
view_meeting: View meeting
view_proposal: View proposal
+ validators:
+ too_much_caps: Is using too many capital letters (over %{percent}% of the
+ text)
layouts:
decidim:
admin:
diff --git a/examples/custom_validations.png b/examples/custom_validations.png
new file mode 100644
index 000000000..c32b3d527
Binary files /dev/null and b/examples/custom_validations.png differ
diff --git a/lib/decidim/decidim_awesome/awesome.rb b/lib/decidim/decidim_awesome/awesome.rb
index e211514ab..10219e02c 100644
--- a/lib/decidim/decidim_awesome/awesome.rb
+++ b/lib/decidim/decidim_awesome/awesome.rb
@@ -61,6 +61,41 @@ module DecidimAwesome
false
end
+ # Configuration options to handle different validations in proposals
+ # (maybe in the future will apply to other places)
+ # Set it to :disabled if you don't want to use this feature
+ config_accessor :validate_title_min_length do
+ 15
+ end
+
+ config_accessor :validate_title_max_caps_percent do
+ 25
+ end
+
+ config_accessor :validate_title_max_marks_together do
+ 1
+ end
+
+ config_accessor :validate_title_start_with_caps do
+ true
+ end
+
+ config_accessor :validate_body_min_length do
+ 15
+ end
+
+ config_accessor :validate_body_max_caps_percent do
+ 25
+ end
+
+ config_accessor :validate_body_max_marks_together do
+ 1
+ end
+
+ config_accessor :validate_body_start_with_caps do
+ true
+ end
+
config_accessor :intergram_for_public do
false
end
diff --git a/lib/decidim/decidim_awesome/checksums.yml b/lib/decidim/decidim_awesome/checksums.yml
index bdd1c01ee..51e064ee4 100644
--- a/lib/decidim/decidim_awesome/checksums.yml
+++ b/lib/decidim/decidim_awesome/checksums.yml
@@ -2,6 +2,8 @@ decidim-admin:
/app/views/layouts/decidim/admin/_header.html.erb:
decidim-0.25: 1aff077428830b12306d6c42e6b37216
decidim-core:
+ /app/validators/etiquette_validator.rb:
+ decidim-0.25: f7a4a652005385a994208f1ab41c4f08
/app/views/layouts/decidim/_head.html.erb:
decidim-0.25: eb490aa482477ff70f541d20cddec773
decidim-0.26: 0927fc81123addec70853c2e7986c538
diff --git a/lib/decidim/decidim_awesome/config.rb b/lib/decidim/decidim_awesome/config.rb
index 32ff56e32..0823c5354 100644
--- a/lib/decidim/decidim_awesome/config.rb
+++ b/lib/decidim/decidim_awesome/config.rb
@@ -63,8 +63,8 @@ def organization_config
def unfiltered_config
valid = @vars.map { |v| [v.var.to_sym, v.value] }.to_h
- map_defaults do |key|
- valid[key].presence
+ map_defaults do |key, val|
+ valid.has_key?(key) ? valid[key] : val
end
end
@@ -146,8 +146,8 @@ def map_defaults
defaults.map do |key, val|
value = false
unless val == :disabled
- value = yield(key) || val
- value = val.merge(value.transform_keys(&:to_sym)) if val.is_a? Hash
+ value = yield(key, val)
+ value = val.merge(value.transform_keys(&:to_sym)) if val.is_a?(Hash) && value.is_a?(Hash)
end
[key, value]
end.to_h
@@ -158,8 +158,8 @@ def calculate_config
valid = @vars.filter { |item| enabled_for_organization?(item.var) && valid_in_context?(item.all_constraints) }
.map { |v| [v.var.to_sym, v.value] }.to_h
- map_defaults do |key|
- valid[key].presence
+ map_defaults do |key, val|
+ valid.has_key?(key) ? valid[key] : val
end
end
diff --git a/lib/decidim/decidim_awesome/engine.rb b/lib/decidim/decidim_awesome/engine.rb
index ecd21d42b..d62874a18 100644
--- a/lib/decidim/decidim_awesome/engine.rb
+++ b/lib/decidim/decidim_awesome/engine.rb
@@ -27,25 +27,52 @@ class Engine < ::Rails::Engine
# Include additional helpers globally
ActionView::Base.include(Decidim::DecidimAwesome::AwesomeHelpers)
- # override user's admin property
- Decidim::User.include(Decidim::DecidimAwesome::UserOverride) if DecidimAwesome.enabled?(:scoped_admins)
-
- # redirect unauthorized scoped admins to allowed places or custom redirects if configured
- Decidim::ErrorsController.include(Decidim::DecidimAwesome::NotFoundRedirect) if DecidimAwesome.enabled?([:scoped_admins, :custom_redirects])
+ # Override EtiquetteValidator
+ EtiquetteValidator.include(Decidim::DecidimAwesome::EtiquetteValidatorOverride) if DecidimAwesome.enabled?([:validate_title_max_caps_percent,
+ :validate_title_max_marks_together,
+ :validate_title_start_with_caps,
+ :validate_body_max_caps_percent,
+ :validate_body_max_marks_together,
+ :validate_body_start_with_caps])
# Custom fields need to deal with several places
- if DecidimAwesome.enabled?(:proposal_custom_fields)
- Decidim::Proposals::ApplicationHelper.include(Decidim::DecidimAwesome::Proposals::ApplicationHelperOverride)
+ if DecidimAwesome.enabled?([:proposal_custom_fields,
+ :validate_title_min_length,
+ :validate_title_max_caps_percent,
+ :validate_title_max_marks_together,
+ :validate_title_start_with_caps,
+ :validate_body_min_length,
+ :validate_body_max_caps_percent,
+ :validate_body_max_marks_together,
+ :validate_body_start_with_caps])
Decidim::Proposals::ProposalWizardCreateStepForm.include(Decidim::DecidimAwesome::Proposals::ProposalWizardCreateStepFormOverride)
- Decidim::AmendmentsHelper.include(Decidim::DecidimAwesome::AmendmentsHelperOverride)
end
+ # override user's admin property
+ Decidim::User.include(Decidim::DecidimAwesome::UserOverride) if DecidimAwesome.enabled?(:scoped_admins)
+
Decidim::MenuPresenter.include(Decidim::DecidimAwesome::MenuPresenterOverride)
Decidim::MenuItemPresenter.include(Decidim::DecidimAwesome::MenuItemPresenterOverride)
# Late registering of components to take into account initializer values
DecidimAwesome.registered_components.each do |manifest, block|
- Decidim.register_component(manifest, &block) unless DecidimAwesome.disabled_components.include?(manifest)
+ next if DecidimAwesome.disabled_components.include?(manifest)
+ next if Decidim.find_component_manifest(manifest)
+
+ Decidim.register_component(manifest, &block)
+ end
+ end
+
+ initializer "decidim_decidim_awesome.overrides", after: "decidim.action_controller" do
+ config.to_prepare do
+ # redirect unauthorized scoped admins to allowed places or custom redirects if configured
+ Decidim::ErrorsController.include(Decidim::DecidimAwesome::NotFoundRedirect) if DecidimAwesome.enabled?([:scoped_admins, :custom_redirects])
+
+ # Custom fields need to deal with several places
+ if DecidimAwesome.enabled?(:proposal_custom_fields)
+ Decidim::Proposals::ApplicationHelper.include(Decidim::DecidimAwesome::Proposals::ApplicationHelperOverride)
+ Decidim::AmendmentsHelper.include(Decidim::DecidimAwesome::AmendmentsHelperOverride)
+ end
end
end
diff --git a/lib/decidim/decidim_awesome/test/initializer.rb b/lib/decidim/decidim_awesome/test/initializer.rb
index 18a5b3586..37410c7de 100644
--- a/lib/decidim/decidim_awesome/test/initializer.rb
+++ b/lib/decidim/decidim_awesome/test/initializer.rb
@@ -15,7 +15,15 @@
:proposal_custom_fields,
:menu,
:scoped_admins,
- :custom_redirects
+ :custom_redirects,
+ :validate_title_min_length,
+ :validate_title_max_caps_percent,
+ :validate_title_max_marks_together,
+ :validate_title_start_with_caps,
+ :validate_body_min_length,
+ :validate_body_max_caps_percent,
+ :validate_body_max_marks_together,
+ :validate_body_start_with_caps
].each do |conf|
config.send("#{conf}=", :disabled)
end
diff --git a/lib/decidim/decidim_awesome/test/shared_examples/summary_examples.rb b/lib/decidim/decidim_awesome/test/shared_examples/summary_examples.rb
index e339a9f33..638bc7435 100644
--- a/lib/decidim/decidim_awesome/test/shared_examples/summary_examples.rb
+++ b/lib/decidim/decidim_awesome/test/shared_examples/summary_examples.rb
@@ -36,6 +36,7 @@
expect(Decidim::Proposals::ApplicationHelper.included_modules).to include(Decidim::DecidimAwesome::Proposals::ApplicationHelperOverride)
expect(Decidim::Proposals::ProposalWizardCreateStepForm.included_modules).to include(Decidim::DecidimAwesome::Proposals::ProposalWizardCreateStepFormOverride)
expect(Decidim::AmendmentsHelper.included_modules).to include(Decidim::DecidimAwesome::AmendmentsHelperOverride)
+ expect(EtiquetteValidator.included_modules).to include(Decidim::DecidimAwesome::EtiquetteValidatorOverride)
end
else
it "concerns are not registered" do
@@ -44,6 +45,7 @@
expect(Decidim::Proposals::ApplicationHelper.included_modules).not_to include(Decidim::DecidimAwesome::Proposals::ApplicationHelperOverride)
expect(Decidim::Proposals::ProposalWizardCreateStepForm.included_modules).not_to include(Decidim::DecidimAwesome::Proposals::ProposalWizardCreateStepFormOverride)
expect(Decidim::AmendmentsHelper.included_modules).not_to include(Decidim::DecidimAwesome::AmendmentsHelperOverride)
+ expect(EtiquetteValidator.included_modules).not_to include(Decidim::DecidimAwesome::EtiquetteValidatorOverride)
end
end
end
diff --git a/lib/decidim/decidim_awesome/version.rb b/lib/decidim/decidim_awesome/version.rb
index 398d35c55..8a1120d7a 100644
--- a/lib/decidim/decidim_awesome/version.rb
+++ b/lib/decidim/decidim_awesome/version.rb
@@ -3,7 +3,7 @@
module Decidim
# This holds the decidim-decidim_awesome version.
module DecidimAwesome
- VERSION = "0.8.3"
+ VERSION = "0.8.4"
COMPAT_DECIDIM_VERSION = [">= 0.25.0", "< 0.27"].freeze
end
end
diff --git a/spec/awesome_summary_spec.rb b/spec/awesome_summary_spec.rb
index 8906801ac..3eaee713f 100644
--- a/spec/awesome_summary_spec.rb
+++ b/spec/awesome_summary_spec.rb
@@ -35,6 +35,15 @@
let!(:intergram_for_public) { create(:awesome_config, organization: organization, var: :intergram_for_public, value: true) }
let!(:config_public_settings) { create(:awesome_config, organization: organization, var: :intergram_for_public_settings, value: intergram) }
let!(:config_admins_settings) { create(:awesome_config, organization: organization, var: :intergram_for_admins_settings, value: intergram) }
+ let!(:validate_title_min_length) { create(:awesome_config, organization: organization, var: :validate_title_min_length, value: 10) }
+ let!(:validate_title_max_caps_percent) { create(:awesome_config, organization: organization, var: :validate_title_max_caps_percent, value: 10) }
+ let!(:validate_title_max_marks_together) { create(:awesome_config, organization: organization, var: :validate_title_max_marks_together, value: 10) }
+ let!(:validate_title_start_with_caps) { create(:awesome_config, organization: organization, var: :validate_title_start_with_caps, value: true) }
+ let!(:validate_body_min_length) { create(:awesome_config, organization: organization, var: :validate_body_min_length, value: 10) }
+ let!(:validate_body_max_caps_percent) { create(:awesome_config, organization: organization, var: :validate_body_max_caps_percent, value: 10) }
+ let!(:validate_body_max_marks_together) { create(:awesome_config, organization: organization, var: :validate_body_max_marks_together, value: 10) }
+ let!(:validate_body_start_with_caps) { create(:awesome_config, organization: organization, var: :validate_body_start_with_caps, value: true) }
+
let(:styles) { "body {background: red;}" }
let(:intergram) do
{ chat_id: "some-id" }
diff --git a/spec/controllers/admin/config_controller_spec.rb b/spec/controllers/admin/config_controller_spec.rb
index 918e3c7af..5f0edc4f4 100644
--- a/spec/controllers/admin/config_controller_spec.rb
+++ b/spec/controllers/admin/config_controller_spec.rb
@@ -62,7 +62,17 @@ module Admin
end
context "and proposals is disabled" do
- let(:disabled) { editors + [:allow_images_in_proposals] }
+ let(:disabled) do
+ editors + [:allow_images_in_proposals,
+ :validate_title_min_length,
+ :validate_title_max_caps_percent,
+ :validate_title_max_marks_together,
+ :validate_title_start_with_caps,
+ :validate_body_min_length,
+ :validate_body_max_caps_percent,
+ :validate_body_max_marks_together,
+ :validate_body_start_with_caps]
+ end
it "returns surveys" do
expect(controller.helpers.config_var).to eq(:surveys)
diff --git a/spec/forms/admin/config_form_spec.rb b/spec/forms/admin/config_form_spec.rb
index 45be4fde6..06aea9a42 100644
--- a/spec/forms/admin/config_form_spec.rb
+++ b/spec/forms/admin/config_form_spec.rb
@@ -29,11 +29,20 @@ module Admin
let(:valid_fields) { '[{"foo":"bar"}]' }
let(:invalid_fields) { '[{"foo":"bar"}]{"baz":"zet"}' }
+ let(:validate_title_min_length) { 15 }
+ let(:validate_title_max_caps_percent) { 25 }
+ let(:validate_title_max_marks_together) { 2 }
+ let(:validate_title_start_with_caps) { true }
+ let(:validate_body_min_length) { 15 }
+ let(:validate_body_max_caps_percent) { 25 }
+ let(:validate_body_max_marks_together) { 2 }
+ let(:validate_body_start_with_caps) { true }
+
context "when everything is OK" do
it { is_expected.to be_valid }
end
- context "when custom styles" do
+ describe "custom styles" do
let(:attributes) do
{
scoped_styles: custom_styles
@@ -49,11 +58,11 @@ module Admin
}
end
- it { is_expected.not_to be_valid }
+ it { is_expected.to be_invalid }
end
end
- context "when proposal custom fields" do
+ describe "proposal custom fields" do
let(:attributes) do
{
proposal_custom_fields: custom_fields
@@ -69,7 +78,7 @@ module Admin
}
end
- it { is_expected.not_to be_valid }
+ it { is_expected.to be_invalid }
end
context "and sending labels with html" do
@@ -81,6 +90,125 @@ module Admin
end
end
end
+
+ describe "validators" do
+ let(:attributes) do
+ {
+ validate_title_min_length: validate_title_min_length,
+ validate_title_max_caps_percent: validate_title_max_caps_percent,
+ validate_title_max_marks_together: validate_title_max_marks_together,
+ validate_title_start_with_caps: validate_title_start_with_caps,
+ validate_body_min_length: validate_body_min_length,
+ validate_body_max_caps_percent: validate_body_max_caps_percent,
+ validate_body_max_marks_together: validate_body_max_marks_together,
+ validate_body_start_with_caps: validate_body_start_with_caps
+ }
+ end
+
+ it { is_expected.to be_valid }
+
+ context "and title start with caps is false" do
+ let(:validate_title_start_with_caps) { false }
+
+ it { is_expected.to be_valid }
+ end
+
+ context "and title min length is empty" do
+ let(:validate_title_min_length) { nil }
+
+ it { is_expected.to be_invalid }
+ end
+
+ context "and title min length is zero" do
+ let(:validate_title_min_length) { 0 }
+
+ it { is_expected.to be_invalid }
+ end
+
+ context "and title min length greater than 100" do
+ let(:validate_title_min_length) { 101 }
+
+ it { is_expected.to be_invalid }
+ end
+
+ context "and body min length is empty" do
+ let(:validate_body_min_length) { nil }
+
+ it { is_expected.to be_invalid }
+ end
+
+ context "and body min length is zero" do
+ let(:validate_body_min_length) { 0 }
+
+ it { is_expected.to be_valid }
+ end
+
+ context "and title max caps percent empty" do
+ let(:validate_title_max_caps_percent) { nil }
+
+ it { is_expected.to be_invalid }
+ end
+
+ context "and title max caps percent is zero" do
+ let(:validate_title_max_caps_percent) { 0 }
+
+ it { is_expected.to be_valid }
+ end
+
+ context "and title max caps percent is bigger than 100" do
+ let(:validate_title_max_caps_percent) { 101 }
+
+ it { is_expected.to be_invalid }
+ end
+
+ context "and body start with caps is false" do
+ let(:validate_body_start_with_caps) { false }
+
+ it { is_expected.to be_valid }
+ end
+
+ context "and body max caps percent empty" do
+ let(:validate_body_max_caps_percent) { nil }
+
+ it { is_expected.to be_invalid }
+ end
+
+ context "and body max caps percent is zero" do
+ let(:validate_body_max_caps_percent) { 0 }
+
+ it { is_expected.to be_valid }
+ end
+
+ context "and body max caps percent is bigger than 100" do
+ let(:validate_body_max_caps_percent) { 101 }
+
+ it { is_expected.to be_invalid }
+ end
+
+ context "and title max marks together is empty" do
+ let(:validate_title_max_marks_together) { nil }
+
+ it { is_expected.to be_invalid }
+ end
+
+ context "and title max marks together is zero" do
+ let(:validate_title_max_marks_together) { 0 }
+
+ it { is_expected.to be_invalid }
+ end
+
+ context "and body max marks together is empty" do
+ let(:validate_body_max_marks_together) { nil }
+
+ it { is_expected.to be_invalid }
+ end
+
+ context "and body max marks together is zero" do
+ let(:validate_body_max_marks_together) { 0 }
+
+ it { is_expected.to be_invalid }
+ end
+ end
end
end
end
diff --git a/spec/forms/proposal_wizard_create_step_form_spec.rb b/spec/forms/proposal_wizard_create_step_form_spec.rb
index 2cef5543c..a5b6020b4 100644
--- a/spec/forms/proposal_wizard_create_step_form_spec.rb
+++ b/spec/forms/proposal_wizard_create_step_form_spec.rb
@@ -106,5 +106,162 @@ module Decidim::Proposals
it { is_expected.to be_valid }
end
end
+
+ shared_examples "starts with caps" do |prop|
+ let!(:config) { create :awesome_config, organization: organization, var: "validate_#{prop}_start_with_caps", value: enabled }
+ let!(:constraint) { create(:config_constraint, awesome_config: config, settings: { "participatory_space_manifest" => "participatory_processes", "participatory_space_slug" => slug }) }
+
+ let(:enabled) { false }
+ let(prop.to_sym) { "í don't start with caps" }
+
+ it { is_expected.to be_valid }
+
+ context "when scoped under different context" do
+ let(:slug) { "another-slug" }
+
+ it { is_expected.to be_invalid }
+
+ context "when starts with caps" do
+ let(prop.to_sym) { "Í start with caps" }
+
+ it { is_expected.to be_valid }
+ end
+ end
+
+ context "when enabled" do
+ let(:enabled) { true }
+
+ it { is_expected.to be_invalid }
+
+ context "when starts with caps" do
+ let(prop.to_sym) { "Í start with caps" }
+
+ it { is_expected.to be_valid }
+ end
+ end
+ end
+
+ shared_examples "minimum length" do |prop|
+ let!(:config) { create :awesome_config, organization: organization, var: "validate_#{prop}_min_length", value: min_length }
+ let!(:constraint) { create(:config_constraint, awesome_config: config, settings: { "participatory_space_manifest" => "participatory_processes", "participatory_space_slug" => slug }) }
+
+ let(:min_length) { 10 }
+ let(prop.to_sym) { "I am 10 yo" }
+
+ it { is_expected.to be_valid }
+
+ context "when scoped under different context" do
+ let(:slug) { "another-slug" }
+
+ it { is_expected.to be_invalid }
+
+ context "when has more than 15 chars" do
+ let(prop.to_sym) { "I am 17 years old" }
+
+ it { is_expected.to be_valid }
+ end
+ end
+
+ context "when less than allowed" do
+ let(:min_length) { 11 }
+
+ it { is_expected.to be_invalid }
+ end
+
+ # rubocop:disable RSpec/EmptyExampleGroup
+ context "when min_length is zero" do
+ let(:min_length) { 0 }
+ let(prop.to_sym) { "" }
+
+ if prop == :body
+ it { is_expected.to be_valid }
+ else
+ it { is_expected.to be_invalid }
+ end
+ end
+ # rubocop:enable RSpec/EmptyExampleGroup
+ end
+
+ shared_examples "max caps percent" do |prop|
+ let!(:config) { create :awesome_config, organization: organization, var: "validate_#{prop}_max_caps_percent", value: percent }
+ let!(:constraint) { create(:config_constraint, awesome_config: config, settings: { "participatory_space_manifest" => "participatory_processes", "participatory_space_slug" => slug }) }
+
+ let(:percent) { 90 }
+ let(prop.to_sym) { "Í ÁM A SÈMI-CÁPS text" }
+
+ it { is_expected.to be_valid }
+
+ shared_examples "invalid percentage" do |per|
+ it "error message returns percentage" do
+ expect(form).to be_invalid
+ expect(form.errors.messages.values.flatten.first).to include("over #{per}% of the text")
+ end
+ end
+
+ context "when scoped under different context" do
+ let(:slug) { "another-slug" }
+
+ it_behaves_like "invalid percentage", 25
+
+ context "when has less than 25% caps" do
+ let(prop.to_sym) { "Í only have some CÁPS" }
+
+ it { is_expected.to be_valid }
+ end
+ end
+
+ context "when less than allowed" do
+ let(:percent) { 11 }
+
+ it_behaves_like "invalid percentage", 11
+ end
+ end
+
+ shared_examples "max marks together" do |prop|
+ let!(:config) { create :awesome_config, organization: organization, var: "validate_#{prop}_max_marks_together", value: max_marks }
+ let!(:constraint) { create(:config_constraint, awesome_config: config, settings: { "participatory_space_manifest" => "participatory_processes", "participatory_space_slug" => slug }) }
+
+ let(:max_marks) { 5 }
+ let(prop.to_sym) { "Am I a little bit noisy??!!!" }
+
+ it { is_expected.to be_valid }
+
+ context "when scoped under different context" do
+ let(:slug) { "another-slug" }
+
+ it { is_expected.to be_invalid }
+
+ context "when has only 1 mark" do
+ let(prop.to_sym) { "I am not noisy!" }
+
+ it { is_expected.to be_valid }
+ end
+
+ context "when has 2 marks" do
+ let(prop.to_sym) { "I am not noisy!?" }
+
+ it { is_expected.to be_invalid }
+ end
+ end
+
+ context "when less than allowed" do
+ let(:max_marks) { 4 }
+
+ it { is_expected.to be_invalid }
+ end
+ end
+
+ describe "etiquette validations" do
+ let(:body) { "A body longer than the permitted" }
+
+ it_behaves_like "minimum length", :title
+ it_behaves_like "minimum length", :body
+ it_behaves_like "starts with caps", :title
+ it_behaves_like "starts with caps", :body
+ it_behaves_like "max caps percent", :title
+ it_behaves_like "max caps percent", :body
+ it_behaves_like "max marks together", :title
+ it_behaves_like "max marks together", :body
+ end
end
end
diff --git a/spec/lib/system_checker_spec.rb b/spec/lib/system_checker_spec.rb
index c5c332195..4f767799d 100644
--- a/spec/lib/system_checker_spec.rb
+++ b/spec/lib/system_checker_spec.rb
@@ -17,7 +17,7 @@ module Decidim::DecidimAwesome
end
it "has 5 modified files in core" do
- expect(subject.overrides["decidim-core"].files.length).to eq(5)
+ expect(subject.overrides["decidim-core"].files.length).to eq(6)
end
it "has 5 modified files in proposals" do
diff --git a/spec/system/admin/admin_spec.rb b/spec/system/admin/admin_spec.rb
index 9e2e40206..2781ea6cd 100644
--- a/spec/system/admin/admin_spec.rb
+++ b/spec/system/admin/admin_spec.rb
@@ -105,6 +105,8 @@
it "renders the page" do
expect(page).to have_content(/Tweaks for proposals/i)
expect(page).to have_content("\"Rich text editor for participants\" is enabled")
+ expect(page).to have_content("User input validations for the \"title\" field")
+ expect(page).to have_content("User input validations for the \"body\" field")
end
context "and rich text editor for participants is disabled" do
@@ -115,10 +117,36 @@
expect(page).not_to have_content("\"Rich text editor for participants\" is enabled")
end
end
+
+ context "when all title validators are disabled" do
+ let(:disabled_features) { [:validate_title_min_length, :validate_title_max_caps_percent, :validate_title_max_marks_together, :validate_title_start_with_caps] }
+
+ it "does not show title options" do
+ expect(page).not_to have_content("User input validations for the \"title\" field")
+ expect(page).to have_content("User input validations for the \"body\" field")
+ end
+ end
+
+ context "when all body validators are disabled" do
+ let(:disabled_features) { [:validate_body_min_length, :validate_body_max_caps_percent, :validate_body_max_marks_together, :validate_body_start_with_caps] }
+
+ it "does not show body options" do
+ expect(page).to have_content("User input validations for the \"title\" field")
+ expect(page).not_to have_content("User input validations for the \"body\" field")
+ end
+ end
+ end
+
+ context "when some proposals hacks are disabled" do
+ [:allow_images_in_proposals, :validate_title_min_length, :validate_title_max_caps_percent, :validate_title_max_marks_together, :validate_title_start_with_caps, :validate_body_min_length, :validate_body_max_caps_percent, :validate_body_max_marks_together, :validate_body_start_with_caps].each do |var|
+ let(:disabled_features) { [var] }
+
+ it_behaves_like "has menu link", "proposals"
+ end
end
- context "when proposal hacks are disabled" do
- let(:disabled_features) { [:allow_images_in_proposals] }
+ context "when all proposals hacks are disabled" do
+ let(:disabled_features) { [:allow_images_in_proposals, :validate_title_min_length, :validate_title_max_caps_percent, :validate_title_max_marks_together, :validate_title_start_with_caps, :validate_body_min_length, :validate_body_max_caps_percent, :validate_body_max_marks_together, :validate_body_start_with_caps] }
it_behaves_like "do not have menu link", "proposals"
end