decidim-term_customizer 0.17.1 → 0.18.0

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