decidim-term_customizer 0.17.1 → 0.18.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 (31) hide show
  1. checksums.yaml +4 -4
  2. data/app/assets/javascripts/decidim/term_customizer/admin/translation_sets_admin.js.es6 +3 -1
  3. data/app/assets/javascripts/decidim/term_customizer/admin/translations_admin.js.es6 +6 -1
  4. data/app/commands/decidim/term_customizer/admin/create_translation.rb +3 -0
  5. data/app/commands/decidim/term_customizer/admin/destroy_translations.rb +3 -0
  6. data/app/commands/decidim/term_customizer/admin/duplicate_translation_set.rb +61 -0
  7. data/app/commands/decidim/term_customizer/admin/import_set_translations.rb +3 -0
  8. data/app/commands/decidim/term_customizer/admin/import_translation_keys.rb +3 -0
  9. data/app/commands/decidim/term_customizer/admin/update_translation.rb +8 -3
  10. data/app/controllers/decidim/term_customizer/admin/add_translations_controller.rb +2 -1
  11. data/app/controllers/decidim/term_customizer/admin/translation_sets_controller.rb +33 -0
  12. data/app/controllers/decidim/term_customizer/admin/translations_controller.rb +9 -3
  13. data/app/controllers/decidim/term_customizer/admin/translations_destroys_controller.rb +4 -1
  14. data/app/views/decidim/term_customizer/admin/translation_sets/index.html.erb +4 -0
  15. data/config/locales/ca.yml +6 -0
  16. data/config/locales/en.yml +6 -0
  17. data/config/locales/es.yml +6 -0
  18. data/config/locales/fi.yml +6 -0
  19. data/config/locales/fr.yml +6 -0
  20. data/config/locales/sv.yml +6 -0
  21. data/lib/decidim/term_customizer.rb +17 -0
  22. data/lib/decidim/term_customizer/admin_engine.rb +4 -0
  23. data/lib/decidim/term_customizer/context.rb +11 -0
  24. data/lib/decidim/term_customizer/context/base.rb +33 -0
  25. data/lib/decidim/term_customizer/context/controller_context.rb +29 -0
  26. data/lib/decidim/term_customizer/context/job_context.rb +53 -0
  27. data/lib/decidim/term_customizer/engine.rb +10 -29
  28. data/lib/decidim/term_customizer/plural_forms_form.rb +23 -0
  29. data/lib/decidim/term_customizer/plural_forms_manager.rb +103 -0
  30. data/lib/decidim/term_customizer/version.rb +2 -2
  31. metadata +22 -16
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 966d2f30474a9b53bbdc7ae485ea6a726a550c4cdc111329257f259ba3eca5ee
4
- data.tar.gz: 0fec267d0fbe868fac3840aefa9843456974662eebf5701a0689de0c509c3203
3
+ metadata.gz: ddc3ca2291e44d29a3d8a137006a7ba1e94ad55490a77059eb2a3fc9837a8fa2
4
+ data.tar.gz: c387dad256fb3d261ca71926116831ebca56d89dc5d402fd2f6cfedb7bd59ed2
5
5
  SHA512:
6
- metadata.gz: aa686d4635bf8f37c1eab0a6e4d3e794be3d5ab9623de5763e29d6f40036e99563722a206e2f3532bbd2e1ff3ce30efd824cd9f41c04c3e029cb8566f9b141ec
7
- data.tar.gz: 5d760d7ea2ba5f43a8c20e5f4836ecc9c9443f1c3a68d0b667748233e1a71bcde0cf5b89341ecf742edc44c6fe76ba3f2d2791fe62e5d31133e813652110777a
6
+ metadata.gz: f141278212906ccb425248ee375d604e478e564ae4a424f17d1d3dffa9588560dbd03055df58a0f7ee5ce33d443e0ab15a35228b025d035a9f0357d723db1a9c
7
+ data.tar.gz: 76663c880f493298b93252390259a9c1f3f3dc3279b2f18b7b92b73e3af4abade578a0756c79c496167b48d78295dd02dfdc4d96bdae4e00bb21933eccd393ca
@@ -15,5 +15,7 @@ $(() => {
15
15
  }
16
16
  );
17
17
 
18
- $(".constraints-list", $fields).constraintSection();
18
+ $(".constraints-list .constraint-section", $fields).each((_i, el) => {
19
+ $(el).constraintSection();
20
+ });
19
21
  });
