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
@@ -9,10 +9,14 @@ module Decidim
9
9
  helper_method :meeting, :blank_service
10
10
 
11
11
  def new
12
+ enforce_permission_to :copy, :meeting, meeting: meeting
13
+
12
14
  @form = form(MeetingCopyForm).from_model(meeting)
13
15
  end
14
16
 
15
17
  def create
18
+ enforce_permission_to :copy, :meeting, meeting: meeting
19
+
16
20
  @form = form(MeetingCopyForm).from_params(params, current_component: current_component)
17
21
 
18
22
  CopyMeeting.call(@form, meeting) do
@@ -8,10 +8,14 @@ module Decidim
8
8
  helper_method :blank_service
9
9
 
10
10
  def new
11
+ enforce_permission_to :create, :meeting
12
+
11
13
  @form = form(MeetingForm).instance
12
14
  end
13
15
 
14
16
  def create
17
+ enforce_permission_to :create, :meeting
18
+
15
19
  @form = form(MeetingForm).from_params(params, current_component: current_component)
16
20
 
17
21
  CreateMeeting.call(@form) do
@@ -28,10 +32,14 @@ module Decidim
28
32
  end
29
33
 
30
34
  def edit
35
+ enforce_permission_to :update, :meeting, meeting: meeting
36
+
31
37
  @form = form(MeetingForm).from_model(meeting)
32
38
  end
33
39
 
34
40
  def update
41
+ enforce_permission_to :update, :meeting, meeting: meeting
42
+
35
43
  @form = form(MeetingForm).from_params(params, current_component: current_component)
36
44
 
37
45
  UpdateMeeting.call(@form, meeting) do
@@ -48,6 +56,8 @@ module Decidim
48
56
  end
49
57
 
50
58
  def destroy
59
+ enforce_permission_to :destroy, :meeting, meeting: meeting
60
+
51
61
  DestroyMeeting.call(meeting, current_user) do
52
62
  on(:ok) do
53
63
  flash[:notice] = I18n.t("meetings.destroy.success", scope: "decidim.meetings.admin")
@@ -0,0 +1,69 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Decidim
4
+ module Meetings
5
+ module Admin
6
+ # This controller allows an admin to manage minutes from a Meeting
7
+ class MinutesController < Admin::ApplicationController
8
+ helper_method :current_meeting, :minutes
9
+
10
+ def new
11
+ enforce_permission_to :create, :minutes, meeting: current_meeting
12
+
13
+ @form = form(MinutesForm).instance
14
+ end
15
+
16
+ def create
17
+ enforce_permission_to :create, :minutes, meeting: current_meeting
18
+
19
+ @form = form(MinutesForm).from_params(params)
20
+
21
+ CreateMinutes.call(@form, current_meeting) do
22
+ on(:ok) do
23
+ flash[:notice] = I18n.t("minutes.create.success", scope: "decidim.meetings.admin")
24
+ redirect_to meetings_path
25
+ end
26
+
27
+ on(:invalid) do
28
+ flash.now[:alert] = I18n.t("minutes.create.invalid", scope: "decidim.meetings.admin")
29
+ render action: "new"
30
+ end
31
+ end
32
+ end
33
+
34
+ def edit
35
+ enforce_permission_to :update, :minutes, minutes: minutes, meeting: current_meeting
36
+
37
+ @form = form(MinutesForm).from_model(minutes)
38
+ end
39
+
40
+ def update
41
+ enforce_permission_to :update, :minutes, minutes: minutes, meeting: current_meeting
42
+
43
+ @form = form(MinutesForm).from_params(params)
44
+ UpdateMinutes.call(@form, current_meeting, minutes) do
45
+ on(:ok) do
46
+ flash[:notice] = I18n.t("minutes.update.success", scope: "decidim.meetings.admin")
47
+ redirect_to meetings_path
48
+ end
49
+
50
+ on(:invalid) do
51
+ flash.now[:alert] = I18n.t("minutes.update.invalid", scope: "decidim.meetings.admin")
52
+ render action: "edit"
53
+ end
54
+ end
55
+ end
56
+
57
+ private
58
+
59
+ def current_meeting
60
+ @current_meeting ||= Meeting.where(component: current_component).find(params[:meeting_id])
61
+ end
62
+
63
+ def minutes
64
+ @minutes ||= Minutes.where(meeting: current_meeting).find(params[:id])
65
+ end
66
+ end
67
+ end
68
+ end
69
+ end
@@ -6,10 +6,14 @@ module Decidim
6
6
  # This controller allows an admin to manage meeting registrations from a Participatory Process
