decidim-templates 0.23.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.

Potentially problematic release.


This version of decidim-templates might be problematic. Click here for more details.

Files changed (93) hide show
  1. checksums.yaml +7 -0
  2. data/LICENSE-AGPLv3.txt +661 -0
  3. data/README.md +31 -0
  4. data/Rakefile +9 -0
  5. data/app/assets/config/decidim_templates_manifest.css +3 -0
  6. data/app/assets/config/decidim_templates_manifest.js +0 -0
  7. data/app/assets/images/decidim/templates/icon.svg +1 -0
  8. data/app/assets/stylesheets/decidim/templates/templates.scss +14 -0
  9. data/app/commands/decidim/templates/admin/apply_questionnaire_template.rb +74 -0
  10. data/app/commands/decidim/templates/admin/copy_questionnaire_template.rb +79 -0
  11. data/app/commands/decidim/templates/admin/create_questionnaire_template.rb +34 -0
  12. data/app/commands/decidim/templates/admin/destroy_template.rb +41 -0
  13. data/app/commands/decidim/templates/admin/update_template.rb +35 -0
  14. data/app/controllers/decidim/templates/admin/application_controller.rb +34 -0
  15. data/app/controllers/decidim/templates/admin/concerns/templatable.rb +46 -0
  16. data/app/controllers/decidim/templates/admin/questionnaire_templates/questionnaires_controller.rb +38 -0
  17. data/app/controllers/decidim/templates/admin/questionnaire_templates_controller.rb +156 -0
  18. data/app/controllers/decidim/templates/application_controller.rb +13 -0
  19. data/app/forms/decidim/templates/admin/template_form.rb +17 -0
  20. data/app/helpers/decidim/templates/admin/templates_helper.rb +32 -0
  21. data/app/helpers/decidim/templates/application_helper.rb +10 -0
  22. data/app/models/concerns/decidim/templates/templatable.rb +24 -0
  23. data/app/models/decidim/templates/application_record.rb +10 -0
  24. data/app/models/decidim/templates/template.rb +33 -0
  25. data/app/permissions/decidim/templates/admin/permissions.rb +26 -0
  26. data/app/views/decidim/templates/admin/questionnaire_templates/_choose.html.erb +35 -0
  27. data/app/views/decidim/templates/admin/questionnaire_templates/_form.html.erb +16 -0
  28. data/app/views/decidim/templates/admin/questionnaire_templates/_preview.html.erb +73 -0
  29. data/app/views/decidim/templates/admin/questionnaire_templates/edit.html.erb +20 -0
  30. data/app/views/decidim/templates/admin/questionnaire_templates/index.html.erb +52 -0
  31. data/app/views/decidim/templates/admin/questionnaire_templates/new.html.erb +7 -0
  32. data/app/views/decidim/templates/admin/questionnaire_templates/preview.js.erb +11 -0
  33. data/app/views/layouts/decidim/admin/templates.html.erb +21 -0
  34. data/config/locales/am-ET.yml +1 -0
  35. data/config/locales/ar.yml +1 -0
  36. data/config/locales/bg.yml +1 -0
  37. data/config/locales/ca.yml +60 -0
  38. data/config/locales/cs.yml +60 -0
  39. data/config/locales/da.yml +1 -0
  40. data/config/locales/de.yml +54 -0
  41. data/config/locales/el.yml +1 -0
  42. data/config/locales/en.yml +61 -0
  43. data/config/locales/eo.yml +1 -0
  44. data/config/locales/es-MX.yml +60 -0
  45. data/config/locales/es-PY.yml +60 -0
  46. data/config/locales/es.yml +60 -0
  47. data/config/locales/et.yml +1 -0
  48. data/config/locales/eu.yml +1 -0
  49. data/config/locales/fi-plain.yml +60 -0
  50. data/config/locales/fi.yml +60 -0
  51. data/config/locales/fr-CA.yml +60 -0
  52. data/config/locales/fr.yml +60 -0
  53. data/config/locales/ga-IE.yml +1 -0
  54. data/config/locales/gl.yml +1 -0
  55. data/config/locales/hr.yml +1 -0
  56. data/config/locales/hu.yml +1 -0
  57. data/config/locales/id-ID.yml +1 -0
  58. data/config/locales/is-IS.yml +1 -0
  59. data/config/locales/it.yml +60 -0
  60. data/config/locales/ja.yml +60 -0
  61. data/config/locales/ko-KR.yml +1 -0
  62. data/config/locales/ko.yml +1 -0
  63. data/config/locales/lt.yml +1 -0
  64. data/config/locales/lv.yml +1 -0
  65. data/config/locales/mt.yml +1 -0
  66. data/config/locales/nl.yml +60 -0
  67. data/config/locales/no.yml +5 -0
  68. data/config/locales/om-ET.yml +1 -0
  69. data/config/locales/pl.yml +1 -0
  70. data/config/locales/pt-BR.yml +1 -0
  71. data/config/locales/pt.yml +1 -0
  72. data/config/locales/ro-RO.yml +1 -0
  73. data/config/locales/ru.yml +1 -0
  74. data/config/locales/sk.yml +1 -0
  75. data/config/locales/sl.yml +1 -0
  76. data/config/locales/so-SO.yml +1 -0
  77. data/config/locales/sr-CS.yml +1 -0
  78. data/config/locales/sv.yml +1 -0
  79. data/config/locales/ti-ER.yml +1 -0
  80. data/config/locales/tr-TR.yml +1 -0
  81. data/config/locales/uk.yml +1 -0
  82. data/config/locales/vi-VN.yml +1 -0
  83. data/config/locales/vi.yml +1 -0
  84. data/config/locales/zh-CN.yml +60 -0
  85. data/config/locales/zh-TW.yml +1 -0
  86. data/lib/decidim/templates.rb +12 -0
  87. data/lib/decidim/templates/admin.rb +10 -0
  88. data/lib/decidim/templates/admin_engine.rb +54 -0
  89. data/lib/decidim/templates/engine.rb +23 -0
  90. data/lib/decidim/templates/test/factories.rb +27 -0
  91. data/lib/decidim/templates/test/shared_examples/uses_questionnaire_templates.rb +108 -0
  92. data/lib/decidim/templates/version.rb +10 -0
  93. metadata +191 -0
