Allow administrators to define the cookies vendors the application uses

This commit is contained in:
taitus
2024-11-29 13:08:29 +01:00
parent 390c749d24
commit 6753505e7c
31 changed files with 375 additions and 0 deletions

View File

@@ -0,0 +1,5 @@
<%= back_link_to admin_settings_path(anchor: "tab-cookies-consent") %>
<%= header %>
<%= render Admin::Cookies::Vendors::FormComponent.new(vendor) %>

View File

@@ -0,0 +1,15 @@
class Admin::Cookies::Vendors::EditComponent < ApplicationComponent
include Header
attr_reader :vendor
def initialize(vendor)
@vendor = vendor
end
private
def title
t("admin.cookies.vendors.edit.title")
end
end

View File

@@ -0,0 +1,10 @@
<%= form_for [:admin, vendor], html: { class: "vendor-form" } do |f| %>
<%= render "shared/errors", resource: vendor %>
<%= f.text_field :name, hint: t("admin.cookies.vendors.form.name.help_text") %>
<%= f.text_area :description, hint: t("admin.cookies.vendors.form.description.help_text"), rows: 4 %>
<%= f.text_field :cookie, hint: t("admin.cookies.vendors.form.cookie.help_text") %>
<%= f.text_area :script, hint: t("admin.cookies.vendors.form.script.help_text"), rows: 10 %>
<%= f.submit %>
<% end %>

View File

@@ -0,0 +1,7 @@
class Admin::Cookies::Vendors::FormComponent < ApplicationComponent
attr_reader :vendor
def initialize(vendor)
@vendor = vendor
end
end

View File

@@ -0,0 +1,5 @@
<%= back_link_to admin_settings_path(anchor: "tab-cookies-consent") %>
<%= header %>
<%= render Admin::Cookies::Vendors::FormComponent.new(vendor) %>

View File

@@ -0,0 +1,15 @@
class Admin::Cookies::Vendors::NewComponent < ApplicationComponent
include Header
attr_reader :vendor
def initialize(vendor)
@vendor = vendor
end
private
def title
t("admin.cookies.vendors.new.title")
end
end

View File

@@ -0,0 +1,5 @@
<tr id="<%= dom_id(vendor) %>" class="vendor">
<td><%= vendor.name %></td>
<td><%= vendor.cookie %></td>
<td><%= render Admin::TableActionsComponent.new(vendor) %></td>
</tr>

View File

@@ -0,0 +1,9 @@
class Admin::Cookies::Vendors::RowComponent < ApplicationComponent
with_collection_parameter :vendor
attr_reader :vendor
def initialize(vendor:)
@vendor = vendor
end
end

View File

@@ -0,0 +1,22 @@
<h3><%= t("admin.cookies.vendors.third_party_cookies") %></h3>
<% if vendors.any? %>
<table>
<thead>
<tr>
<th><%= attribute_name(:name) %></th>
<th><%= attribute_name(:cookie) %></th>
<th><%= t("admin.shared.actions") %></th>
</tr>
</thead>
<tbody>
<%= render Admin::Cookies::Vendors::RowComponent.with_collection(vendors) %>
</tbody>
</table>
<% else %>
<div class="callout primary clear">
<%= t("admin.cookies.vendors.empty") %>
</div>
<% end %>
<%= link_to t("admin.cookies.vendors.create_button"), new_admin_cookies_vendor_path, class: "button" %>

View File

@@ -0,0 +1,11 @@
class Admin::Cookies::Vendors::TableComponent < ApplicationComponent
private
def vendors
::Cookies::Vendor.all
end
def attribute_name(attribute)
::Cookies::Vendor.human_attribute_name(attribute)
end
end

View File

@@ -4,3 +4,5 @@
<%= render Admin::Settings::RowComponent.new("feature.cookies_consent", type: :feature, tab: tab) %>
<%= render Admin::Settings::RowComponent.new("cookies_consent.more_info_link", type: :text, tab: tab) %>
<% end %>
<%= render Admin::Cookies::Vendors::TableComponent.new %>

View File

@@ -0,0 +1,38 @@
class Admin::Cookies::VendorsController < Admin::BaseController
load_and_authorize_resource :vendor, class: "::Cookies::Vendor"
def create
if @vendor.save
redirect_to admin_settings_path(anchor: "tab-cookies-consent"),
notice: t("admin.cookies.vendors.create.notice")
else
render :new
end
end
def update
if @vendor.update(vendor_params)
redirect_to admin_settings_path(anchor: "tab-cookies-consent"),
notice: t("admin.cookies.vendors.update.notice")
else
render :edit
end
end
def destroy
@vendor.destroy!
redirect_to admin_settings_path(anchor: "tab-cookies-consent"),
notice: t("admin.cookies.vendors.destroy.notice")
end
private
def vendor_params
params.require(:cookies_vendor).permit(allowed_params)
end
def allowed_params
[:name, :description, :cookie, :script]
end
end

View File

