decidim-navigation_maps 1.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (82) hide show
  1. checksums.yaml +7 -0
  2. data/LICENSE-AGPLv3.txt +661 -0
  3. data/README.md +144 -0
  4. data/Rakefile +40 -0
  5. data/app/assets/config/admin/decidim_navigation_maps_manifest.css +3 -0
  6. data/app/assets/config/admin/decidim_navigation_maps_manifest.js +3 -0
  7. data/app/assets/config/decidim_navigation_maps_manifest.css +3 -0
  8. data/app/assets/config/decidim_navigation_maps_manifest.js +1 -0
  9. data/app/assets/images/decidim/navigation_maps/icon.svg +1 -0
  10. data/app/assets/javascripts/decidim/navigation_maps/admin/map_editor.js +88 -0
  11. data/app/assets/javascripts/decidim/navigation_maps/admin/navigation_maps.js +143 -0
  12. data/app/assets/javascripts/decidim/navigation_maps/map_view.js +123 -0
  13. data/app/assets/javascripts/decidim/navigation_maps/navigation_maps.js +44 -0
  14. data/app/assets/stylesheets/decidim/navigation_maps/_variables.scss +3 -0
  15. data/app/assets/stylesheets/decidim/navigation_maps/admin/navigation_maps.scss +102 -0
  16. data/app/assets/stylesheets/decidim/navigation_maps/navigation_maps.scss +58 -0
  17. data/app/cells/decidim/navigation_maps/content_blocks/navigation_map/_styles.erb +18 -0
  18. data/app/cells/decidim/navigation_maps/content_blocks/navigation_map/_tabs.erb +5 -0
  19. data/app/cells/decidim/navigation_maps/content_blocks/navigation_map/_tabs_content.erb +8 -0
  20. data/app/cells/decidim/navigation_maps/content_blocks/navigation_map/_template.erb +13 -0
  21. data/app/cells/decidim/navigation_maps/content_blocks/navigation_map/show.erb +21 -0
  22. data/app/cells/decidim/navigation_maps/content_blocks/navigation_map_cell.rb +22 -0
  23. data/app/cells/decidim/navigation_maps/content_blocks/navigation_map_settings_form/_form.erb +36 -0
  24. data/app/cells/decidim/navigation_maps/content_blocks/navigation_map_settings_form/_modal.erb +12 -0
  25. data/app/cells/decidim/navigation_maps/content_blocks/navigation_map_settings_form/_tabs.erb +6 -0
  26. data/app/cells/decidim/navigation_maps/content_blocks/navigation_map_settings_form/_tabs_content.erb +11 -0
  27. data/app/cells/decidim/navigation_maps/content_blocks/navigation_map_settings_form/show.erb +35 -0
  28. data/app/cells/decidim/navigation_maps/content_blocks/navigation_map_settings_form_cell.rb +32 -0
  29. data/app/commands/decidim/navigation_maps/save_area.rb +58 -0
  30. data/app/commands/decidim/navigation_maps/save_blueprints.rb +72 -0
  31. data/app/controllers/decidim/navigation_maps/admin/application_controller.rb +13 -0
  32. data/app/controllers/decidim/navigation_maps/admin/areas_controller.rb +94 -0
  33. data/app/controllers/decidim/navigation_maps/admin/blueprints_controller.rb +64 -0
  34. data/app/forms/decidim/navigation_maps/area_form.rb +37 -0
  35. data/app/forms/decidim/navigation_maps/blueprint_form.rb +39 -0
  36. data/app/forms/decidim/navigation_maps/blueprint_forms.rb +9 -0
  37. data/app/models/decidim/navigation_maps/application_record.rb +10 -0
  38. data/app/models/decidim/navigation_maps/blueprint.rb +27 -0
  39. data/app/models/decidim/navigation_maps/blueprint_area.rb +32 -0
  40. data/app/queries/decidim/navigation_maps/organization_blueprints.rb +20 -0
  41. data/app/uploaders/decidim/navigation_maps/blueprint_uploader.rb +22 -0
  42. data/app/views/decidim/navigation_maps/admin/areas/_form.html.erb +39 -0
  43. data/app/views/decidim/navigation_maps/admin/areas/new.html.erb +17 -0
  44. data/app/views/decidim/navigation_maps/admin/areas/show.html.erb +14 -0
  45. data/config/i18n-tasks.yml +10 -0
  46. data/config/locales/ca.yml +45 -0
  47. data/config/locales/cs.yml +45 -0
  48. data/config/locales/en.yml +46 -0
  49. data/config/locales/es.yml +45 -0
  50. data/db/migrate/20191022092624_create_decidim_navigation_maps_blueprints.rb +13 -0
  51. data/db/migrate/20191120185739_add_title_to_navigation_maps_blueprints.rb +10 -0
  52. data/db/migrate/20191125142751_create_decidim_navigation_maps_blueprint_areas.rb +18 -0
  53. data/db/migrate/20191126045831_add_link_type_to_decidim_navigation_maps_blueprint_areas.rb +8 -0
  54. data/db/migrate/20191126154019_add_area_id_to_decidim_navigation_maps_blueprint_areas.rb +16 -0
  55. data/db/migrate/20191127093746_add_color_to_navigation_maps_blueprint_areas.rb +7 -0
  56. data/lib/decidim/navigation_maps.rb +13 -0
  57. data/lib/decidim/navigation_maps/admin.rb +10 -0
  58. data/lib/decidim/navigation_maps/admin_engine.rb +34 -0
  59. data/lib/decidim/navigation_maps/engine.rb +34 -0
  60. data/lib/decidim/navigation_maps/navigation_map_cell_helpers.rb +28 -0
  61. data/lib/decidim/navigation_maps/test/factories.rb +29 -0
  62. data/lib/decidim/navigation_maps/version.rb +9 -0
  63. data/vendor/assets/images/draw/layers-2x.png +0 -0
  64. data/vendor/assets/images/draw/layers.png +0 -0
  65. data/vendor/assets/images/draw/marker-icon-2x.png +0 -0
  66. data/vendor/assets/images/draw/marker-icon.png +0 -0
  67. data/vendor/assets/images/draw/marker-shadow.png +0 -0
  68. data/vendor/assets/images/draw/spritesheet-2x.png +0 -0
  69. data/vendor/assets/images/draw/spritesheet.png +0 -0
  70. data/vendor/assets/images/draw/spritesheet.svg +156 -0
  71. data/vendor/assets/images/images/layers-2x.png +0 -0
  72. data/vendor/assets/images/images/layers.png +0 -0
  73. data/vendor/assets/images/images/marker-icon-2x.png +0 -0
  74. data/vendor/assets/images/images/marker-icon.png +0 -0
  75. data/vendor/assets/images/images/marker-shadow.png +0 -0
  76. data/vendor/assets/javascripts/jquery.form.js +1277 -0
  77. data/vendor/assets/javascripts/jsrender.min.js +4 -0
  78. data/vendor/assets/javascripts/jsrender.min.js.map +1 -0
  79. data/vendor/assets/javascripts/leaflet-geoman.min.js +1 -0
  80. data/vendor/assets/javascripts/leaflet.js +5 -0
  81. data/vendor/assets/stylesheets/leaflet-geoman.css +164 -0
  82. metadata +183 -0
