decidim-core 0.0.5 → 0.0.6

Sign up to get free protection for your applications and to get access to all the features.
Files changed (121) hide show
  1. checksums.yaml +4 -4
  2. data/app/assets/config/decidim_core_manifest.js +2 -0
  3. data/app/assets/javascripts/decidim/map.js.es6.erb +95 -0
  4. data/app/assets/stylesheets/decidim/map.css +3 -0
  5. data/app/assets/stylesheets/decidim/modules/_layout.scss +2 -4
  6. data/app/assets/stylesheets/decidim/modules/_margins.scss +3 -2
  7. data/app/assets/stylesheets/decidim/modules/_process-header.scss +15 -2
  8. data/app/assets/stylesheets/decidim/modules/_process-phase.scss +4 -1
  9. data/app/assets/stylesheets/decidim/utils/_settings.scss +3 -3
  10. data/app/commands/decidim/create_report.rb +90 -0
  11. data/app/controllers/concerns/decidim/devise_controllers.rb +26 -0
  12. data/app/controllers/concerns/decidim/payload_info.rb +23 -0
  13. data/app/controllers/concerns/decidim/user_profile.rb +2 -1
  14. data/app/controllers/decidim/application_controller.rb +25 -1
  15. data/app/controllers/decidim/authorizations_controller.rb +10 -2
  16. data/app/controllers/decidim/cookie_policy_controller.rb +5 -0
  17. data/app/controllers/decidim/devise/confirmations_controller.rb +1 -9
  18. data/app/controllers/decidim/devise/invitations_controller.rb +1 -9
  19. data/app/controllers/decidim/devise/omniauth_registrations_controller.rb +1 -10
  20. data/app/controllers/decidim/devise/passwords_controller.rb +1 -9
  21. data/app/controllers/decidim/devise/registrations_controller.rb +2 -15
  22. data/app/controllers/decidim/devise/sessions_controller.rb +9 -15
  23. data/{lib → app/controllers}/decidim/features/base_controller.rb +5 -1
  24. data/app/controllers/decidim/participatory_process_groups_controller.rb +22 -0
  25. data/app/controllers/decidim/participatory_process_steps_controller.rb +2 -0
  26. data/app/controllers/decidim/participatory_processes_controller.rb +14 -3
  27. data/app/controllers/decidim/reports_controller.rb +33 -0
  28. data/app/controllers/decidim/static_map_controller.rb +18 -0
  29. data/app/forms/decidim/report_form.rb +12 -0
  30. data/app/helpers/decidim/application_helper.rb +0 -2
  31. data/app/helpers/decidim/feature_reference_helper.rb +22 -0
  32. data/app/helpers/decidim/map_helper.rb +41 -0
  33. data/app/helpers/decidim/organization_scopes_helper.rb +41 -0
  34. data/app/helpers/decidim/replace_buttons_helper.rb +42 -0
  35. data/app/helpers/decidim/resource_helper.rb +2 -1
  36. data/app/helpers/decidim/translations_helper.rb +1 -1
  37. data/app/mailers/decidim/reported_mailer.rb +35 -0
  38. data/app/middleware/decidim/current_organization.rb +29 -3
  39. data/app/models/decidim/abilities/everyone.rb +1 -0
  40. data/app/models/decidim/moderation.rb +13 -0
  41. data/app/models/decidim/newsletter.rb +1 -0
  42. data/app/models/decidim/organization.rb +2 -0
  43. data/app/models/decidim/participatory_process.rb +11 -0
  44. data/app/models/decidim/participatory_process_group.rb +15 -0
  45. data/app/models/decidim/participatory_process_step.rb +1 -1
  46. data/app/models/decidim/report.rb +23 -0
  47. data/app/services/decidim/public_processes.rb +42 -0
  48. data/app/services/decidim/resource_search.rb +17 -1
  49. data/app/services/decidim/static_map_generator.rb +49 -0
  50. data/app/uploaders/decidim/application_uploader.rb +4 -1
  51. data/app/uploaders/decidim/official_image_footer_uploader.rb +2 -2
  52. data/app/uploaders/decidim/official_image_header_uploader.rb +1 -1
  53. data/app/validators/geocoding_validator.rb +22 -0
  54. data/app/views/decidim/authorizations/new.html.erb +1 -1
  55. data/app/views/decidim/participatory_process_groups/_participatory_process_group.html.erb +22 -0
  56. data/app/views/decidim/participatory_process_groups/show.html.erb +11 -0
  57. data/app/views/decidim/participatory_processes/index.html.erb +1 -1
  58. data/app/views/decidim/participatory_processes/show.html.erb +18 -5
  59. data/app/views/decidim/reported_mailer/hide.html.erb +5 -0
  60. data/app/views/decidim/reported_mailer/report.html.erb +5 -0
  61. data/app/views/decidim/shared/_address_details.html.erb +9 -0
  62. data/app/views/decidim/shared/_flag_modal.html.erb +20 -0
  63. data/app/views/decidim/shared/_share_modal.html.erb +5 -5
  64. data/app/views/decidim/shared/_static_map.html.erb +17 -0
  65. data/app/views/devise/mailer/invite_collaborator.html.erb +17 -0
  66. data/app/views/devise/mailer/invite_collaborator.text.erb +15 -0
  67. data/app/views/layouts/decidim/_application.html.erb +5 -12
  68. data/app/views/layouts/decidim/{_social_meta.html.erb → _head.html.erb} +13 -0
  69. data/app/views/layouts/decidim/_head_extra.html.erb +7 -0
  70. data/app/views/layouts/decidim/_process_header.html.erb +15 -9
  71. data/app/views/layouts/decidim/_process_header_steps.html.erb +3 -3
  72. data/app/views/layouts/decidim/{_header.html.erb → _wrapper.html.erb} +47 -10
  73. data/app/views/pages/home/_highlighted_processes.html.erb +2 -2
  74. data/config/i18n-tasks.yml +3 -0
  75. data/config/locales/ca.yml +40 -1
  76. data/config/locales/en.yml +40 -1
  77. data/config/locales/es.yml +40 -1
  78. data/config/locales/eu.yml +235 -1
  79. data/config/locales/fi.yml +375 -0
  80. data/config/routes.rb +4 -0
  81. data/db/migrate/20170215115407_add_organization_custom_reference.rb +11 -0
  82. data/db/migrate/20170220110740_remove_steps_short_description.rb +21 -0
  83. data/db/migrate/20170221094835_add_scopes_to_processes.rb +6 -0
  84. data/db/migrate/20170228142440_add_participatory_process_groups.rb +15 -0
  85. data/db/migrate/20170306144354_add_secondary_hosts_to_organizations.rb +5 -0
  86. data/db/migrate/20170307084957_create_reports.rb +15 -0
  87. data/db/migrate/20170308091316_create_moderations.rb +12 -0
  88. data/db/migrate/20170313095436_add_available_authorizations_to_organization.rb +10 -0
  89. data/db/seeds.rb +20 -10
  90. data/lib/decidim/core.rb +12 -1
  91. data/lib/decidim/core/api.rb +1 -0
  92. data/lib/decidim/core/api/decidim_type.rb +12 -0
  93. data/lib/decidim/core/api/process_step_type.rb +0 -6
  94. data/lib/decidim/core/api/user_type.rb +4 -0
  95. data/lib/decidim/core/engine.rb +27 -0
  96. data/lib/decidim/core/test.rb +5 -0
  97. data/lib/decidim/core/test/factories.rb +52 -3
  98. data/lib/decidim/core/test/shared_examples/comments_examples.rb +215 -0
  99. data/lib/decidim/core/test/shared_examples/has_reference.rb +30 -0
  100. data/lib/decidim/core/test/shared_examples/manage_moderations_examples.rb +75 -0
  101. data/lib/decidim/core/test/shared_examples/reportable.rb +44 -0
  102. data/lib/decidim/core/test/shared_examples/reports_examples.rb +64 -0
  103. data/lib/decidim/core/version.rb +2 -2
  104. data/lib/decidim/has_reference.rb +50 -0
  105. data/lib/decidim/query_extensions.rb +4 -0
  106. data/lib/decidim/reportable.rb +46 -0
  107. data/vendor/assets/javascripts/leaflet-svg-icon.js +179 -0
  108. data/vendor/assets/javascripts/leaflet-tilelayer-here.js +166 -0
  109. data/vendor/assets/javascripts/leaflet.js +9 -0
  110. data/vendor/assets/javascripts/leaflet.markercluster.js +7 -0
  111. data/vendor/assets/javascripts/quill.min.js +8 -8
  112. data/vendor/assets/javascripts/quill.min.js.map +1 -1
  113. data/vendor/assets/stylesheets/MarkerCluster.Default.css +60 -0
  114. data/vendor/assets/stylesheets/MarkerCluster.css +14 -0
  115. data/vendor/assets/stylesheets/leaflet.css +624 -0
  116. data/vendor/assets/stylesheets/quill.bubble.css +75 -4
  117. data/vendor/assets/stylesheets/quill.core.css +21 -2
  118. data/vendor/assets/stylesheets/quill.snow.css +29 -3
  119. metadata +107 -27
  120. data/app/views/layouts/decidim/_footer.html.erb +0 -36
  121. data/app/views/layouts/decidim/_meta.html.erb +0 -4