7
7
  class RegistrationsController < Admin::ApplicationController
8
8
  def edit
9
+ enforce_permission_to :update, :meeting, meeting: meeting
10
+
9
11
  @form = MeetingRegistrationsForm.from_model(meeting)
10
12
  end
11
13
 
12
14
  def update
15
+ enforce_permission_to :update, :meeting, meeting: meeting
16
+
13
17
  @form = MeetingRegistrationsForm.from_params(params).with_context(current_organization: meeting.organization, meeting: meeting)
14
18
 
15
19
  UpdateRegistrations.call(@form, meeting) do
@@ -26,6 +30,8 @@ module Decidim
26
30
  end
27
31
 
28
32
  def export
33
+ enforce_permission_to :export_registrations, :meeting, meeting: meeting
34
+
29
35
  ExportMeetingRegistrations.call(meeting, params[:format], current_user) do
30
36
  on(:ok) do |export_data|
31
37
  send_data export_data.read, type: "text/#{export_data.extension}", filename: export_data.filename("registrations")
@@ -22,6 +22,12 @@ module Decidim
22
22
  end
23
23
  end
24
24
 
25
+ def show
26
+ return if meeting.current_user_can_visit_meeting?(current_user)
27
+ flash[:alert] = I18n.t("meeting.not_allowed", scope: "decidim.meetings")
28
+ redirect_to action: "index"
29
+ end
30
+
25
31
  private
26
32
 
27
33
  def meeting
@@ -29,7 +35,7 @@ module Decidim
29
35
  end
30
36
 
31
37
  def meetings
32
- @meetings ||= paginate(search.results)
38
+ @meetings ||= paginate(search.results).visible_meeting_for(current_user)
33
39
  end
34
40
 
35
41
  def geocoded_meetings
@@ -5,33 +5,33 @@ module Decidim
5
5
  # Exposes the registration resource so users can join and leave meetings.
6
6
  class RegistrationsController < Decidim::Meetings::ApplicationController
7
7
  def create
8
- authorize! :join, meeting
8
+ enforce_permission_to :join, :meeting, meeting: meeting
9
9
 
10
10
  JoinMeeting.call(meeting, current_user) do
11
11
  on(:ok) do
12
12
  flash[:notice] = I18n.t("registrations.create.success", scope: "decidim.meetings")
13
- redirect_to meeting_path(meeting)
13
+ redirect_after_path
14
14
  end
15
15
 
16
16
  on(:invalid) do
17
17
  flash.now[:alert] = I18n.t("registrations.create.invalid", scope: "decidim.meetings")
18
- redirect_to meeting_path(meeting)
18
+ redirect_after_path
19
19
  end
20
20
  end
21
21
  end
22
22
 
23
23
  def destroy
24
- authorize! :leave, meeting
24
+ enforce_permission_to :leave, :meeting, meeting: meeting
25
25
 
26
26
  LeaveMeeting.call(meeting, current_user) do
27
27
  on(:ok) do
28
28
  flash[:notice] = I18n.t("registrations.destroy.success", scope: "decidim.meetings")
29
- redirect_to meeting_path(meeting)
29
+ redirect_after_path
30
30
  end
31
31
 
32
32
  on(:invalid) do
33
33
  flash.now[:alert] = I18n.t("registrations.destroy.invalid", scope: "decidim.meetings")
34
- redirect_to meeting_path(meeting)
34
+ redirect_after_path
35
35
  end
36
36
  end
37
37
  end
@@ -41,6 +41,12 @@ module Decidim
41
41
  def meeting
42
42
  @meeting ||= Meeting.where(component: current_component).find(params[:meeting_id])
43
43
  end
