decidim-meetings 0.11.2 → 0.12.0.pre

Sign up to get free protection for your applications and to get access to all the features.
Files changed (105) hide show
  1. checksums.yaml +4 -4
  2. data/app/assets/config/admin/decidim_meetings_manifest.js +2 -0
  3. data/app/assets/javascripts/decidim/meetings/admin/agendas.js.es6 +158 -0
  4. data/app/assets/javascripts/decidim/meetings/admin/meetings_form.js.es6 +19 -0
  5. data/app/cells/decidim/meetings/join_meeting_button/registration_confirm.erb +15 -0
  6. data/app/cells/decidim/meetings/join_meeting_button/show.erb +27 -0
  7. data/app/cells/decidim/meetings/join_meeting_button_cell.rb +37 -0
  8. data/app/cells/decidim/meetings/meeting_cell.rb +2 -13
  9. data/app/cells/decidim/meetings/meeting_list_item_cell.rb +8 -0
  10. data/app/cells/decidim/meetings/meeting_m/address.erb +14 -0
  11. data/app/cells/decidim/meetings/meeting_m/data.erb +2 -0
  12. data/app/cells/decidim/meetings/meeting_m/date.erb +8 -0
  13. data/app/cells/decidim/meetings/meeting_m/footer.erb +9 -0
  14. data/app/cells/decidim/meetings/meeting_m/multiple_dates.erb +17 -0
  15. data/app/cells/decidim/meetings/meeting_m/single_date.erb +9 -0
  16. data/app/cells/decidim/meetings/meeting_m/tags.erb +1 -0
  17. data/app/cells/decidim/meetings/meeting_m_cell.rb +37 -4
  18. data/app/commands/decidim/meetings/admin/copy_meeting.rb +3 -0
  19. data/app/commands/decidim/meetings/admin/create_agenda.rb +93 -0
  20. data/app/commands/decidim/meetings/admin/create_meeting.rb +3 -0
  21. data/app/commands/decidim/meetings/admin/create_minutes.rb +55 -0
  22. data/app/commands/decidim/meetings/admin/update_agenda.rb +90 -0
  23. data/app/commands/decidim/meetings/admin/update_meeting.rb +4 -1
  24. data/app/commands/decidim/meetings/admin/update_minutes.rb +62 -0
  25. data/app/controllers/decidim/meetings/admin/agenda_controller.rb +75 -0
  26. data/app/controllers/decidim/meetings/admin/attachments_controller.rb +1 -1
  27. data/app/controllers/decidim/meetings/admin/invites_controller.rb +4 -0
  28. data/app/controllers/decidim/meetings/admin/meeting_closes_controller.rb +4 -0
  29. data/app/controllers/decidim/meetings/admin/meeting_copies_controller.rb +4 -0
  30. data/app/controllers/decidim/meetings/admin/meetings_controller.rb +10 -0
  31. data/app/controllers/decidim/meetings/admin/minutes_controller.rb +69 -0
  32. data/app/controllers/decidim/meetings/admin/registrations_controller.rb +6 -0
  33. data/app/controllers/decidim/meetings/meetings_controller.rb +7 -1
  34. data/app/controllers/decidim/meetings/registrations_controller.rb +12 -6
  35. data/app/forms/decidim/meetings/admin/meeting_agenda_form.rb +54 -0
  36. data/app/forms/decidim/meetings/admin/meeting_agenda_items_form.rb +33 -0
  37. data/app/forms/decidim/meetings/admin/meeting_copy_form.rb +8 -0
  38. data/app/forms/decidim/meetings/admin/meeting_form.rb +8 -0
  39. data/app/forms/decidim/meetings/admin/meeting_registration_invite_form.rb +2 -1
  40. data/app/forms/decidim/meetings/admin/minutes_form.rb +20 -0
  41. data/app/helpers/decidim/meetings/admin/application_helper.rb +13 -0
  42. data/{lib/decidim/meetings/view_model.rb → app/helpers/decidim/meetings/meeting_cells_helper.rb} +7 -1
  43. data/app/helpers/decidim/meetings/meetings_helper.rb +67 -0
  44. data/app/models/decidim/meetings/agenda.rb +19 -0
  45. data/app/models/decidim/meetings/agenda_item.rb +35 -0
  46. data/app/models/decidim/meetings/meeting.rb +52 -3
  47. data/app/models/decidim/meetings/minutes.rb +19 -0
  48. data/app/permissions/decidim/meetings/admin/permissions.rb +69 -0
  49. data/app/permissions/decidim/meetings/permissions.rb +41 -0
  50. data/app/presenters/decidim/meetings/admin_log/meeting_presenter.rb +4 -1
  51. data/app/presenters/decidim/meetings/admin_log/minutes_presenter.rb +42 -0
  52. data/app/presenters/decidim/meetings/admin_log/value_types/organizer_presenter.rb +69 -0
  53. data/app/types/decidim/meetings/meeting_type.rb +4 -4
  54. data/app/views/decidim/meetings/admin/agenda/_agenda_item.html.erb +50 -0
  55. data/app/views/decidim/meetings/admin/agenda/_agenda_item_child.html.erb +26 -0
  56. data/app/views/decidim/meetings/admin/agenda/_agenda_item_fields.html.erb +19 -0
  57. data/app/views/decidim/meetings/admin/agenda/_form.html.erb +66 -0
  58. data/app/views/decidim/meetings/admin/agenda/edit.html.erb +7 -0
  59. data/app/views/decidim/meetings/admin/agenda/new.html.erb +8 -0
  60. data/app/views/decidim/meetings/admin/agenda/show.html.erb +0 -0
  61. data/app/views/decidim/meetings/admin/meeting_copies/_form.html.erb +15 -1
  62. data/app/views/decidim/meetings/admin/meetings/_form.html.erb +15 -0
  63. data/app/views/decidim/meetings/admin/meetings/index.html.erb +9 -7
  64. data/app/views/decidim/meetings/admin/minutes/_form.html.erb +23 -0
  65. data/app/views/decidim/meetings/admin/minutes/edit.html.erb +7 -0
  66. data/app/views/decidim/meetings/admin/minutes/new.html.erb +7 -0
  67. data/app/views/decidim/meetings/admin/registrations/_form.html.erb +11 -9
  68. data/app/views/decidim/meetings/meetings/_linked_meetings.html.erb +0 -1
  69. data/app/views/decidim/meetings/meetings/_meeting_agenda.html.erb +31 -0
  70. data/app/views/decidim/meetings/meetings/_meeting_minutes.html.erb +48 -0
  71. data/app/views/decidim/meetings/meetings/_meetings.html.erb +1 -1
  72. data/app/views/decidim/meetings/meetings/show.html.erb +23 -12
  73. data/app/views/decidim/participatory_spaces/_meeting.html.erb +1 -1
  74. data/app/views/decidim/participatory_spaces/_upcoming_meeting_for_card.html.erb +6 -0
  75. data/config/locales/ca.yml +106 -4
  76. data/config/locales/en.yml +106 -4
  77. data/config/locales/es.yml +106 -4
  78. data/config/locales/eu.yml +106 -4
  79. data/config/locales/fi.yml +106 -4
  80. data/config/locales/fr.yml +106 -4
  81. data/config/locales/gl.yml +106 -4
  82. data/config/locales/it.yml +106 -4
  83. data/config/locales/nl.yml +106 -4
  84. data/config/locales/pl.yml +112 -4
  85. data/config/locales/pt-BR.yml +106 -4
  86. data/config/locales/pt.yml +106 -4
  87. data/config/locales/ru.yml +97 -4
  88. data/config/locales/sv.yml +106 -4
  89. data/config/locales/uk.yml +112 -4
  90. data/db/migrate/20180403145218_add_meeting_types.rb +8 -0
  91. data/db/migrate/20180404075312_add_organizer_to_meetings.rb +7 -0
  92. data/db/migrate/20180406080619_create_meetings_minutes.rb +15 -0
  93. data/db/migrate/20180419131924_create_decidim_agendas.rb +13 -0
  94. data/db/migrate/20180419132104_create_decidim_agenda_items.rb +16 -0
  95. data/db/migrate/20180516102301_index_meetings_as_searchable_resources.rb +24 -0
  96. data/lib/decidim/meetings/admin_engine.rb +2 -0
  97. data/lib/decidim/meetings/component.rb +3 -2
  98. data/lib/decidim/meetings/engine.rb +16 -6
  99. data/lib/decidim/meetings/test/factories.rb +44 -0
  100. data/lib/decidim/meetings/version.rb +1 -1
  101. metadata +63 -21
  102. data/app/cells/decidim/meetings/meeting_m/header.erb +0 -5
  103. data/app/cells/decidim/meetings/meeting_m/show.erb +0 -21
  104. data/app/models/decidim/meetings/abilities/current_user_ability.rb +0 -43
  105. data/app/views/decidim/meetings/meetings/_registration_confirm.html.erb +0 -15