@@ -41,7 +41,8 @@ module Decidim
41
41
  safe_join(linked_resources.map do |klass, resources|
42
42
  resource_manifest = klass.constantize.resource_manifest
43
43
  content_tag(:div, class: "section") do
44
- content_tag(:h3, I18n.t(resource_manifest.name, scope: "decidim.resource_links.#{link_name}"), class: "section-heading") +
44
+ i18n_name = "#{resource.class.name.demodulize.underscore}_#{resource_manifest.name}"
45
+ content_tag(:h3, I18n.t(i18n_name, scope: "decidim.resource_links.#{link_name}"), class: "section-heading") +
45
46
  render(partial: resource_manifest.template, locals: { resources: resources })
46
47
  end
47
48
  end)
@@ -10,7 +10,7 @@ module Decidim
10
10
  #
11
11
  # Returns a String with the translation.
12
12
  def translated_attribute(attribute)
13
- attribute.try(:[], I18n.locale.to_s)
13
+ attribute.try(:[], I18n.locale.to_s) || ""
14
14
  end
15
15
 
16
16
  # Public: Creates a translation for each available language in the list
@@ -0,0 +1,35 @@
1
+ # frozen_string_literal: true
2
+ module Decidim
3
+ # A custom mailer for sending notifications to an admin when a report is created..
4
+ class ReportedMailer < Decidim::ApplicationMailer
5
+ helper Decidim::ResourceHelper
6
+
7
+ helper_method :reported_content
8
+
9
+ def report(user, report)
10
+ with_user(user) do
11
+ @report = report
12
+ @organization = user.organization
13
+ @user = user
14
+ subject = I18n.t("report.subject", scope: "decidim.reported_mailer")
15
+ mail(to: user.email, subject: subject)
16
+ end
17
+ end
18
+
19
+ def hide(user, report)
20
+ with_user(user) do
21
+ @report = report
22
+ @organization = user.organization
23
+ @user = user
24
+ subject = I18n.t("hide.subject", scope: "decidim.reported_mailer")
25
+ mail(to: user.email, subject: subject)
26
+ end
27
+ end
28
+
29
+ private
30
+
31
+ def reported_content
32
+ @reported_content ||= @report.moderation.reportable.reported_content
33
+ end
34
+ end
35
+ end
@@ -14,15 +14,41 @@ module Decidim
14
14
  #
