decidim-extra_user_fields 0.24.3

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.
Files changed (35) hide show
  1. checksums.yaml +7 -0
  2. data/LICENSE-AGPLv3.txt +661 -0
  3. data/README.md +27 -0
  4. data/Rakefile +9 -0
  5. data/app/assets/config/decidim_extra_user_fields_manifest.js +0 -0
  6. data/app/assets/images/decidim/extra_user_fields/icon.svg +1 -0
  7. data/app/commands/concerns/decidim/extra_user_fields/commands_overrides.rb +49 -0
  8. data/app/commands/concerns/decidim/extra_user_fields/omniauth_commands_overrides.rb +53 -0
  9. data/app/commands/decidim/extra_user_fields/admin/export_users.rb +45 -0
  10. data/app/controllers/decidim/extra_user_fields/admin/application_controller.rb +12 -0
  11. data/app/controllers/decidim/extra_user_fields/admin/extra_user_fields_controller.rb +21 -0
  12. data/app/forms/concerns/decidim/extra_user_fields/forms_definitions.rb +35 -0
  13. data/app/helpers/decidim/extra_user_fields/admin/application_helper.rb +24 -0
  14. data/app/helpers/decidim/extra_user_fields/application_helper.rb +15 -0
  15. data/app/models/decidim/extra_user_fields/application_record.rb +10 -0
  16. data/app/overrides/decidim/account/show/extra_user_fields.html.erb.deface +3 -0
  17. data/app/overrides/decidim/admin/officializations/index/_export_users_dropdown.html.erb.deface +5 -0
  18. data/app/overrides/decidim/devise/omniauth_registrations/new/extra_user_fields.html.erb.deface +3 -0
  19. data/app/overrides/decidim/devise/registrations/new/extra_user_fields.html.erb.deface +3 -0
  20. data/app/serializers/decidim/extra_user_fields/user_export_serializer.rb +22 -0
  21. data/app/views/decidim/extra_user_fields/_profile_form.html.erb +4 -0
  22. data/app/views/decidim/extra_user_fields/_registration_form.html.erb +15 -0
  23. data/app/views/decidim/extra_user_fields/admin/export_users/_dropdown.html.erb +4 -0
  24. data/config/i18n-tasks.yml +10 -0
  25. data/config/locales/ca.yml +21 -0
  26. data/config/locales/en.yml +21 -0
  27. data/config/locales/es.yml +21 -0
  28. data/lib/decidim/extra_user_fields/admin.rb +10 -0
  29. data/lib/decidim/extra_user_fields/admin_engine.rb +38 -0
  30. data/lib/decidim/extra_user_fields/engine.rb +57 -0
  31. data/lib/decidim/extra_user_fields/form_builder_methods.rb +18 -0
  32. data/lib/decidim/extra_user_fields/test/factories.rb +13 -0
  33. data/lib/decidim/extra_user_fields/version.rb +14 -0
  34. data/lib/decidim/extra_user_fields.rb +12 -0
  35. metadata +119 -0
