decidim-extra_user_fields 0.24.3

Sign up to get free protection for your applications and to get access to all the features.
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