@@ -20,6 +20,73 @@ module Decidim
20
20
  tail = "... #{link_to(t("read_more", scope: "decidim.meetings"), link)}".html_safe
21
21
  CGI.unescapeHTML html_truncate(description, max_length: max_length, tail: tail)
22
22
  end
23
+
24
+ # Public: The css class applied based on the meeting type to
25
+ # the css class.
26
+ #
27
+ # type - The String type of the meeting.
28
+ #
29
+ # Returns a String.
30
+ def meeting_type_badge_css_class(type)
31
+ case type
32
+ when "private"
33
+ "alert"
34
+ when "transparent"
35
+ "secondary"
36
+ end
37
+ end
38
+
39
+ # Public: This method is used to calculate the start and end time
40
+ # of each agenda item passed
41
+ #
42
+ # agenda_items - an Active record of agenda items
43
+ # meeting - the meeting of the agenda, to know the start and end time
44
+ # start_time_parent - used to pass the start time of parent agenda item
45
+ #
46
+ # Returns an Array.
47
+ def calculate_start_and_end_time_of_agenda_items(agenda_items, meeting, start_time_parent = nil)
48
+ array = []
49
+
50
+ agenda_items.each_with_index do |agenda_item, index|
51
+ hash = {
52
+ agenda_item_id: agenda_item.id,
53
+ start_time: nil,
54
+ end_time: nil
55
+ }
56
+ if index.zero?
57
+ start = if agenda_item.parent?
58
+ meeting.start_time
59
+ else
60
+ start_time_parent
61
+ end
62
+
63
+ hash[:start_time] = start
64
+ else
65
+ hash[:start_time] = array[index - 1][:end_time]
66
+ end
67
+
68
+ hash[:end_time] = hash[:start_time] + agenda_item.duration.minutes
69
+
70
+ array.push(hash)
71
+ end
72
+
73
+ array
74
+ end
75
+
76
+ # Public: This method is used to build the html for show start
77
+ # and end time of each agenda item
78
+ #
79
+ # agenda_item_id - an id of agenda item
80
+ # agenda_items_times - is a hash with the two times
81
+ #
82
+ # Returns an HMTL.
83
+ def display_duration_agenda_items(agenda_item_id, index, agenda_items_times)
84
+ html = ""
85
+ if agenda_item_id == agenda_items_times[index][:agenda_item_id]
86
+ html += "[ #{agenda_items_times[index][:start_time].strftime("%H:%M")} - #{agenda_items_times[index][:end_time].strftime("%H:%M")}]"
87
+ end
88
+ html.html_safe
89
+ end
23
90
  end
