decidim-user_fields 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 0d7b7ee3b6215fc3fb95f7d1501369650d051fe38e731492fc9ce5628ad75577
4
+ data.tar.gz: c4d1e5eb6e11d01ea3a61196a73ad107c34b96c43c52c6ab388ba733e98ad00d
5
+ SHA512:
6
+ metadata.gz: 8adddf951a431b592d6062f04b31478c0ddeef718b492b78ddadaf995b1bf2b208f2fa8937a47bad2009f4410b4518845d264fff123477ab72c6622719a4b5d7
7
+ data.tar.gz: cfda033cd73225d2834d8617f8898f248a97ddae6a4f8d32a7997315340a2dec0bd133ba065c62b277a2806ddf96a31eafa082b10b1ef7a83ba815a644d3bb37
data/README.md ADDED
@@ -0,0 +1,121 @@
1
+ <h1 align="center"><img src="https://github.com/octree-gva/meta/blob/main/decidim/static/header.png?raw=true" alt="Decidim - Octree Participatory democracy on a robust and open source solution" /></h1>
2
+ <h4 align="center">
3
+ <a href="https://www.octree.ch">Octree</a> |
4
+ <a href="https://octree.ch/en/contact-us/">Contact Us</a> |
5
+ <a href="https://blog.octree.ch">Our Blog (FR)</a><br/><br/>
6
+ <a href="https://decidim.org">Decidim</a> |
7
+ <a href="https://docs.decidim.org/en/">Decidim Docs</a> |
8
+ <a href="https://meta.decidim.org">Participatory Governance (meta decidim)</a><br/><br/>
9
+ <a href="https://matrix.to/#/+decidim:matrix.org">Decidim Community (Matrix+Element.io)</a>
10
+ </h4>
11
+ <p align="center">
12
+ <a href="https://participe.gland.ch">
13
+ <img
14
+ src="https://github.com/octree-gva/meta/blob/main/decidim/static/participe_gland.png?raw=true"
15
+ alt="Participe Gland — Donnez vie à vos idées avec le budget participatif" />
16
+ </a>
17
+ <a href="https://mkutano.community"><img src="https://github.com/octree-gva/decidim-module-mkutano_custom_registration_flow/blob/main/mkutano-logo.png?raw=true" alt="MKUTANO is a participatory platform where black canadians can effectively & democratically organize at scale" /></a>
18
+ <a href="https://opencollective.com/voca">
19
+ <img
20
+ src="https://github.com/octree-gva/meta/blob/main/decidim/static/opencollective_chip.png?raw=true"
21
+ alt="Voca – Open-Source SaaS platform for Decidim" />
22
+ </a>
23
+ </p>
24
+
25
+
26
+ # Decidim User Fields
27
+ Add custom user fields, through configuration file and without migration.
28
+ This module aims to configure in a blast new fields for subscription and profile edition. It supports:
29
+
30
+ - User registration
31
+ - User profiles
32
+ - User invitations.
33
+
34
+ ⚠️ It **does not support**:
35
+
36
+ - Omniauth registrations
37
+
38
+ ## Setup the module
39
+ Add the gem to your gemfile
40
+ ```
41
+ gem "decidim-user_fields"
42
+ ```
43
+
44
+ Run bundle
45
+ ```
46
+ bundle install
47
+ ```
48
+
49
+ Create an initializer `config/initializers/custom_user_fields.rb`
50
+ ```
51
+ Decidim::CustomUserFields.configure do |config|
52
+ config.add_field :birthdate, type: :date, required: true
53
+ config.add_field :province, type: :select, options: [:vd, :vs, :ar], default: :vd, required: true
54
+ config.add_field :adress, type: :textarea, required: false, rows: 10
55
+ config.add_field :purpose, type: :text, required: false
56
+ end
57
+ ```
58
+
59
+ ### Available field types
60
+
61
+ **`:date`**
62
+
63
+ parameters:
64
+ * `required`: must choose a date
65
+ * `min`: date in ISO8601 where the user can not select before
66
+ * `max`: date in ISO8601 where the user can not select after
67
+
68
+ **`:textarea`**
69
+
70
+ parameters:
71
+ * `required`: if the field is required
72
+ * `min`: minimal length
73
+ * `max`: maximal length
74
+ * `rows`: how much rows the field should display
75
+
76
+ **`:text`**
77
+
78
+ parameters:
79
+ * `required`: if the field is required
80
+
81
+ ### Labels
82
+ Labels are translated, and are under the traduction scope `decidim.custom_user_fields`.
83
+ Example of a `fr.yml` file:
84
+
85
+ ```yml
86
+ fr:
87
+ activemodel:
88
+ attributes:
89
+ user:
90
+ birthday: "Birthday"
91
+ adress: "Addresse"
92
+ purpose: "Raison d'être ici"
93
+ province: "Canton"
94
+ decidim:
95
+ custom_user_fields:
96
+ province:
97
+ options:
98
+ vd: "Vaud"
99
+ vs: "Valais"
100
+ ar: "Aarau"
101
+ ```
102
+
103
+ ## Run locally
104
+ To run this module locally, we use docker-compose:
105
+ ```
106
+ docker-compose up
107
+ ```
108
+ This will run a `decidim-user_fields` container, which sleeps.
109
+ You can run any command you want in the running container, like:
110
+
111
+ - `docker-compose exec -it decidim-user_fields bundle exec rails db:seed`: seed the database (run on [`localhost:3000`](https://localhost:3000))
112
+ - `docker-compose exec -it decidim-user_fields bin/webpack-dev-server`: compile assets and watch for changes
113
+ - `docker-compose exec -it decidim-user_fields bin/rails s -b 0.0.0.0`: run rails server in development
114
+ - `docker-compose exec -it decidim-user_fields bundle exec rspec /home/decidim/module/spec`: run tests for the module
115
+ - `docker-compose exec -it decidim-user_fields bundle exec rubocop -a /home/decidim/module`: correct lint error with rubocop
116
+ - `docker-compose exec -it decidim-user_fields bash`: navigate your container in bash
117
+
118
+ While developping locally, you have two environments variables that can help you:
119
+ - `ROOT`: the root of the application using the module
120
+ - `MODULE_ROOT`: the place where your gem code is.
121
+
@@ -0,0 +1,3 @@
1
+ <!-- insert_after "erb[loud]:contains('email_field')" original "d629550ba83d41a6d1c41553b7adbed311549633" -->
2
+
3
+ <%= render partial: "/decidim/custom_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/custom_user_fields/admin/export_users/dropdown" %>
5
+ </div>
@@ -0,0 +1,3 @@
1
+ <!-- insert_bottom "div.user-nickname" -->
2
+ <%= f.hidden_field :email %>
3
+ <%= render partial: "/decidim/custom_user_fields/registration_form", locals: { f:f, resource: resource } %>
@@ -0,0 +1,3 @@
1
+ <!-- replace "erb[loud]:contains('decidim_form_for')" -->
2
+ <% form = @form || Decidim::RegistrationForm.from_model(resource).with_context(current_organization: current_organization, current_user: current_user) %>
3
+ <%= decidim_form_for form, namespace: "invitation", as: resource_name, url: invitation_path(resource_name, invite_redirect: params[:invite_redirect]), html: { method: :put, class: "register-form new_user" } do |f| %>
@@ -0,0 +1,3 @@
1
+ <!-- insert_after "erb[loud]:contains('email_field')" -->
2
+
3
+ <%= render partial: "/decidim/custom_user_fields/registration_form", locals: { f: f, resource: resource } %>
@@ -0,0 +1,5 @@
1
+ <div class="custom-user-fields__section">
2
+ <% Decidim::CustomUserFields.custom_fields.collect do |field|
3
+ concat field.form_tag(f)
4
+ end %>
5
+ </div>
@@ -0,0 +1,5 @@
1
+ <div class="custom-user-fields__section">
2
+ <% Decidim::CustomUserFields.custom_fields.collect do |field|
3
+ concat field.form_tag(f)
4
+ end %>
5
+ </div>
@@ -0,0 +1,27 @@
1
+ module Decidim
2
+ module CustomUserFields
3
+ include ActiveSupport::Configurable
4
+
5
+ def self.configure
6
+ yield self
7
+ end
8
+
9
+ ##
10
+ # If users should receive emails on notification
11
+ # by default
12
+ config_accessor :default_email_on_notification do
13
+ false
14
+ end
15
+
16
+ def self.custom_fields
17
+ @custom_fields ||= []
18
+ end
19
+
20
+ def self.add_field(field_type, field_definition)
21
+ custom_fields.push(FieldDefinition.new(field_type, field_definition))
22
+ self
23
+ end
24
+
25
+ class Error < StandardError; end
26
+ end
27
+ end
@@ -0,0 +1,43 @@
1
+ require 'rails'
2
+ require 'decidim/core'
3
+ require 'deface'
4
+
5
+ module Decidim
6
+ module CustomUserFields
7
+ class Engine < ::Rails::Engine
8
+ isolate_namespace Decidim::CustomUserFields
9
+ routes do
10
+ # Add engine routes here
11
+ # resources :custom_user_fields
12
+ # root to: "custom_user_fields#index"
13
+ end
14
+
15
+ initializer 'decidim_custom_user_fields.registration_additions' do
16
+ config.to_prepare do
17
+ Decidim::RegistrationForm.class_eval do
18
+ include CustomUserFields::FormsDefinition
19
+ def self.require_password_on_accepting
20
+ Decidim::User.require_password_on_accepting
21
+ end
22
+ end
23
+
24
+ Decidim::OmniauthRegistrationForm.class_eval do
25
+ include CustomUserFields::FormsDefinition
26
+ end
27
+
28
+ Decidim::AccountForm.class_eval do
29
+ include CustomUserFields::FormsDefinition
30
+ end
31
+
32
+ Decidim::CreateRegistration.class_eval do
33
+ prepend CustomUserFields::Command
34
+ end
35
+
36
+ Decidim::UpdateAccount.class_eval do
37
+ prepend CustomUserFields::Command
38
+ end
39
+ end
40
+ end
41
+ end
42
+ end
43
+ end
@@ -0,0 +1,34 @@
1
+ require 'forwardable'
2
+
3
+ module Decidim
4
+ module CustomUserFields
5
+ class FieldDefinition
6
+ extend Forwardable
7
+
8
+ attr_reader :type,
9
+ :name,
10
+ :field
11
+
12
+ def_delegators :@field,
13
+ :configure_form,
14
+ :map_model,
15
+ :form_tag
16
+
17
+ def initialize(name, kwargs)
18
+ @name = name.to_s.to_sym
19
+ @type = kwargs[:type]
20
+ # TODO: parse kwargs
21
+ case type
22
+ when :text
23
+ @field = Fields::TextField.new(self, kwargs)
24
+ when :textarea
25
+ @field = Fields::TextAreaField.new(self, kwargs)
26
+ when :date
27
+ @field = Fields::DateField.new(self, kwargs)
28
+ else
29
+ raise Error, "field type #{type} is not supported"
30
+ end
31
+ end
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,31 @@
1
+ module Decidim
2
+ module CustomUserFields
3
+ module Fields
4
+ class DateField < GenericField
5
+ def configure_form(form)
6
+ form.attribute(name, String)
7
+ form.validates(name, presence: required?)
8
+ end
9
+
10
+ def map_model(form, data)
11
+ form[name] = data[name] if data[name].present?
12
+ end
13
+
14
+ def form_tag(form_tag)
15
+ field_options = {}
16
+ if options[:min].present? || options[:max].present?
17
+ field_options[:min] = options[:min]
18
+ field_options[:max] = options[:max]
19
+ end
20
+
21
+ content_tag(
22
+ :div,
23
+ form_tag.date_field(name, **field_options),
24
+ class: class_name
25
+ )
26
+ end
27
+ end
28
+ end
29
+ end
30
+ end
31
+
@@ -0,0 +1,39 @@
1
+ module Decidim
2
+ module CustomUserFields
3
+ module Fields
4
+ class GenericField
5
+ extend Forwardable
6
+ include ActionView::Helpers::TagHelper
7
+
8
+ def_delegators :@definition, :name, :type
9
+
10
+ attr_reader :definition, :options
11
+ def initialize(definition, options)
12
+ @definition = definition
13
+ @options = options
14
+ end
15
+
16
+ def configure_form(_form)
17
+ raise Error, "Configure Form is not implemented for #{definition.type} type"
18
+ end
19
+
20
+ def map_model(_form, _model_data)
21
+ raise Error, "Map Model is not implemented for #{definition.type} type"
22
+ end
23
+
24
+ def form_tag(_form)
25
+ raise Error, "Form Tag is not implemented for #{definition.type} type"
26
+ end
27
+
28
+ def class_name
29
+ class_specifier = name.to_s.underscore
30
+ "field field--#{type} field--#{class_specifier}"
31
+ end
32
+
33
+ def required?
34
+ options[:required].present? && options[:required]
35
+ end
36
+ end
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,34 @@
1
+ module Decidim
2
+ module CustomUserFields
3
+ module Fields
4
+ class TextAreaField < GenericField
5
+ def configure_form(form)
6
+ form.attribute(name, String)
7
+ form.validates(name, presence: required?)
8
+ if options[:min].present? || options[:max].present?
9
+ min_max_options = {}
10
+ min_max_options[:minimum] = options[:min].to_i
11
+ min_max_options[:maximum] = options[:max].to_i
12
+ form.validates(name, length: min_max_options, allow_blank: !required?)
13
+ end
14
+ end
15
+
16
+ def map_model(form, data)
17
+ form[name] = data[name] if data[name].present?
18
+ end
19
+
20
+ def form_tag(form_tag)
21
+ field_options = {
22
+ rows: options[:row] || 2
23
+ }
24
+ content_tag(
25
+ :div,
26
+ form_tag.text_area(name, **field_options),
27
+ class: class_name,
28
+ )
29
+ end
30
+ end
31
+ end
32
+ end
33
+ end
34
+
@@ -0,0 +1,24 @@
1
+ module Decidim
2
+ module CustomUserFields
3
+ module Fields
4
+ class TextField < GenericField
5
+ def configure_form(form)
6
+ form.attribute(name, String)
7
+ form.validates(name, presence: required?)
8
+ end
9
+
10
+ def map_model(form, data)
11
+ form[name] = data[name] if data[name].present?
12
+ end
13
+
14
+ def form_tag(form_tag)
15
+ content_tag(
16
+ :div,
17
+ form_tag.text_field(name),
18
+ class: class_name
19
+ )
20
+ end
21
+ end
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,8 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Decidim
4
+ module CustomUserFields
5
+ module ApplicationHelper
6
+ end
7
+ end
8
+ end
@@ -0,0 +1,49 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'active_support/concern'
4
+
5
+ module Decidim
6
+ module CustomUserFields
7
+ # Changes in methods to store extra fields in user profile
8
+ module Command
9
+ extend ActiveSupport::Concern
10
+
11
+ private
12
+
13
+ def create_user
14
+ user_payload = {
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: Decidim::CustomUserFields.default_email_on_notification,
24
+ accepted_tos_version: @form.current_organization.tos_version,
25
+ locale: @form.current_locale,
26
+ extended_data: extended_data
27
+ }
28
+ @user = User.create!(user_payload)
29
+ end
30
+
31
+ def update_personal_data
32
+ @user.name = @form.name
33
+ @user.nickname = @form.nickname
34
+ @user.email = @form.email
35
+ @user.personal_url = @form.personal_url
36
+ @user.about = @form.about
37
+ @user.extended_data = extended_data
38
+ end
39
+
40
+ def extended_data
41
+ custom_data = {}
42
+ Decidim::CustomUserFields.custom_fields.each do |field_def|
43
+ custom_data[field_def.name] = @form[field_def.name]
44
+ end
45
+ @extended_data ||= (@user&.extended_data || {}).merge(custom_data)
46
+ end
47
+ end
48
+ end
49
+ end
@@ -0,0 +1,24 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'active_support/concern'
4
+
5
+ module Decidim
6
+ module CustomUserFields
7
+ # Extra user fields definitions for forms
8
+ module FormsDefinition
9
+ extend ActiveSupport::Concern
10
+
11
+ included do |inst|
12
+ include ::Decidim::CustomUserFields::ApplicationHelper
13
+ Decidim::CustomUserFields.custom_fields.each do |field_def|
14
+ field_def.configure_form(inst)
15
+ end
16
+ end
17
+
18
+ def map_model(model)
19
+ extended_data = model.extended_data.with_indifferent_access
20
+ field_def.map_model(self, model)
21
+ end
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,11 @@
1
+ module Decidim
2
+ module CustomUserFields
3
+ def self.version
4
+ '0.0.1'
5
+ end
6
+
7
+ def self.decidim_version
8
+ '>= 0.26'
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,18 @@
1
+ require 'decidim/custom_user_fields/fields/generic_field'
2
+ require 'decidim/custom_user_fields/fields/date_field'
3
+ require 'decidim/custom_user_fields/fields/text_area_field'
4
+ require 'decidim/custom_user_fields/fields/text_field'
5
+ require 'decidim/custom_user_fields/field_definition'
6
+
7
+ require 'decidim/custom_user_fields/helpers/application_helper'
8
+ require 'decidim/custom_user_fields/custom_user_fields'
9
+ require 'decidim/custom_user_fields/overrides/command'
10
+ require 'decidim/custom_user_fields/overrides/form_definition'
11
+
12
+ require 'decidim/custom_user_fields/engine'
13
+
14
+ Decidim.register_global_engine(
15
+ :decidim_custom_user_fields, # this is the name of the global method to access engine routes
16
+ ::Decidim::CustomUserFields::Engine,
17
+ at: '/decidim_custom_user_fields'
18
+ )
metadata ADDED
@@ -0,0 +1,106 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: decidim-user_fields
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - Hadrien Froger
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2024-04-19 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: decidim-core
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: '0.26'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: '0.26'
27
+ - !ruby/object:Gem::Dependency
28
+ name: deface
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: 1.8.1
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ version: 1.8.1
41
+ - !ruby/object:Gem::Dependency
42
+ name: decidim-dev
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ version: '0.26'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ version: '0.26'
55
+ description: Allows to collect and manage some extra user fields on registration and
56
+ profile edition.
57
+ email:
58
+ - hadrien@octree.ch
59
+ executables: []
60
+ extensions: []
61
+ extra_rdoc_files: []
62
+ files:
63
+ - README.md
64
+ - app/overrides/decidim/account/show/extra_user_fields.html.erb.deface
65
+ - app/overrides/decidim/admin/officializations/index/_export_users_dropdown.html.erb.deface
66
+ - app/overrides/decidim/devise/invitations/edit/extra_user_fields.html.erb.deface
67
+ - app/overrides/decidim/devise/invitations/edit/extra_user_fields_form.html.erb.deface
68
+ - app/overrides/decidim/devise/registrations/new/extra_user_fields.html.erb.deface
69
+ - app/views/decidim/custom_user_fields/_profile_form.html.erb
70
+ - app/views/decidim/custom_user_fields/_registration_form.html.erb
71
+ - lib/decidim/custom_user_fields/custom_user_fields.rb
72
+ - lib/decidim/custom_user_fields/engine.rb
73
+ - lib/decidim/custom_user_fields/field_definition.rb
74
+ - lib/decidim/custom_user_fields/fields/date_field.rb
75
+ - lib/decidim/custom_user_fields/fields/generic_field.rb
76
+ - lib/decidim/custom_user_fields/fields/text_area_field.rb
77
+ - lib/decidim/custom_user_fields/fields/text_field.rb
78
+ - lib/decidim/custom_user_fields/helpers/application_helper.rb
79
+ - lib/decidim/custom_user_fields/overrides/command.rb
80
+ - lib/decidim/custom_user_fields/overrides/form_definition.rb
81
+ - lib/decidim/custom_user_fields/version.rb
82
+ - lib/decidim/user_fields.rb
83
+ homepage: https://github.com/octree-gva/decidim-module-mkutano-extra_user_fields
84
+ licenses:
85
+ - AGPL-3.0
86
+ metadata: {}
87
+ post_install_message:
88
+ rdoc_options: []
89
+ require_paths:
90
+ - lib
91
+ required_ruby_version: !ruby/object:Gem::Requirement
92
+ requirements:
93
+ - - ">="
94
+ - !ruby/object:Gem::Version
95
+ version: '2.7'
96
+ required_rubygems_version: !ruby/object:Gem::Requirement
97
+ requirements:
98
+ - - ">="
99
+ - !ruby/object:Gem::Version
100
+ version: '0'
101
+ requirements: []
102
+ rubygems_version: 3.5.3
103
+ signing_key:
104
+ specification_version: 4
105
+ summary: Configure user fields for your decidim users
106
+ test_files: []