decidim-term_customizer 0.16.0

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