15
15
  # env - A Hash.
16
16
  def call(env)
17
- env["decidim.current_organization"] = detect_current_organization(env)
18
- @app.call(env)
17
+ organization = detect_current_organization(env)
18
+ if organization
19
+ env["decidim.current_organization"] = organization
20
+ @app.call(env)
21
+ else
22
+ organization = find_secondary_host_org(env)
23
+ return @app.call(env) unless organization
24
+
25
+ location = new_location_for(env, organization.host)
26
+
27
+ [301, { "Location" => location, 'Content-Type' => 'text/html', 'Content-Length' => '0'}, []]
28
+ end
19
29
  end
20
30
 
21
31
  private
22
32
 
23
33
  def detect_current_organization(env)
24
- host = Rack::Request.new(env).host.downcase
34
+ host = host_for(env)
25
35
  Decidim::Organization.where(host: host).first
26
36
  end
37
+
38
+ def find_secondary_host_org(env)
39
+ host = host_for(env)
40
+ Decidim::Organization.where("? = ANY(secondary_hosts)", host).first
41
+ end
42
+
43
+ def host_for(env)
44
+ Rack::Request.new(env).host.downcase
45
+ end
46
+
47
+ def new_location_for(env, host)
48
+ request = Rack::Request.new(env)
49
+ url = URI(request.url)
50
+ url.host = host
51
+ url.to_s
52
+ end
27
53
  end
