decidim-decidim_awesome 0.5.1 → 0.6.4

Sign up to get free protection for your applications and to get access to all the features.
Files changed (60) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +30 -14
  3. data/Rakefile +6 -0
  4. data/app/assets/config/decidim_admin_decidim_awesome_manifest.js +1 -0
  5. data/app/assets/config/decidim_decidim_awesome_manifest.js +1 -0
  6. data/app/assets/javascripts/decidim/decidim_awesome/admin/form_exit_warn.js.es6 +30 -0
  7. data/app/assets/javascripts/decidim/decidim_awesome/awesome_map/legacy_map.js.es6 +225 -0
  8. data/app/assets/javascripts/decidim/decidim_awesome/awesome_map/legacy_proposals.js.es6 +82 -0
  9. data/app/assets/javascripts/decidim/decidim_awesome/awesome_map/map.js.es6 +20 -20
  10. data/app/assets/javascripts/decidim/decidim_awesome/awesome_map/meetings.js.es6 +15 -13
  11. data/app/assets/javascripts/decidim/decidim_awesome/awesome_map/proposals.js.es6 +29 -13
  12. data/app/assets/javascripts/decidim/decidim_awesome/editors/quill_editor.js.es6 +2 -4
  13. data/app/awesome_overrides/presenters/decidim/proposals/proposal_presenter_override.rb +39 -6
  14. data/app/commands/decidim/decidim_awesome/admin/create_scoped_style.rb +34 -0
  15. data/app/commands/decidim/decidim_awesome/admin/destroy_scoped_style.rb +40 -0
  16. data/app/commands/decidim/decidim_awesome/create_editor_image.rb +5 -3
  17. data/app/controllers/decidim/decidim_awesome/admin/checks_controller.rb +29 -1
  18. data/app/controllers/decidim/decidim_awesome/admin/config_controller.rb +29 -2
  19. data/app/controllers/decidim/decidim_awesome/admin/constraints_controller.rb +0 -2
  20. data/app/controllers/decidim/decidim_awesome/editor_images_controller.rb +1 -1
  21. data/app/controllers/decidim/decidim_awesome/map_component/map_controller.rb +8 -1
  22. data/app/forms/decidim/decidim_awesome/admin/config_form.rb +11 -0
  23. data/app/forms/decidim/decidim_awesome/editor_image_form.rb +2 -0
  24. data/app/helpers/decidim/decidim_awesome/admin/config_constraints_helpers.rb +4 -0
  25. data/app/helpers/decidim/decidim_awesome/map_helper.rb +37 -1
  26. data/app/models/decidim/decidim_awesome/editor_image.rb +4 -3
  27. data/app/uploaders/decidim/decidim_awesome/image_uploader.rb +9 -0
  28. data/app/views/decidim/decidim_awesome/admin/checks/index.html.erb +49 -7
  29. data/app/views/decidim/decidim_awesome/admin/config/_form_styles.html.erb +28 -0
  30. data/app/views/decidim/decidim_awesome/admin/config/show.html.erb +1 -1
  31. data/app/views/decidim/decidim_awesome/iframe_component/iframe/show.html.erb +6 -1
  32. data/app/views/decidim/decidim_awesome/map_component/map/show.html.erb +46 -20
  33. data/app/views/layouts/decidim/admin/decidim_awesome.html.erb +5 -0
  34. data/app/views/layouts/decidim/decidim_awesome/_awesome_config.html.erb +1 -1
  35. data/app/views/layouts/decidim/decidim_awesome/_custom_styles.html.erb +3 -0
  36. data/app/views/v0.22/layouts/decidim/_head.html.erb +1 -0
  37. data/app/views/{v0.21 → v0.23}/layouts/decidim/_head.html.erb +5 -0
  38. data/app/views/{v0.21 → v0.23}/layouts/decidim/admin/_header.html.erb +3 -0
  39. data/config/locales/ca.yml +53 -7
  40. data/config/locales/cs.yml +47 -1
  41. data/config/locales/en.yml +32 -2
  42. data/config/locales/es.yml +84 -38
  43. data/config/locales/eu.yml +171 -0
  44. data/config/locales/fr.yml +47 -1
  45. data/config/locales/sv.yml +47 -1
  46. data/lib/decidim/decidim_awesome.rb +12 -0
  47. data/lib/decidim/decidim_awesome/admin_engine.rb +2 -0
  48. data/lib/decidim/decidim_awesome/awesome_helpers.rb +17 -3
  49. data/lib/decidim/decidim_awesome/checksums.yml +6 -4
  50. data/lib/decidim/decidim_awesome/config.rb +13 -12
  51. data/lib/decidim/decidim_awesome/iframe_component/component.rb +3 -3
  52. data/lib/decidim/decidim_awesome/map_component/component.rb +6 -0
  53. data/lib/decidim/decidim_awesome/test/factories.rb +1 -1
  54. data/lib/decidim/decidim_awesome/test/layouts/decidim/_head.html.erb +2 -0
  55. data/lib/decidim/decidim_awesome/test/layouts/decidim/admin/_header.html.erb +3 -0
  56. data/lib/decidim/decidim_awesome/test/shared_examples/editor_examples.rb +71 -0
  57. data/lib/decidim/decidim_awesome/version.rb +2 -3
  58. data/vendor/assets/javascripts/jsrender.min.js +4 -0
  59. metadata +29 -18
  60. data/app/helpers/decidim/decidim_awesome/application_helper.rb +0 -10