@@ -142,6 +142,8 @@ module Abilities
can :manage, LocalCensusRecord
can [:create, :read], LocalCensusRecords::Import
can :manage, Cookies::Vendor
if Rails.application.config.multitenancy && Tenant.default?
can [:create, :read, :update, :hide, :restore], Tenant
end

5
app/models/cookies.rb Normal file
View File

@@ -0,0 +1,5 @@
module Cookies
def self.table_name_prefix
"cookies_"
end
end

View File

@@ -0,0 +1,4 @@
class Cookies::Vendor < ApplicationRecord
validates :name, presence: true
validates :cookie, presence: true, uniqueness: true, format: { with: /\A[a-zA-Z0-9\_]+\Z/ }
end

View File

@@ -0,0 +1 @@
<%= render Admin::Cookies::Vendors::EditComponent.new(@vendor) %>

View File

@@ -0,0 +1 @@
<%= render Admin::Cookies::Vendors::NewComponent.new(@vendor) %>

View File

@@ -151,6 +151,9 @@ en:
votation_type:
one: Votation type
other: Votation types
cookies/vendor:
one: cookie vendor
other: cookie vendors
attributes:
budget:
name: "Name"
@@ -534,6 +537,10 @@ en:
votation_type/vote_type:
unique: Unique answer
multiple: Multiple answers
cookies/vendor:
name: Vendor name
cookie: Cookie name
script: Javascript code
errors:
models:
user:

View File

@@ -1808,3 +1808,27 @@ en:
tags: "Tags"
tags_description: "Generates automatic tags on all items that can be tagged on."
title: "AI / Machine learning"
cookies:
vendors:
empty: No vendors found
third_party_cookies: Third party cookies
create_button: Create cookie vendor
create:
notice: Cookie vendor created successfully
new:
title: New cookie vendor
edit:
title: Edit cookie vendor
update:
notice: Cookie vendor updated successfully
destroy:
notice: Cookie vendor deleted successfully
form:
name:
help_text: This information will be publicly visible.
cookie:
help_text: This information will be used internally. The cookie name must be unique, and it can only contain letters, digits and underscores.
description:
help_text: This information will be publicly visible.
script:
help_text: This is the script to run when this cookie is accepted by the user. You can enter vanilla Javascript code here and introduce references to other application javascript.

View File

@@ -151,6 +151,9 @@ es:
votation_type:
one: Tipo de votación
other: Tipos de votación
cookies/vendor:
one: proveedor de cookies
other: proveedores de cookies
attributes:
budget:
name: "Nombre"
@@ -534,6 +537,10 @@ es:
votation_type/vote_type:
unique: Respuesta única
multiple: Respuesta múltiple
cookies/vendor:
name: Nombre del proveedor
cookie: Nombre de la cookie
script: Código javascript
errors:
models:
user:

View File

@@ -1808,3 +1808,27 @@ es:
tags: "Etiquetas"
tags_description: "Genera etiquetas automáticas para todos los elementos que pueden ser etiquetados."
title: "IA / Machine learning"
cookies:
vendors:
empty: No se han encontrado cookies de terceros
third_party_cookies: Cookies de terceros
create_button: Nueva cookie
create:
notice: Cookie creada correctamente
new:
title: Nueva cookie
edit:
title: Editar cookie
update:
notice: Cookie actualizada correctamente
destroy:
notice: Cookie eliminada correctamente
form:
name:
help_text: Esta información será visible públicamente.
cookie:
help_text: Esta información se utilizará internamente. El nombre de la cookie debe ser único, solo puede contener letras, dígitos y guiones bajos.
description:
help_text: Esta información será visible públicamente.
script:
help_text: Este es el script que se ejecutará cuando esta cookie sea aceptada por el usuario. Puede introducir código Javascript aquí e introducir referencias a otro javascript de la aplicación.

View File

@@ -304,6 +304,10 @@ namespace :admin do
post :execute, on: :collection
delete :cancel, on: :collection
end
namespace :cookies do
resources :vendors, except: [:index, :show]
end
end
end

View File

@@ -0,0 +1,14 @@
class CreateCookiesVendors < ActiveRecord::Migration[6.1]
def change
create_table :cookies_vendors do |t|
t.string :name
t.text :description
t.string :cookie
t.text :script
t.timestamps
t.index :cookie, unique: true
end
end
end

View File

@@ -454,6 +454,16 @@ ActiveRecord::Schema[7.0].define(version: 2024_10_26_112901) do
t.datetime "updated_at", precision: nil, null: false
end
create_table "cookies_vendors", force: :cascade do |t|
t.string "name"
t.text "description"
t.string "cookie"
t.text "script"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.index ["cookie"], name: "index_cookies_vendors_on_cookie", unique: true
end
create_table "dashboard_actions", id: :serial, force: :cascade do |t|
t.string "title", limit: 80
t.text "description"

View File

@@ -0,0 +1,7 @@
FactoryBot.define do
factory :cookies_vendor, class: "Cookies::Vendor" do
name { "Vendor name" }
sequence(:cookie) { |n| "vendor_cookie_#{n}" }
description { "Vendor description" }
end
end