28
54
  end
@@ -7,6 +7,7 @@ module Decidim
7
7
  include CanCan::Ability
8
8
 
9
9
  def initialize(user, _context)
10
+ can :read, ParticipatoryProcessGroup
10
11
  can :read, ParticipatoryProcess, &:published?
11
12
  can :read, :public_pages
12
13
  can :manage, :locales
@@ -0,0 +1,13 @@
1
+ # frozen_string_literal: true
2
+ module Decidim
3
+ # A moderation belongs to a reportable and includes many reports
4
+ class Moderation < ApplicationRecord
5
+ belongs_to :reportable, foreign_key: "decidim_reportable_id", foreign_type: "decidim_reportable_type", polymorphic: true
6
+ belongs_to :participatory_process, foreign_key: "decidim_participatory_process_id", class_name: Decidim::ParticipatoryProcess
7
+ has_many :reports, foreign_key: "decidim_moderation_id", class_name: "Decidim::Report"
8
+
9
+ validates :reportable, :participatory_process, presence: true
10
+
11
+ delegate :feature, :organization, to: :reportable
12
+ end
13
+ end
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  module Decidim
2
3
  # This model holds all the data needed to send a newsletter.
3
4
  class Newsletter < ApplicationRecord
@@ -4,6 +4,7 @@ module Decidim
4
4
  # installation we can find many organizations and each of them can start
5
5
  # their own participatory processes.
6
6
  class Organization < ApplicationRecord
7
+ has_many :participatory_process_groups, foreign_key: "decidim_organization_id", class_name: Decidim::ParticipatoryProcessGroup, inverse_of: :organization
7
8
  has_many :participatory_processes, foreign_key: "decidim_organization_id", class_name: Decidim::ParticipatoryProcess, inverse_of: :organization
8
9
  has_many :static_pages, foreign_key: "decidim_organization_id", class_name: Decidim::StaticPage, inverse_of: :organization
9
10
  has_many :scopes, -> { order(name: :asc) }, foreign_key: "decidim_organization_id", class_name: Decidim::Scope, inverse_of: :organization
@@ -11,6 +12,7 @@ module Decidim
11
12
  has_many :users, foreign_key: "decidim_organization_id", class_name: Decidim::User
12
13
 
13
14
  validates :name, :host, uniqueness: true
15
+ validates :reference_prefix, presence: true
14
16
 
15
17
  mount_uploader :homepage_image, Decidim::HomepageImageUploader
16
18
  mount_uploader :official_img_header, Decidim::OfficialImageHeaderUploader
@@ -12,6 +12,13 @@ module Decidim
12
12
  foreign_key: "decidim_organization_id",
