decidim-term_customizer 0.16.0

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 (57) hide show
  1. checksums.yaml +7 -0
  2. data/LICENSE-AGPLv3.txt +661 -0
  3. data/README.md +180 -0
  4. data/Rakefile +48 -0
  5. data/app/assets/config/translation_sets_admin_manifest.js +2 -0
  6. data/app/assets/javascripts/decidim/term_customizer/admin/constraint_fields.js.es6 +45 -0
  7. data/app/assets/javascripts/decidim/term_customizer/admin/multifield/component.js.es6 +103 -0
  8. data/app/assets/javascripts/decidim/term_customizer/admin/multifield.js.es6 +5 -0
  9. data/app/assets/javascripts/decidim/term_customizer/admin/translation_sets_admin.js.es6 +19 -0
  10. data/app/assets/javascripts/decidim/term_customizer/admin/translations_admin.js.es6 +72 -0
  11. data/app/commands/decidim/term_customizer/admin/create_translation.rb +53 -0
  12. data/app/commands/decidim/term_customizer/admin/create_translation_set.rb +68 -0
  13. data/app/commands/decidim/term_customizer/admin/import_translation_keys.rb +59 -0
  14. data/app/commands/decidim/term_customizer/admin/update_translation.rb +65 -0
  15. data/app/commands/decidim/term_customizer/admin/update_translation_set.rb +61 -0
  16. data/app/controllers/decidim/term_customizer/admin/add_translations_controller.rb +72 -0
  17. data/app/controllers/decidim/term_customizer/admin/application_controller.rb +15 -0
  18. data/app/controllers/decidim/term_customizer/admin/translation_sets_controller.rb +107 -0
  19. data/app/controllers/decidim/term_customizer/admin/translations_controller.rb +102 -0
  20. data/app/forms/decidim/term_customizer/admin/translation_form.rb +39 -0
  21. data/app/forms/decidim/term_customizer/admin/translation_key_import_form.rb +15 -0
  22. data/app/forms/decidim/term_customizer/admin/translation_set_constraint_form.rb +58 -0
  23. data/app/forms/decidim/term_customizer/admin/translation_set_form.rb +18 -0
  24. data/app/forms/decidim/term_customizer/admin/translation_set_subject_component_form.rb +12 -0
  25. data/app/forms/decidim/term_customizer/admin/translation_set_subject_form.rb +61 -0
  26. data/app/helpers/decidim/term_customizer/admin/application_helper.rb +13 -0
  27. data/app/models/decidim/term_customizer/application_record.rb +10 -0
  28. data/app/models/decidim/term_customizer/constraint.rb +36 -0
  29. data/app/models/decidim/term_customizer/translation.rb +23 -0
  30. data/app/models/decidim/term_customizer/translation_set.rb +19 -0
  31. data/app/permissions/decidim/term_customizer/admin/permissions.rb +66 -0
  32. data/app/queries/decidim/term_customizer/organization_translation_sets.rb +37 -0
  33. data/app/queries/decidim/term_customizer/set_translations.rb +21 -0
  34. data/app/views/decidim/term_customizer/admin/add_translations/index.html.erb +59 -0
  35. data/app/views/decidim/term_customizer/admin/translation_sets/_constraint_fields.html.erb +58 -0
  36. data/app/views/decidim/term_customizer/admin/translation_sets/_form.html.erb +29 -0
  37. data/app/views/decidim/term_customizer/admin/translation_sets/edit.html.erb +9 -0
  38. data/app/views/decidim/term_customizer/admin/translation_sets/index.html.erb +44 -0
  39. data/app/views/decidim/term_customizer/admin/translation_sets/new.html.erb +9 -0
  40. data/app/views/decidim/term_customizer/admin/translations/_form.html.erb +15 -0
  41. data/app/views/decidim/term_customizer/admin/translations/edit.html.erb +7 -0
  42. data/app/views/decidim/term_customizer/admin/translations/index.html.erb +52 -0
  43. data/app/views/decidim/term_customizer/admin/translations/new.html.erb +7 -0
  44. data/config/locales/en.yml +65 -0
  45. data/config/locales/fi.yml +65 -0
  46. data/config/locales/sv.yml +65 -0
  47. data/lib/decidim/term_customizer/admin.rb +10 -0
  48. data/lib/decidim/term_customizer/admin_engine.rb +46 -0
  49. data/lib/decidim/term_customizer/engine.rb +35 -0
  50. data/lib/decidim/term_customizer/i18n_backend.rb +82 -0
  51. data/lib/decidim/term_customizer/resolver.rb +108 -0
  52. data/lib/decidim/term_customizer/test/factories.rb +43 -0
  53. data/lib/decidim/term_customizer/translation_directory.rb +50 -0
  54. data/lib/decidim/term_customizer/translation_store.rb +67 -0
  55. data/lib/decidim/term_customizer/version.rb +8 -0
  56. data/lib/decidim/term_customizer.rb +21 -0
  57. metadata +170 -0