@@ -32,8 +32,13 @@ $(() => {
32
32
  const re = new RegExp(`(${sanitizedSearch.split(" ").join("|")})`, "gi");
33
33
  const modelId = item[0];
34
34
  const title = item[1];
35
+ // The terms are already escaped but when they are rendered to a data
36
+ // attribute, they get unescaped when those values are used. The only
37
+ // character we need to replace is the ampersand
38
+ const value = title.replace(/&/g, "&");
39
+
35
40
  const val = `${title} - ${modelId}`;
36
- return `<div class="autocomplete-suggestion" data-model-id="${modelId}" data-val="${title}">${val.replace(re, "<b>$1</b>")}</div>`;
41
+ return `<div class="autocomplete-suggestion" data-model-id="${modelId}" data-val="${value}">${val.replace(re, "<b>$1</b>")}</div>`;
37
42
  },
38
43
  onSelect: function(event, term, item) {
39
44
  const $suggestions = $search.data("sc");
@@ -6,6 +6,8 @@ module Decidim
6
6
  # A command with all the business logic when creating a new translation
7
7
  # set in the system.
8
8
  class CreateTranslation < Rectify::Command
9
+ include TermCustomizer::PluralFormsForm
10
+
9
11
  # Public: Initializes the command.
10
12
  #
11
13
  # form - A form object with the params.
@@ -24,6 +26,7 @@ module Decidim
24
26
 
25
27
  transaction do
26
28
  @translations = create_translations
29
+ create_plural_forms(@translations)
27
30
  end
28
31
 
29
32
  if @translations.length.positive?
@@ -6,6 +6,8 @@ module Decidim
6
6
  # A command with all the business logic when an admin destroys
7
7
  # translations from a translation set.
8
8
  class DestroyTranslations < Rectify::Command
9
+ include TermCustomizer::PluralFormsForm
10
+
9
11
  # Public: Initializes the command.
10
12
  #
11
13
  # form - A form object with the params.
@@ -22,6 +24,7 @@ module Decidim
22
24
  def call
23
25
  return broadcast(:invalid) unless form.valid?
24
26
 
27
+ destroy_plural_forms(form.translations)
25
28
  destroy_translations
26
29
 
27
30
  broadcast(:ok)
@@ -0,0 +1,61 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Decidim
4
+ module TermCustomizer
5
+ module Admin
6
+ # This command is executed when the user duplicates a translation set from
7
+ # the admin panel.
8
+ class DuplicateTranslationSet < Rectify::Command
9
+ # Initializes a DuplicateTranslationSet Command.
10
+ #
11
+ # form - A form object with the params.
12
+ # set - The instance of the translation set to be duplicated.
13
+ def initialize(form, set)
14
+ @form = form
15
+ @set = set
16
+ end
17
+
18
+ # Updates the blog if valid.
19
+ #
20
+ # Broadcasts :ok if successful, :invalid otherwise.
21
+ def call
22
+ return broadcast(:invalid) if form.invalid?
23
+
24
+ transaction do
25
+ duplicate_translation_set!
26
+ end
27
+
28
+ broadcast(:ok, set)
29
+ end
30
+
31
+ private
32
+
33
+ attr_reader :form, :set
34
+
35
+ def duplicate_translation_set!
36
+ duplicated = TermCustomizer::TranslationSet.create!(name: form.name)
37
+
38
+ # Add the constraints
39
+ set.constraints.each do |c|
40
+ duplicated.constraints.create!(
41
+ organization: form.current_organization,
42
+ subject: c.subject,
43
+ subject_type: c.subject_type
44
+ )
45
+ end
46
+
47
+ # Add the translations
48
+ set.translations.each do |t|
49
+ duplicated.translations.create!(
50
+ locale: t.locale,
51
+ key: t.key,
52
+ value: t.value
53
+ )
54
+ end
55
+
56
+ duplicated
57
+ end
58
+ end
59
+ end
60
+ end
61
+ end
@@ -10,6 +10,8 @@ module Decidim
10
10
  # supported import formats or a ZIP file containing a supported import
11
11
  # file.
12
12
  class ImportSetTranslations < Rectify::Command
13
+ include TermCustomizer::PluralFormsForm
14
+
13
15
  # Public: Initializes the command.
14
16
  #
15
17
  # form - A form object with the params.
@@ -30,6 +32,7 @@ module Decidim
30
32
  return broadcast(:invalid) if form.invalid?
31
33
 
32
34
  @translations = import_translations
35
+ create_plural_forms(@translations)
33
36
 
34
37
  if @translations.length.positive?
35
38
  broadcast(:ok, @translations)
@@ -6,6 +6,8 @@ module Decidim
6
6
  # A command with all the business logic when creating new translations
7
7
  # from the keys submitted through the form.
8
8
  class ImportTranslationKeys < Rectify::Command
9
+ include TermCustomizer::PluralFormsForm
10
+
9
11
  # Public: Initializes the command.
10
12
  #
11
13
  # form - A form object with the params.
@@ -24,6 +26,7 @@ module Decidim
24
26
 
25
27
  transaction do
26
28
  @translations = create_translations
29
+ create_plural_forms(@translations)
27
30
  end
28
31
 
29
32
  if @translations.length.positive?
@@ -6,6 +6,8 @@ module Decidim
6
6
  # This command is executed when the user changes a translation from the
7
7
  # admin panel.
8
8
  class UpdateTranslation < Rectify::Command
9
+ include TermCustomizer::PluralFormsForm
10
+
9
11
  # Public: Initializes the command.
10
12
  #
11
13
  # form - A form object with the params.
@@ -25,7 +27,8 @@ module Decidim
25
27
  return broadcast(:invalid) if form.invalid?
26
28
 
27
29
  transaction do
28
- update_translations!
30
+ @translations = update_translations!
31
+ create_plural_forms(@translations)
29
32
  end
30
33
 
31
34
  broadcast(:ok, translation)
@@ -36,7 +39,7 @@ module Decidim
36
39
  attr_reader :form, :translation
37
40
 
38
41
  def update_translations!
39
- form.value.each do |locale, value|
42
+ form.value.map do |locale, value|
40
43
  l_translation = TermCustomizer::Translation.find_by(
41
44
  translation_set: translation.translation_set,
42
45
  key: translation.key,
@@ -50,13 +53,15 @@ module Decidim
50
53
  locale: locale
51
54
  )
52
55
  else
53
- TermCustomizer::Translation.create!(
56
+ l_translation = TermCustomizer::Translation.create!(
54
57
  translation_set: translation.translation_set,
55
58
  key: form.key,
56
59
  value: value,
57
60
  locale: locale
58
61
  )
59
62
  end
63
+
64
+ l_translation
60
65
  end
61
66
  end
62
67
  end
@@ -16,6 +16,7 @@ module Decidim
16
16
  enforce_permission_to :create, :translation
17
17
  @form = form(TranslationKeyImportForm).from_params(
18
18
  params,
19
+ current_organization: current_organization,
19
20
  translation_set: set
20
21
  )
21
22
 
@@ -40,7 +41,7 @@ module Decidim
40
41
  translations = directory.translations_search(params[:term])
41
42
  translations.reject! { |k| reject_keys.include?(k) }
42
43
 
43
- render json: translations.map { |k, v| [k, v] }
44
+ render json: translations.map { |k, v| [k, ERB::Util.html_escape(v)] }
44
45
  end
45
46
 
46
47
  private
@@ -75,6 +75,39 @@ module Decidim
75
75
  redirect_to translation_sets_path
76
76
  end
77
77
 
78
+ def duplicate
79
+ enforce_permission_to :create, :translation_set
80
+
81
+ # Automatically generate the name
82
+ name_attrs = {}.tap do |hash|
83
+ current_organization.available_locales.each do |locale|
84
+ hash["name_#{locale}"] = I18n.t(
85
+ "translation_sets.duplicate.copied_set_name",
86
+ name: set.name[locale],
87
+ locale: locale,
88
+ scope: "decidim.term_customizer.admin"
89
+ )
90
+ end
91
+ end
92
+
93
+ @form = form(TranslationSetForm).from_params(
94
+ params.merge(name_attrs),
95
+ current_organization: current_organization
96
+ )
97
+
98
+ DuplicateTranslationSet.call(@form, set) do
99
+ on(:ok) do
100
+ flash[:notice] = I18n.t("translation_sets.duplicate.success", scope: "decidim.term_customizer.admin")
101
+ redirect_to translation_sets_path
102
+ end
103
+
104
+ on(:invalid) do
105
+ flash.now[:alert] = I18n.t("translation_sets.duplicate.error", scope: "decidim.term_customizer.admin")
106
+ redirect_to translation_sets_path
107
+ end
108
+ end
109
+ end
110
+
78
111
  private
79
112
 
80
113
  def sets
@@ -69,10 +69,13 @@ module Decidim
69
69
  enforce_permission_to :destroy, :translation, translation: translation
70
70
 
71
71
  # Destroy all locales of the translation key
72
- Decidim::TermCustomizer::Translation.where(
72
+ pfm = TermCustomizer::PluralFormsManager.new(current_organization)
73
+ translations = Decidim::TermCustomizer::Translation.where(
73
74
  translation_set: set,
74
75
  key: translation.key
75
- ).destroy_all
76
+ )
77
+ pfm.destroy!(translations)
78
+ translations.destroy_all
76
79
 
77
80
  flash[:notice] = I18n.t("translations.destroy.success", scope: "decidim.term_customizer.admin")
78
81
 
@@ -99,7 +102,10 @@ module Decidim
99
102
  def import
100
103
  enforce_permission_to :import, :translation_set, translation_set: set
101
104
 
102
- @import = form(Admin::TranslationsImportForm).from_params(params)
105
+ @import = form(Admin::TranslationsImportForm).from_params(
106
+ params,
107
+ current_organization: current_organization
108
+ )
103
109
  ImportSetTranslations.call(@import, set) do
104
110
  on(:ok) do
105
111
  flash[:notice] = I18n.t("translations.import.success", scope: "decidim.term_customizer.admin")
@@ -38,7 +38,10 @@ module Decidim
38
38
  def set_form
39
39
  @form = form(Admin::TranslationsDestroyForm).from_params(
40
40
  params
41
- ).with_context(translation_set: set)
41
+ ).with_context(
42
+ current_organization: current_organization,
43
+ translation_set: set
44
+ )
42
45
  end
43
46
 
44
47
  def translation_set
@@ -46,6 +46,10 @@
46
46
  <%= icon_link_to "pencil", edit_translation_set_path(set), t("actions.configure", scope: "decidim.admin"), class: "action-icon--new" %>
47
47
  <% end %>
48
48
 
49
+ <% if allowed_to? :create, :translation_set %>
50
+ <%= icon_link_to "fork", duplicate_translation_set_path(set), t("actions.duplicate", scope: "decidim.term_customizer.admin"), method: :post, class: "action-icon--new", data: { confirm: t("actions.confirm_duplicate", scope: "decidim.term_customizer.admin") } %>
51
+ <% end %>
52
+
49
53
  <% if allowed_to? :destroy, :translation_set, translation_set: set %>
50
54
  <%= icon_link_to "circle-x", translation_set_path(set), t("actions.destroy", scope: "decidim.admin"), method: :delete, class: "action-icon--remove", data: { confirm: t("actions.confirm_destroy", scope: "decidim.admin") } %>
51
55
  <% end %>
@@ -26,6 +26,8 @@ ca:
26
26
  back: Enrere
27
27
  cancel: Cancel
28
28
  clear_cache: Clear cache
29
+ confirm_duplicate: Are you sure you want to duplicate this set?
30
+ duplicate: Duplicate
29
31
  help: Help
30
32
  import: Import
31
33
  new_translation: Nova traducció
@@ -62,6 +64,10 @@ ca:
62
64
  success: Traducció creada amb èxit
63
65
  destroy:
64
66
  success: La traducció ha estat esborrada amb èxit.
67
+ duplicate:
68
+ copied_set_name: Copy of %{name}
69
+ error: Error duplicating translation set.
70
+ success: Translation set successfully duplicated.
65
71
  edit:
66
72
  save: Guardar
67
73
  title: Set/Joc de traducció
@@ -27,6 +27,8 @@ en:
27
27
  back: Back
28
28
  cancel: Cancel
29
29
  clear_cache: Clear cache
30
+ confirm_duplicate: Are you sure you want to duplicate this set?
31
+ duplicate: Duplicate
30
32
  help: Help
31
33
  import: Import
32
34
  new_translation: New translation
@@ -70,6 +72,10 @@ en:
70
72
  success: Translation set successfully created.
71
73
  destroy:
72
74
  success: Translation set successfully deleted.
75
+ duplicate:
76
+ copied_set_name: Copy of %{name}
77
+ error: Error duplicating translation set.
78
+ success: Translation set successfully duplicated.
73
79
  edit:
74
80
  save: Save
75
81
  title: Translation set
@@ -26,6 +26,8 @@ es:
26
26
  back: Volver
27
27
  cancel: Cancel
28
28
  clear_cache: Clear cache
29
+ confirm_duplicate: Are you sure you want to duplicate this set?
30
+ duplicate: Duplicate
29
31
  help: Help
30
32
  import: Import
31
33
  new_translation: Nueva traducción
@@ -62,6 +64,10 @@ es:
62
64
  success: Gurpo de tranducciones creado exitosamente.
63
65
  destroy:
64
66
  success: El grupo de traducciones ha sido creado exitosamente.
67
+ duplicate:
68
+ copied_set_name: Copy of %{name}
69
+ error: Error duplicating translation set.
70
+ success: Translation set successfully duplicated.
65
71
  edit:
66
72
  save: Guardar
67
73
  title: Grupo de traducciones
@@ -26,6 +26,8 @@ fi:
26
26
  back: Takaisin
27
27
  cancel: Peruuta
28
28
  clear_cache: Tyhjennä välimuisti
29
+ confirm_duplicate: Haluatko varmasti kopioida tämän paketin?
30
+ duplicate: Kopioi
29
31
  help: Ohjeet
30
32
  import: Tuo
31
33
  new_translation: Uusi käännös
@@ -62,6 +64,10 @@ fi:
62
64
  success: Käännöspaketin luonti onnistui.
63
65
  destroy:
64
66
  success: Käännöspaketin poisto onnistui.
67
+ duplicate:
68
+ copied_set_name: 'Kopio: %{name}'
69
+ error: Käännöspaketin kopiointi epäonnistui.
70
+ success: Käännöspaketin kopiointi onnistui.
65
71
  edit:
66
72
  save: Tallenna
67
73
  title: Käännöspaketti
@@ -26,6 +26,8 @@ fr:
26
26
  back: Retour
27
27
  cancel: Cancel
28
28
  clear_cache: Purger le cache
29
+ confirm_duplicate: Are you sure you want to duplicate this set?
30
+ duplicate: Duplicate
29
31
  help: Help
30
32
  import: Import
31
33
  new_translation: Nouvelle traduction
@@ -62,6 +64,10 @@ fr:
62
64
  success: Jeu de traduction créé avec succès.
63
65
  destroy:
64
66
  success: Le jeu de traduction a été supprimé.
67
+ duplicate:
68
+ copied_set_name: Copy of %{name}
69
+ error: Error duplicating translation set.
70
+ success: Translation set successfully duplicated.
65
71
  edit:
66
72
  save: Sauvegarder
67
73
  title: Jeu de traductions
@@ -26,6 +26,8 @@ sv:
26
26
  back: Back
27
27
  cancel: Cancel
28
28
  clear_cache: Clear cache
29
+ confirm_duplicate: Are you sure you want to duplicate this set?
30
+ duplicate: Duplicate
29
31
  help: Help
30
32
  import: Import
31
33
  new_translation: New translation
@@ -62,6 +64,10 @@ sv:
62
64
  success: Translation set successfully created.
63
65
  destroy:
64
66
  success: Translation set successfully deleted.
67
+ duplicate:
68
+ copied_set_name: Copy of %{name}
69
+ error: Error duplicating translation set.
70
+ success: Translation set successfully duplicated.
65
71
  edit:
66
72
  save: Save
67
73
  title: Translation set
@@ -4,12 +4,17 @@ require_relative "term_customizer/version"
4
4
  require_relative "term_customizer/engine"
5
5
  require_relative "term_customizer/admin"
6
6
  require_relative "term_customizer/admin_engine"
7
+ require_relative "term_customizer/context"
7
8
 
8
9
  module Decidim
9
10
  module TermCustomizer
11
+ include ActiveSupport::Configurable
12
+
10
13
  autoload :I18nBackend, "decidim/term_customizer/i18n_backend"
11
14
  autoload :Import, "decidim/term_customizer/import"
12
15
  autoload :Loader, "decidim/term_customizer/loader"
16
+ autoload :PluralFormsForm, "decidim/term_customizer/plural_forms_form"
17
+ autoload :PluralFormsManager, "decidim/term_customizer/plural_forms_manager"
13
18
  autoload :Resolver, "decidim/term_customizer/resolver"
14
19
  autoload :TranslationDirectory, "decidim/term_customizer/translation_directory"
15
20
  autoload :TranslationImportCollection, "decidim/term_customizer/translation_import_collection"
@@ -19,6 +24,18 @@ module Decidim
19
24
 
20
25
  EMPTY_HASH = {}.freeze
21
26
 
27
+ # In case you want to customize the context detection for the controllers
28
+ # and views, configure your own context resolver.
29
+ config_accessor :controller_context_class do
30
+ Decidim::TermCustomizer::Context::ControllerContext
31
+ end
32
+
33
+ # In case you want to customize the context detection for the jobs,
34
+ # configure your own context resolver.
35
+ config_accessor :job_context_class do
36
+ Decidim::TermCustomizer::Context::JobContext
37
+ end
38
+
22
39
  class << self
23
40
  attr_accessor :loader
24
41
  end
@@ -10,6 +10,10 @@ module Decidim
10
10
 
11
11
  routes do
12
12
  resources :translation_sets, path: :sets, except: [:show] do
13
+ member do
14
+ post :duplicate
15
+ end
16
+
13
17
  resources :translations, except: [:show] do
14
18
  collection do
15
19
  post :export
@@ -0,0 +1,11 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Decidim
4
+ module TermCustomizer
5
+ module Context
6
+ autoload :Base, "decidim/term_customizer/context/base"
7
+ autoload :ControllerContext, "decidim/term_customizer/context/controller_context"
8
+ autoload :JobContext, "decidim/term_customizer/context/job_context"
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,33 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Decidim
4
+ module TermCustomizer
5
+ module Context
6
+ # A context object resolves and stores the translation context for
7
+ # different application contexts. Contexts can be e.g.
8
+ # - Controller context, which is used to display translations in
9
+ # controller messages and the views.
10
+ # - Job context, which is used to display messages within jobs, mainly
11
+ # when sending emails.
12
+ #
13
+ # The initialization method gets the data for the context which is used
14
+ # to resolve the translation context objects (organization, participatory
15
+ # space and component). These are then used to load the correct
16
+ # translations for each context based on the translation set constraints.
17
+ class Base
18
+ attr_reader :organization, :space, :component
19
+
20
+ def initialize(data)
21
+ @data = data
22
+
23
+ # Implement the resolve! method in the sub-classes
24
+ resolve!
25
+ end
26
+
27
+ protected
28
+
29
+ attr_reader :data
30
+ end
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,29 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Decidim
4
+ module TermCustomizer
5
+ module Context
6
+ class ControllerContext < Base
7
+ def resolve!
8
+ env = data[:headers].env
9
+ controller = env["action_controller.instance"]
10
+
11
+ @organization = env["decidim.current_organization"]
12
+
13
+ # E.g. at the participatory process controller the
14
+ # `decidim.current_participatory_space` environment variable has not
15
+ # been set. Therefore, we need to fetch it directly from the
16
+ # controller using its private method.
17
+ @space =
18
+ if controller.respond_to?(:current_participatory_space, true)
19
+ controller.send(:current_participatory_space)
20
+ else
21
+ env["decidim.current_participatory_space"]
22
+ end
23
+
24
+ @component = env["decidim.current_component"]
25
+ end
26
+ end
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,53 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Decidim
4
+ module TermCustomizer
5
+ module Context
6
+ class JobContext < Base
7
+ def resolve!
8
+ # Figure out the organization and user through the job arguments if
9
+ # passed for the job.
10
+ user = nil
11
+ data[:job].arguments.each do |arg|
12
+ @organization ||= organization_from_argument(arg)
13
+ @space ||= space_from_argument(arg)
14
+ @component ||= component_from_argument(arg)
15
+ user ||= arg if arg.is_a?(Decidim::User)
16
+ end
17
+
18
+ # In case a component was found, define the space as the component
19
+ # space to avoid any conflicts.
20
+ @space = component.participatory_space if component
21
+
22
+ # In case a space was found, define the organization as the space
23
+ # organization to avoid any conflicts.
24
+ @organization = space.organization if space
25
+
26
+ # In case an organization could not be resolved any other way, check
27
+ # it through the user (if the user was passed).
28
+ @organization ||= user.organization if user
29
+ end
30
+
31
+ protected
32
+
33
+ def organization_from_argument(arg)
34
+ return arg if arg.is_a?(Decidim::Organization)
35
+
36
+ arg.organization if arg.respond_to?(:organization)
37
+ end
38
+
39
+ def space_from_argument(arg)
40
+ return arg if arg.is_a?(Decidim::Participable)
41
+
42
+ arg.participatory_space if arg.respond_to?(:participatory_space)
43
+ end
44
+
45
+ def component_from_argument(arg)
46
+ return arg if arg.is_a?(Decidim::Component)
47
+
48
+ arg.component if arg.respond_to?(:component)
49
+ end
50
+ end
51
+ end
52
+ end
53
+ end
@@ -17,25 +17,13 @@ module Decidim
17
17
  # done through a notification to get access to the `current_*`
18
18
  # environment variables within Decidim.
19
19
  ActiveSupport::Notifications.subscribe "start_processing.action_controller" do |_name, _started, _finished, _unique_id, data|
20
- env = data[:headers].env
21
- controller = data[:headers].env["action_controller.instance"]
22
-
23
- # E.g. at the participatory process controller the
24
- # `decidim.current_participatory_space` environment variable has not
25
- # been set. Therefore, we need to fetch it directly from the
26
- # controller using its private method.
27
- space =
28
- if controller.respond_to?(:current_participatory_space, true)
29
- controller.send(:current_participatory_space)
30
- else
31
- env["decidim.current_participatory_space"]
32
- end
20
+ context = TermCustomizer.controller_context_class.new(data)
33
21
 
34
22
  # Create a new resolver instance within the current request scope
35
23
  resolver = Resolver.new(
36
- env["decidim.current_organization"],
37
- space,
38
- env["decidim.current_component"]
24
+ context.organization,
25
+ context.space,
26
+ context.component
39
27
  )
40
28
 
41
29
  # Create the loader for the backend to fetch the translations from
@@ -58,22 +46,15 @@ module Decidim
58
46
  # job that may be fired by another job (i.e. the notification job is
59
47
  # always performed last).
60
48
  ActiveSupport::Notifications.subscribe "perform_start.active_job" do |_name, _started, _finished, _unique_id, data|
61
- # Figure out the organization and user through the job arguments if
62
- # passed for the job.
63
- organization = nil
64
- user = nil
65
- data[:job].arguments.each do |arg|
66
- organization = arg if arg.is_a?(Decidim::Organization)
67
- user = arg if arg.is_a?(Decidim::User)
68
- end
69
-
70
- # In case an organization was not passed for the job, check it through
71
- # the user.
72
- organization = user.organization if organization.nil? && user
49
+ context = TermCustomizer.job_context_class.new(data)
73
50
 
74
51
  # Create resolver for the target organization or global context in
75
52
  # case organization was not found
76
- resolver = Resolver.new(organization, nil, nil)
53
+ resolver = Resolver.new(
54
+ context.organization,
55
+ context.space,
56
+ context.component
57
+ )
77
58
 
78
59
  # Create the loader for the backend to fetch the translations from
79
60
  TermCustomizer.loader = Loader.new(resolver)
@@ -0,0 +1,23 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Decidim
4
+ module TermCustomizer
5
+ module PluralFormsForm
6
+ private
7
+
8
+ def create_plural_forms(translations)
9
+ plural_forms_manager.fill!(translations)
10
+ end
11
+
12
+ def destroy_plural_forms(translations)
13
+ plural_forms_manager.destroy!(translations)
14
+ end
15
+
16
+ def plural_forms_manager
17
+ @plural_forms_manager ||= TermCustomizer::PluralFormsManager.new(
18
+ form.current_organization
19
+ )
20
+ end
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,103 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Decidim
4
+ module TermCustomizer
5
+ # Checks that all the plural forms are in the database for the given keys.
6
+ class PluralFormsManager
7
+ attr_reader :organization
8
+
9
+ @plural_keys = [:zero, :one, :few, :other]
10
+
11
+ class << self
12
+ attr_accessor :plural_keys
13
+ end
14
+
15
+ def initialize(organization)
16
+ @organization = organization
17
+ @default_locale = organization.default_locale
18
+ end
19
+
20
+ def fill!(translations)
21
+ each_plural_form(translations) do |translation, key|
22
+ add_locales_for!(translation, key)
23
+ end
24
+ end
25
+
26
+ def destroy!(translations)
27
+ each_plural_form(translations) do |translation, key|
28
+ destroy_locales_for!(translation, key)
29
+ end
30
+ end
31
+
32
+ private
33
+
34
+ attr_reader :default_locale
35
+
36
+ def each_plural_form(translations)
37
+ keys = self.class.plural_keys.map(&:to_s)
38
+ translations.each do |tr|
39
+ # Check that the last part of the translation key matches with some
40
+ # of the plural translation keys.
41
+ next unless tr.key =~ /\.(#{keys.join("|")})$/
42
+
43
+ parts = tr.key.split(".")
44
+ plural_part = parts.pop
45
+ base_part = parts.join(".")
46
+
47
+ # If it's not a hash, it's not a plural translation
48
+ next unless I18n.exists?(base_part, default_locale)
49
+ next unless I18n.t(base_part, locale: default_locale).is_a?(Hash)
50
+
51
+ keys.each do |plural_key|
52
+ # Do not check for the translation itself
53
+ next if plural_part == plural_key
54
+
55
+ full_plural_key = "#{base_part}.#{plural_key}"
56
+
57
+ # Check that the translation actually exists, no need to process if
58
+ # it does not exist.
59
+ next unless I18n.exists?(full_plural_key, default_locale)
60
+
61
+ yield tr, full_plural_key
62
+ end
63
+ end
64
+ end
65
+
66
+ def add_locales_for!(translation, target_key)
67
+ organization.available_locales.each do |locale|
68
+ # Skip adding the plural form for the translation itself
69
+ next if target_key == translation.key
70
+
71
+ # Check that the translation is not already added in the set
72
+ next if translation.translation_set.translations.where(
73
+ key: target_key,
74
+ locale: locale
75
+ ).any?
76
+
77
+ # Add the plural form
78
+ translation.translation_set.translations.create!(
79
+ key: target_key,
80
+ locale: locale,
81
+ value: I18n.t(target_key, locale: locale, default: "")
82
+ )
83
+ end
84
+ end
85
+
86
+ def destroy_locales_for!(translation, target_key)
87
+ organization.available_locales.each do |locale|
88
+ # Skip deleting the plural form for the translation itself
89
+ next if target_key == translation.key
90
+
91
+ # Find the plural form the plural form
92
+ target = translation.translation_set.translations.find_by(
93
+ key: target_key,
94
+ locale: locale
95
+ )
96
+ next unless target
97
+
98
+ target.destroy!
99
+ end
100
+ end
101
+ end
102
+ end
103
+ end
@@ -2,7 +2,7 @@
2
2
 
3
3
  module Decidim
4
4
  module TermCustomizer
5
- VERSION = "0.17.1"
6
- DECIDIM_VERSION = "~> 0.17.0"
5
+ VERSION = "0.18.0"
6
+ DECIDIM_VERSION = "~> 0.18.0"
7
7
  end
8
8
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: decidim-term_customizer
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.17.1
4
+ version: 0.18.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Antti Hukkanen
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2019-04-30 00:00:00.000000000 Z
11
+ date: 2019-08-28 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: decidim-admin
@@ -16,28 +16,28 @@ dependencies:
16
16
  requirements:
17
17
  - - "~>"
18
18
  - !ruby/object:Gem::Version
19
- version: 0.17.0
19
+ version: 0.18.0
20
20
  type: :runtime
21
21
  prerelease: false
22
22
  version_requirements: !ruby/object:Gem::Requirement
23
23
  requirements:
24
24
  - - "~>"
25
25
  - !ruby/object:Gem::Version
26
- version: 0.17.0
26
+ version: 0.18.0
27
27
  - !ruby/object:Gem::Dependency
28
28
  name: decidim-core
29
29
  requirement: !ruby/object:Gem::Requirement
30
30
  requirements:
31
31
  - - "~>"
32
32
  - !ruby/object:Gem::Version
33
- version: 0.17.0
33
+ version: 0.18.0
34
34
  type: :runtime
35
35
  prerelease: false
36
36
  version_requirements: !ruby/object:Gem::Requirement
37
37
  requirements:
38
38
  - - "~>"
39
39
  - !ruby/object:Gem::Version
40
- version: 0.17.0
40
+ version: 0.18.0
41
41
  - !ruby/object:Gem::Dependency
42
42
  name: dalli
43
43
  requirement: !ruby/object:Gem::Requirement
@@ -64,56 +64,56 @@ dependencies:
64
64
  requirements:
65
65
  - - "~>"
66
66
  - !ruby/object:Gem::Version
67
- version: 0.17.0
67
+ version: 0.18.0
68
68
  type: :development
69
69
  prerelease: false
70
70
  version_requirements: !ruby/object:Gem::Requirement
71
71
  requirements:
72
72
  - - "~>"
73
73
  - !ruby/object:Gem::Version
74
- version: 0.17.0
74
+ version: 0.18.0
75
75
  - !ruby/object:Gem::Dependency
76
76
  name: decidim-dev
77
77
  requirement: !ruby/object:Gem::Requirement
78
78
  requirements:
79
79
  - - "~>"
80
80
  - !ruby/object:Gem::Version
81
- version: 0.17.0
81
+ version: 0.18.0
82
82
  type: :development
83
83
  prerelease: false
84
84
  version_requirements: !ruby/object:Gem::Requirement
85
85
  requirements:
86
86
  - - "~>"
87
87
  - !ruby/object:Gem::Version
88
- version: 0.17.0
88
+ version: 0.18.0
89
89
  - !ruby/object:Gem::Dependency
90
90
  name: decidim-participatory_processes
91
91
  requirement: !ruby/object:Gem::Requirement
92
92
  requirements:
93
93
  - - "~>"
94
94
  - !ruby/object:Gem::Version
95
- version: 0.17.0
95
+ version: 0.18.0
96
96
  type: :development
97
97
  prerelease: false
98
98
  version_requirements: !ruby/object:Gem::Requirement
99
99
  requirements:
100
100
  - - "~>"
101
101
  - !ruby/object:Gem::Version
102
- version: 0.17.0
102
+ version: 0.18.0
103
103
  - !ruby/object:Gem::Dependency
104
104
  name: decidim-proposals
105
105
  requirement: !ruby/object:Gem::Requirement
106
106
  requirements:
107
107
  - - "~>"
108
108
  - !ruby/object:Gem::Version
109
- version: 0.17.0
109
+ version: 0.18.0
110
110
  type: :development
111
111
  prerelease: false
112
112
  version_requirements: !ruby/object:Gem::Requirement
113
113
  requirements:
114
114
  - - "~>"
115
115
  - !ruby/object:Gem::Version
116
- version: 0.17.0
116
+ version: 0.18.0
117
117
  description: Adds a UI to customize the terms and limit the customizations to specific
118
118
  places.
119
119
  email:
@@ -135,6 +135,7 @@ files:
135
135
  - app/commands/decidim/term_customizer/admin/create_translation.rb
136
136
  - app/commands/decidim/term_customizer/admin/create_translation_set.rb
137
137
  - app/commands/decidim/term_customizer/admin/destroy_translations.rb
138
+ - app/commands/decidim/term_customizer/admin/duplicate_translation_set.rb
138
139
  - app/commands/decidim/term_customizer/admin/import_set_translations.rb
139
140
  - app/commands/decidim/term_customizer/admin/import_translation_keys.rb
140
141
  - app/commands/decidim/term_customizer/admin/update_translation.rb
@@ -190,6 +191,10 @@ files:
190
191
  - lib/decidim/term_customizer.rb
191
192
  - lib/decidim/term_customizer/admin.rb
192
193
  - lib/decidim/term_customizer/admin_engine.rb
194
+ - lib/decidim/term_customizer/context.rb
195
+ - lib/decidim/term_customizer/context/base.rb
196
+ - lib/decidim/term_customizer/context/controller_context.rb
197
+ - lib/decidim/term_customizer/context/job_context.rb
193
198
  - lib/decidim/term_customizer/engine.rb
194
199
  - lib/decidim/term_customizer/i18n_backend.rb
195
200
  - lib/decidim/term_customizer/import.rb
@@ -202,6 +207,8 @@ files:
202
207
  - lib/decidim/term_customizer/import/readers/json.rb
203
208
  - lib/decidim/term_customizer/import/readers/xls.rb
204
209
  - lib/decidim/term_customizer/loader.rb
210
+ - lib/decidim/term_customizer/plural_forms_form.rb
211
+ - lib/decidim/term_customizer/plural_forms_manager.rb
205
212
  - lib/decidim/term_customizer/resolver.rb
206
213
  - lib/decidim/term_customizer/test/factories.rb
207
214
  - lib/decidim/term_customizer/translation_directory.rb
@@ -229,8 +236,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
229
236
  - !ruby/object:Gem::Version
230
237
  version: '0'
231
238
  requirements: []
232
- rubyforge_project:
233
- rubygems_version: 2.7.6
239
+ rubygems_version: 3.0.3
234
240
  signing_key:
235
241
  specification_version: 4
236
242
  summary: Provides possibility to customize Decidim's localized terms.