decidim-navigation_maps 1.0.1

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