@@ -0,0 +1,102 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Decidim
4
+ module TermCustomizer
5
+ module Admin
6
+ class TranslationsController < TermCustomizer::Admin::ApplicationController
7
+ helper_method :collection, :set, :translations, :translation
8
+
9
+ def index
10
+ enforce_permission_to :read, :translation
11
+ @translations = collection
12
+ end
13
+
14
+ def new
15
+ enforce_permission_to :create, :translation
16
+
17
+ @form = form(TranslationForm).from_model(Translation.new)
18
+ end
19
+
20
+ def create
21
+ enforce_permission_to :create, :translation
22
+ @form = form(TranslationForm).from_params(
23
+ params,
24
+ current_organization: current_organization,
25
+ translation_set: set
26
+ )
27
+
28
+ CreateTranslation.call(@form) do
29
+ on(:ok) do
30
+ flash[:notice] = I18n.t("translations.create.success", scope: "decidim.term_customizer.admin")
31
+ redirect_to translation_set_translations_path(set)
32
+ end
33
+
34
+ on(:invalid) do
35
+ flash.now[:alert] = I18n.t("translations.create.error", scope: "decidim.term_customizer.admin")
36
+ render action: "new"
37
+ end
38
+ end
39
+ end
40
+
41
+ def edit
42
+ enforce_permission_to :update, :translation, translation: translation
43
+ @form = form(TranslationForm).from_model(translation)
44
+ end
45
+
46
+ def update
47
+ enforce_permission_to :update, :translation, translation: translation
48
+ @form = form(TranslationForm).from_params(
49
+ params,
50
+ current_organization: current_organization,
51
+ translation_set: set
52
+ )
53
+
54
+ UpdateTranslation.call(@form, translation) do
55
+ on(:ok) do
56
+ flash[:notice] = I18n.t("translations.update.success", scope: "decidim.term_customizer.admin")
57
+ redirect_to translation_set_translations_path(set)
58
+ end
59
+
60
+ on(:invalid) do
61
+ flash.now[:alert] = I18n.t("translations.update.invalid", scope: "decidim.term_customizer.admin")
62
+ render action: "edit"
63
+ end
64
+ end
65
+ end
66
+
67
+ def destroy
68
+ enforce_permission_to :destroy, :translation, translation: translation
69
+
70
+ # Destroy all locales of the translation key
71
+ Decidim::TermCustomizer::Translation.where(
72
+ translation_set: set,
73
+ key: translation.key
74
+ ).destroy_all
75
+
76
+ flash[:notice] = I18n.t("translations.destroy.success", scope: "decidim.term_customizer.admin")
77
+
78
+ redirect_to translation_set_translations_path(set)
79
+ end
80
+
81
+ private
82
+
83
+ def translation_set
84
+ @translation_set ||= OrganizationTranslationSets.new(
85
+ current_organization
86
+ ).query.find(params[:translation_set_id])
87
+ end
88
+
89
+ def translations
90
+ @translations ||= SetTranslations.new(translation_set, current_locale)
91
+ end
92
+
93
+ def translation
94
+ @translation ||= Decidim::TermCustomizer::Translation.find(params[:id])
95
+ end
96
+
97
+ alias collection translations
98
+ alias set translation_set
99
+ end
100
+ end
101
+ end
102
+ end
@@ -0,0 +1,39 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Decidim
4
+ module TermCustomizer
5
+ module Admin
6
+ class TranslationForm < Decidim::Form
7
+ include TranslatableAttributes
8
+
9
+ mimic :translation
10
+
11
+ delegate :translation_set, to: :context, prefix: false, allow_nil: true
12
+
13
+ attribute :key, String
14
+ translatable_attribute :value, String
15
+
16
+ validates :key, presence: true
17
+ validates :key, format: { with: %r{\A([a-z0-9_/]+\.?)*[a-z0-9_/]+\z} }, unless: -> { key.blank? }
18
+ validates :value, translatable_presence: true
19
+ validate :key_uniqueness
20
+
21
+ def map_model(model)
22
+ self.value = Hash[Decidim::TermCustomizer::Translation.where(
23
+ translation_set: model.translation_set,
24
+ key: model.key
25
+ ).map do |translation|
26
+ [translation.locale, translation.value]
27
+ end]
28
+ end
29
+
30
+ def key_uniqueness
31
+ errors.add(:key, :taken) if translation_set && translation_set.translations.where(
32
+ locale: I18n.locale,
33
+ key: key
34
+ ).exists?
35
+ end
36
+ end
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,15 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Decidim
4
+ module TermCustomizer
5
+ module Admin
6
+ class TranslationKeyImportForm < Decidim::Form
7
+ mimic :translation
8
+
9
+ delegate :translation_set, to: :context, prefix: false, allow_nil: true
10
+
11
+ attribute :keys, Array[String]
12
+ end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,58 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Decidim
4
+ module TermCustomizer
5
+ module Admin
6
+ class TranslationSetConstraintForm < Decidim::Form
7
+ mimic :constraint
8
+
9
+ attribute :subject_manifest, String
10
+ attribute :subject_model, Array[TermCustomizer::Admin::TranslationSetSubjectForm]
11
+ attribute :deleted, Boolean, default: false
12
+
13
+ def to_param
14
+ return id if id.present?
15
+
16
+ "constraint-id"
17
+ end
18
+
19
+ def map_model(model)
20
+ self.subject_manifest = model.manifest_name
21
+
22
+ return unless model.subject
23
+
24
+ self.subject_model = [
25
+ TermCustomizer::Admin::TranslationSetSubjectForm.from_model(
26
+ model.subject
27
+ )
28
+ ]
29
+ end
30
+
31
+ def subject_type
32
+ return unless subject_form
33
+
34
+ subject_form.manifest.try(:model_class_name)
35
+ end
36
+
37
+ def subject
38
+ return component if component
39
+ return unless subject_form
40
+
41
+ subject_form.subject
42
+ end
43
+
44
+ def component
45
+ return unless subject_form
46
+
47
+ subject_form.component
48
+ end
49
+
50
+ def subject_form
51
+ @subject_form ||= subject_model.find do |sm|
52
+ sm.subject_manifest == subject_manifest
53
+ end
54
+ end
55
+ end
56
+ end
57
+ end
58
+ end
@@ -0,0 +1,18 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Decidim
4
+ module TermCustomizer
5
+ module Admin
6
+ class TranslationSetForm < Decidim::Form
7
+ include TranslatableAttributes
8
+
9
+ mimic :translation_set
10
+
11
+ translatable_attribute :name, String
12
+ attribute :constraints, Array[TermCustomizer::Admin::TranslationSetConstraintForm]
13
+
14
+ validates :name, translatable_presence: true
15
+ end
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,12 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Decidim
4
+ module TermCustomizer
5
+ module Admin
6
+ class TranslationSetSubjectComponentForm < Decidim::Form
7
+ attribute :subject_id, Integer
8
+ attribute :component_id, Integer
9
+ end
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,61 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Decidim
4
+ module TermCustomizer
5
+ module Admin
6
+ class TranslationSetSubjectForm < Decidim::Form
7
+ attribute :subject_manifest, String
8
+ attribute :subject_id, Integer
9
+ attribute :component_model, Array[TermCustomizer::Admin::TranslationSetSubjectComponentForm]
10
+
11
+ def map_model(model)
12
+ component = model if model.is_a?(Decidim::Component)
13
+ subject = if component
14
+ model.participatory_space
15
+ else
16
+ model
17
+ end
18
+
19
+ self.subject_manifest = Decidim.participatory_space_manifests.find do |m|
20
+ m.model_class_name == subject.class.name
21
+ end.try(:name)
22
+ self.subject_id = subject.id
23
+
24
+ return unless component
25
+
26
+ self.component_model = [
27
+ TermCustomizer::Admin::TranslationSetSubjectComponentForm.from_params(
28
+ subject_id: subject.id,
29
+ component_id: component.id
30
+ )
31
+ ]
32
+ end
33
+
34
+ def subject
35
+ return unless manifest
36
+
37
+ @subject ||= manifest.model_class_name.constantize.find_by(id: subject_id)
38
+ end
39
+
40
+ def component
41
+ return unless component_form
42
+ return unless subject
43
+
44
+ subject.components.find_by(id: component_form.component_id)
45
+ end
46
+
47
+ def manifest
48
+ @manifest ||= Decidim.participatory_space_manifests.find do |m|
49
+ m.name == subject_manifest.to_sym
50
+ end
51
+ end
52
+
53
+ def component_form
54
+ @component_form ||= component_model.find do |cm|
55
+ cm.subject_id == subject_id
56
+ end
57
+ end
58
+ end
59
+ end
60
+ end
61
+ end
@@ -0,0 +1,13 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Decidim
4
+ module TermCustomizer
5
+ module Admin
6
+ module ApplicationHelper
7
+ def tabs_id_for_constraint(constraint)
8
+ "constraint_#{constraint.to_param}"
9
+ end
10
+ end
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,10 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Decidim
4
+ module TermCustomizer
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,36 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Decidim
4
+ module TermCustomizer
5
+ class Constraint < TermCustomizer::ApplicationRecord
6
+ self.table_name = "decidim_term_customizer_constraints"
7
+
8
+ belongs_to :organization, foreign_key: :decidim_organization_id, class_name: "Decidim::Organization"
9
+ belongs_to :translation_set, class_name: "Decidim::TermCustomizer::TranslationSet", foreign_key: "translation_set_id"
10
+ belongs_to :subject, optional: true, polymorphic: true
11
+ has_many :translations, through: :translation_set
12
+
13
+ def component
14
+ subject if subject.is_a?(Decidim::Component)
15
+ end
16
+
17
+ def space
18
+ return component.participatory_space if component
19
+
20
+ subject
21
+ end
22
+
23
+ def manifest
24
+ space_class = space ? space.class.name : subject_type
25
+
26
+ Decidim.participatory_space_manifests.find do |manifest|
27
+ manifest.model_class_name == space_class
28
+ end
29
+ end
30
+
31
+ def manifest_name
32
+ manifest.try(:name)
33
+ end
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,23 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Decidim
4
+ module TermCustomizer
5
+ class Translation < TermCustomizer::ApplicationRecord
6
+ self.table_name = "decidim_term_customizer_translations"
7
+
8
+ belongs_to :translation_set, class_name: "Decidim::TermCustomizer::TranslationSet", foreign_key: "translation_set_id"
9
+ has_many :constraints, through: :translation_set
10
+
11
+ validates :locale, presence: true
12
+ validates :key, presence: true
13
+ validates :key, format: { with: %r{\A([a-z0-9_/]+\.?)*[a-z0-9_/]+\z} }, unless: -> { key.blank? }
14
+ validates :key, uniqueness: { scope: [:translation_set, :locale] }, unless: -> { key.blank? }
15
+
16
+ class << self
17
+ def available_locales
18
+ Translation.select("DISTINCT locale").to_a.map { |t| t.locale.to_sym }
19
+ end
20
+ end
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,19 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Decidim
4
+ module TermCustomizer
5
+ class TranslationSet < TermCustomizer::ApplicationRecord
6
+ self.table_name = "decidim_term_customizer_translation_sets"
7
+
8
+ has_many :translations,
9
+ class_name: "Decidim::TermCustomizer::Translation",
10
+ foreign_key: :translation_set_id,
11
+ dependent: :destroy
12
+
13
+ has_many :constraints,
14
+ class_name: "Decidim::TermCustomizer::Constraint",
15
+ foreign_key: :translation_set_id,
16
+ dependent: :destroy
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,66 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Decidim
4
+ module TermCustomizer
5
+ module Admin
6
+ class Permissions < Decidim::DefaultPermissions
7
+ def permissions
8
+ return permission_action unless user
9
+ return permission_action unless permission_action.scope == :admin
10
+
11
+ unless user.admin?
12
+ disallow!
13
+ return permission_action
14
+ end
15
+
16
+ if read_admin_dashboard_action?
17
+ allow!
18
+ return permission_action
19
+ end
20
+
21
+ allowed_translation_set_action?
22
+ allowed_translation_action?
23
+
24
+ permission_action
25
+ end
26
+
27
+ private
28
+
29
+ def translation_set
30
+ @translation_set ||= context.fetch(:translation_set, nil)
31
+ end
32
+
33
+ def translation
34
+ @translation ||= context.fetch(:translation, nil)
35
+ end
36
+
37
+ def allowed_translation_set_action?
38
+ return unless permission_action.subject == :translation_set
39
+
40
+ case permission_action.action
41
+ when :create, :read
42
+ allow!
43
+ when :update, :destroy
44
+ toggle_allow(translation_set.present?)
45
+ end
46
+ end
47
+
48
+ def allowed_translation_action?
49
+ return unless permission_action.subject == :translation
50
+
51
+ case permission_action.action
52
+ when :create, :read
53
+ allow!
54
+ when :update, :destroy
55
+ toggle_allow(translation.present?)
56
+ end
57
+ end
58
+
59
+ def read_admin_dashboard_action?
60
+ permission_action.action == :read &&
61
+ permission_action.subject == :admin_dashboard
62
+ end
63
+ end
64
+ end
65
+ end
66
+ end