decidim-system 0.28.1 → 0.29.0.rc1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +3 -3
- data/app/cells/decidim/system/system_checks/show.erb +13 -0
- data/app/cells/decidim/system/system_checks_cell.rb +48 -0
- data/app/commands/decidim/system/create_default_content_blocks.rb +0 -3
- data/app/commands/decidim/system/{populate_help.rb → create_default_help_pages.rb} +14 -7
- data/app/commands/decidim/system/create_default_pages.rb +0 -1
- data/app/commands/decidim/system/{register_organization.rb → create_organization.rb} +11 -10
- data/app/controllers/decidim/system/dashboard_controller.rb +2 -0
- data/app/controllers/decidim/system/organizations_controller.rb +7 -1
- data/app/forms/decidim/system/base_organization_form.rb +127 -0
- data/app/forms/decidim/system/register_organization_form.rb +23 -3
- data/app/forms/decidim/system/update_organization_form.rb +18 -88
- data/app/models/decidim/system/admin.rb +2 -2
- data/app/packs/stylesheets/decidim/system/application.scss +57 -0
- data/app/views/decidim/system/dashboard/show.html.erb +4 -0
- data/app/views/decidim/system/oauth_applications/_form.html.erb +1 -1
- data/app/views/decidim/system/oauth_applications/index.html.erb +2 -2
- data/app/views/decidim/system/organizations/edit.html.erb +5 -3
- data/app/views/decidim/system/shared/_admins_list.html.erb +2 -2
- data/app/views/decidim/system/shared/_organizations_list.html.erb +3 -3
- data/app/views/layouts/decidim/system/_js_configuration.html.erb +1 -1
- data/app/views/layouts/decidim/system/login.html.erb +1 -1
- data/config/locales/ar.yml +0 -1
- data/config/locales/bg.yml +22 -4
- data/config/locales/ca.yml +18 -4
- data/config/locales/cs.yml +5 -3
- data/config/locales/de.yml +16 -4
- data/config/locales/el.yml +0 -4
- data/config/locales/en.yml +18 -4
- data/config/locales/es-MX.yml +18 -4
- data/config/locales/es-PY.yml +18 -4
- data/config/locales/es.yml +18 -4
- data/config/locales/eu.yml +18 -4
- data/config/locales/fi-plain.yml +18 -4
- data/config/locales/fi.yml +18 -4
- data/config/locales/fr-CA.yml +18 -4
- data/config/locales/fr.yml +18 -4
- data/config/locales/gl.yml +0 -1
- data/config/locales/hu.yml +0 -2
- data/config/locales/id-ID.yml +0 -1
- data/config/locales/it.yml +1 -1
- data/config/locales/ja.yml +18 -4
- data/config/locales/lt.yml +0 -4
- data/config/locales/lv.yml +0 -1
- data/config/locales/nl.yml +0 -1
- data/config/locales/no.yml +0 -1
- data/config/locales/pl.yml +120 -1
- data/config/locales/pt-BR.yml +0 -2
- data/config/locales/pt.yml +0 -1
- data/config/locales/ro-RO.yml +0 -1
- data/config/locales/ru.yml +0 -1
- data/config/locales/sk.yml +0 -1
- data/config/locales/sv.yml +0 -1
- data/config/locales/tr-TR.yml +22 -1
- data/config/locales/zh-CN.yml +0 -1
- data/config/locales/zh-TW.yml +0 -4
- data/db/seeds.rb +1 -1
- data/decidim-system.gemspec +1 -1
- data/lib/decidim/system/engine.rb +4 -0
- data/lib/decidim/system/version.rb +1 -1
- data/lib/tasks/decidim_system.rake +1 -1
- metadata +18 -16
- data/app/mailers/decidim/system/application_mailer.rb +0 -12
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 947a58b195365e516146ac29a1b1ccf623416523defcc0fe31ae44e3fd697a36
|
4
|
+
data.tar.gz: 713c800019aebcf933159515236a8c5c4dd5c79543f2822e1ac75059b0ddf293
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: f88a26f5fdd1527aafaaadf95c4a6e450e08846d2852277a17c4fac539cd55b5311d7d8d5d71b6399b225194d7668bd2cb6d1993df265e86aec9fc29ba2e19c1
|
7
|
+
data.tar.gz: 675c8a7bf6cd5eb07ef01aba70ba8423673c5c4e4a624d49bc3b6dbe6548444e3c690b27b4afc8f96fcb3e597334947d3f1e85432dd7f9503be4b5008063748f
|
data/README.md
CHANGED
@@ -49,7 +49,7 @@ bin/rails decidim_system:create_admin
|
|
49
49
|
You will be asked for an email and a password. For security, the password will not get displayed back at you and you will need to confirm it.
|
50
50
|
|
51
51
|
Once you have created your first admin you can access the system dashboard at `/system`. For instance, if you have Decidim running at `https://example.org`, this URL would be `https://example.org/system`.
|
52
|
-
You will be able to
|
52
|
+
You will be able to log in with your newly created user.
|
53
53
|
|
54
54
|
From the system dashboard you can add new admins.
|
55
55
|
|
@@ -73,11 +73,11 @@ system_admin.save
|
|
73
73
|
|
74
74
|
## Managing organizations
|
75
75
|
|
76
|
-
Once you have your system admin setup you can also start managing the organizations in your deploy. To do it,
|
76
|
+
Once you have your system admin setup you can also start managing the organizations in your deploy. To do it, log in at the system dashboard and create a new organization
|
77
77
|
following the form instructions. After creating it, a new admin user will be created and invited to start managing it.
|
78
78
|
|
79
79
|
Remember that System admins and regular Admins are completely different users (they do not even share the same database table), so you cannot use your
|
80
|
-
system user to
|
80
|
+
system user to log in in as an organization admin.
|
81
81
|
|
82
82
|
## Contributing
|
83
83
|
|
@@ -0,0 +1,13 @@
|
|
1
|
+
<ul>
|
2
|
+
<% checks.each do |key, check| %>
|
3
|
+
<li>
|
4
|
+
<% if check[:check_method] %>
|
5
|
+
<%= icon "checkbox-circle-line", class: "fill-success inline" %>
|
6
|
+
<%= t("#{key}.success", scope: "decidim.system.system_checks") %>
|
7
|
+
<% else %>
|
8
|
+
<%= icon "close-circle-line", class: "fill-alert inline" %>
|
9
|
+
<%= t("#{key}.error", scope: "decidim.system.system_checks", error_extra: check[:error_extra]) %>
|
10
|
+
<% end %>
|
11
|
+
</li>
|
12
|
+
<% end %>
|
13
|
+
</ul>
|
@@ -0,0 +1,48 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Decidim
|
4
|
+
module System
|
5
|
+
class SystemChecksCell < Decidim::ViewModel
|
6
|
+
def show
|
7
|
+
render
|
8
|
+
end
|
9
|
+
|
10
|
+
private
|
11
|
+
|
12
|
+
def checks
|
13
|
+
{
|
14
|
+
secret_key: {
|
15
|
+
check_method: correct_secret_key_base?,
|
16
|
+
error_extra: generated_secret_key
|
17
|
+
},
|
18
|
+
active_job_queue: {
|
19
|
+
check_method: correct_active_job_queue?,
|
20
|
+
error_extra: active_job_queue_link
|
21
|
+
}
|
22
|
+
}
|
23
|
+
end
|
24
|
+
|
25
|
+
def correct_secret_key_base?
|
26
|
+
Rails.application.secrets.secret_key_base&.length == 128
|
27
|
+
end
|
28
|
+
|
29
|
+
def generated_secret_key
|
30
|
+
SecureRandom.hex(64)
|
31
|
+
end
|
32
|
+
|
33
|
+
def correct_active_job_queue?
|
34
|
+
# The default ActiveJob queue is not recommended for production environments,
|
35
|
+
# as it can lose jobs when restarting
|
36
|
+
Rails.application.config.active_job.queue_adapter != :async
|
37
|
+
end
|
38
|
+
|
39
|
+
def active_job_queue_link
|
40
|
+
link_to(t("active_job_queue.decidim_documentation", scope: "decidim.system.system_checks"),
|
41
|
+
"https://docs.decidim.org/en/develop/services/activejob",
|
42
|
+
class: "underline text-primary",
|
43
|
+
target: "_blank",
|
44
|
+
rel: "nofollow noopener noreferrer")
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
@@ -5,9 +5,6 @@ module Decidim
|
|
5
5
|
# A command with all the business logic to create the default content blocks
|
6
6
|
# for a newly-created organization.
|
7
7
|
class CreateDefaultContentBlocks < Decidim::Command
|
8
|
-
DEFAULT_CONTENT_BLOCKS =
|
9
|
-
[:hero, :sub_hero, :highlighted_content_banner, :how_to_participate, :stats, :metrics, :footer_sub_hero].freeze
|
10
|
-
|
11
8
|
# Public: Initializes the command.
|
12
9
|
#
|
13
10
|
# form - A form object with the params.
|
@@ -3,7 +3,9 @@
|
|
3
3
|
module Decidim
|
4
4
|
module System
|
5
5
|
# A command that will create default help pages for an organization.
|
6
|
-
class
|
6
|
+
class CreateDefaultHelpPages < Decidim::Command
|
7
|
+
include Decidim::TranslatableAttributes
|
8
|
+
|
7
9
|
# Public: Initializes the command.
|
8
10
|
#
|
9
11
|
# organization - An organization
|
@@ -17,16 +19,17 @@ module Decidim
|
|
17
19
|
def call
|
18
20
|
ActiveRecord::Base.transaction do
|
19
21
|
topic = Decidim::StaticPageTopic.create!(
|
20
|
-
title: multi_translation("decidim.help.main_topic.title", organization:
|
21
|
-
description: multi_translation("decidim.help.main_topic.description", organization:
|
22
|
+
title: multi_translation("decidim.help.main_topic.title", organization: organization_name),
|
23
|
+
description: multi_translation("decidim.help.main_topic.description", organization: organization_name),
|
22
24
|
organization: @organization,
|
25
|
+
show_in_footer: true,
|
23
26
|
weight: 0
|
24
27
|
)
|
25
28
|
|
26
29
|
Decidim::StaticPage.create!(
|
27
30
|
slug: "help",
|
28
|
-
title: multi_translation("decidim.help.main_topic.default_page.title", organization:
|
29
|
-
content: multi_translation("decidim.help.main_topic.default_page.content", organization:
|
31
|
+
title: multi_translation("decidim.help.main_topic.default_page.title", organization: organization_name),
|
32
|
+
content: multi_translation("decidim.help.main_topic.default_page.content", organization: organization_name),
|
30
33
|
topic:,
|
31
34
|
organization: @organization,
|
32
35
|
weight: 0
|
@@ -49,8 +52,12 @@ module Decidim
|
|
49
52
|
end
|
50
53
|
end
|
51
54
|
|
52
|
-
def multi_translation(key, **
|
53
|
-
Decidim::TranslationsHelper.multi_translation(key, @organization.available_locales, **
|
55
|
+
def multi_translation(key, **)
|
56
|
+
Decidim::TranslationsHelper.multi_translation(key, @organization.available_locales, **)
|
57
|
+
end
|
58
|
+
|
59
|
+
def organization_name
|
60
|
+
translated_attribute(@organization.name)
|
54
61
|
end
|
55
62
|
end
|
56
63
|
end
|
@@ -23,7 +23,6 @@ module Decidim
|
|
23
23
|
translated_slug = I18n.t(slug, scope: "decidim.system.default_pages")
|
24
24
|
page.title = localized_attribute(translated_slug, :title)
|
25
25
|
page.content = localized_attribute(translated_slug, :content)
|
26
|
-
page.show_in_footer = true
|
27
26
|
page.allow_public_access = true if slug == "terms-of-service"
|
28
27
|
end
|
29
28
|
|
@@ -2,10 +2,14 @@
|
|
2
2
|
|
3
3
|
module Decidim
|
4
4
|
module System
|
5
|
+
class InvitationFailedError < ActiveRecord::RecordInvalid
|
6
|
+
end
|
7
|
+
|
5
8
|
# A command with all the business logic when creating a new organization in
|
6
9
|
# the system. It creates the organization and invites the admin to the
|
7
10
|
# system.
|
8
|
-
|
11
|
+
|
12
|
+
class CreateOrganization < Decidim::Command
|
9
13
|
# Public: Initializes the command.
|
10
14
|
#
|
11
15
|
# form - A form object with the params.
|
@@ -24,21 +28,21 @@ module Decidim
|
|
24
28
|
|
25
29
|
@organization = nil
|
26
30
|
invite_form = nil
|
27
|
-
invitation_failed = false
|
28
31
|
|
29
32
|
transaction do
|
30
33
|
@organization = create_organization
|
31
34
|
CreateDefaultPages.call(@organization)
|
32
|
-
|
35
|
+
CreateDefaultHelpPages.call(@organization)
|
33
36
|
CreateDefaultContentBlocks.call(@organization)
|
34
37
|
invite_form = invite_user_form(@organization)
|
35
|
-
|
38
|
+
raise InvitationFailedError if invite_form.invalid?
|
36
39
|
end
|
37
|
-
return broadcast(:invalid) if invitation_failed
|
38
40
|
|
39
41
|
Decidim::InviteUser.call(invite_form) if @organization && invite_form
|
40
42
|
|
41
43
|
broadcast(:ok)
|
44
|
+
rescue InvitationFailedError
|
45
|
+
broadcast(:invalid_invitation)
|
42
46
|
rescue ActiveRecord::RecordInvalid, ActiveRecord::RecordNotUnique
|
43
47
|
broadcast(:invalid)
|
44
48
|
end
|
@@ -49,13 +53,13 @@ module Decidim
|
|
49
53
|
|
50
54
|
def create_organization
|
51
55
|
Decidim::Organization.create!(
|
52
|
-
name: form.name,
|
56
|
+
name: { form.default_locale => form.name },
|
53
57
|
host: form.host,
|
54
58
|
secondary_hosts: form.clean_secondary_hosts,
|
55
59
|
reference_prefix: form.reference_prefix,
|
56
60
|
available_locales: form.available_locales,
|
57
61
|
available_authorizations: form.clean_available_authorizations,
|
58
|
-
|
62
|
+
external_domain_allowlist: ["decidim.org", "github.com"],
|
59
63
|
users_registration_mode: form.users_registration_mode,
|
60
64
|
force_users_to_authenticate_before_access_organization: form.force_users_to_authenticate_before_access_organization,
|
61
65
|
badges_enabled: true,
|
@@ -72,10 +76,7 @@ module Decidim
|
|
72
76
|
|
73
77
|
def default_colors
|
74
78
|
{
|
75
|
-
alert: "#ec5840",
|
76
79
|
primary: "#53bf40",
|
77
|
-
success: "#57d685",
|
78
|
-
warning: "#ffae00",
|
79
80
|
tertiary: "#bf4053",
|
80
81
|
secondary: "#4053bf"
|
81
82
|
}
|
@@ -16,12 +16,17 @@ module Decidim
|
|
16
16
|
def create
|
17
17
|
@form = form(RegisterOrganizationForm).from_params(params)
|
18
18
|
|
19
|
-
|
19
|
+
CreateOrganization.call(@form) do
|
20
20
|
on(:ok) do
|
21
21
|
flash[:notice] = t("organizations.create.success_html", scope: "decidim.system", host: @form.host, email: @form.organization_admin_email)
|
22
22
|
redirect_to organizations_path
|
23
23
|
end
|
24
24
|
|
25
|
+
on(:invalid_invitation) do
|
26
|
+
flash.now[:alert] = t("organizations.create.error_invitation", scope: "decidim.system")
|
27
|
+
render :new
|
28
|
+
end
|
29
|
+
|
25
30
|
on(:invalid) do
|
26
31
|
flash.now[:alert] = t("organizations.create.error", scope: "decidim.system")
|
27
32
|
render :new
|
@@ -39,6 +44,7 @@ module Decidim
|
|
39
44
|
end
|
40
45
|
|
41
46
|
def update
|
47
|
+
@organization = Organization.find(params[:id])
|
42
48
|
@form = form(UpdateOrganizationForm).from_params(params)
|
43
49
|
|
44
50
|
UpdateOrganization.call(params[:id], @form) do
|
@@ -0,0 +1,127 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "decidim/translatable_attributes"
|
4
|
+
|
5
|
+
module Decidim
|
6
|
+
module System
|
7
|
+
# A form object to be inherited to create and update organizations from the system dashboard.
|
8
|
+
#
|
9
|
+
class BaseOrganizationForm < Form
|
10
|
+
include TranslatableAttributes
|
11
|
+
include JsonbAttributes
|
12
|
+
|
13
|
+
mimic :organization
|
14
|
+
|
15
|
+
attribute :host, String
|
16
|
+
attribute :secondary_hosts, String
|
17
|
+
attribute :force_users_to_authenticate_before_access_organization, Boolean
|
18
|
+
attribute :available_authorizations, Array[String]
|
19
|
+
attribute :users_registration_mode, String
|
20
|
+
attribute :default_locale, String
|
21
|
+
|
22
|
+
jsonb_attribute :smtp_settings, [
|
23
|
+
[:from, String],
|
24
|
+
[:from_email, String],
|
25
|
+
[:from_label, String],
|
26
|
+
[:user_name, String],
|
27
|
+
[:encrypted_password, String],
|
28
|
+
[:address, String],
|
29
|
+
[:port, Integer],
|
30
|
+
[:authentication, String],
|
31
|
+
[:enable_starttls_auto, Boolean]
|
32
|
+
]
|
33
|
+
|
34
|
+
jsonb_attribute :content_security_policy, [
|
35
|
+
[:"default-src", String],
|
36
|
+
[:"img-src", String],
|
37
|
+
[:"media-src", String],
|
38
|
+
[:"script-src", String],
|
39
|
+
[:"style-src", String],
|
40
|
+
[:"frame-src", String],
|
41
|
+
[:"font-src", String],
|
42
|
+
[:"connect-src", String]
|
43
|
+
]
|
44
|
+
|
45
|
+
attribute :password, String
|
46
|
+
attribute :file_upload_settings, FileUploadSettingsForm
|
47
|
+
|
48
|
+
OMNIATH_PROVIDERS_ATTRIBUTES = Decidim::OmniauthProvider.available.keys.map do |provider|
|
49
|
+
Rails.application.secrets.dig(:omniauth, provider).keys.map do |setting|
|
50
|
+
if setting == :enabled
|
51
|
+
["omniauth_settings_#{provider}_enabled".to_sym, Boolean]
|
52
|
+
else
|
53
|
+
["omniauth_settings_#{provider}_#{setting}".to_sym, String]
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end.flatten(1)
|
57
|
+
|
58
|
+
jsonb_attribute :omniauth_settings, OMNIATH_PROVIDERS_ATTRIBUTES
|
59
|
+
|
60
|
+
validates :host, :users_registration_mode, presence: true
|
61
|
+
validate :validate_organization_uniqueness
|
62
|
+
validate :validate_secret_key_base_for_encryption
|
63
|
+
validates :users_registration_mode, inclusion: { in: Decidim::Organization.users_registration_modes }
|
64
|
+
|
65
|
+
def map_model(model)
|
66
|
+
self.default_locale = model.default_locale
|
67
|
+
self.secondary_hosts = model.secondary_hosts.join("\n")
|
68
|
+
self.omniauth_settings = (model.omniauth_settings || {}).transform_values do |v|
|
69
|
+
Decidim::OmniauthProvider.value_defined?(v) ? Decidim::AttributeEncryptor.decrypt(v) : v
|
70
|
+
end
|
71
|
+
self.file_upload_settings = FileUploadSettingsForm.from_model(model.file_upload_settings)
|
72
|
+
end
|
73
|
+
|
74
|
+
def clean_secondary_hosts
|
75
|
+
return unless secondary_hosts
|
76
|
+
|
77
|
+
secondary_hosts.split("\n").map(&:chomp).select(&:present?)
|
78
|
+
end
|
79
|
+
|
80
|
+
def clean_available_authorizations
|
81
|
+
return unless available_authorizations
|
82
|
+
|
83
|
+
available_authorizations.select(&:present?)
|
84
|
+
end
|
85
|
+
|
86
|
+
def password
|
87
|
+
encrypted_password.nil? ? super : Decidim::AttributeEncryptor.decrypt(encrypted_password)
|
88
|
+
end
|
89
|
+
|
90
|
+
def encrypted_smtp_settings
|
91
|
+
smtp_settings["from"] = set_from
|
92
|
+
|
93
|
+
encrypted = smtp_settings.merge(encrypted_password: Decidim::AttributeEncryptor.encrypt(password))
|
94
|
+
# if all are empty, nil is returned so it does not break ENV vars configuration
|
95
|
+
encrypted.values.all?(&:blank?) ? nil : encrypted
|
96
|
+
end
|
97
|
+
|
98
|
+
def set_from
|
99
|
+
return from_email if from_label.blank?
|
100
|
+
|
101
|
+
"#{from_label} <#{from_email}>"
|
102
|
+
end
|
103
|
+
|
104
|
+
def encrypted_omniauth_settings
|
105
|
+
encrypted = omniauth_settings.transform_values do |v|
|
106
|
+
Decidim::OmniauthProvider.value_defined?(v) ? Decidim::AttributeEncryptor.encrypt(v) : v
|
107
|
+
end
|
108
|
+
# if all are empty, nil is returned so it does not break ENV vars configuration
|
109
|
+
encrypted.values.all?(&:blank?) ? nil : encrypted
|
110
|
+
end
|
111
|
+
|
112
|
+
private
|
113
|
+
|
114
|
+
# We need a valid secret key base for encrypting the SMTP password with it
|
115
|
+
# It is also necessary for other things in Rails (like Cookies encryption)
|
116
|
+
def validate_secret_key_base_for_encryption
|
117
|
+
return if Rails.application.secrets.secret_key_base&.length == 128
|
118
|
+
|
119
|
+
errors.add(:password, I18n.t("activemodel.errors.models.organization.attributes.password.secret_key"))
|
120
|
+
end
|
121
|
+
|
122
|
+
def validate_organization_uniqueness
|
123
|
+
raise "#{self.class.name} is expected to implement #validate_organization_uniqueness"
|
124
|
+
end
|
125
|
+
end
|
126
|
+
end
|
127
|
+
end
|
@@ -6,10 +6,12 @@ module Decidim
|
|
6
6
|
module System
|
7
7
|
# A form object used to create organizations from the system dashboard.
|
8
8
|
#
|
9
|
-
class RegisterOrganizationForm <
|
9
|
+
class RegisterOrganizationForm < BaseOrganizationForm
|
10
10
|
include JsonbAttributes
|
11
11
|
mimic :organization
|
12
12
|
|
13
|
+
attribute :name, String
|
14
|
+
|
13
15
|
attribute :organization_admin_email, String
|
14
16
|
attribute :organization_admin_name, String
|
15
17
|
attribute :available_locales, Array
|
@@ -18,11 +20,29 @@ module Decidim
|
|
18
20
|
attribute :users_registration_mode, String
|
19
21
|
attribute :force_users_to_authenticate_before_access_organization, Boolean
|
20
22
|
|
21
|
-
validates :organization_admin_email, :organization_admin_name, :name, :
|
23
|
+
validates :organization_admin_email, :organization_admin_name, :name, :reference_prefix, presence: true
|
24
|
+
validates :name, presence: true
|
22
25
|
validates :available_locales, presence: true
|
23
26
|
validates :default_locale, presence: true
|
24
27
|
validates :default_locale, inclusion: { in: :available_locales }
|
25
|
-
|
28
|
+
|
29
|
+
private
|
30
|
+
|
31
|
+
def validate_organization_uniqueness
|
32
|
+
base_query = Decidim::Organization.pluck(:name)
|
33
|
+
|
34
|
+
organization_names = []
|
35
|
+
|
36
|
+
base_query.each do |value|
|
37
|
+
organization_names += value.except("machine_translations").values
|
38
|
+
organization_names += value.fetch("machine_translations", {}).values
|
39
|
+
end
|
40
|
+
|
41
|
+
organization_names = organization_names.map(&:downcase)
|
42
|
+
|
43
|
+
errors.add(:name, :taken) if organization_names.include?(name&.downcase)
|
44
|
+
errors.add(:host, :taken) if Decidim::Organization.where(host:).where.not(id:).exists?
|
45
|
+
end
|
26
46
|
end
|
27
47
|
end
|
28
48
|
end
|
@@ -6,106 +6,36 @@ module Decidim
|
|
6
6
|
module System
|
7
7
|
# A form object used to update organizations from the system dashboard.
|
8
8
|
#
|
9
|
-
class UpdateOrganizationForm <
|
10
|
-
|
11
|
-
include JsonbAttributes
|
9
|
+
class UpdateOrganizationForm < BaseOrganizationForm
|
10
|
+
translatable_attribute :name, String
|
12
11
|
|
13
|
-
|
12
|
+
validate :validate_organization_name_presence
|
14
13
|
|
15
|
-
|
16
|
-
attribute :host, String
|
17
|
-
attribute :secondary_hosts, String
|
18
|
-
attribute :force_users_to_authenticate_before_access_organization, Boolean
|
19
|
-
attribute :available_authorizations, Array[String]
|
20
|
-
attribute :users_registration_mode, String
|
21
|
-
jsonb_attribute :smtp_settings, [
|
22
|
-
[:from, String],
|
23
|
-
[:from_email, String],
|
24
|
-
[:from_label, String],
|
25
|
-
[:user_name, String],
|
26
|
-
[:encrypted_password, String],
|
27
|
-
[:address, String],
|
28
|
-
[:port, Integer],
|
29
|
-
[:authentication, String],
|
30
|
-
[:enable_starttls_auto, Boolean]
|
31
|
-
]
|
32
|
-
|
33
|
-
jsonb_attribute :content_security_policy, [
|
34
|
-
[:"default-src", String],
|
35
|
-
[:"img-src", String],
|
36
|
-
[:"media-src", String],
|
37
|
-
[:"script-src", String],
|
38
|
-
[:"style-src", String],
|
39
|
-
[:"frame-src", String],
|
40
|
-
[:"font-src", String],
|
41
|
-
[:"connect-src", String]
|
42
|
-
]
|
43
|
-
|
44
|
-
attribute :password, String
|
45
|
-
attribute :file_upload_settings, FileUploadSettingsForm
|
46
|
-
|
47
|
-
OMNIATH_PROVIDERS_ATTRIBUTES = Decidim::OmniauthProvider.available.keys.map do |provider|
|
48
|
-
Rails.application.secrets.dig(:omniauth, provider).keys.map do |setting|
|
49
|
-
if setting == :enabled
|
50
|
-
["omniauth_settings_#{provider}_enabled".to_sym, Boolean]
|
51
|
-
else
|
52
|
-
["omniauth_settings_#{provider}_#{setting}".to_sym, String]
|
53
|
-
end
|
54
|
-
end
|
55
|
-
end.flatten(1)
|
56
|
-
|
57
|
-
jsonb_attribute :omniauth_settings, OMNIATH_PROVIDERS_ATTRIBUTES
|
58
|
-
|
59
|
-
validates :name, :host, :users_registration_mode, presence: true
|
60
|
-
validate :validate_organization_uniqueness
|
61
|
-
validates :users_registration_mode, inclusion: { in: Decidim::Organization.users_registration_modes }
|
62
|
-
|
63
|
-
def map_model(model)
|
64
|
-
self.secondary_hosts = model.secondary_hosts.join("\n")
|
65
|
-
self.omniauth_settings = (model.omniauth_settings || {}).transform_values do |v|
|
66
|
-
Decidim::OmniauthProvider.value_defined?(v) ? Decidim::AttributeEncryptor.decrypt(v) : v
|
67
|
-
end
|
68
|
-
self.file_upload_settings = FileUploadSettingsForm.from_model(model.file_upload_settings)
|
69
|
-
end
|
70
|
-
|
71
|
-
def clean_secondary_hosts
|
72
|
-
return unless secondary_hosts
|
73
|
-
|
74
|
-
secondary_hosts.split("\n").map(&:chomp).select(&:present?)
|
75
|
-
end
|
76
|
-
|
77
|
-
def clean_available_authorizations
|
78
|
-
return unless available_authorizations
|
14
|
+
private
|
79
15
|
|
80
|
-
|
16
|
+
def validate_organization_name_presence
|
17
|
+
translated_attr = "name_#{current_organization.try(:default_locale) || Decidim.default_locale.to_s}".to_sym
|
18
|
+
errors.add(translated_attr, :blank) if send(translated_attr).blank?
|
81
19
|
end
|
82
20
|
|
83
|
-
def
|
84
|
-
|
85
|
-
end
|
21
|
+
def validate_organization_uniqueness
|
22
|
+
base_query = persisted? ? Decidim::Organization.where.not(id:).all : Decidim::Organization.all
|
86
23
|
|
87
|
-
|
88
|
-
smtp_settings["from"] = set_from
|
24
|
+
organization_names = []
|
89
25
|
|
90
|
-
|
91
|
-
|
26
|
+
base_query.pluck(:name).each do |value|
|
27
|
+
organization_names += value.except("machine_translations").values
|
28
|
+
organization_names += value.fetch("machine_translations", {}).values
|
29
|
+
end
|
92
30
|
|
93
|
-
|
94
|
-
return from_email if from_label.blank?
|
31
|
+
organization_names = organization_names.map(&:downcase).compact_blank
|
95
32
|
|
96
|
-
|
97
|
-
|
33
|
+
name.each do |language, value|
|
34
|
+
next if value.is_a?(Hash)
|
98
35
|
|
99
|
-
|
100
|
-
omniauth_settings.transform_values do |v|
|
101
|
-
Decidim::OmniauthProvider.value_defined?(v) ? Decidim::AttributeEncryptor.encrypt(v) : v
|
36
|
+
errors.add("name_#{language}", :taken) if organization_names.include?(value.downcase)
|
102
37
|
end
|
103
|
-
end
|
104
38
|
|
105
|
-
private
|
106
|
-
|
107
|
-
def validate_organization_uniqueness
|
108
|
-
errors.add(:name, :taken) if Decidim::Organization.where(name:).where.not(id:).exists?
|
109
39
|
errors.add(:host, :taken) if Decidim::Organization.where(host:).where.not(id:).exists?
|
110
40
|
end
|
111
41
|
end
|
@@ -13,8 +13,8 @@ module Decidim
|
|
13
13
|
private
|
14
14
|
|
15
15
|
# Changes default Devise behaviour to use ActiveJob to send async emails.
|
16
|
-
def send_devise_notification(notification, *
|
17
|
-
devise_mailer.send(notification, self, *
|
16
|
+
def send_devise_notification(notification, *)
|
17
|
+
devise_mailer.send(notification, self, *).deliver_later
|
18
18
|
end
|
19
19
|
end
|
20
20
|
end
|
@@ -107,3 +107,60 @@ dl {
|
|
107
107
|
.ts-dropdown {
|
108
108
|
margin: 0;
|
109
109
|
}
|
110
|
+
|
111
|
+
.tabs--lang {
|
112
|
+
@apply bg-transparent flex items-center gap-x-1;
|
113
|
+
|
114
|
+
li {
|
115
|
+
@apply p-0.5 rounded-t-sm text-secondary text-xs;
|
116
|
+
|
117
|
+
background-color: rgba(243, 244, 247, 1);
|
118
|
+
|
119
|
+
&.is-active,
|
120
|
+
&:hover {
|
121
|
+
@apply border-b border-secondary;
|
122
|
+
}
|
123
|
+
|
124
|
+
&:hover {
|
125
|
+
background-color: rgba(243, 244, 247, 1);
|
126
|
+
}
|
127
|
+
}
|
128
|
+
|
129
|
+
a {
|
130
|
+
@apply text-xs p-0;
|
131
|
+
|
132
|
+
&::before {
|
133
|
+
@apply content-[''] w-2 h-2 inline-block bg-white rounded-full mr-2 border;
|
134
|
+
|
135
|
+
border-color: rgba(116, 129, 144, 1);
|
136
|
+
}
|
137
|
+
}
|
138
|
+
|
139
|
+
.tabs-title > a[aria-selected="true"] {
|
140
|
+
@apply font-bold text-secondary;
|
141
|
+
|
142
|
+
background-color: rgba(243, 244, 247, 1);
|
143
|
+
|
144
|
+
&::before {
|
145
|
+
@apply border-white;
|
146
|
+
|
147
|
+
background-color: rgba(40, 167, 69, 1);
|
148
|
+
}
|
149
|
+
}
|
150
|
+
|
151
|
+
a.is-tab-error {
|
152
|
+
color: red;
|
153
|
+
}
|
154
|
+
}
|
155
|
+
|
156
|
+
.label--tabs {
|
157
|
+
@apply flex justify-between items-end;
|
158
|
+
|
159
|
+
label {
|
160
|
+
@apply inline-block;
|
161
|
+
}
|
162
|
+
}
|
163
|
+
|
164
|
+
.tabs-panel[aria-hidden="true"] {
|
165
|
+
@apply hidden;
|
166
|
+
}
|
@@ -4,6 +4,10 @@
|
|
4
4
|
<h1 class="h1"><%= t("decidim.system.titles.dashboard") %></h1>
|
5
5
|
<% end %>
|
6
6
|
|
7
|
+
<h2 class="h3"><%= t ".system_checks" %></h2>
|
8
|
+
|
9
|
+
<%= cell "decidim/system/system_checks", nil %>
|
10
|
+
|
7
11
|
<h2 class="h3"><%= t ".current_organizations" %></h2>
|
8
12
|
|
9
13
|
<%= link_to t("actions.new_organization", scope: "decidim.system"), [:new, :organization], class: "button button__sm md:button__lg button__primary" %>
|
@@ -2,7 +2,7 @@
|
|
2
2
|
<%= form.text_field :name %>
|
3
3
|
<%= form.text_field :redirect_uri %>
|
4
4
|
<%= form.select :decidim_organization_id,
|
5
|
-
Decidim::Organization.
|
5
|
+
Decidim::Organization.all.map { |o| [organization_name(o), o.id] },
|
6
6
|
{ include_blank: t(".select_organization") },
|
7
7
|
{ multiple: false } %>
|
8
8
|
<%= form.text_field :organization_name %>
|