@@ -0,0 +1,156 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Decidim
4
+ module Templates
5
+ module Admin
6
+ # Controller that allows managing templates.
7
+ #
8
+ class QuestionnaireTemplatesController < Decidim::Templates::Admin::ApplicationController
9
+ include Decidim::TranslatableAttributes
10
+
11
+ skip_before_action :verify_authenticity_token, only: :preview
12
+
13
+ helper_method :template
14
+
15
+ def index
16
+ enforce_permission_to :index, :templates
17
+ @templates = collection
18
+
19
+ respond_to do |format|
20
+ format.html { render :index }
21
+ format.json do
22
+ term = params[:term]
23
+
24
+ @templates = search(term)
25
+
26
+ render json: @templates.map { |t| { value: t.id, label: translated_attribute(t.name) } }
27
+ end
28
+ end
29
+ end
30
+
31
+ def new
32
+ enforce_permission_to :create, :template
33
+ @form = form(TemplateForm).instance
34
+ end
35
+
36
+ def create
37
+ enforce_permission_to :create, :template
38
+
39
+ @form = form(TemplateForm).from_params(params)
40
+
41
+ CreateQuestionnaireTemplate.call(@form) do
42
+ on(:ok) do |questionnaire_template|
43
+ flash[:notice] = I18n.t("templates.create.success", scope: "decidim.admin")
44
+ redirect_to edit_questionnaire_template_path(questionnaire_template)
45
+ end
46
+
47
+ on(:invalid) do
48
+ flash.now[:alert] = I18n.t("templates.create.error", scope: "decidim.admin")
49
+ render :new
50
+ end
51
+ end
52
+ end
53
+
54
+ def copy
55
+ enforce_permission_to :copy, :template
56
+
57
+ CopyQuestionnaireTemplate.call(template) do
58
+ on(:ok) do
59
+ flash[:notice] = I18n.t("templates.copy.success", scope: "decidim.admin")
60
+ redirect_to action: :index
61
+ end
62
+
63
+ on(:invalid) do
64
+ flash[:alert] = I18n.t("templates.copy.error", scope: "decidim.admin")
65
+ redirect_to action: :index
66
+ end
67
+ end
68
+ end
69
+
70
+ def edit
71
+ enforce_permission_to :update, :template, template: template
72
+ @form = form(TemplateForm).from_model(template)
73
+ @preview_form = form(Decidim::Forms::QuestionnaireForm).from_model(template.templatable)
74
+ end
75
+
76
+ def update
77
+ enforce_permission_to :update, :template, template: template
78
+ @form = form(TemplateForm).from_params(params)
79
+ UpdateTemplate.call(template, @form, current_user) do
80
+ on(:ok) do |questionnaire_template|
81
+ flash[:notice] = I18n.t("templates.update.success", scope: "decidim.admin")
82
+ redirect_to edit_questionnaire_template_path(questionnaire_template)
83
+ end
84
+
85
+ on(:invalid) do |template|
86
+ @template = template
87
+ flash.now[:error] = I18n.t("templates.update.error", scope: "decidim.admin")
88
+ render action: :edit
89
+ end
90
+ end
91
+ end
92
+
93
+ def destroy
94
+ enforce_permission_to :destroy, :template, template: template
95
+
96
+ DestroyTemplate.call(template, current_user) do
97
+ on(:ok) do
98
+ flash[:notice] = I18n.t("templates.destroy.success", scope: "decidim.admin")
99
+ redirect_to action: :index
100
+ end
101
+ end
102
+ end
103
+
104
+ def apply
105
+ questionnaire = Decidim::Forms::Questionnaire.find_by(id: params[:questionnaire_id])
106
+ template = Decidim::Templates::Template.find_by(id: params[:questionnaire][:questionnaire_template_id])
107
+
108
+ ApplyQuestionnaireTemplate.call(questionnaire, template) do
109
+ on(:ok) do
110
+ flash[:notice] = I18n.t("templates.apply.success", scope: "decidim.admin")
111
+ redirect_to params[:url]
112
+ end
113
+ on(:invalid) do
114
+ flash.now[:error] = I18n.t("templates.apply.error", scope: "decidim.admin")
115
+ end
116
+ end
117
+ end
118
+
119
+ def preview
120
+ respond_to do |format|
121
+ format.js do
122
+ @template = template
123
+ @questionnaire = @template.templatable
124
+ @preview_form = form(Decidim::Forms::QuestionnaireForm).from_model(@questionnaire)
125
+ end
126
+ end
127
+ end
128
+
129
+ def skip
130
+ questionnaire = Decidim::Forms::Questionnaire.find_by(id: params[:questionnaire_id])
131
+ # rubocop:disable Rails/SkipsModelValidations
132
+ questionnaire.touch
133
+ # rubocop:enable Rails/SkipsModelValidations
134
+ redirect_to params[:url]
135
+ end
136
+
137
+ private
138
+
139
+ def collection
140
+ @collection ||= current_organization.templates.where(templatable_type: "Decidim::Forms::Questionnaire")
141
+ end
142
+
143
+ def template
144
+ @template ||= Template.find_by(id: params[:id])
145
+ end
146
+
147
+ def search(term)
148
+ locales = current_organization.available_locales
149
+ @templates
150
+ .where(locales.map { |l| "name ->> '#{l}' ILIKE :text" }.join(" OR "), text: "%#{term}%")
151
+ .or(@templates.where(locales.map { |l| "description ->> '#{l}' ILIKE :text" }.join(" OR "), text: "%#{term}%"))
152
+ end
153
+ end
154
+ end
155
+ end
156
+ end
@@ -0,0 +1,13 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Decidim
4
+ module Templates
5
+ # This controller is the abstract class from which all other controllers of
6
+ # this engine inherit.
7
+ #
8
+ # Note that it inherits from `Decidim::Components::BaseController`, which
9
+ # override its layout and provide all kinds of useful methods.
10
+ class ApplicationController < Decidim::Components::BaseController
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,17 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Decidim
4
+ module Templates
5
+ module Admin
6
+ # This class holds a Form to update templates.
7
+ class TemplateForm < Decidim::Form
8
+ include TranslatableAttributes
9
+
10
+ translatable_attribute :name, String
11
+ translatable_attribute :description, String
12
+
13
+ validates :name, translatable_presence: true
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,32 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Decidim
4
+ module Templates
5
+ module Admin
6
+ # Custom helpers, scoped to the templates engine.
7
+ #
8
+ module TemplatesHelper
9
+ def select_template(form, templates)
10
+ prompt_options = {
11
+ url: decidim_admin_templates.questionnaire_templates_url(format: :json),
12
+ change_url: decidim_admin_templates.preview_questionnaire_templates_url(format: :js),
13
+ placeholder: t("placeholder", scope: "decidim.templates.admin.questionnaire_templates.choose")
14
+ }
15
+
16
+ default_options = templates.last(5).map { |questionnaire_template| { value: questionnaire_template.id, label: translated_attribute(questionnaire_template.name) } }
17
+
18
+ form.autocomplete_select(
19
+ :questionnaire_template_id,
20
+ false,
21
+ {
22
+ multiple: false,
23
+ label: t("label", scope: "decidim.templates.admin.questionnaire_templates.choose"),
24
+ default_options: default_options
25
+ },
26
+ prompt_options
27
+ )
28
+ end
29
+ end
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,10 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Decidim
4
+ module Templates
5
+ # Custom helpers, scoped to the templates engine.
6
+ #
7
+ module ApplicationHelper
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,24 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "active_support/concern"
4
+
5
+ module Decidim
6
+ module Templates
7
+ # A concern with the components needed when you want to create templates for a model
8
+ module Templatable
9
+ extend ActiveSupport::Concern
10
+
11
+ included do
12
+ has_one :template,
13
+ class_name: "Decidim::Templates::Template",
14
+ inverse_of: :templatable,
15
+ dependent: :destroy,
16
+ as: :templatable
17
+
18
+ def pristine?
19
+ created_at.to_i == updated_at.to_i
20
+ end
21
+ end
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,10 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Decidim
4
+ module Templates
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,33 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Templates can be defined from the admin panel to store and use objects
4
+ # with given values and use them to create new ones using these values as default
5
+ #
6
+ # The model class we want to create these templates from must include the Templatable
7
+ # concern. A controller should be created to manage templates for the model,
8
+ # as well as the routes for the controller actions. The command classes to use in
9
+ # these actions should also be created to define the particular data management
10
+ # for the model's templates.
11
+ module Decidim
12
+ module Templates
13
+ class Template < ApplicationRecord
14
+ belongs_to :organization,
15
+ foreign_key: "decidim_organization_id",
16
+ class_name: "Decidim::Organization"
17
+
18
+ belongs_to :templatable, foreign_key: "templatable_id", foreign_type: "templatable_type", polymorphic: true, optional: true
19
+
20
+ before_destroy :destroy_templatable
21
+
22
+ validates :name, presence: true
23
+
24
+ def resource_name
25
+ [templatable_type.demodulize.tableize.singularize, "templates"].join("_")
26
+ end
27
+
28
+ def destroy_templatable
29
+ templatable.destroy
30
+ end
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,26 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Decidim
4
+ module Templates
5
+ module Admin
6
+ class Permissions < Decidim::DefaultPermissions
7
+ def permissions
8
+ return permission_action unless user
9
+
10
+ return permission_action if permission_action.scope != :admin
11
+
12
+ case permission_action.subject
13
+ when :template
14
+ allow! if [:read, :create, :update, :destroy, :copy].include? permission_action.action
15
+ when :templates
16
+ allow! if permission_action.action == :index
17
+ when :questionnaire
18
+ allow!
19
+ end
20
+
21
+ permission_action
22
+ end
23
+ end
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,35 @@
1
+ <div class="card">
2
+ <div class="card-divider">
3
+ <h2 class="card-title"><%= form_title %></h2>
4
+ <div class="button--title">
5
+ <%= link_to t(".skip_template"), decidim_admin_templates.skip_questionnaire_templates_path(questionnaire_id: questionnaire, url: request.url), class: "button tiny button--simple", method: :post %>
6
+ </div>
7
+ </div>
8
+
9
+ <div class="card-section">
10
+ <div class="row column">
11
+ <%= t(".description") %>
12
+ </div>
13
+
14
+ <%= decidim_form_for([questionnaire], url: decidim_admin_templates.apply_questionnaire_templates_url(questionnaire_id: questionnaire, url: request.url), method: :post, html: { class: "form choose_questionnaire_templates" }) do |form| %>
15
+ <div class="row column">
16
+ <%= select_template form, templates %>
17
+ </div>
18
+
19
+ <div class="card-section questionnaire-template-preview">
20
+ <h3 class="template-name"></h3>
21
+ <p class="template-description"></p>
22
+ <div class="row column">
23
+ <div class="row column choose-template-preview">
24
+ </div>
25
+ </div>
26
+ </div>
27
+
28
+ <div class="button--double form-general-submit create-from-template">
29
+ <%= form.submit t(".create_from_template"), class: "button" %>
30
+ </div>
31
+ <% end %>
32
+ </div>
33
+ </div>
34
+
35
+ <%= stylesheet_link_tag "decidim/templates/templates", media: "all" %>
@@ -0,0 +1,16 @@
1
+ <div class="card">
2
+ <div class="card-divider">
3
+ <h2 class="card-title"><%= t(".template_title") %></h2>
4
+ <%= form.submit t(".save"), class: "button tiny button--title" %>
5
+ </div>
6
+
7
+ <div class="card-section">
8
+ <div class="row column">
9
+ <%= form.translated :text_field, :name %>
10
+ </div>
11
+
12
+ <div class="row column">
13
+ <%= form.translated :text_area, :description, rows: 3 %>
14
+ </div>
15
+ </div>
16
+ </div>
@@ -0,0 +1,73 @@
1
+ <% columns = @preview_form.responses.map(&:question).any?(&:matrix?) ? 9 : 6 %>
2
+
3
+ <div class="row columns">
4
+ <h3 class="section-heading"><%= translated_attribute questionnaire.title %></h3>
5
+ <div class="row">
6
+ <div class="columns large-<%= columns %> medium-centered lead">
7
+ <%= decidim_sanitize translated_attribute questionnaire.description %>
8
+ </div>
9
+ </div>
10
+ </div>
11
+
12
+ <div class="row">
13
+ <div class="columns large-<%= columns %> medium-centered">
14
+ <div class="card">
15
+ <div class="card__content">
16
+ <div class="answer-questionnaire">
17
+ <%= decidim_form_for(@preview_form, url: "", method: :post, html: { class: "form answer-questionnaire" }, data: { "safe-path" => "" }) do |form| %>
18
+ <%= invisible_captcha %>
19
+ <% answer_idx = 0 %>
20
+ <% cleaned_answer_idx = 0 %>
21
+ <% @preview_form.responses_by_step.each_with_index do |step_answers, step_index| %>
22
+ <div id="step-<%= step_index %>" class="<%= step_index.zero? ? "questionnaire-step" : "questionnaire-step hide" %>" data-toggler=".hide">
23
+ <% if @preview_form.total_steps > 1 %>
24
+ <h4 class="section-heading">
25
+ <%= t(".current_step", step: step_index + 1) %> <span class="answer-questionnaire__steps"><%= t(".of_total_steps", total_steps: @preview_form.total_steps) %></span>
26
+ </h4>
27
+ <% end %>
28
+
29
+ <% step_answers.each do |answer| %>
30
+ <div class="row column answer" data-max-choices="<%= answer.question.max_choices %>">
31
+ <%= fields_for "questionnaire[responses][#{answer_idx}]", answer do |answer_form| %>
32
+ <%= render(
33
+ "decidim/forms/questionnaires/answer",
34
+ answer_form: answer_form,
35
+ answer: answer,
36
+ answer_idx: answer_idx,
37
+ cleaned_answer_idx: cleaned_answer_idx,
38
+ disabled: false
39
+ ) %>
40
+ <% end %>
41
+ </div>
42
+ <% if !answer.question.separator? %>
43
+ <% cleaned_answer_idx += 1 %>
44
+ <% end %>
45
+ <% answer_idx += 1 %>
46
+ <% end %>
47
+
48
+ <% if step_index + 1 == @preview_form.total_steps %>
49
+ <div class="row column tos-agreement">
50
+ <%= form.check_box :tos_agreement, label: t(".tos_agreement"), id: "questionnaire_tos_agreement", disabled: false %>
51
+ <div class="help-text">
52
+ <%= decidim_sanitize translated_attribute questionnaire.tos %>
53
+ </div>
54
+ </div>
55
+ <% end %>
56
+
57
+ <%= cell(
58
+ "decidim/forms/step_navigation",
59
+ step_index,
60
+ total_steps: @preview_form.total_steps,
61
+ button_disabled: true,
62
+ form: form
63
+ ) %>
64
+ </div>
65
+ <% end %>
66
+ <% end %>
67
+ </div>
68
+ </div>
69
+ </div>
70
+ </div>
71
+ </div>
72
+
73
+ <%= stylesheet_link_tag "decidim/templates/templates", media: "all" %>