24
91
  end
25
92
  end
@@ -0,0 +1,19 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Decidim
4
+ module Meetings
5
+ # The data store for a Agena in the Decidim::Meetings component. It stores a
6
+ # title, and visible field to render inside a meeting.
7
+ class Agenda < Meetings::ApplicationRecord
8
+ include Decidim::Traceable
9
+ include Decidim::Loggable
10
+
11
+ belongs_to :meeting, foreign_key: "decidim_meeting_id", class_name: "Decidim::Meetings::Meeting"
12
+ has_many :agenda_items, foreign_key: "decidim_agenda_id", class_name: "Decidim::Meetings::AgendaItem", dependent: :destroy, inverse_of: :agenda
13
+
14
+ def self.log_presenter_class_for(_log)
15
+ Decidim::Meetings::AdminLog::AgendaPresenter
16
+ end
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,35 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Decidim
4
+ module Meetings
5
+ # The data store for a AgendaItem in the Decidim::Meetings component. It stores a
6
+ # title, description and duration to render inside meeting.
7
+ class AgendaItem < Meetings::ApplicationRecord
8
+ include Decidim::Traceable
9
+ include Decidim::Loggable
10
+
11
+ belongs_to :agenda, -> { order(:position) }, foreign_key: "decidim_agenda_id", class_name: "Decidim::Meetings::Agenda"
12
+
13
+ has_many :agenda_item_children, foreign_key: "parent_id", class_name: "Decidim::Meetings::AgendaItem", inverse_of: :parent, dependent: :destroy
14
+ belongs_to :parent, foreign_key: "parent_id", class_name: "Decidim::Meetings::AgendaItem", inverse_of: :agenda_item_children, optional: true
15
+
16
+ default_scope { order(:position) }
17
+
18
+ def self.first_class
19
+ where(parent_id: nil)
20
+ end
21
+
22
+ def parent?
23
+ return true unless parent_id
24
+ end
25
+
26
+ def self.agenda_item_children
27
+ where.not(parent_id: nil)
28
+ end
29
+
30
+ def self.log_presenter_class_for(_log)
31
+ Decidim::Meetings::AdminLog::AgendaItemPresenter
32
+ end
33
+ end
34
+ end
35
+ end
@@ -14,24 +14,48 @@ module Decidim
14
14
  include Decidim::HasCategory