@@ -0,0 +1,58 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Decidim
4
+ module NavigationMaps
5
+ # This command creates or updates areas for a blueprints
6
+ class SaveArea < Rectify::Command
7
+ # Creates or updates an area for a blueprint.
8
+ #
9
+ # form - The form with the data.
10
+ def initialize(form)
11
+ @form = form
12
+ end
13
+
14
+ # Executes the command. Broadcasts these events:
15
+ #
16
+ # - :ok when everything is valid
17
+ # - :invalid if the form wasn't valid and we couldn't proceed.
18
+ #
19
+ # Returns nothing.
20
+ def call
21
+ return broadcast(:invalid, form.errors.full_messages.join(", ")) if form.invalid?
22
+
23
+ initialize_area
24
+ update_area
25
+ save_area!
26
+
27
+ broadcast(:ok, @area)
28
+ end
29
+
30
+ private
31
+
32
+ attr_reader :form, :current_blueprint
33
+
34
+ def initialize_area
35
+ @area = BlueprintArea.find_or_initialize_by(area_id: form.area_id, blueprint: @form.context.current_blueprint)
36
+ end
37
+
38
+ def update_area
39
+ @area.link_type = link_type
40
+ @area.link = form.link
41
+ @area.title = form.title unless form.no_popup
42
+ @area.description = form.description unless form.no_popup
43
+ @area.color = form.color
44
+ @area.area_id = form.area_id if form.area_id
45
+ @area.area = form.area if form.area
46
+ @area.area_type = form.area_type if form.area_type
47
+ end
48
+
49
+ def save_area!
50
+ @area.save!
51
+ end
52
+
53
+ def link_type
54
+ form.no_popup ? "direct" : "link"
55
+ end
56
+ end
57
+ end
58
+ end
@@ -0,0 +1,72 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Decidim
4
+ module NavigationMaps
5
+ # This command creates or updates the blueprints for the organization.
6
+ class SaveBlueprints < Rectify::Command
7
+ # Creates a blueprint.
8
+ #
9
+ # forms - The form with the data.
10
+ def initialize(forms)
11
+ @forms = forms
12
+ @blueprints = forms.blueprints || {}
13
+ end
14
+
15
+ # Executes the command. Broadcasts these events:
16
+ #
17
+ # - :ok when everything is valid
18
+ # - :invalid if the form wasn't valid and we couldn't proceed.
19
+ #
20
+ # Returns nothing.
21
+ def call
22
+ @blueprints.each do |form|
23
+ next if form.invalid?
24
+
25
+ initialize_blueprint(form)
26
+ if form.remove
27
+ destroy_blueprint!(form)
28
+ else
29
+ create_areas(form.blueprint) if form.blueprint
30
+ destroy_areas_except(form.blueprint.keys) if form.blueprint
31
+ update_blueprint!(form)
32
+ end
33
+ end
34
+
35
+ broadcast(:ok)
36
+ end
37
+
38
+ private
39
+
40
+ attr_reader :forms
41
+
42
+ def initialize_blueprint(form)
43
+ @blueprint = Blueprint.find_or_initialize_by(id: form.id) do |blueprint|
44
+ blueprint.organization = @forms.current_organization
45
+ end
46
+ end
47
+
48
+ def update_blueprint!(form)
49
+ @blueprint.image = form.image if form.image.present?
50
+ @blueprint.title = form.title
51
+ @blueprint.description = form.description
52
+ @blueprint.save!
53
+ end
54
+
55
+ def destroy_blueprint!(form)
56
+ @blueprint.destroy! if form.id
57
+ end
58
+
59
+ def destroy_areas_except(except)
60
+ @blueprint.areas.where.not(area_id: except).destroy_all
61
+ end
62
+
63
+ def create_areas(blueprint)
64
+ blueprint.each do |key, area|
65
+ a = BlueprintArea.find_or_initialize_by(area_id: key, blueprint: @blueprint)
66
+ a.area = area[:geometry]
67
+ a.area_type = area[:type]
68
+ end
69
+ end
70
+ end
71
+ end
72
+ end
@@ -0,0 +1,13 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Decidim
4
+ module NavigationMaps
5
+ module Admin
6
+ # This controller is the abstract class from which all other controllers of
7
+ # this engine inherit.
8
+ #
9
+ class ApplicationController < ::Decidim::Admin::ApplicationController
10
+ end
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,94 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Decidim
4
+ module NavigationMaps
5
+ module Admin
6
+ class AreasController < ::Decidim::NavigationMaps::Admin::ApplicationController
7
+ layout false
8
+ helper_method :link_suggestions
9
+
10
+ before_action do
11
+ enforce_permission_to :update, :organization, organization: current_organization
12
+ end
13
+
14
+ def index
15
+ render json: blueprint.areas
16
+ end
17
+
18
+ def new
19
+ @form = form(AreaForm).instance(current_blueprint: blueprint)
20
+ end
21
+
22
+ def create
23
+ parse_areas
24
+ @form = form(AreaForm).from_params(params, current_blueprint: blueprint)
25
+
26
+ SaveArea.call(@form) do
27
+ on(:ok) do |area|
28
+ render json: { message: I18n.t("navigation_maps.admin.areas.create.success", scope: "decidim"),
29
+ area_id: area.area_id,
30
+ blueprint_id: area.blueprint.id,
31
+ area: area.to_geoson }
32
+ end
33
+
34
+ on(:invalid) do |message|
35
+ render json: { message: I18n.t("navigation_maps.admin.areas.create.error", scope: "decidim"), error: message }, status: :unprocessable_entity
36
+ end
37
+ end
38
+ end
39
+
40
+ def show
41
+ @form = form(AreaForm).from_model(area)
42
+ end
43
+
44
+ def update
45
+ @form = form(AreaForm).from_params(params, current_blueprint: blueprint)
46
+ SaveArea.call(@form) do
47
+ on(:ok) do |area|
48
+ render json: { message: I18n.t("navigation_maps.admin.areas.update.success", scope: "decidim"),
49
+ area_id: area.area_id,
50
+ blueprint_id: area.blueprint.id,
51
+ area: area.to_geoson }
52
+ end
53
+
54
+ on(:invalid) do |message|
55
+ render json: { message: I18n.t("navigation_maps.admin.areas.update.error", scope: "decidim"), error: message }, status: :unprocessable_entity
56
+ end
57
+ end
58
+ end
59
+
60
+ private
61
+
62
+ def link_suggestions
63
+ return if blueprints_count.zero?
64
+
65
+ maps = blueprints_count.times.collect do |n|
66
+ "<a href=\"#map#{n}\">#map#{n}</a>"
67
+ end
68
+ t("link_suggestions", scope: "decidim.navigation_maps.admin.areas.show", map: maps.join(",")).html_safe
69
+ end
70
+
71
+ def parse_areas
72
+ return unless params[:blueprint_area]
73
+ return if params[:blueprint_area][:area].blank?
74
+
75
+ feature = JSON.parse params[:blueprint_area][:area]
76
+ params[:blueprint_area][:area] = feature["geometry"] if feature["geometry"]
77
+ params[:blueprint_area][:area_type] = feature["type"] if feature["type"]
78
+ end
79
+
80
+ def blueprint
81
+ @blueprint ||= Blueprint.find(params[:blueprint_id])
82
+ end
83
+
84
+ def area
85
+ @area ||= BlueprintArea.find_by(area_id: params[:area_id], blueprint: blueprint)
86
+ end
87
+
88
+ def blueprints_count
89
+ @blueprints_count ||= Blueprint.where(organization: current_organization).count
90
+ end
91
+ end
92
+ end
93
+ end
94
+ end
@@ -0,0 +1,64 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Decidim
4
+ module NavigationMaps
5
+ module Admin
6
+ class BlueprintsController < ::Decidim::NavigationMaps::Admin::ApplicationController
7
+ before_action do
8
+ enforce_permission_to :update, :organization, organization: current_organization
9
+ end
10
+
11
+ def index
12
+ render json: organization_blueprints
13
+ end
14
+
15
+ def show
16
+ render json: organization_blueprints.find(params[:id])
17
+ end
18
+
19
+ def create
20
+ save_settings
21
+ parse_blueprints
22
+ @form = form(BlueprintForms).from_params(params).with_context(current_organization: current_organization)
23
+ SaveBlueprints.call(@form) do
24
+ on(:ok) do
25
+ render plain: I18n.t("navigation_maps.create.success", scope: "decidim")
26
+ end
27
+
28
+ on(:invalid) do
29
+ render plain: I18n.t("navigation_maps.create.error", scope: "decidim"), status: :unprocessable_entity
30
+ end
31
+ end
32
+ end
33
+
34
+ private
35
+
36
+ # Manually save original settings if the settings array is present
37
+ def save_settings
38
+ return unless params[:content_block]
39
+ return unless params[:content_block][:settings]
40
+
41
+ @form = form(Decidim::Admin::ContentBlockForm).from_params(params)
42
+ Decidim::Admin::UpdateContentBlock.call(@form, content_block, :homepage)
43
+ end
44
+
45
+ def content_block
46
+ @content_block ||= Decidim::ContentBlock.for_scope(:homepage, organization: current_organization).find_by(manifest_name: :navigation_map)
47
+ end
48
+
49
+ # Convert blueprint data to an object
50
+ def parse_blueprints
51
+ return unless params[:blueprints]
52
+
53
+ params[:blueprints].each do |_key, data|
54
+ data[:blueprint] = JSON.parse data[:blueprint] if data[:blueprint].present?
55
+ end
56
+ end
57
+
58
+ def organization_blueprints
59
+ @organization_blueprints ||= OrganizationBlueprints.new(current_organization).query
60
+ end
61
+ end
62
+ end
63
+ end
64
+ end
@@ -0,0 +1,37 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Decidim
4
+ module NavigationMaps
5
+ # A form object used to configure the blueprint content block from the admin panel.
6
+ #
7
+ class AreaForm < Decidim::Form
8
+ include TranslatableAttributes
9
+
10
+ mimic :blueprint_area
11
+
12
+ attribute :area, Object
13
+ attribute :area_type, String
14
+ attribute :area_id, String
15
+ attribute :link, String
16
+ attribute :link_type, String
17
+ attribute :no_popup, String
18
+ attribute :color, String
19
+ translatable_attribute :title, String
20
+ translatable_attribute :description, String
21
+
22
+ def no_popup
23
+ return link_type == "direct" unless super
24
+
25
+ super.to_i.nonzero?
26
+ end
27
+
28
+ def color
29
+ return "#2262cc" if super.blank?
30
+
31
+ return "#" + super unless super.match?(/^#/)
32
+
33
+ super
34
+ end
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,39 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Decidim
4
+ module NavigationMaps
5
+ # A form object used to configure the blueprint content block from the admin panel.
6
+ #
7
+ class BlueprintForm < Decidim::Form
8
+ include TranslatableAttributes
9
+
10
+ mimic :blueprint
11
+
12
+ attribute :blueprint, Object
13
+ attribute :id, Integer
14
+ attribute :remove, Boolean
15
+ attribute :image
16
+ translatable_attribute :title, String
17
+ translatable_attribute :description, String
18
+
19
+ # validate :check_image_or_blueprint
20
+ validates :title, translatable_presence: true
21
+
22
+ # def check_image_or_blueprint
23
+ # return if image.present? || blueprint.present?
24
+
25
+ # errors.add(:image, "no image") unless image.present?
26
+ # errors.add(:blueprint, "no blueprint") unless blueprint.present?
27
+ # end
28
+
29
+ def ident
30
+ id || "_"
31
+ end
32
+
33
+ def image?
34
+ return unless image && image.respond_to?(:url)
35
+ return image.content_type.start_with? "image" if image.content_type.present?
36
+ end
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,9 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Decidim
4
+ module NavigationMaps
5
+ class BlueprintForms < Decidim::Form
6
+ attribute :blueprints, Array[BlueprintForm]
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,10 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Decidim
4
+ module NavigationMaps
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,27 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Decidim
4
+ module NavigationMaps
5
+ # Abstract class from which all models in this engine inherit.
6
+ class Blueprint < ApplicationRecord
7
+ self.table_name = "decidim_navigation_maps_blueprints"
8
+
9
+ belongs_to :organization, foreign_key: :decidim_organization_id, class_name: "Decidim::Organization"
10
+ has_many :areas,
11
+ foreign_key: "decidim_navigation_maps_blueprint_id",
12
+ class_name: "Decidim::NavigationMaps::BlueprintArea",
13
+ dependent: :destroy
14
+
15
+ validates :organization, presence: true
16
+ validates :image,
17
+ file_size: { less_than_or_equal_to: ->(_record) { Decidim.maximum_attachment_size } },
18
+ file_content_type: { allow: ["image/jpeg", "image/png", "image/svg+xml"] }
19
+
20
+ mount_uploader :image, Decidim::NavigationMaps::BlueprintUploader
21
+
22
+ def blueprint
23
+ areas.map { |area| [area.area_id.to_s, area.to_geoson] }.to_h
24
+ end
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,32 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Decidim
4
+ module NavigationMaps
5
+ # Abstract class from which all models in this engine inherit.
6
+ class BlueprintArea < ApplicationRecord
7
+ include Decidim::TranslatableAttributes
8
+
9
+ self.table_name = "decidim_navigation_maps_blueprint_areas"
10
+
11
+ belongs_to :blueprint, foreign_key: :decidim_navigation_maps_blueprint_id, class_name: "Decidim::NavigationMaps::Blueprint"
12
+
13
+ attribute :link_type, :string, default: "link"
14
+
15
+ validates :blueprint, presence: true
16
+
17
+ def to_geoson
18
+ {
19
+ type: area_type,
20
+ geometry: area,
21
+ properties: {
22
+ link: link,
23
+ popup: link_type == "direct",
24
+ color: color,
25
+ title: translated_attribute(title),
26
+ description: translated_attribute(description)
27
+ }
28
+ }
29
+ end
30
+ end
31
+ end
32
+ end