44
+
45
+ def redirect_after_path
46
+ referer = request.headers["Referer"]
47
+ return redirect_to(meeting_path(meeting)) if referer =~ /invitation_token/
48
+ redirect_back fallback_location: meeting_path(meeting)
49
+ end
44
50
  end
45
51
  end
46
52
  end
@@ -0,0 +1,54 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Decidim
4
+ module Meetings
5
+ module Admin
6
+ # This class holds a Form to update meeting agenda items
7
+ class MeetingAgendaForm < Decidim::Form
8
+ include TranslatableAttributes
9
+
10
+ translatable_attribute :title, String
11
+ attribute :agenda_items, Array[MeetingAgendaItemsForm]
12
+ attribute :visible, Boolean
13
+
14
+ validates :title, translatable_presence: true
15
+ validate :agenda_duration_too_long
16
+ validate :agenda_items_duration_too_long
17
+
18
+ def map_model(model)
19
+ self.agenda_items = model.agenda_items.first_class.map do |agenda_item|
20
+ MeetingAgendaItemsForm.from_model(agenda_item)
21
+ end
22
+ end
23
+
24
+ private
25
+
26
+ def meeting
27
+ @meeting ||= context[:meeting]
28
+ end
29
+
30
+ def agenda_duration_too_long
31
+ if agenda_duration > meeting.meeting_duration
32
+ difference = agenda_duration - meeting.meeting_duration
33
+ errors.add(:base, :too_many_minutes, count: difference)
34
+ end
35
+ end
36
+
37
+ def agenda_duration
38
+ @agenda_duration ||= agenda_items.sum(&:duration)
39
+ end
40
+
41
+ def agenda_items_duration_too_long
42
+ agenda_items.each do |agenda_item|
43
+ children_duration = agenda_item.agenda_item_children.sum(&:duration)
44
+
45
+ if children_duration > agenda_item.duration
46
+ difference = children_duration - agenda_item.duration
47
+ errors.add(:base, :too_many_minutes_child, parent_title: translated_attribute(agenda_item.title), count: difference)
48
+ end
49
+ end
50
+ end
51
+ end
52
+ end
53
+ end
54
+ end
@@ -0,0 +1,33 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Decidim
4
+ module Meetings
5
+ module Admin
6
+ # This class holds a Form to update meeting agenda items
7
+ class MeetingAgendaItemsForm < Decidim::Form
8
+ include TranslatableAttributes
9
+
10
+ translatable_attribute :title, String
11
+ translatable_attribute :description, String
12
+
13
+ attribute :duration, Integer, default: 0
14
+ attribute :parent_id, Integer
15
+ attribute :position, Integer
16
+ attribute :deleted, Boolean, default: false
17
+ attribute :agenda_item_children, Array[MeetingAgendaItemsForm]
18
+
19
+ validates :title, translatable_presence: true, unless: :deleted
20
+ validates :position, numericality: { greater_than_or_equal_to: 0 }, unless: :deleted
21
+ validates :duration, presence: true, numericality: { greater_than_or_equal_to: 0 }
22
+
23
+ def to_param
24
+ id || "meeting-agenda-item-id"
25
+ end
26
+
27
+ def to_param_child
28
+ id || "meeting-agenda-item-child-id"
29
+ end
30
+ end
31
+ end
32
+ end
33
+ end
@@ -19,6 +19,9 @@ module Decidim
19
19
  attribute :longitude, Float
20
20
  attribute :start_time, Decidim::Attributes::TimeWithZone
21
21
  attribute :end_time, Decidim::Attributes::TimeWithZone
22
+ attribute :private_meeting, Boolean
23
+ attribute :transparent, Boolean
24
+ attribute :organizer_id, Integer
22
25
  attribute :services, Array[MeetingServiceForm]
23
26
 
24
27
  mimic :meeting
@@ -32,6 +35,7 @@ module Decidim
32
35
  validates :address, geocoding: true, if: -> { Decidim.geocoder.present? }
33
36
  validates :start_time, presence: true, date: { before: :end_time }
34
37
  validates :end_time, presence: true, date: { after: :start_time }
38
+ validates :organizer, presence: true, if: ->(form) { form.organizer_id.present? }
35
39
 