15
15
  include Decidim::Followable
16
16
  include Decidim::Comments::Commentable
17
+ include Decidim::Searchable
17
18
  include Decidim::Traceable
18
19
  include Decidim::Loggable
19
20
 
21
+ belongs_to :organizer, foreign_key: "organizer_id", class_name: "Decidim::User", optional: true
20
22
  has_many :registrations, class_name: "Decidim::Meetings::Registration", foreign_key: "decidim_meeting_id", dependent: :destroy
23
+ has_one :minutes, class_name: "Decidim::Meetings::Minutes", foreign_key: "decidim_meeting_id", dependent: :destroy
24
+ has_one :agenda, class_name: "Decidim::Meetings::Agenda", foreign_key: "decidim_meeting_id", dependent: :destroy
21
25
 
22
26
  component_manifest_name "meetings"
23
27
 
24
28
  validates :title, presence: true
29
+ validate :organizer_belongs_to_organization
25
30
 
26
31
  geocoded_by :address, http_headers: ->(proposal) { { "Referer" => proposal.component.organization.host } }
27
32
 
28
33
  scope :past, -> { where(arel_table[:end_time].lteq(Time.current)) }
29
34
  scope :upcoming, -> { where(arel_table[:start_time].gt(Time.current)) }
30
35
 