@@ -1,4 +1,4 @@
1
- // = require decidim/map
1
+ // = require jsrender.min
2
2
  // = require leaflet.featuregroup.subgroup
3
3
  // = require decidim/decidim_awesome/awesome_map/categories
4
4
  // = require decidim/decidim_awesome/awesome_map/proposals
@@ -8,8 +8,15 @@
8
8
  ((exports) => {
9
9
  const { fetchProposals, fetchMeetings, getCategory } = exports.AwesomeMap;
10
10
 
11
- const collapsedMenu = $("#map").data("collapsed");
12
- const components = $("#map").data("components");
11
+ const collapsedMenu = $("#awesome-map").data("collapsed");
12
+ const show = {
13
+ withdrawn: $("#awesome-map").data("show-withdrawn"),
14
+ accepted: $("#awesome-map").data("show-accepted"),
15
+ evaluating: $("#awesome-map").data("show-evaluating"),
16
+ notAnswered: $("#awesome-map").data("show-not-answered"),
17
+ rejected: $("#awesome-map").data("show-rejected")
18
+ };
19
+ const components = $("#awesome-map").data("components");
13
20
  const popupMeetingTemplateId = "marker-meeting-popup";
14
21
  const popupProposalTemplateId = "marker-proposal-popup";
15
22
 
@@ -30,7 +37,7 @@
30
37
  let tmpl = component.type === "proposals" ? popupProposalTemplateId : popupMeetingTemplateId,
31
38
  node = document.createElement("div");
32
39
 
33
- $.tmpl($(`#${tmpl}`), element).appendTo(node);
40
+ $($.templates(`#${tmpl}`).render(element)).appendTo(node);
34
41
 
35
42
  marker.bindPopup(node, {
36
43
  maxwidth: 640,
@@ -91,7 +98,7 @@
91
98
  // add control layer for amendments if any
92
99
  if(component.amendments) {
93
100
  layers.amendments = {
94
- label: `<span id="awesome_map-component-${component.d}" title="0">${window.DecidimAwesome.texts.amendment}/span>`,
101
+ label: `<span id="awesome_map-component-${component.d}" title="0">${window.DecidimAwesome.texts.amendments}</span>`,
95
102
  group: L.featureGroup.subGroup(cluster)
96
103
  }
97
104
  control.addOverlay(layers.amendments.group, layers.amendments.label);
@@ -99,7 +106,10 @@
99
106
  }
100
107
 
101
108
  fetchProposals(component, '', (element, marker) => {
102
- drawMarker(element, marker, component).addTo(layers.proposals.group);
109
+ console.log(element.state, show[element.state || 'notAnswered'], show, element);
110
+ if(show[element.state || 'notAnswered']) {
111
+ drawMarker(element, marker, component).addTo(layers.proposals.group)
112
+ }
103
113
  }, () => {
104
114
  // finall call
105
115
  map.fitBounds(cluster.getBounds(), { padding: [50, 50] });
@@ -116,7 +126,7 @@
116
126
  if(component.type == "meetings") {
117
127
  // add control layer for meetings
118
128
  layers.meetings = {
119
- label: `<span id="awesome_map-component-${component.id}" title="0">${component.name || window.DecidimAwesome.texts.meetings}/span>`,
129
+ label: `<span id="awesome_map-component-${component.id}" title="0">${component.name || window.DecidimAwesome.texts.meetings}</span>`,
120
130
  group: L.featureGroup.subGroup(cluster)
121
131
  };
122
132
  control.addOverlay(layers.meetings.group, layers.meetings.label);
@@ -198,18 +208,8 @@
198
208
 
199
209
  };
200
210
 
201
- // currentMap might not be loaded yet so let's delay a bit
202
- // TODO: improve this
203
- const waitMap = () => {
204
- if(exports.Decidim && exports.Decidim.currentMap) {
205
- loadElements(exports.Decidim.currentMap);
206
- } else {
207
- setTimeout(() => {
208
- waitMap();
209
- }, 100);
210
- }
211
- };
212
-
213
- waitMap();
211
+ $("#map").on("ready.decidim", (ev, map) => {
212
+ loadElements(map);
213
+ });
214
214
 
215
215
  })(window);
@@ -93,7 +93,7 @@
93
93
  });
94
94
 
95
95
  element.title.translation = ApiFetcher.findTranslation(element.title.translations);
96
- element.description.translation = ApiFetcher.findTranslation(element.description.translations);
96
+ element.description.translation = ApiFetcher.findTranslation(element.description.translations).replace(/\n/g, "<br>");;
97
97
  element.location.translation = ApiFetcher.findTranslation(element.location.translations);
98
98
  element.locationHints.translation = ApiFetcher.findTranslation(element.locationHints.translations);
99
99
  callback(element, marker);
@@ -107,19 +107,21 @@
107
107
  };
108
108
  const api = new ApiFetcher(query, variables);
109
109
  api.fetchAll((result) => {
110
- result.component.meetings.edges.forEach((element) => {
111
- if(!element.node) return;
112
-
113
- if(element.node.coordinates) {
114
- element.node.link = component.url + '/meetings/' + element.node.id;
115
- createMarker(element.node, callback);
116
- }
117
- });
110
+ if(result) {
111
+ result.component.meetings.edges.forEach((element) => {
112
+ if(!element.node) return;
113
+
114
+ if(element.node.coordinates) {
115
+ element.node.link = component.url + '/meetings/' + element.node.id;
116
+ createMarker(element.node, callback);
117
+ }
118
+ });
118
119
 
119
- if (result.component.meetings.pageInfo.hasNextPage) {
120
- fetchMeetings(component, result.component.meetings.pageInfo.endCursor, callback, finalCall);
121
- } else {
122
- finalCall();
120
+ if (result.component.meetings.pageInfo.hasNextPage) {
121
+ fetchMeetings(component, result.component.meetings.pageInfo.endCursor, callback, finalCall);
122
+ } else {
123
+ finalCall();
124
+ }
123
125
  }
124
126
  });
125
127
  };
@@ -16,8 +16,19 @@
16
16
  edges {
17
17
  node {
18
18
  id
19
- title
20
- body
19
+ state
20
+ title {
21
+ translations {
22
+ text
23
+ locale
24
+ }
25
+ }
26
+ body {
27
+ translations {
28
+ text
29
+ locale
30
+ }
31
+ }
21
32
  address
22
33
  coordinates {
23
34
  latitude
@@ -47,6 +58,9 @@
47
58
  })
48
59
  });