36
40
  def map_model(model)
37
41
  self.services = model.services.map do |service|
@@ -48,6 +52,10 @@ module Decidim
48
52
  end
49
53
 
50
54
  alias component current_component
55
+
56
+ def organizer
57
+ @organizer ||= current_organization.users.find_by(id: organizer_id)
58
+ end
51
59
  end
52
60
  end
53
61
  end
@@ -20,6 +20,9 @@ module Decidim
20
20
  attribute :services, Array[MeetingServiceForm]
21
21
  attribute :decidim_scope_id, Integer
22
22
  attribute :decidim_category_id, Integer
23
+ attribute :private_meeting, Boolean
24
+ attribute :transparent, Boolean
25
+ attribute :organizer_id, Integer
23
26
 
24
27
  validates :title, translatable_presence: true
25
28
  validates :description, translatable_presence: true
@@ -32,6 +35,7 @@ module Decidim
32
35
  validates :current_component, presence: true
33
36
  validates :category, presence: true, if: ->(form) { form.decidim_category_id.present? }
34
37
  validates :scope, presence: true, if: ->(form) { form.decidim_scope_id.present? }
38
+ validates :organizer, presence: true, if: ->(form) { form.organizer_id.present? }
35
39
 
36
40
  validate :scope_belongs_to_participatory_space_scope
37
41
 
@@ -53,6 +57,10 @@ module Decidim
53
57
  services.size
54
58
  end
55
59
 
60
+ def organizer
61
+ @organizer ||= current_organization.users.find_by(id: organizer_id)
62
+ end
63
+
56
64
  alias component current_component
57
65
 
58
66
  # Finds the Scope from the given decidim_scope_id, uses participatory space scope if missing.
@@ -9,7 +9,8 @@ module Decidim
9
9
  attribute :name, String
10
10
  attribute :email, String
11
11
 
12
- validates :name, :email, presence: true
12
+ validates :name, presence: true
13
+ validates :email, presence: true, 'valid_email_2/email': { disposable: true }
13
14
  end
14
15
  end
15
16
  end
@@ -0,0 +1,20 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Decidim
4
+ module Meetings
5
+ module Admin
6
+ # This class holds a Form to create/update minutes from Decidim's admin panel.
7
+ class MinutesForm < Decidim::Form
8
+ include TranslatableAttributes
9
+
10
+ translatable_attribute :description, String
11
+
12
+ attribute :video_url, String
13
+ attribute :audio_url, String
14
+ attribute :visible, Boolean
15
+
16
+ validates :description, translatable_presence: true
17
+ end
18
+ end
19
+ end
20
+ end
@@ -8,9 +8,22 @@ module Decidim
8
8
  module ApplicationHelper
9
9
  include Decidim::MapHelper
10
10
 
11
+ def meeting_organizer_picker_text(form)
12
+ return "" if form.object.organizer.blank?
13
+ "#{form.object.organizer.name} (@#{form.object.organizer.nickname})"
14
+ end
15
+
11
16
  def tabs_id_for_service(service)
12
17
  "meeting_service_#{service.to_param}"
13
18
  end
19
+
20
+ def tabs_id_for_agenda_item(agenda_item)
21
+ "meeting_agenda_item_#{agenda_item.to_param}"
22
+ end
23
+
24
+ def tabs_id_for_agenda_item_child(agenda_item)
25
+ "meeting_agenda_item_#{agenda_item.to_param_child}"
26
+ end
14
27
  end
15
28
  end
16
29
  end
@@ -2,11 +2,17 @@
2
2
 
3
3
  module Decidim
4
4
  module Meetings
5
- class ViewModel < Decidim::ViewModel
5
+ # Custom helpers used in meetings views
6
+ module MeetingCellsHelper
6
7
  include TranslatableAttributes
7
8
  include LayoutHelper
8
9
  include Decidim::Meetings::MeetingsHelper
9
10
  include Decidim::SanitizeHelper
11
+ include Decidim::Meetings::Engine.routes.url_helpers
12
+
13
+ def description
14
+ decidim_sanitize meeting_description(model)
15
+ end
10
16
  end
11
17
  end
12
18
  end