13
13
  class_name: Decidim::Organization,
14
14
  inverse_of: :participatory_processes
15
+ belongs_to :participatory_process_group,
16
+ foreign_key: "decidim_participatory_process_group_id",
17
+ class_name: Decidim::ParticipatoryProcessGroup,
18
+ inverse_of: :participatory_processes
19
+ belongs_to :scope,
20
+ foreign_key: "decidim_scope_id",
21
+ class_name: Decidim::Scope
15
22
  has_many :steps,
16
23
  -> { order(position: :asc) },
17
24
  foreign_key: "decidim_participatory_process_id",
@@ -74,5 +81,9 @@ module Decidim
74
81
  def documents
75
82
  @documents ||= attachments.select(&:document?)
76
83
  end
84
+
85
+ def hashtag
86
+ attributes["hashtag"].to_s.gsub("#", "")
87
+ end
77
88
  end
78
89
  end
@@ -0,0 +1,15 @@
1
+ # frozen_string_literal: true
2
+ module Decidim
3
+ class ParticipatoryProcessGroup < ApplicationRecord
4
+ has_many :participatory_processes,
5
+ foreign_key: "decidim_participatory_process_group_id",
6
+ class_name: Decidim::ParticipatoryProcess,
7
+ inverse_of: :participatory_process_group
8
+ belongs_to :organization,
9
+ foreign_key: "decidim_organization_id",
10
+ class_name: Decidim::Organization,
11
+ inverse_of: :participatory_process_groups
12
+
13
+ mount_uploader :hero_image, Decidim::HeroImageUploader
14
+ end
15
+ end
@@ -30,7 +30,7 @@ module Decidim
30
30
  # positioning.
31
31
  def set_position
32
32
  return if position.present?
33
- return self.position = 0 if participatory_process.steps.empty?
33
+ return self.position = 0 if participatory_process.steps.select(&:persisted?).empty?
34
34
 
35
35
  self.position = participatory_process.steps.pluck(:position).last + 1
36
36
  end
@@ -0,0 +1,23 @@
1
+ # frozen_string_literal: true
2
+ module Decidim
3
+ # A reportable can be reported one time for each user.
4
+ class Report < ApplicationRecord
5
+ REASONS = %w(spam offensive does_not_belong).freeze
6
+
7
+ belongs_to :moderation, foreign_key: "decidim_moderation_id", class_name: Decidim::Moderation
8
+ belongs_to :user, foreign_key: "decidim_user_id", class_name: Decidim::User
9
+
10
+ validates :moderation, :user, :reason, presence: true
11
+ validates :user, uniqueness: { scope: :decidim_moderation_id }
12
+ validates :reason, inclusion: { in: REASONS }
13
+ validate :user_and_moderation_same_organization
14
+
15
+ private
16
+
17
+ # Private: check if the moderation and the user have the same organization
18
+ def user_and_moderation_same_organization
19
+ return if !moderation || !user
20
+ errors.add(:moderation, :invalid) unless user.organization == moderation.organization
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,42 @@
1
+ # frozen_string_literal: true
2
+ module Decidim
3
+ # This service is in charge of gathering the ParticipatoryProcess and
4
+ # ParticipatoryProcessGroup that are public and should be displayed together.
5
+ class PublicProcesses
6
+
7
+ # Initializes the PublicProcesses
8
+ #
9
+ # organization - The current organization
10
+ def initialize(organization)
11
+ @organization = organization
12
+ end
13
+
14
+ # Public: The collection of published processes and groups from the given
15
+ # organization to be displayed at processes index.
16
+ #
17
+ # Returns an Array.
18
+ def collection
19
+ (participatory_processes + participatory_process_groups).flatten
20
+ end
21
+
22
+ # Public: The collection of published ParticipatoryProcess to be displayed at the
23
+ # process index.
24
+ #
25
+ # Returns an ActiveRecord::Relation.
26
+ def participatory_processes
27
+ @participatory_processes ||= ordered_processes.published
28
+ end
29
+
30
+ private
31
+
32
+ attr_reader :organization
33
+
34
+ def ordered_processes
35
+ @sorted_processes ||= Decidim::ParticipatoryProcess.where(organization: organization).order("promoted DESC").includes(:active_step).order("decidim_participatory_process_steps.end_date ASC")
36
+ end
37
+
38
+ def participatory_process_groups
39
+ Decidim::ParticipatoryProcessGroup.where(organization: organization)
40
+ end
41
+ end
42
+ end
@@ -7,7 +7,7 @@ module Decidim
7
7
  #