View File

@@ -172,6 +172,10 @@ describe Abilities::Administrator do
it { should be_able_to(:manage, Widget::Card) }
it { should be_able_to(:create, Cookies::Vendor) }
it { should be_able_to(:update, Cookies::Vendor) }
it { should be_able_to(:destroy, Cookies::Vendor) }
describe "tenants" do
context "with multitenancy disabled" do
before { allow(Rails.application.config).to receive(:multitenancy).and_return(false) }

View File

@@ -372,4 +372,8 @@ describe Abilities::Common do
it { should_not be_able_to(:read, SDG::Manager) }
it { should_not be_able_to(:create, SDG::Manager) }
it { should_not be_able_to(:delete, SDG::Manager) }
it { should_not be_able_to(:create, Cookies::Vendor) }
it { should_not be_able_to(:update, Cookies::Vendor) }
it { should_not be_able_to(:destroy, Cookies::Vendor) }
end

View File

@@ -113,4 +113,8 @@ describe Abilities::Moderator do
it { should_not be_able_to(:read, SDG::Manager) }
it { should_not be_able_to(:create, SDG::Manager) }
it { should_not be_able_to(:delete, SDG::Manager) }
it { should_not be_able_to(:create, Cookies::Vendor) }
it { should_not be_able_to(:update, Cookies::Vendor) }
it { should_not be_able_to(:destroy, Cookies::Vendor) }
end

View File

@@ -51,4 +51,8 @@ describe Abilities::Valuator do
it { should_not be_able_to(:read, SDG::Manager) }
it { should_not be_able_to(:create, SDG::Manager) }
it { should_not be_able_to(:delete, SDG::Manager) }
it { should_not be_able_to(:create, Cookies::Vendor) }
it { should_not be_able_to(:update, Cookies::Vendor) }
it { should_not be_able_to(:destroy, Cookies::Vendor) }
end

View File

@@ -0,0 +1,43 @@
require "rails_helper"
describe Cookies::Vendor do
let(:cookies_vendor) { build(:cookies_vendor) }
it "is valid" do
expect(cookies_vendor).to be_valid
end
it "is not valid without a name" do
cookies_vendor.name = nil
expect(cookies_vendor).not_to be_valid
end
it "is not valid without the cookie name" do
cookies_vendor.cookie = nil
expect(cookies_vendor).not_to be_valid
end
it "is not valid when cookie_name contains whitespaces, special characters" do
cookies_vendor.cookie = "cookie vendor name"
expect(cookies_vendor).not_to be_valid
cookies_vendor.cookie = "cookie_vendor/name"
expect(cookies_vendor).not_to be_valid
cookies_vendor.cookie = "cookie_vendor_name"
expect(cookies_vendor).to be_valid
end
it "is not valid when the cookie name already exists" do
create(:cookies_vendor, cookie: "existing_name")
cookies_vendor.cookie = "existing_name"
expect(cookies_vendor).not_to be_valid
end
end

View File

@@ -0,0 +1,62 @@
require "rails_helper"
describe "Admin cookies vendors", :admin do
describe "Index" do
scenario "Shows existing cookies and links to actions" do
create(:cookies_vendor, name: "Third party", cookie: "third_party")
visit admin_settings_path(anchor: "tab-cookies-consent")
expect(page).to have_content "Third party"
expect(page).to have_content "third_party"
expect(page).to have_link "Edit"
expect(page).to have_button "Delete"
expect(page).to have_link "Create cookie vendor"
end
end
describe "Create" do
scenario "Shows a notice and the new cookie after creation" do
visit admin_settings_path(anchor: "tab-cookies-consent")
click_link "Create cookie vendor"
fill_in "Vendor name", with: "Vendor name"
fill_in "Cookie name", with: "vendor_cookie"
fill_in "Description", with: "Cookie details"
click_button "Create cookie vendor"
expect(page).to have_content "Cookie vendor created successfully"
expect(page).to have_content "Vendor name"
expect(page).to have_content "vendor_cookie"
end
end
describe "Update" do
scenario "Shows a notice and the cookie changes after update" do
create(:cookies_vendor, name: "Third party", cookie: "third_party")
visit admin_settings_path(anchor: "tab-cookies-consent")
click_link "Edit"
fill_in "Vendor name", with: "Cool Company Name"
click_button "Update cookie vendor"
expect(page).to have_content "Cookie vendor updated successfully"
expect(page).to have_content "Cool Company Name"
end
end
describe "Destroy" do
scenario "Shows a notice and removes cookie" do
create(:cookies_vendor, name: "Analitics cookie", cookie: "analitics_cookie")
visit admin_settings_path(anchor: "tab-cookies-consent")
expect(page).to have_content "Analitics cookie"
expect(page).to have_content "analitics_cookie"
accept_confirm { click_button "Delete Analitics cookie" }
expect(page).to have_content "Cookie vendor deleted successfully"
expect(page).not_to have_content "Analitics cookie"
expect(page).not_to have_content "analitics_cookie"
end
end
end