49
60
 
61
+ element.title.translation = ApiFetcher.findTranslation(element.title.translations);
62
+ element.body.translation = ApiFetcher.findTranslation(element.body.translations).replace(/\n/g, "<br>");
63
+
50
64
  callback(element, marker);
51
65
  };
52
66
 
@@ -57,18 +71,20 @@
57
71
  };
58
72
  const api = new ApiFetcher(query, variables);
59
73
  api.fetchAll((result) => {
60
- result.component.proposals.edges.forEach((element) => {
61
- if(!element.node) return;
62
-
63
- if(element.node.coordinates) {
64
- element.node.link = component.url + '/proposals/' + element.node.id;
65
- createMarker(element.node, callback);
74
+ if(result) {
75
+ result.component.proposals.edges.forEach((element) => {
76
+ if(!element.node) return;
77
+
78
+ if(element.node.coordinates) {
79
+ element.node.link = component.url + '/proposals/' + element.node.id;
80
+ createMarker(element.node, callback);
81
+ }
82
+ });
83
+ if (result.component.proposals.pageInfo.hasNextPage) {
84
+ fetchProposals(component, result.component.proposals.pageInfo.endCursor, callback, finalCall);
85
+ } else {
86
+ finalCall();
66
87
  }
67
- });
68
- if (result.component.proposals.pageInfo.hasNextPage) {
69
- fetchProposals(component, result.component.proposals.pageInfo.endCursor, callback, finalCall);
70
- } else {
71
- finalCall();
72
88
  }
73
89
  });
74
90
  };
@@ -1,4 +1,3 @@
1
- // = require quill.min
2
1
  // = require image-upload.min
3
2
  // = require image-resize.min
4
3
  // = require inscrybmde.min.js
@@ -11,9 +10,9 @@
11
10
  exports.DecidimAwesome = exports.DecidimAwesome || {};
12
11
 
13
12
  // Redefines Quill editor with images
14
- if(exports.DecidimAwesome.allow_images_in_full_editor || exports.DecidimAwesome.allow_images_in_small_editor) {
13
+ if(exports.DecidimAwesome.allow_images_in_full_editor || exports.DecidimAwesome.allow_images_in_small_editor || exports.DecidimAwesome.use_markdown_editor) {
15
14
 
16
- const quillFormats = ["bold", "italic", "link", "underline", "header", "list", "video", "image"];
15
+ const quillFormats = ["bold", "italic", "link", "underline", "header", "list", "video", "image", "alt"];
17
16
 
18
17
  const createQuillEditor = (container) => {
19
18
  const toolbar = $(container).data("toolbar");
@@ -54,7 +53,6 @@
54
53
  toolbar: quillToolbar
55
54
  };
56
55
  const $input = $(container).siblings('input[type="hidden"]');
57
- // https://github.com/decidim/decidim/pull/6422
58
56
  container.innerHTML = $input.val() || "";
59
57
  const token = $( 'meta[name="csrf-token"]' ).attr( 'content' );
60
58
 
@@ -2,17 +2,44 @@
2
2
 
3
3
  # Tune Proposal presenter to use markdown if configured
4
4
  Decidim::Proposals::ProposalPresenter.class_eval do
5
- def body(links: false, extras: true, strip_tags: false)
6
- text = proposal.body
7
- text = strip_tags(text) if strip_tags
5
+ # rubocop:disable Metrics/CyclomaticComplexity
6
+ # rubocop:disable Metrics/PerceivedComplexity
7
+ def body(links: false, extras: true, strip_tags: false, all_locales: false)
8
+ return unless proposal
9
+
10
+ if defined? handle_locales
11
+ return handle_locales(proposal.body, all_locales) do |content|
12
+ content = strip_tags(sanitize_text(content)) if strip_tags
13
+
14
+ renderer = Decidim::ContentRenderers::HashtagRenderer.new(content)
15
+ content = renderer.render(links: links, extras: extras).html_safe
16
+
17
+ if use_markdown?(proposal) && !all_locales # avoid rendering in editors
18
+ content = render_markdown(content)
19
+ elsif links
20
+ content = Decidim::ContentRenderers::LinkRenderer.new(content).render
21
+ end
22
+ content
23
+ end
24
+ end
25
+ # rubocop:enable Metrics/CyclomaticComplexity
26
+ # rubocop:enable Metrics/PerceivedComplexity
27
+
28
+ if defined? translated_attribute
29
+ text = translated_attribute(proposal.body)
30
+
31
+ text = strip_tags(sanitize_text(text)) if strip_tags
32
+ else
33
+ # TODO: remove when 0.22 is diched
34
+ text = proposal.body
35
+ text = strip_tags(text) if strip_tags
36
+ end
8
37
 
9
38
  renderer = Decidim::ContentRenderers::HashtagRenderer.new(text)
10
39
  text = renderer.render(links: links, extras: extras).html_safe
11
40
 
12
41
  if use_markdown? proposal
13
- text = Decidim::DecidimAwesome::ContentRenderers::MarkdownRenderer.new(text).render
14
- # HACK: to avoid the replacement of lines to <br> that simple_format does
15
- text = text.gsub(">\n", ">").gsub("\n<", "<")
42
+ text = render_markdown(text)
16
43
  elsif links
17
44
  text = Decidim::ContentRenderers::LinkRenderer.new(text).render
18
45
  end
@@ -28,4 +55,10 @@ Decidim::Proposals::ProposalPresenter.class_eval do
28
55
  config.context_from_component proposal
29
56
  config.enabled_for? :use_markdown_editor
30
57
  end
58
+
59
+ def render_markdown(content)
60
+ content = Decidim::DecidimAwesome::ContentRenderers::MarkdownRenderer.new(content).render
61
+ # HACK: to avoid the replacement of lines to <br> that simple_format does
62
+ content.gsub(">\n", ">").gsub("\n<", "<")
63
+ end
31
64
  end
@@ -0,0 +1,34 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Decidim
4
+ module DecidimAwesome
5
+ module Admin
6
+ class CreateScopedStyle < Rectify::Command
7
+ # Public: Initializes the command.
8
+ #
9
+ def initialize(organization)
10
+ @organization = organization
11
+ @ident = rand(36**8).to_s(36)
12
+ end
13
+
14
+ # Executes the command. Broadcasts these events:
15
+ #
16
+ # - :ok when everything is valid.
17
+ # - :invalid if we couldn't proceed.
18
+ #
19
+ # Returns nothing.
20
+ def call
21
+ styles = AwesomeConfig.find_or_initialize_by(var: :scoped_styles, organization: @organization)
22
+ styles.value = {} unless styles.value.is_a? Hash
23
+ # TODO: prevent (unlikely) colisions with exisiting values
24
+ styles.value[@ident] = ""
25
+ styles.save!
26
+
27
+ broadcast(:ok, @ident)
28
+ rescue StandardError => e
29
+ broadcast(:invalid, e.message)
30
+ end
31
+ end
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,40 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Decidim
4
+ module DecidimAwesome
5
+ module Admin
6
+ class DestroyScopedStyle < Rectify::Command
7
+ # Public: Initializes the command.
8
+ #
9
+ # key - the key to destroy inise scoped_styles
10
+ # organization
11
+ def initialize(key, organization)
12
+ @key = key
13
+ @organization = organization
14
+ end
15
+
16
+ # Executes the command. Broadcasts these events:
17
+ #
18
+ # - :ok when everything is valid.
19
+ # - :invalid if we couldn't proceed.
20
+ #
21
+ # Returns nothing.
22
+ def call
23
+ styles = AwesomeConfig.find_by(var: :scoped_styles, organization: @organization)
24
+ return broadcast(:invalid, "Not a hash") unless styles&.value.is_a? Hash
25
+ return broadcast(:invalid, "#{key} key invalid") unless styles.value.has_key?(@key)
26
+
27
+ styles.value.except!(@key)
28
+ styles.save!
29
+ # remove constrains associated (a new config var is generated automatically, by removing it, it will trigger destroy on dependents)
30
+ constraint = AwesomeConfig.find_by(var: "scoped_style_#{@key}", organization: @organization)
31
+ constraint.destroy! if constraint.present?
32
+
33
+ broadcast(:ok, @key)
34
+ rescue StandardError => e
35
+ broadcast(:invalid, e.message)
36
+ end
37
+ end
38
+ end
39
+ end
40
+ end
@@ -19,12 +19,14 @@ module Decidim
19
19
  def call
20
20
  return broadcast(:invalid) if form.invalid?
21
21
 
22
- image = EditorImage.create!(
23
- image: form.image,
22
+ image = EditorImage.new(
24
23
  path: form.path,
25
24
  decidim_author_id: form.current_user.id,
26
- organization: form.current_organization
25
+ organization: form.organization,
26
+ image: form.image
27
27
  )
28
+
29
+ image.save!
28
30
  broadcast(:ok, image)
29
31
  end
30
32
 
@@ -12,10 +12,18 @@ module Decidim
12
12
 
13
13
  layout "decidim/admin/decidim_awesome"
14
14
 
15
- helper_method :overrides, :valid?, :decidim_version, :decidim_version_valid?
15
+ helper_method :overrides, :valid?, :decidim_version, :decidim_version_valid?, :head, :admin_head, :head_addons, :admin_addons
16
16
 
17
17
  private
18
18
 
19
+ def head
20
+ @head ||= Nokogiri::HTML(render_to_string(partial: "layouts/decidim/head"))
21
+ end
22
+
23
+ def admin_head
24
+ @admin_head = Nokogiri::HTML(render_to_string(partial: "layouts/decidim/admin/header"))
25
+ end
26
+
19
27
  def overrides
20
28
  SystemChecker.to_h
21
29
  end
@@ -31,6 +39,26 @@ module Decidim
31
39
  def decidim_version_valid?
32
40
  Gem::Dependency.new("", DecidimAwesome::COMPAT_DECIDIM_VERSION).match?("", decidim_version)
33
41
  end
42
+
43
+ def head_addons(part)
44
+ case part
45
+ when :CSS
46
+ ['<%= stylesheet_link_tag "decidim/decidim_awesome/application", media: "all" %>',
47
+ '<%= stylesheet_link_tag(tenant_stylesheets, media: "all") if tenant_stylesheets %>'].join("\n")
48
+ when :JavaScript
49
+ ['<%= render partial: "layouts/decidim/decidim_awesome/awesome_config" %>',
50
+ '<%= javascript_include_tag "decidim/decidim_awesome/application" %>'].join("\n")
51
+ end
52
+ end
53
+
54
+ def admin_addons(part)
55
+ case part
56
+ when :CSS
57
+ '<%= stylesheet_link_tag "decidim/decidim_awesome/admin", media: "all" %>'
58
+ when :JavaScript
59
+ '<%= javascript_include_tag "decidim/decidim_awesome/admin" %>'
60
+ end
61
+ end
34
62
  end
35
63
  end
36
64
  end
@@ -18,8 +18,7 @@ module Decidim
18
18
  end
19
19
 
20
20
  def update
21
- @form = form(ConfigForm).from_params(params)
22
-
21
+ @form = form(ConfigForm).from_params(params[:config])
23
22
  UpdateConfig.call(@form) do
24
23
  on(:ok) do
25
24
  flash[:notice] = I18n.t("config.update.success", scope: "decidim.decidim_awesome.admin")
@@ -33,6 +32,34 @@ module Decidim
33
32
  end
34
33
  end
35
34
 
35
+ def new_scoped_style
36
+ CreateScopedStyle.call(current_organization) do
37
+ on(:ok) do |key|
38
+ flash[:notice] = I18n.t("config.create_scoped_style.success", key: key, scope: "decidim.decidim_awesome.admin")
39
+ end
40
+
41
+ on(:invalid) do |message|
42
+ flash[:alert] = I18n.t("config.create_scoped_style.error", error: message, scope: "decidim.decidim_awesome.admin")
43
+ end
44
+ end
45
+
46
+ redirect_to decidim_admin_decidim_awesome.config_path(:styles)
47
+ end
48
+
49
+ def destroy_scoped_style
50
+ DestroyScopedStyle.call(params[:key], current_organization) do
51
+ on(:ok) do |key|
52
+ flash[:notice] = I18n.t("config.destroy_scoped_style.success", key: key, scope: "decidim.decidim_awesome.admin")
53
+ end
54
+
55
+ on(:invalid) do |message|
56
+ flash[:alert] = I18n.t("config.destroy_scoped_style.error", error: message, scope: "decidim.decidim_awesome.admin")
57
+ end
58
+ end
59
+
60
+ redirect_to decidim_admin_decidim_awesome.config_path(:styles)
61
+ end
62
+
36
63
  private
37
64
 
38
65
  def constraints_for(key)