8
8
  # scope - The scope used to create the base query
9
9
  # options - A hash of options to modify the search. These options will be
10
- # converted to methods by SearchLight so they can be used on filter #
10
+ # converted to methods by SearchLight so they can be used on filter
11
11
  # methods. (Default {})
12
12
  def initialize(scope, options = {})
13
13
  super(options)
@@ -28,6 +28,22 @@ module Decidim
28
28
  query.where(decidim_category_id: category_ids)
29
29
  end
30
30
 
31
+ # Handles the scope_id filter. When we want to show only those that do not
32
+ # have a scope_id set, we cannot pass an empty String or nil because Searchlight
33
+ # will automatically filter out these params, so the method will not be used.
34
+ # Instead, we need to pass a fake ID and then convert it inside. In this case,
35
+ # in order to select those elements that do not have a scope_id set we use
36
+ # `"global"` as parameter, and in the method we do the needed changes to search
37
+ # properly.
38
+ #
39
+ # You can use the `search_organization_scopes` helper method, defined in
40
+ # `Decidim::OrganizationScopesHelper`, to render the collection needed for the
41
+ # `collection_check_boxes` form method.
42
+ def search_scope_id
43
+ clean_scope_ids = [scope_id].flatten.map{ |id| id == "global" ? nil : id }
44
+ query.where(decidim_scope_id: clean_scope_ids)
45
+ end
46
+
31
47
  private
32
48
 
33
49
  # Private: Creates an array of category ids.
@@ -0,0 +1,49 @@
1
+ # frozen_string_literal: true
2
+ require "httparty"
3
+
4
+ module Decidim
5
+ # This class generates a url to create a static map image for a geocoded resource
6
+ class StaticMapGenerator
7
+ def initialize(resource, options = {})
8
+ @resource = resource
9
+ @options = options
10
+
11
+ @options[:zoom] ||= 15
12
+ @options[:width] ||= 120
13
+ @options[:height] ||= 120
14
+ end
15
+
16
+ def data
17
+ return if Decidim.geocoder.nil?
18
+
19
+ Rails.cache.fetch(@resource.cache_key) do
20
+ request = HTTParty.get(uri, headers: { "Referer" => organization.host })
21
+ request.body
22
+ end
23
+ end
24
+
25
+ private
26
+
27
+ def uri
28
+ params = {
29
+ c: "#{@resource.latitude}, #{@resource.longitude}",
30
+ z: @options[:zoom],
31
+ w: @options[:width],
32
+ h: @options[:height],
33
+ f: "1",
34
+ app_id: Decidim.geocoder.fetch(:here_app_id),
35
+ app_code: Decidim.geocoder.fetch(:here_app_code)
36
+ }
37
+
38
+ uri = URI.parse(Decidim.geocoder.fetch(:static_map_url)).tap do |uri|
39
+ uri.query = URI.encode_www_form params
40
+ end
41
+
42
+ uri
43
+ end
44
+
45
+ def organization
46
+ @organization ||= @resource.feature.organization
47
+ end
48
+ end
49
+ end
@@ -7,7 +7,10 @@ module Decidim
7
7
  # Override the directory where uploaded files will be stored.
8
8
  # This is a sensible default for uploaders that are meant to be mounted:
9
9
  def store_dir
