decidim-templates 0.23.0

Sign up to get free protection for your applications and to get access to all the features.

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" %>