data/README.md ADDED
@@ -0,0 +1,27 @@
1
+ # Decidim::ExtraUserFields
2
+
3
+ * Allows to collect and manage some extra user fields on registration and profile edition.
4
+ * Adds a link in admin participants panel to download users of organization in several formats.
5
+
6
+ ## Installation
7
+
8
+ Add this line to your application's Gemfile:
9
+
10
+ For Decidim 0.24:
11
+ ```ruby
12
+ gem "decidim-extra_user_fields", git: "https://github.com/PopulateTools/decidim-module-extra_user_fields", branch: "release/0.24-stable"
13
+ ```
14
+
15
+ And then execute:
16
+
17
+ ```bash
18
+ bundle
19
+ ```
20
+
21
+ ## Contributing
22
+
23
+ See [Decidim](https://github.com/decidim/decidim).
24
+
25
+ ## License
26
+
27
+ This engine is distributed under the GNU AFFERO GENERAL PUBLIC LICENSE.
data/Rakefile ADDED
@@ -0,0 +1,9 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "decidim/dev/common_rake"
4
+
5
+ desc "Generates a dummy app for testing"
6
+ task test_app: "decidim:generate_external_test_app"
7
+
8
+ desc "Generates a development app."
9
+ task development_app: "decidim:generate_external_development_app"
@@ -0,0 +1 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 35 35"><path d="M17.5 35A17.5 17.5 0 1 1 35 17.5 17.52 17.52 0 0 1 17.5 35zm0-33.06A15.56 15.56 0 1 0 33.06 17.5 15.57 15.57 0 0 0 17.5 1.94zm9.5 13.7H8a1 1 0 0 1 0-1.94h19a1 1 0 0 1 0 1.94zm0 3.68H8a1 1 0 0 1 0-1.94h19a1 1 0 0 1 0 1.94zM22.26 23H8a1 1 0 0 1 0-1.94h14.26a1 1 0 0 1 0 1.94z"/></svg>
@@ -0,0 +1,49 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "active_support/concern"
4
+
5
+ module Decidim
6
+ module ExtraUserFields
7
+ # Changes in methods to store extra fields in user profile
8
+ module CommandsOverrides
9
+ extend ActiveSupport::Concern
10
+
11
+ private
12
+
13
+ def create_user
14
+ @user = User.create!(
15
+ email: @form.email,
16
+ name: @form.name,
17
+ nickname: @form.nickname,
18
+ password: @form.password,
19
+ password_confirmation: @form.password_confirmation,
20
+ organization: @form.current_organization,
21
+ tos_agreement: @form.tos_agreement,
22
+ newsletter_notifications_at: @form.newsletter_at,
23
+ email_on_notification: true,
24
+ accepted_tos_version: @form.current_organization.tos_version,
25
+ locale: @form.current_locale,
26
+ extended_data: extended_data
27
+ )
28
+ end
29
+
30
+ def update_personal_data
31
+ @user.name = @form.name
32
+ @user.nickname = @form.nickname
33
+ @user.email = @form.email
34
+ @user.personal_url = @form.personal_url
35
+ @user.about = @form.about
36
+ @user.extended_data = extended_data
37
+ end
38
+
39
+ def extended_data
40
+ @extended_data ||= (@user&.extended_data || {}).merge(
41
+ country: @form.country,
42
+ postal_code: @form.postal_code,
43
+ date_of_birth: @form.date_of_birth,
44
+ gender: @form.gender
45
+ )
46
+ end
47
+ end
48
+ end
49
+ end
@@ -0,0 +1,53 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "active_support/concern"
4
+
5
+ module Decidim
6
+ module ExtraUserFields
7
+ # Changes in methods to store extra fields in user profile
8
+ module OmniauthCommandsOverrides
9
+ extend ActiveSupport::Concern
10
+
11
+ private
12
+
13
+ def create_or_find_user
14
+ generated_password = SecureRandom.hex
15
+
16
+ @user = User.find_or_initialize_by(
17
+ email: verified_email,
18
+ organization: organization
19
+ )
20
+
21
+ if @user.persisted?
22
+ # If user has left the account unconfirmed and later on decides to sign
23
+ # in with omniauth with an already verified account, the account needs
24
+ # to be marked confirmed.
25
+ @user.skip_confirmation! if !@user.confirmed? && @user.email == verified_email
26
+ else
27
+ @user.email = (verified_email || form.email)
28
+ @user.name = form.name
29
+ @user.nickname = form.normalized_nickname
30
+ @user.newsletter_notifications_at = nil
31
+ @user.email_on_notification = true
32
+ @user.password = generated_password
33
+ @user.password_confirmation = generated_password
34
+ @user.remote_avatar_url = form.avatar_url if form.avatar_url.present?
35
+ @user.skip_confirmation! if verified_email
36
+ end
37
+
38
+ @user.tos_agreement = "1"
39
+ @user.extended_data = extended_data
40
+ @user.save!
41
+ end
42
+
43
+ def extended_data
44
+ @extended_data ||= (@user&.extended_data || {}).merge(
45
+ country: form.country,
46
+ postal_code: form.postal_code,
47
+ date_of_birth: form.date_of_birth,
48
+ gender: form.gender
49
+ )
50
+ end
51
+ end
52
+ end
53
+ end
@@ -0,0 +1,45 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Decidim
4
+ module ExtraUserFields
5
+ module Admin
6
+ # Command to export organization users from the participants admin panel.
7
+ class ExportUsers < Rectify::Command
8
+ # format - a string representing the export format
9
+ # current_user - the user performing the action
10
+ def initialize(format, current_user)
11
+ @format = format
12
+ @current_user = current_user
13
+ end
14
+
15
+ # Exports the current organization not deleted users.
16
+ #
17
+ # Broadcasts :ok if successful, :invalid otherwise.
18
+ def call
19
+ broadcast(:ok, export_data)
20
+ end
21
+
22
+ private
23
+
24
+ attr_reader :current_user, :format
25
+
26
+ def export_data
27
+ Decidim.traceability.perform_action!(
28
+ :export_users,
29
+ current_organization,
30
+ current_user
31
+ ) do
32
+ Decidim::Exporters
33
+ .find_exporter(format)
34
+ .new(collection, Decidim::ExtraUserFields::UserExportSerializer)
35
+ .export
36
+ end
37
+ end
38
+
39
+ def collection
40
+ current_organization.users.not_deleted
41
+ end
42
+ end
43
+ end
44
+ end
45
+ end
@@ -0,0 +1,12 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Decidim
4
+ module ExtraUserFields
5
+ module Admin
6
+ # This controller is the abstract class from which all other controllers of
7
+ # this engine inherit.
8
+ class ApplicationController < Decidim::Admin::ApplicationController
9
+ end
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,21 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Decidim
4
+ module ExtraUserFields
5
+ module Admin
6
+ # This controller is the abstract class from which all other controllers of
7
+ # this engine inherit.
8
+ class ExtraUserFieldsController < Admin::ApplicationController
9
+ def export_users
10
+ enforce_permission_to :read, :officialization
11
+
12
+ ExportUsers.call(params[:format], current_user) do
13
+ on(:ok) do |export_data|
14
+ send_data export_data.read, type: "text/#{export_data.extension}", filename: export_data.filename("participants")
15
+ end
16
+ end
17
+ end
18
+ end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,35 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "active_support/concern"
4
+
5
+ module Decidim
6
+ module ExtraUserFields
7
+ # Extra user fields definitions for forms
8
+ module FormsDefinitions
9
+ extend ActiveSupport::Concern
10
+
11
+ included do
12
+ include ::Decidim::ExtraUserFields::ApplicationHelper
13
+
14
+ attribute :country, String
15
+ attribute :postal_code, String
16
+ attribute :date_of_birth, Decidim::Attributes::LocalizedDate
17
+ attribute :gender, String
18
+
19
+ validates :country, presence: true
20
+ validates :postal_code, presence: true
21
+ validates :date_of_birth, presence: true
22
+ validates :gender, presence: true
23
+ end
24
+
25
+ def map_model(model)
26
+ extended_data = model.extended_data.with_indifferent_access
27
+
28
+ self.country = extended_data[:country]
29
+ self.postal_code = extended_data[:postal_code]
30
+ self.date_of_birth = Date.parse(extended_data[:date_of_birth]) if extended_data[:date_of_birth].present?
31
+ self.gender = extended_data[:gender]
32
+ end
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,24 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Decidim
4
+ module ExtraUserFields
5
+ module Admin
6
+ # Custom helpers, scoped to the meetings admin engine.
7
+ #
8
+ module ApplicationHelper
9
+ def extra_user_fields_export_users_dropdown
10
+ content_tag(:ul, class: "vertical menu add-components") do
11
+ Decidim::ExtraUserFields::AdminEngine::DEFAULT_EXPORT_FORMATS.map do |format|
12
+ content_tag(:li, class: "exports--format--#{format.downcase} export--users") do
13
+ link_to(
14
+ t("decidim.admin.exports.export_as", name: t("decidim.extra_user_fields.admin.exports.users"), export_format: format.upcase),
15
+ AdminEngine.routes.url_helpers.extra_user_fields_export_users_path(format: format)
16
+ )
17
+ end
18
+ end.join.html_safe
19
+ end
20
+ end
21
+ end
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,15 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Decidim
4
+ module ExtraUserFields
5
+ # Custom helpers, scoped to the extra_user_fields engine.
6
+ #
7
+ module ApplicationHelper
8
+ def gender_options_for_select
9
+ Decidim::ExtraUserFields::Engine::DEFAULT_GENDER_OPTIONS.map do |gender|
10
+ [gender, I18n.t(gender, scope: "decidim.extra_user_fields.genders")]
11
+ end
12
+ end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,10 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Decidim
4
+ module ExtraUserFields
5
+ # Abstract class from which all models in this engine inherit.
6
+ class ApplicationRecord < ActiveRecord::Base
7
+ self.abstract_class = true
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,3 @@
1
+ <!-- insert_after "erb[loud]:contains('email_field')" original "d629550ba83d41a6d1c41553b7adbed311549633" -->
2
+
3
+ <%= render partial: "/decidim/extra_user_fields/profile_form", locals: { f: f } %>
@@ -0,0 +1,5 @@
1
+ <!-- insert_bottom "h2.card-title" -->
2
+
3
+ <div class="button--title">
4
+ <%= render partial: "decidim/extra_user_fields/admin/export_users/dropdown" %>
5
+ </div>
@@ -0,0 +1,3 @@
1
+ <!-- insert_after "erb[loud]:contains('email_field')" -->
2
+
3
+ <%= render partial: "/decidim/extra_user_fields/registration_form", locals: { f: f } %>
@@ -0,0 +1,3 @@
1
+ <!-- insert_after "erb[loud]:contains('email_field')" -->
2
+
3
+ <%= render partial: "/decidim/extra_user_fields/registration_form", locals: { f: f } %>
@@ -0,0 +1,22 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Decidim
4
+ # This class serializes a User so can be exported to CSV
5
+ module ExtraUserFields
6
+ class UserExportSerializer < Decidim::DataPortabilitySerializers::DataPortabilityUserSerializer
7
+ # Public: Exports a hash with the serialized data for the user including
8
+ # extra user fields
9
+ def serialize
10
+ super.merge(extra_user_fields)
11
+ end
12
+
13
+ def extra_user_fields
14
+ extended_data = resource.extended_data.symbolize_keys
15
+
16
+ [:gender, :country, :postal_code, :date_of_birth].each_with_object({}) do |key, fields|
17
+ fields[key] = extended_data[key]
18
+ end
19
+ end
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,4 @@
1
+ <%= f.date_field :date_of_birth %>
2
+ <%= f.collection_select :gender, f.object.gender_options_for_select, :first, :last %>
3
+ <%= f.custom_country_select :country %>
4
+ <%= f.text_field :postal_code %>
@@ -0,0 +1,15 @@
1
+ <div class="field">
2
+ <%= f.date_field :date_of_birth %>
3
+ </div>
4
+
5
+ <div class="field">
6
+ <%= f.collection_select :gender, f.object.gender_options_for_select, :first, :last %>
7
+ </div>
8
+
9
+ <div class="field">
10
+ <%= f.custom_country_select :country %>
11
+ </div>
12
+
13
+ <div class="field">
14
+ <%= f.text_field :postal_code %>
15
+ </div>
@@ -0,0 +1,4 @@
1
+ <span class="exports dropdown tiny button button--simple" data-toggle="export-dropdown"><%= t "actions.export", scope: "decidim.admin" %></span>
2
+ <div class="dropdown-pane" id="export-dropdown" data-dropdown data-position=bottom data-alignment=right data-auto-focus="true" data-close-on-click="true">
3
+ <%= extra_user_fields_export_users_dropdown %>
4
+ </div>
@@ -0,0 +1,10 @@
1
+ ---
2
+
3
+ base_locale: en
4
+ locales: [en]
5
+
6
+ ignore_unused:
7
+ - "decidim.components.extra_user_fields.name"
8
+
9
+ ignore_missing:
10
+ - decidim.participatory_processes.scopes.global
@@ -0,0 +1,21 @@
1
+ ---
2
+ ca:
3
+ activemodel:
4
+ attributes:
5
+ user:
6
+ country: País
7
+ date_of_birth: Data de naixement
8
+ gender: Gènere
9
+ postal_code: Codi postal
10
+ decidim:
11
+ components:
12
+ extra_user_fields:
13
+ name: ExtraUserFields
14
+ extra_user_fields:
15
+ admin:
16
+ exports:
17
+ users: Participants
18
+ genders:
19
+ female: Dona
20
+ male: Home
21
+ other: Altre
@@ -0,0 +1,21 @@
1
+ ---
2
+ en:
3
+ activemodel:
4
+ attributes:
5
+ user:
6
+ country: Country
7
+ date_of_birth: Date of birth
8
+ gender: Gender
9
+ postal_code: Postal code
10
+ decidim:
11
+ components:
12
+ extra_user_fields:
13
+ name: ExtraUserFields
14
+ extra_user_fields:
15
+ admin:
16
+ exports:
17
+ users: Participants
18
+ genders:
19
+ female: Female
20
+ male: Male
21
+ other: Other
@@ -0,0 +1,21 @@
1
+ ---
2
+ es:
3
+ activemodel:
4
+ attributes:
5
+ user:
6
+ country: País
7
+ date_of_birth: Fecha de nacimiento
8
+ gender: Género
9
+ postal_code: Código postal
10
+ decidim:
11
+ components:
12
+ extra_user_fields:
13
+ name: ExtraUserFields
14
+ extra_user_fields:
15
+ admin:
16
+ exports:
17
+ users: Participantes
18
+ genders:
19
+ female: Mujer
20
+ male: Hombre
21
+ other: Otro
@@ -0,0 +1,10 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Decidim
4
+ module ExtraUserFields
5
+ # This module contains all the domain logic associated to Decidim's ExtraUserFields
6
+ # component admin panel.
7
+ module Admin
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,38 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Decidim
4
+ module ExtraUserFields
5
+ # This is the engine that runs on the public interface of `ExtraUserFields`.
6
+ class AdminEngine < ::Rails::Engine
7
+ isolate_namespace Decidim::ExtraUserFields::Admin
8
+
9
+ DEFAULT_EXPORT_FORMATS = %w(CSV JSON Excel).freeze
10
+
11
+ paths["db/migrate"] = nil
12
+ paths["lib/tasks"] = nil
13
+
14
+ routes do
15
+ # Add admin engine routes here
16
+ namespace :extra_user_fields do
17
+ get :export_users
18
+ end
19
+ end
20
+
21
+ initializer "decidim_extra_user_fields.admin_mount_routes" do
22
+ Decidim::Core::Engine.routes do
23
+ mount Decidim::ExtraUserFields::AdminEngine, at: "/admin/extra_user_fields", as: "decidim_extra_user_fields"
24
+ end
25
+ end
26
+
27
+ initializer "decidim_extra_user_fields.admin_export_users" do
28
+ Decidim::Admin::ApplicationHelper.class_eval do
29
+ include ExtraUserFields::Admin::ApplicationHelper
30
+ end
31
+ end
32
+
33
+ def load_seed
34
+ nil
35
+ end
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,57 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "rails"
4
+ require "decidim/core"
5
+ require "country_select"
6
+ require "deface"
7
+
8
+ module Decidim
9
+ module ExtraUserFields
10
+ # This is the engine that runs on the public interface of extra_user_fields.
11
+ class Engine < ::Rails::Engine
12
+ isolate_namespace Decidim::ExtraUserFields
13
+
14
+ DEFAULT_GENDER_OPTIONS = [:male, :female, :other].freeze
15
+
16
+ routes do
17
+ # Add engine routes here
18
+ # resources :extra_user_fields
19
+ # root to: "extra_user_fields#index"
20
+ end
21
+
22
+ initializer "decidim_extra_user_fields.assets" do |app|
23
+ app.config.assets.precompile += %w(decidim_extra_user_fields_manifest.js decidim_extra_user_fields_manifest.css)
24
+ end
25
+
26
+ initializer "decidim_extra_user_fields.registration_additions" do
27
+ Decidim::RegistrationForm.class_eval do
28
+ include ExtraUserFields::FormsDefinitions
29
+ end
30
+
31
+ Decidim::OmniauthRegistrationForm.class_eval do
32
+ include ExtraUserFields::FormsDefinitions
33
+ end
34
+
35
+ Decidim::AccountForm.class_eval do
36
+ include ExtraUserFields::FormsDefinitions
37
+ end
38
+
39
+ Decidim::CreateRegistration.class_eval do
40
+ prepend ExtraUserFields::CommandsOverrides
41
+ end
42
+
43
+ Decidim::CreateOmniauthRegistration.class_eval do
44
+ prepend ExtraUserFields::OmniauthCommandsOverrides
45
+ end
46
+
47
+ Decidim::UpdateAccount.class_eval do
48
+ prepend ExtraUserFields::CommandsOverrides
49
+ end
50
+
51
+ Decidim::FormBuilder.class_eval do
52
+ include ExtraUserFields::FormBuilderMethods
53
+ end
54
+ end
55
+ end
56
+ end
57
+ end
@@ -0,0 +1,18 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Decidim
4
+ module ExtraUserFields
5
+ # This module adds the FormBuilder methods for extra user fields
6
+ module FormBuilderMethods
7
+ def custom_country_select(name, options = {})
8
+ label_text = options[:label].to_s
9
+ label_text = label_for(name) if label_text.blank?
10
+
11
+ template = ""
12
+ template += label(name, label_text + required_for_attribute(name)) if options.fetch(:label, true)
13
+ template += country_select(name)
14
+ template.html_safe
15
+ end
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,13 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "decidim/core/test/factories"
4
+
5
+ FactoryBot.define do
6
+ factory :extra_user_fields_component, parent: :component do
7
+ name { Decidim::Components::Namer.new(participatory_space.organization.available_locales, :extra_user_fields).i18n_name }
8
+ manifest_name :extra_user_fields
9
+ participatory_space { create(:participatory_process, :with_steps) }
10
+ end
11
+
12
+ # Add engine factories here
13
+ end
@@ -0,0 +1,14 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Decidim
4
+ # This holds the decidim-extra_user_fields version.
5
+ module ExtraUserFields
6
+ def self.version
7
+ "0.24.3"
8
+ end
9
+
10
+ def self.decidim_version
11
+ "0.24.3"
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,12 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "decidim/extra_user_fields/admin"
4
+ require "decidim/extra_user_fields/engine"
5
+ require "decidim/extra_user_fields/admin_engine"
6
+ require "decidim/extra_user_fields/form_builder_methods"
7
+
8
+ module Decidim
9
+ # This namespace holds the logic of the `ExtraUserFields` module.
10
+ module ExtraUserFields
11
+ end
12
+ end