10
- "uploads/#{model.class.to_s.underscore}/#{mounted_as}/#{model.id}"
10
+ default_path = "uploads/#{model.class.to_s.underscore}/#{mounted_as}/#{model.id}"
11
+
12
+ return File.join(Decidim.base_uploads_path, default_path) if Decidim.base_uploads_path.present?
13
+ default_path
11
14
  end
12
15
  end
13
16
  end
@@ -3,10 +3,10 @@ module Decidim
3
3
  # This class deals with uploading hero images to ParticipatoryProcesses.
4
4
  class OfficialImageFooterUploader < ImageUploader
5
5
  include CarrierWave::MiniMagick
6
- process resize_to_limit: [300, 90]
6
+ process resize_to_limit: [600, 180]
7
7
 
8
8
  def max_image_height_or_width
9
- 300
9
+ 600
10
10
  end
11
11
  end
12
12
  end
@@ -3,7 +3,7 @@ module Decidim
3
3
  # This class deals with uploading hero images to ParticipatoryProcesses.
4
4
  class OfficialImageHeaderUploader < ImageUploader
5
5
  include CarrierWave::MiniMagick
6
- process resize_to_limit: [80, 80]
6
+ process resize_to_limit: [160, 160]
7
7
 
8
8
  def max_image_height_or_width
9
9
  300
@@ -0,0 +1,22 @@
1
+ # coding: utf-8
2
+ # frozen_string_literal: true
3
+ # This validator takes care of ensuring the validated content is
4
+ # an existing address and computes its coordinates.
5
+ class GeocodingValidator < ActiveModel::EachValidator
6
+ def validate_each(record, attribute, value)
7
+ if Decidim.geocoder.present? && record.feature.present?
8
+ organization = record.feature.organization
9
+ Geocoder.configure(Geocoder.config.merge(http_headers: { "Referer" => organization.host }))
10
+ coordinates = Geocoder.coordinates(value)
11
+
12
+ if coordinates.present?
13
+ record.latitude = coordinates.first
14
+ record.longitude = coordinates.last
15
+ else
16
+ record.errors.add(attribute, :invalid)
17
+ end
18
+ else
19
+ record.errors.add(attribute, :invalid)
20
+ end
21
+ end
22
+ end
@@ -22,7 +22,7 @@
22
22
  </div>
23
23
  <% end %>
24
24
  <% end %>
25
- <p class="text-center skip"><%= t("decidim.authorizations.skip_verification", link: link_to(t("decidim.authorizations.current_participatory_processes"), participatory_processes_path).html_safe).html_safe %>.</p>
25
+ <p class="text-center skip"><%= t("decidim.authorizations.skip_verification", link: link_to(t("decidim.authorizations.current_participatory_processes"), stored_location).html_safe).html_safe %>.</p>
26
26
  </div>
27
27
  </div>
28
28
  </div>
@@ -0,0 +1,22 @@
1
+ <div class="column">
2
+ <article class="card card--process">
3
+ <%= link_to participatory_process_group_path(participatory_process_group), class: "card__link" do %>
4
+ <div class="card__image-top"
5
+ style="background-image:url(<%= participatory_process_group.hero_image.url %>)"></div>
6
+ <% end %>
7
+ <div class="card__content">
8
+ <%= link_to participatory_process_group_path(participatory_process_group), class: "card__link" do %>
9
+ <h4 class="card__title"><%= translated_attribute participatory_process_group.name %></h4>
10
+ <% end %>
11
+ <p class="card__desc"><%== html_truncate(translated_attribute(participatory_process_group.description), length: 630, separator: '...') %></p>
12
+ </div>
13
+ <div class="card__footer">
14
+ <div class="card__support">
15
+ <span class="card--process__small"></span>
16
+ <%= link_to participatory_process_group_path(participatory_process_group), class: "card__button button small" do %>
17
+ <%= t(".browse", scope: "layouts") %>
18
+ <% end %>
19
+ </div>
20
+ </div>
21
+ </article>
22
+ </div>