Skip to content

Commit dd3d9e3

Browse files
ElviaBthmicrostudi
andauthored
Add a new cookie policy/category manager (#521)
* add cookie_manager to the menu * add controller and admin view * add crud of cookie_categories * normalize --locales * run linter * fix erb_lint * add controllers and forms specs * add commands * add commands specs * add overridesç * fix lint error * add category.erb override * fix controlles specs * add has_cookie_categories * refactor data_consent_cell * add improvements to categories * add breadcrumb item * fix overrides bug * add icons to admin index view * fix specs * add translation * fix system spec * apply review suggested changes * fix specs * apply UI improvements * refactor base on review suggestions * add feature info to README * apply changes * add data_consent_cell override * add presets cookies * fix spec * fix common cookie item dropdown * fix lint * add services * update conditions for default categories * add services and helpers * add changes related to previous review * update ReadMe * refactor * go nuclear * apply review refactor * fix spec * normalize --locales * add type to cookie settings modal * fixes * simplify code * fix specs * fix specs * update destroy cookie spec * add breadcrumb * add non_editable_fields_unchanged method * register icon * fix override spec * add warning for iFrame * fixes * fix form bugs * fix spec * replace visibility state default to visible * fix specs. correct validations * fix presets command * add admin system test * add public system spec --------- Co-authored-by: Ivan Vergés <ivan@pokecode.net>
1 parent 7d96c22 commit dd3d9e3

60 files changed

Lines changed: 5558 additions & 208 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ Compatibility:
99

1010
Features:
1111
- Added version update checking from GitHub on System Compatibility page
12+
- Add a new cookie policy/category manager ([#521](https://github.com/decidim-ice/decidim-module-decidim_awesome/pull/521))
1213
- Add Process Groups Extended content block with status and taxonomy filters ([#519](https://github.com/decidim-ice/decidim-module-decidim_awesome/pull/519))
1314
- Configurable Awesome Processes homepage content block ([#518](https://github.com/decidim-ice/decidim-module-decidim_awesome/pull/518))
1415

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
# frozen_string_literal: true
2+
3+
module Decidim
4+
module DecidimAwesome
5+
module DataConsentCellOverride
6+
# [ {
7+
# slug: "essential",
8+
# mandatory: true,
9+
# title: "Essential cookies",
10+
# description: "These cookies are necessary for the website to function and cannot be switched"
11+
# visibility: visible
12+
# items: [
13+
# { type: "cookie", name: "_session_id", service: "this page", description: "Session ID cookie", expiration: ... },
14+
# { type: "cookie", name: "decidim-consent", service: "this page", description: "Consent cookie", expiration: ... },
15+
# { type: "local_storage", name: "pwaInstallPromptSeen", service: "this page", description: "PWA install prompt seen", expiration: ... }
16+
# ]
17+
# }, etc... ]
18+
19+
def categories
20+
@categories ||= CookieManagementStore.new(model, awesome_categories).categories.values.map do |category|
21+
next if category["visibility"] == "hidden"
22+
23+
category.tap do |cat|
24+
cat["title"] = translated_attribute(category["title"])
25+
cat["description"] = translated_attribute(category["description"])
26+
cat["items"] = cat["items"].values.map do |item|
27+
item.tap do |i|
28+
i["service"] = translated_attribute(i["service"])
29+
i["description"] = translated_attribute(i["description"])
30+
i["expiration"] = translated_attribute(i["expiration"])
31+
end
32+
end.compact
33+
end
34+
end.compact
35+
end
36+
37+
private
38+
39+
def awesome_categories
40+
Decidim::DecidimAwesome::AwesomeConfig.find_by(organization: model, var: :cookie_management)&.value
41+
end
42+
end
43+
end
44+
end
Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
<div class="cookies__category" data-id="<%= category["slug"] %>">
2+
<div class="cookies__category-trigger">
3+
<label for="dc-<%= category["slug"] %>" class="cookies__category-toggle">
4+
<input
5+
<%== %(checked="checked") if category["mandatory"] %>
6+
id="dc-<%= category["slug"] %>"
7+
type="checkbox"
8+
name="<%= category["slug"] %>"
9+
<%= "disabled" if category["mandatory"] %>>
10+
<span class="sr-only"><%= t("layouts.decidim.data_consent.modal.toggle", consent_category: category["title"]) %></span>
11+
<span class="cookies__category-toggle-content"></span>
12+
<%= icon "check-line", class: "cookies__category-toggle-icon" %>
13+
<%= icon "close-line", class: "cookies__category-toggle-icon" %>
14+
</label>
15+
16+
<div id="accordion-trigger-<%= category["slug"] %>" data-controls="accordion-panel-<%= category["slug"] %>" aria-labelledby="accordion-title-<%= category["slug"] %>">
17+
<h3 id="accordion-title-<%= category["slug"] %>" class="cookies__category-trigger-title">
18+
<%= category["title"] %>
19+
</h3>
20+
21+
<span>
22+
<%= icon "arrow-down-s-line", class: "cookies__category-trigger-arrow" %>
23+
<%= icon "arrow-up-s-line", class: "cookies__category-trigger-arrow" %>
24+
</span>
25+
</div>
26+
27+
</div>
28+
29+
<div id="accordion-panel-<%= category["slug"] %>" class="cookies__category-panel" aria-hidden="true">
30+
<p><%= category["description"] %></p>
31+
32+
<% if category["items"].present? %>
33+
<div>
34+
<div class="cookies__category-panel__tr">
35+
<div class="cookies__category-panel__th">
36+
<%= t("layouts.decidim.data_consent.details.columns.name") %>
37+
</div>
38+
<div class="cookies__category-panel__th">
39+
<%= t("layouts.decidim.data_consent.details.columns.service") %>
40+
</div>
41+
<div class="cookies__category-panel__th">
42+
<%= t("layouts.decidim.data_consent.details.columns.expiration") %>
43+
</div>
44+
<div class="cookies__category-panel__th">
45+
<%= t("layouts.decidim.data_consent.details.columns.description") %>
46+
</div>
47+
</div>
48+
49+
<% category["items"].each do |item| %>
50+
<div class="cookies__category-panel__tr">
51+
<div class="cookies__category-panel__td">
52+
<%= item["name"] %>
53+
</div>
54+
<div class="cookies__category-panel__td">
55+
<%= item["service"].presence || t("layouts.decidim.data_consent.details.items.#{item["name"]}.service") %>
56+
<span class="cookies__category-panel__type">(<%= t("layouts.decidim.data_consent.details.types.#{item["type"]}") %>)</span>
57+
</div>
58+
<div class="cookies__category-panel__td">
59+
<%= translated_attribute(item["expiration"]) %>
60+
</div>
61+
<div class="cookies__category-panel__td">
62+
<%= item["description"].presence || t("layouts.decidim.data_consent.details.items.#{item["name"]}.description") %>
63+
</div>
64+
</div>
65+
<% end %>
66+
</div>
67+
<% end %>
68+
</div>
69+
</div>
Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
# frozen_string_literal: true
2+
3+
module Decidim
4+
module DecidimAwesome
5+
module Admin
6+
class CreateCookieItemPreset < Decidim::Command
7+
# Public: Initializes the command.
8+
#
9+
# forms - An array of CookieItemForm objects to create.
10+
# category_slug - The slug of the category where the items will be created.
11+
def initialize(forms, category_slug)
12+
@forms = forms
13+
@category_slug = category_slug
14+
@config = AwesomeConfig.find_by(organization: forms.first.current_organization, var: :cookie_management)
15+
end
16+
17+
# Executes the command. Broadcasts these events:
18+
#
19+
# - :ok when all items were created (skipping duplicates).
20+
# - :invalid if any item fails validation or an unexpected error occurs.
21+
#
22+
# Returns nothing.
23+
def call
24+
errors = []
25+
forms.each do |form|
26+
form_with_context = form.with_context(
27+
current_organization: form.current_organization,
28+
current_user: form.current_user,
29+
category:
30+
)
31+
32+
UpdateCookieItem.call(form_with_context, category_slug) do
33+
on(:ok) do
34+
existing_items.merge!(form.name => form.to_params)
35+
end
36+
on(:invalid) do |error_message|
37+
errors << "#{form.name}: #{error_message.presence || form.errors.full_messages.join(", ")}"
38+
end
39+
end
40+
end
41+
42+
return broadcast(:invalid, errors.join(" | ")) if errors.any?
43+
44+
broadcast(:ok)
45+
rescue StandardError => e
46+
broadcast(:invalid, e.message)
47+
end
48+
49+
private
50+
51+
attr_reader :forms, :category_slug
52+
53+
def category
54+
@category ||= @config.value[category_slug]
55+
end
56+
57+
def existing_items
58+
@existing_items ||= category["items"] || {}
59+
end
60+
end
61+
end
62+
end
63+
end
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
# frozen_string_literal: true
2+
3+
module Decidim
4+
module DecidimAwesome
5+
module Admin
6+
class DestroyCookieCategory < Decidim::Command
7+
include NeedsAwesomeConfig
8+
9+
# Public: Initializes the command.
10+
#
11+
# category_slug - The slug of the category to destroy.
12+
# organization - The organization where the category belongs.
13+
# config - The AwesomeConfig instance for cookie management.
14+
def initialize(category_slug, organization)
15+
@category_slug = category_slug
16+
@organization = organization
17+
@config = AwesomeConfig.find_by(organization: organization, var: :cookie_management)
18+
end
19+
20+
attr_reader :category_slug, :organization, :config
21+
22+
# Executes the command. Broadcasts these events:
23+
#
24+
# - :ok when everything is valid.
25+
# - :invalid if the category is not found.
26+
#
27+
# Returns nothing.
28+
def call
29+
return broadcast(:invalid) unless config&.value
30+
31+
config.value.delete(category_slug)
32+
config.save!
33+
34+
broadcast(:ok)
35+
rescue StandardError => e
36+
broadcast(:invalid, e.message)
37+
end
38+
end
39+
end
40+
end
41+
end
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
# frozen_string_literal: true
2+
3+
module Decidim
4+
module DecidimAwesome
5+
module Admin
6+
class DestroyCookieItem < Decidim::Command
7+
# Public: Initializes the command.
8+
#
9+
# category_slug - The slug of the category where the item belongs.
10+
# item_name - The name of the item to destroy.
11+
# organization - The organization where the item belongs.
12+
# config - The AwesomeConfig instance for cookie management.
13+
def initialize(category_slug, item_name, organization)
14+
@category_slug = category_slug.to_s
15+
@item_name = item_name.to_s
16+
@organization = organization
17+
@config = AwesomeConfig.find_by(organization: organization, var: :cookie_management)
18+
end
19+
20+
attr_reader :category_slug, :item_name, :organization, :config
21+
22+
# Executes the command. Broadcasts these events:
23+
#
24+
# - :ok when everything is valid.
25+
# - :invalid if the category is not found or the item is not found.
26+
#
27+
# Returns nothing.
28+
def call
29+
return broadcast(:invalid) unless config&.value
30+
31+
category = config.value[category_slug]
32+
return broadcast(:invalid) unless category
33+
return broadcast(:invalid) unless category["items"]&.has_key?(item_name)
34+
35+
category["items"].delete(item_name)
36+
config.save!
37+
38+
broadcast(:ok)
39+
rescue StandardError => e
40+
broadcast(:invalid, e.message)
41+
end
42+
end
43+
end
44+
end
45+
end
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
# frozen_string_literal: true
2+
3+
module Decidim
4+
module DecidimAwesome
5+
module Admin
6+
class UpdateCookieCategory < Decidim::Command
7+
# Public: Initializes the command.
8+
#
9+
# form - A form object with the params.
10+
# config - The AwesomeConfig instance for cookie management.
11+
def initialize(form)
12+
@form = form
13+
@config = AwesomeConfig.find_or_initialize_by(organization: form.current_organization, var: :cookie_management)
14+
end
15+
16+
attr_reader :form, :config
17+
18+
# Executes the command. Broadcasts these events:
19+
#
20+
# - :ok when everything is valid.
21+
# - :invalid if the form is invalid, category not found, or slug already exists.
22+
#
23+
# Returns nothing.
24+
def call
25+
return broadcast(:invalid) if form.invalid?
26+
27+
config.value ||= {}
28+
# Handle slug change by deleting old key
29+
config.value[form.slug] = config.value.delete(form.id) if form.id && form.slug != form.id
30+
config.value[form.slug] = form.to_params.merge("items" => items)
31+
config.save!
32+
33+
broadcast(:ok)
34+
rescue StandardError => e
35+
broadcast(:invalid, e.message)
36+
end
37+
38+
private
39+
40+
def items
41+
return {} unless config&.value
42+
43+
config.value.dig(form.slug, "items") || {}
44+
end
45+
end
46+
end
47+
end
48+
end
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
# frozen_string_literal: true
2+
3+
module Decidim
4+
module DecidimAwesome
5+
module Admin
6+
class UpdateCookieItem < Decidim::Command
7+
# Public: Initializes the command.
8+
#
9+
# form - A form object with the params.
10+
# category_slug - The slug of the category where the item belongs.
11+
# config - The AwesomeConfig instance for cookie management.
12+
def initialize(form, category_slug)
13+
@form = form
14+
@category_slug = category_slug
15+
@config = AwesomeConfig.find_by(organization: form.current_organization, var: :cookie_management)
16+
end
17+
18+
attr_reader :form, :category_slug, :config
19+
20+
# Executes the command. Broadcasts these events:
21+
#
22+
# - :ok when everything is valid.
23+
# - :invalid if the form is invalid, category/item not found, or name already exists.
24+
#
25+
# Returns nothing.
26+
def call
27+
return broadcast(:invalid) if form.invalid?
28+
return broadcast(:invalid) unless config&.value&.[](category_slug)
29+
30+
config.value[category_slug]["items"] ||= {}
31+
# Handle slug change by deleting old key
32+
config.value[category_slug]["items"][form.name] = config.value[category_slug]["items"].delete(form.id) if form.id && form.name != form.id
33+
config.value[category_slug]["items"][form.name] = form.to_params
34+
config.save!
35+
36+
broadcast(:ok)
37+
rescue ActiveRecord::RecordInvalid => e
38+
broadcast(:invalid, e.record.errors.full_messages.join(", "))
39+
rescue StandardError => e
40+
broadcast(:invalid, e.message)
41+
end
42+
end
43+
end
44+
end
45+
end

0 commit comments

Comments
 (0)