36
+ scope :visible_meeting_for, lambda { |user|
37
+ joins("LEFT JOIN decidim_meetings_registrations ON
38
+ decidim_meetings_registrations.decidim_meeting_id = #{table_name}.id")
39
+ .where("(private_meeting = ? and decidim_meetings_registrations.decidim_user_id = ?)
40
+ or private_meeting = ? or (private_meeting = ? and transparent = ?)", true, user, false, true, true).distinct
41
+ }
42
+
43
+ searchable_fields(
44
+ scope_id: :decidim_scope_id,
45
+ participatory_space: { component: :participatory_space },
46
+ A: :title,
47
+ D: [:description, :address],
48
+ datetime: :start_time
49
+ )
50
+
31
51
  def self.log_presenter_class_for(_log)
32
52
  Decidim::Meetings::AdminLog::MeetingPresenter
33
53
  end
34
54
 
55
+ def can_be_joined_by?(user)
56
+ !closed? && registrations_enabled? && can_participate?(user)
57
+ end
58
+
35
59
  def closed?
36
60
  closed_at.present?
37
61
  end
@@ -74,10 +98,35 @@ module Decidim
74
98
  followers
75
99
  end
76
100
 
101
+ # rubocop:disable Metrics/PerceivedComplexity,Metrics/CyclomaticComplexity
77
102
  def can_participate?(user)
78
- return true unless participatory_space.try(:private_space?)
79
- return true if participatory_space.try(:private_space?) && participatory_space.users.include?(user)
80
- return false if participatory_space.try(:private_space?) && participatory_space.try(:is_transparent?)
103
+ return true unless participatory_space.try(:private_space?) || private_meeting?
104
+ return true if (participatory_space.try(:private_space?) &&
105
+ participatory_space.users.include?(user)) ||
106
+ (private_meeting? && registrations.exists?(decidim_user_id: user.try(:id)))
107
+ return false if (participatory_space.try(:private_space?) &&
108
+ participatory_space.try(:transparent?)) ||
109
+ (private_meeting? && transparent?)
110
+ end
111
+ # rubocop:enable Metrics/PerceivedComplexity,Metrics/CyclomaticComplexity
112
+
113
+ def organizer_belongs_to_organization
114
+ return if !organizer || !organization
115
+ errors.add(:organizer, :invalid) unless organizer.organization == organization
116
+ end
117
+
118
+ def official?
119
+ organizer.nil?
120
+ end
121
+
122
+ def current_user_can_visit_meeting?(current_user)
123
+ (private_meeting? && registrations.exists?(decidim_user_id: current_user.try(:id))) ||
124
+ !private_meeting? || (private_meeting? && transparent?)
125
+ end
126
+
127
+ # Return the duration of the meeting in minutes
128
+ def meeting_duration
129
+ @meeting_duration ||= ((end_time - start_time) / 1.minute).abs
81
130
  end
82
131
  end
83
132
  end
@@ -0,0 +1,19 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Decidim
4
+ module Meetings
5
+ # The data store for a Minutes in the Decidim::Meetings component.
6
+ class Minutes < Meetings::ApplicationRecord
7
+ include Decidim::Traceable
8
+ include Decidim::Loggable
9
+
10
+ belongs_to :meeting, foreign_key: "decidim_meeting_id", class_name: "Decidim::Meetings::Meeting"
11
+
12
+ def self.log_presenter_class_for(_log)
13
+ Decidim::Meetings::AdminLog::MinutesPresenter
14
+ end
15
+
16
+ delegate :component, to: :meeting
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,69 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Decidim
4
+ module Meetings
5
+ module Admin
6
+ class Permissions < Decidim::DefaultPermissions
7
+ def permissions
8
+ return permission_action unless user
9
+ return permission_action unless permission_action.scope == :admin
10
+
11
+ allowed_meeting_action?
12
+ allowed_agenda_action?
13
+ allowed_minutes_action?
14
+
15
+ permission_action
16
+ end
17
+
18
+ private
19
+
20
+ def meeting
21
+ @meeting ||= context.fetch(:meeting, nil)
22
+ end
23
+
24
+ def agenda
25
+ @agenda ||= context.fetch(:agenda, nil)
26
+ end
27
+
28
+ def minutes
29
+ @minutes ||= context.fetch(:minutes, nil)
30
+ end
31
+
32
+ def allowed_meeting_action?
33
+ return unless permission_action.subject == :meeting
34
+
35
+ case permission_action.action
36
+ when :close, :copy, :destroy, :export_registrations, :update
37
+ toggle_allow(meeting.present?)
38
+ when :invite_user
39
+ toggle_allow(meeting.present? && meeting.registrations_enabled?)
40
+ when :create
41
+ allow!
42
+ end
43
+ end
44
+
45
+ def allowed_agenda_action?
46
+ return unless permission_action.subject == :agenda
47
+
48
+ case permission_action.action
49
+ when :create
50
+ toggle_allow(meeting.present?)
51
+ when :update
52
+ toggle_allow(agenda.present? && meeting.present?)
53
+ end
54
+ end
55
+
56
+ def allowed_minutes_action?
57
+ return unless permission_action.subject == :minutes
58
+
59
+ case permission_action.action
60
+ when :create
61
+ toggle_allow(meeting.present?)
62
+ when :update
63
+ toggle_allow(minutes.present? && meeting.present?)
64
+ end
65
+ end
66
+ end
67
+ end
68
+ end
69
+ end
@@ -0,0 +1,41 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Decidim
4
+ module Meetings
5
+ class Permissions < Decidim::DefaultPermissions
6
+ def permissions
7
+ return permission_action unless user
8
+
9
+ # Delegate the admin permission checks to the admin permissions class
10
+ return Decidim::Meetings::Admin::Permissions.new(user, permission_action, context).permissions if permission_action.scope == :admin
11
+ return permission_action if permission_action.scope != :public
12
+
13
+ return permission_action if permission_action.subject != :meeting
14
+
15
+ case permission_action.action
16
+ when :join
17
+ toggle_allow(can_join_meeting?)
18
+ when :leave
19
+ toggle_allow(can_leave_meeting?)
20
+ end
21
+
22
+ permission_action
23
+ end
24
+
25
+ private
26
+
27
+ def meeting
28
+ @meeting ||= context.fetch(:meeting, nil)
29
+ end
30
+
31
+ def can_join_meeting?
32
+ meeting.can_be_joined_by?(user) &&
33
+ authorized?(:join)
34
+ end
35
+
36
+ def can_leave_meeting?
37
+ meeting.registrations_enabled?
38
+ end
39
+ end
40
+ end
41
+ end
@@ -27,7 +27,10 @@ module Decidim
27
27
  location: :i18n,
28
28
  location_hints: :i18n,
29
29
  start_date: :date,
30
- title: :i18n
30
+ title: :i18n,
31
+ private_meeting: :boolean,
32
+ transparent: :boolean,
33
+ organizer_id: "Decidim::Meetings::AdminLog::ValueTypes::OrganizerPresenter"
31
34
  }
32
35
  end
33
36
 
@@ -0,0 +1,42 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Decidim
4
+ module Meetings
5
+ module AdminLog
6
+ # This class holds the logic to present a `Decidim::Meetings::Minutes`
7
+ # for the `AdminLog` log.
8
+ #
9
+ # Usage should be automatic and you shouldn't need to call this class
10
+ # directly, but here's an example:
11
+ #
12
+ # action_log = Decidim::ActionLog.last
13
+ # view_helpers # => this comes from the views
14
+ # MeetingPresenter.new(action_log, view_helpers).present
15
+ class MinutesPresenter < Decidim::Log::BasePresenter
16
+ private
17
+
18
+ def diff_fields_mapping
19
+ {
20
+ description: :i18n,
21
+ video_url: :string,
22
+ audio_url: :string,
23
+ visible: :boolean
24
+ }
25
+ end
26
+
27
+ def action_string
28
+ case action
29
+ when "create", "delete", "update"
30
+ "decidim.meetings.admin_log.minutes.#{action}"
31
+ else
32
+ super
33
+ end
34
+ end
35
+
36
+ def i18n_labels_scope
37
+ "activemodel.attributes.minutes"
38
+ end
39
+ end
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,69 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Decidim
4
+ module Meetings
5
+ module AdminLog
6
+ module ValueTypes
7
+ # This class presents the given value as a Decidim::Meetings::Organizer. Check
8
+ # the `DefaultPresenter` for more info on how value presenters work.
9
+ class OrganizerPresenter < Decidim::Log::ValueTypes::DefaultPresenter
10
+ include Rails.application.routes.mounted_helpers
11
+ include ActionView::Helpers::UrlHelper
12
+ # Public: Presents the value as a Decidim::Meetings::Organizer. If the result can
13
+ # be found, it shows its title. Otherwise it shows its ID.
14
+ #
15
+ # Returns an HTML-safe String.
16
+ def present
17
+ return unless value
18
+ return present_organizer if organizer
19
+ I18n.t("not_found", id: value, scope: "decidim.meetings.admin_log.meeting.value_types.organizer_presenter")
20
+ end
21
+
22
+ private
23
+
24
+ def organizer
25
+ @organizer ||= Decidim::User.find_by(id: value)
26
+ end
27
+
28
+ def present_organizer
29
+ return content_tag(:span, present_user_name, class: "logs__log__author") if organizer.blank?
30
+
31
+ link_to(
32
+ present_user_name,
33
+ user_path,
34
+ class: "logs__log__author",
35
+ title: "@" + present_user_nickname,
36
+ target: "_blank",
37
+ data: {
38
+ tooltip: true,
39
+ "disable-hover": false
40
+ }
41
+ )
42
+ end
43
+
44
+ # Private: Presents the name of the organizer performing the action.
45
+ #
46
+ # Returns an HTML-safe String.
47
+ def present_user_name
48
+ organizer.name.html_safe
49
+ end
50
+
51
+ # Private: Presents the nickname of the organizer performing the action.
52
+ #
53
+ # Returns an HTML-safe String.
54
+ def present_user_nickname
55
+ organizer.nickname.html_safe
56
+ end
57
+
58
+ # Private: Calculates the path for the organizer. Returns the path of the
59
+ # user profile. It's a public link.
60
+ #
61
+ # Returns an HTML-safe String.
62
+ def user_path
63
+ decidim.profile_path(present_user_nickname)
64
+ end
65
+ end
66
+ end
67
+ end
68
+ end
69
+ end