decidim-posts 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (117) hide show
  1. checksums.yaml +7 -0
  2. data/LICENSE-AGPLv3.txt +661 -0
  3. data/README.md +40 -0
  4. data/Rakefile +9 -0
  5. data/app/cells/decidim/posts/comments/add_comment.erb +15 -0
  6. data/app/cells/decidim/posts/comments/comments_loading.erb +1 -0
  7. data/app/cells/decidim/posts/comments/order_control.erb +13 -0
  8. data/app/cells/decidim/posts/comments/show.erb +31 -0
  9. data/app/cells/decidim/posts/comments_cell.rb +158 -0
  10. data/app/cells/decidim/posts/content_blocks/posts/show.erb +3 -0
  11. data/app/cells/decidim/posts/content_blocks/posts_cell.rb +50 -0
  12. data/app/cells/decidim/posts/content_blocks/posts_settings_form/show.erb +3 -0
  13. data/app/cells/decidim/posts/content_blocks/posts_settings_form_cell.rb +23 -0
  14. data/app/cells/decidim/posts/feed_dropdown_metadata_cell.rb +19 -0
  15. data/app/cells/decidim/posts/meeting/show.erb +65 -0
  16. data/app/cells/decidim/posts/meeting_cell.rb +44 -0
  17. data/app/cells/decidim/posts/post/show.erb +30 -0
  18. data/app/cells/decidim/posts/post/survey.erb +68 -0
  19. data/app/cells/decidim/posts/post_attachments/show.erb +40 -0
  20. data/app/cells/decidim/posts/post_attachments_cell.rb +17 -0
  21. data/app/cells/decidim/posts/post_cell.rb +44 -0
  22. data/app/cells/decidim/posts/post_comment/show.erb +17 -0
  23. data/app/cells/decidim/posts/post_comment_cell.rb +49 -0
  24. data/app/cells/decidim/posts/post_header/show.erb +53 -0
  25. data/app/cells/decidim/posts/post_header_cell.rb +79 -0
  26. data/app/cells/decidim/posts/post_host/show.erb +74 -0
  27. data/app/cells/decidim/posts/post_host_cell.rb +73 -0
  28. data/app/cells/decidim/posts/post_metadata/show.erb +9 -0
  29. data/app/cells/decidim/posts/post_metadata_cell.rb +67 -0
  30. data/app/cells/decidim/posts/reaction_menu/show.erb +18 -0
  31. data/app/cells/decidim/posts/reaction_menu/styles.erb +7 -0
  32. data/app/cells/decidim/posts/reaction_menu_cell.rb +26 -0
  33. data/app/cells/decidim/posts/reactions/show.erb +8 -0
  34. data/app/cells/decidim/posts/reactions_cell.rb +22 -0
  35. data/app/commands/decidim/posts/add_reaction_to_resource.rb +90 -0
  36. data/app/commands/decidim/posts/create_post.rb +112 -0
  37. data/app/commands/decidim/posts/destroy_post.rb +20 -0
  38. data/app/commands/decidim/posts/remove_reaction_from_resource.rb +41 -0
  39. data/app/commands/decidim/posts/update_post.rb +72 -0
  40. data/app/controllers/concerns/decidim/posts/admin/filterable.rb +46 -0
  41. data/app/controllers/concerns/decidim/posts/reactionable.rb +31 -0
  42. data/app/controllers/decidim/posts/admin/application_controller.rb +26 -0
  43. data/app/controllers/decidim/posts/admin/posts_controller.rb +93 -0
  44. data/app/controllers/decidim/posts/application_controller.rb +18 -0
  45. data/app/controllers/decidim/posts/meetings_controller.rb +176 -0
  46. data/app/controllers/decidim/posts/posts_controller.rb +202 -0
  47. data/app/controllers/decidim/posts/reactions_controller.rb +54 -0
  48. data/app/controllers/decidim/posts/user_answers_controller.rb +36 -0
  49. data/app/events/decidim/posts/resource_reacted_event.rb +38 -0
  50. data/app/forms/decidim/posts/answer_form.rb +13 -0
  51. data/app/forms/decidim/posts/post_form.rb +121 -0
  52. data/app/forms/decidim/posts/question_form.rb +20 -0
  53. data/app/helpers/decidim/posts/admin/posts_helper.rb +8 -0
  54. data/app/helpers/decidim/posts/application_helper.rb +20 -0
  55. data/app/helpers/decidim/posts/post_cells_helper.rb +35 -0
  56. data/app/helpers/decidim/posts/posts_helper.rb +34 -0
  57. data/app/helpers/decidim/posts/reaction_helper.rb +22 -0
  58. data/app/models/decidim/posts/answer.rb +13 -0
  59. data/app/models/decidim/posts/application_record.rb +10 -0
  60. data/app/models/decidim/posts/post.rb +116 -0
  61. data/app/models/decidim/posts/question.rb +17 -0
  62. data/app/models/decidim/posts/reaction.rb +22 -0
  63. data/app/models/decidim/posts/reaction_type.rb +13 -0
  64. data/app/models/decidim/posts/user_answer.rb +11 -0
  65. data/app/packs/entrypoints/decidim_posts.js +5 -0
  66. data/app/packs/entrypoints/decidim_posts.scss +1 -0
  67. data/app/packs/images/decidim/posts/icon.svg +1 -0
  68. data/app/packs/src/decidim/posts/carousel.js +112 -0
  69. data/app/packs/src/decidim/posts/host_status.js +75 -0
  70. data/app/packs/src/decidim/posts/newFeeds.js +98 -0
  71. data/app/packs/src/decidim/posts/posts.js +272 -0
  72. data/app/packs/src/decidim/posts/submenu.js +46 -0
  73. data/app/packs/src/decidim/posts/survey.js +94 -0
  74. data/app/packs/stylesheets/decidim/posts/_variables.scss +10 -0
  75. data/app/packs/stylesheets/decidim/posts/posts.scss +415 -0
  76. data/app/permissions/decidim/posts/admin/permissions.rb +23 -0
  77. data/app/permissions/decidim/posts/permissions.rb +101 -0
  78. data/app/presenters/decidim/posts/post_presenter.rb +45 -0
  79. data/app/views/decidim/posts/admin/posts/_post-tr.html.erb +45 -0
  80. data/app/views/decidim/posts/admin/posts/index.html.erb +54 -0
  81. data/app/views/decidim/posts/meetings/edit.html.erb +24 -0
  82. data/app/views/decidim/posts/posts/_admin_options.html.erb +30 -0
  83. data/app/views/decidim/posts/posts/_attachment.html.erb +24 -0
  84. data/app/views/decidim/posts/posts/_edit_form.html.erb +16 -0
  85. data/app/views/decidim/posts/posts/_feed.html.erb +8 -0
  86. data/app/views/decidim/posts/posts/_form.html.erb +104 -0
  87. data/app/views/decidim/posts/posts/_index.html.erb +49 -0
  88. data/app/views/decidim/posts/posts/_meeting_form.erb +18 -0
  89. data/app/views/decidim/posts/posts/_new.html.erb +33 -0
  90. data/app/views/decidim/posts/posts/_new_survey.html.erb +20 -0
  91. data/app/views/decidim/posts/posts/_new_survey_answer.html.erb +11 -0
  92. data/app/views/decidim/posts/posts/_new_survey_question.html.erb +17 -0
  93. data/app/views/decidim/posts/posts/_post.html.erb +11 -0
  94. data/app/views/decidim/posts/posts/_post_form.html.erb +5 -0
  95. data/app/views/decidim/posts/posts/_sidebar.html.erb +36 -0
  96. data/app/views/decidim/posts/posts/edit.html.erb +34 -0
  97. data/app/views/decidim/posts/posts/index.html.erb +1 -0
  98. data/app/views/decidim/posts/posts/show.html.erb +12 -0
  99. data/app/views/decidim/posts/reactions/_update_buttons_and_counters.html.erb +1 -0
  100. data/config/assets.rb +9 -0
  101. data/config/i18n-tasks.yml +10 -0
  102. data/config/locales/bs.yml +215 -0
  103. data/config/locales/de.yml +217 -0
  104. data/config/locales/en.yml +216 -0
  105. data/config/locales/hr.yml +219 -0
  106. data/config/locales/it.yml +215 -0
  107. data/config/locales/sr.yml +220 -0
  108. data/config/locales/tr.yml +219 -0
  109. data/lib/decidim/posts/admin.rb +10 -0
  110. data/lib/decidim/posts/admin_engine.rb +25 -0
  111. data/lib/decidim/posts/component.rb +59 -0
  112. data/lib/decidim/posts/content_blocks/content_blocks_homepage.rb +19 -0
  113. data/lib/decidim/posts/engine.rb +84 -0
  114. data/lib/decidim/posts/test/factories.rb +14 -0
  115. data/lib/decidim/posts/version.rb +9 -0
  116. data/lib/decidim/posts.rb +13 -0
  117. metadata +183 -0
data/README.md ADDED
@@ -0,0 +1,40 @@
1
+ # Decidim::Posts component (under development)
2
+
3
+ This Feeds component adds social posts functionality to Decidim.
4
+
5
+ **The module is currently in development and not yet ready.**
6
+
7
+ ## Usage
8
+
9
+ Feeds will be available as a Component for a Participatory
10
+ Space.
11
+
12
+ ## Installation
13
+
14
+ Add this line to your application's Gemfile:
15
+
16
+ ```ruby
17
+ gem "decidim-posts", git: "https://github.com/DecidimAustria/decidim-module-posts.git", branch: "main"
18
+ ```
19
+
20
+ And then execute:
21
+
22
+ ```bash
23
+ bundle
24
+ bundle exec rails decidim_posts:install:migrations
25
+ bundle exec rails db:migrate
26
+ ```
27
+
28
+ ## Contributing
29
+
30
+ Contributions are welcome !
31
+
32
+ We expect the contributions to follow the [Decidim's contribution guide](https://github.com/decidim/decidim/blob/develop/CONTRIBUTING.adoc).
33
+
34
+ ## Security
35
+
36
+ Security is very important to us. If you have any issue regarding security, please disclose the information responsibly by sending an email to __security [at] mitgestalten [dot] jetzt__ and not by creating a Github issue.
37
+
38
+ ## License
39
+
40
+ This engine is distributed under the GNU AFFERO GENERAL PUBLIC LICENSE.
data/Rakefile ADDED
@@ -0,0 +1,9 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "decidim/dev/common_rake"
4
+
5
+ desc "Generates a dummy app for testing"
6
+ task test_app: "decidim:generate_external_test_app"
7
+
8
+ desc "Generates a development app."
9
+ task development_app: "decidim:generate_external_development_app"
@@ -0,0 +1,15 @@
1
+ <div class="add-comment">
2
+ <% if user_signed_in? %>
3
+ <%== cell("decidim/comments/comment_form", model, root_depth:) %>
4
+ <% else %>
5
+ <p>
6
+ <span>
7
+ <%== t(
8
+ "decidim.components.add_comment_form.account_message",
9
+ sign_in_url: decidim.new_user_session_path,
10
+ sign_up_url: decidim.new_user_registration_path
11
+ ) %>
12
+ </span>
13
+ </p>
14
+ <% end %>
15
+ </div>
@@ -0,0 +1 @@
1
+ <%= cell("decidim/announcement", t("decidim.components.comments.loading"), callout_class: "primary loading-comments mb-4") %>
@@ -0,0 +1,13 @@
1
+ <div class="comment-order-by">
2
+ <% available_orders.each do | order_value| %>
3
+ <div class="text-center">
4
+ <%= link_to(
5
+ t("decidim.components.comment_order_selector.order.#{order_value}"),
6
+ decidim_comments.comments_path(commentable_gid: model.to_signed_global_id.to_s, order: order_value, reload: 1),
7
+ class: "button button__sm button__text-secondary only:m-auto comment-order-by__item inline-block #{order_value == order ? "underline font-bold" : ""}",
8
+ remote: true,
9
+ aria: { controls: threads_node_id }
10
+ ) %>
11
+ </div>
12
+ <% end %>
13
+ </div>
@@ -0,0 +1,31 @@
1
+ <%= append_stylesheet_pack_tag "decidim_comments" %>
2
+ <%= content_tag :div, id: node_id, data: { decidim_posts_comments: comments_data } do %>
3
+ <div id="comments">
4
+ <div class="comments">
5
+ <div class="comments__header">
6
+ <h2 class="h4">
7
+ <% if single_comment? %>
8
+ <%= t("decidim.components.comments.comment_details_title") %>
9
+ <% else %>
10
+ <%= icon "chat-1-line", class: "fill-tertiary w-6 h-6 inline-block align-middle" %>
11
+ <span class="comments-count inline-block align-middle">
12
+ <%= t("decidim.components.comments.title", count: comments_count) %>
13
+ </span>
14
+ <% end %>
15
+ </h2>
16
+ <%= render :order_control %>
17
+ </div>
18
+ <%= single_comment_warning %>
19
+ <%= blocked_comments_warning %>
20
+ <div class="comment-threads" id="<%= threads_node_id %>" aria-live="polite">
21
+ <%= comments_loading %>
22
+ <% comments.each do |comment| %>
23
+ <%= cell("decidim/comments/comment_thread", comment, order:) %>
24
+ <% end %>
25
+ </div>
26
+ <%= add_comment %>
27
+ <%= user_comments_blocked_warning %>
28
+ </div>
29
+ <%= cell("decidim/announcement", t("decidim.components.comments.loading"), callout_class: "primary loading-comments hidden") %>
30
+ </div>
31
+ <% end %>
@@ -0,0 +1,158 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Decidim
4
+ module Posts
5
+ # A cell to display a comments section for a commentable object.
6
+ class CommentsCell < Decidim::ViewModel
7
+ include Decidim::IconHelper
8
+
9
+ delegate :user_signed_in?, to: :controller
10
+
11
+ def add_comment
12
+ return if single_comment?
13
+ return if comments_blocked?
14
+ return if user_comments_blocked?
15
+
16
+ render :add_comment
17
+ end
18
+
19
+ def single_comment_warning
20
+ return unless single_comment?
21
+
22
+ render :single_comment_warning
23
+ end
24
+
25
+ def comments_loading
26
+ return if single_comment?
27
+
28
+ render :comments_loading
29
+ end
30
+
31
+ def blocked_comments_warning
32
+ return unless comments_blocked?
33
+ return unless user_comments_blocked?
34
+
35
+ render :blocked_comments_warning
36
+ end
37
+
38
+ def user_comments_blocked_warning
39
+ return if comments_blocked? # Shows already the general warning
40
+ return unless user_comments_blocked?
41
+
42
+ render :user_comments_blocked_warning
43
+ end
44
+
45
+ private
46
+
47
+ def decidim_comments
48
+ Decidim::Comments::Engine.routes.url_helpers
49
+ end
50
+
51
+ def comments
52
+ single_comment? ? [single_comment] : []
53
+ end
54
+
55
+ def comments_count
56
+ model.comments_count
57
+ end
58
+
59
+ def root_depth
60
+ return 0 unless single_comment?
61
+
62
+ single_comment.depth
63
+ end
64
+
65
+ def commentable_path(params = {})
66
+ return resource_locator(Array(options[:polymorphic]).push(model)).path(params) if options[:polymorphic]
67
+
68
+ resource_locator(model).path(params)
69
+ end
70
+
71
+ def alignment_enabled?
72
+ model.comments_have_alignment?
73
+ end
74
+
75
+ def available_orders
76
+ %w(best_rated recent older most_discussed)
77
+ end
78
+
79
+ def order
80
+ options[:order] || "older"
81
+ end
82
+
83
+ def decidim
84
+ Decidim::Core::Engine.routes.url_helpers
85
+ end
86
+
87
+ def node_id
88
+ "comments-for-#{commentable_type.demodulize}-#{model.id}"
89
+ end
90
+
91
+ def threads_node_id
92
+ "#{node_id}-threads"
93
+ end
94
+
95
+ def commentable_type
96
+ model.commentable_type
97
+ end
98
+
99
+ def comments_data
100
+ {
101
+ singleComment: single_comment?,
102
+ toggleTranslations: machine_translations_toggled?,
103
+ commentableGid: model.to_signed_global_id.to_s,
104
+ commentsUrl: decidim_comments.comments_path,
105
+ rootDepth: root_depth,
106
+ order:,
107
+ pollingInterval: 300000
108
+ }
109
+ end
110
+
111
+ def single_comment?
112
+ single_comment.present?
113
+ end
114
+
115
+ def single_comment
116
+ return if options[:single_comment].blank?
117
+
118
+ @single_comment ||= SortedComments.for(model, id: options[:single_comment], order_by: order).first
119
+ end
120
+
121
+ def machine_translations_toggled?
122
+ options[:machine_translations] == true
123
+ end
124
+
125
+ def comments_blocked?
126
+ !model.accepts_new_comments?
127
+ end
128
+
129
+ def user_comments_blocked?
130
+ return false unless user_signed_in?
131
+
132
+ !model.user_allowed_to_comment?(current_user)
133
+ end
134
+
135
+ def comment_permissions?
136
+ [model, current_component].any? do |resource|
137
+ resource.try(:permissions).try(:[], "comment")
138
+ end
139
+ end
140
+
141
+ # action_authorization_link expects current_component to be available
142
+ def current_component
143
+ model.try(:component)
144
+ end
145
+
146
+ def blocked_comments_for_unauthorized_user_warning_link
147
+ options = if current_component.present?
148
+ { resource: model }
149
+ else
150
+ { resource: model, permissions_holder: model }
151
+ end
152
+ action_authorized_link_to(:comment, commentable_path, options) do
153
+ t("decidim.components.comments.blocked_comments_for_unauthorized_user_warning")
154
+ end
155
+ end
156
+ end
157
+ end
158
+ end
@@ -0,0 +1,3 @@
1
+ <section id="feeds" class="home__section feeds">
2
+ <%= render partial: "decidim/posts/posts/index.html" %>
3
+ </section>
@@ -0,0 +1,50 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Decidim
4
+ module Posts
5
+ module ContentBlocks
6
+ class PostsCell < Decidim::ViewModel
7
+ include Cell::ViewModel::Partial
8
+ include FormFactory
9
+
10
+ def show
11
+ @posts = Decidim::Posts::Post
12
+ .where(decidim_component_id: component_id)
13
+ .filter_category(params[:filter_post_category])
14
+ .order(created_at: :desc)
15
+ .limit(10)
16
+ extra_context = {
17
+ current_component: component,
18
+ current_organization: component.organization,
19
+ current_user:,
20
+ current_participatory_space: component.participatory_space
21
+ }
22
+ @form = form(Decidim::Posts::PostForm).from_params(params, extra_context)
23
+ render :show
24
+ end
25
+
26
+ def decidim_feeds
27
+ Decidim::EngineRouter.main_proxy(component)
28
+ end
29
+
30
+ def posts_path
31
+ decidim_feeds.posts_path
32
+ end
33
+
34
+ private
35
+
36
+ def component_id
37
+ @component_id ||= model.settings.try(:component_id)
38
+ end
39
+
40
+ def component
41
+ @component ||= Decidim::Component.find(component_id)
42
+ end
43
+
44
+ def post_creation_params
45
+ params[:post]
46
+ end
47
+ end
48
+ end
49
+ end
50
+ end
@@ -0,0 +1,3 @@
1
+ <% form.fields_for :settings, form.object.settings do |settings_fields| %>
2
+ <%= settings_fields.select :component_id, available_feeds_options, prompt: "", label: t(".component") %>
3
+ <% end %>
@@ -0,0 +1,23 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Decidim
4
+ module Posts
5
+ module ContentBlocks
6
+ class PostsSettingsFormCell < Decidim::ViewModel
7
+ # Found in HighlightedElementsForComponentSettingsFormCell
8
+ #include Decidim::ContentBlocks::HasRelatedComponents
9
+ alias form model
10
+
11
+ def available_feeds_options
12
+ available_feeds.map do |component|
13
+ [translated_attribute(component.name), component.id]
14
+ end
15
+ end
16
+
17
+ def available_feeds
18
+ @available_feeds ||= Decidim::Component.where(manifest_name: "posts").where(participatory_space_id: current_organization.participatory_spaces.pluck(:id))
19
+ end
20
+ end
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,19 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Decidim
4
+ module Posts
5
+ class FeedDropdownMetadataCell < Decidim::ParticipatorySpaceDropdownMetadataCell
6
+ # include FeedsHelper
7
+ include Decidim::ComponentPathHelper
8
+ include ActiveLinkTo
9
+
10
+ def decidim_feeds
11
+ Decidim::Posts::Engine.routes.url_helpers
12
+ end
13
+
14
+ private
15
+
16
+ def nav_items_method = :assembly_nav_items
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,65 @@
1
+ <article class="card__grid posts__post" data-content="calendar">
2
+ <%= cell("decidim/posts/post_header", meeting) %>
3
+ <div class="flex gap-4 py-2 px-2">
4
+ <div class="card__grid-text--meta flex flex-col gap-2">
5
+ <time class="card__calendar" datetime="<%= meeting.start_time.iso8601 %>">
6
+ <span class="card__calendar-month"><%= l(meeting.start_time, format: "%b") %></span>
7
+ <span class="card__calendar-day"><%= l(meeting.start_time, format: "%d") %></span>
8
+ <span class="card__calendar-year"><%= l(meeting.start_time, format: "%Y") %></span>
9
+ </time>
10
+ <% if meeting.start_time.to_date != meeting.end_time.to_date %>
11
+ <span class="self-center font-bold text-xs"><%= t("decidim.components.posts.calendar.date_separator") %></span>
12
+ <time class="card__calendar" datetime="<%= meeting.end_time.iso8601 %>">
13
+ <span class="card__calendar-month"><%= l(meeting.end_time, format: "%b") %></span>
14
+ <span class="card__calendar-day"><%= l(meeting.end_time, format: "%d") %></span>
15
+ <span class="card__calendar-year"><%= l(meeting.end_time, format: "%Y") %></span>
16
+ </time>
17
+ <% end %>
18
+ </div>
19
+ <div class="card__grid-text p-0">
20
+ <h2 class="h3"><%= link_to meeting_title, Decidim::EngineRouter.main_proxy(meeting.component).meeting_path(meeting) %></h2>
21
+ <% should_truncate = strip_tags(meeting_description).length > 300 %>
22
+ <div class="content-block__description editor-content" <%= "data-component='accordion'" if should_truncate %>>
23
+ <% if should_truncate %>
24
+ <% seed = SecureRandom.hex(4) %>
25
+ <div id="panel-view-more-<%= seed %>" aria-hidden="true">
26
+ <%= meeting_description %>
27
+ </div>
28
+ <button class="button button__sm button__text-secondary mt-2" data-controls="panel-view-more-<%= seed %>" aria-expanded="false">
29
+ <span>
30
+ <%= t("view_more", scope: "layouts.decidim.announcements") %>
31
+ </span>
32
+ <%= icon "arrow-down-s-line" %>
33
+ <span>
34
+ <%= t("view_less", scope: "layouts.decidim.announcements") %>
35
+ </span>
36
+ <%= icon "arrow-up-s-line" %>
37
+ </button>
38
+ <% else %>
39
+ <%= meeting_description %>
40
+ <% end %>
41
+ </div>
42
+ <div class="flex gap-4 mt-auto flex-wrap">
43
+ <div class="bg-background p-2 rounded-md font-bold shrink">
44
+ <time datetime="<%= meeting.start_time.iso8601 %>">
45
+ <%= l(meeting.start_time, format: "%H:%M") %>
46
+ </time>
47
+ </time>
48
+ <% if meeting.start_time != meeting.end_time %>
49
+ <%= t("decidim.components.posts.calendar.end_time") %>
50
+ <time datetime="<%= meeting.end_time.iso8601 %>">
51
+ <%= l(meeting.end_time, format: "%H:%M") %>
52
+ </time>
53
+ <% end %>
54
+ </div>
55
+ <% if meeting_location != '' %>
56
+ <div class="bg-background p-2 rounded-md font-bold shrink">
57
+ <%= meeting_location %>
58
+ </div>
59
+ <% end %>
60
+ </div>
61
+ </div>
62
+ </div>
63
+ <%= cell("decidim/posts/post_comment", meeting) %>
64
+ <%= cell("decidim/posts/post_metadata", meeting) %>
65
+ </article>
@@ -0,0 +1,44 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Decidim
4
+ module Posts
5
+ class MeetingCell < Decidim::ViewModel
6
+ include PostCellsHelper
7
+ include Decidim::Meetings::MeetingCellsHelper
8
+ include Cell::ViewModel::Partial
9
+
10
+ def show
11
+ render :show
12
+ end
13
+
14
+ def meeting
15
+ model
16
+ end
17
+
18
+ def meeting_author
19
+ model.decidim_author_id
20
+ end
21
+
22
+ def meeting_title
23
+ translated_attribute model.title
24
+ end
25
+
26
+ def meeting_description
27
+ translated_attribute model.description
28
+ end
29
+
30
+ def meeting_address
31
+ model.address
32
+ end
33
+
34
+ def meeting_location
35
+ translated_attribute model.location
36
+ end
37
+
38
+ def comments_enabled
39
+ model.comments_enabled?
40
+ end
41
+
42
+ end
43
+ end
44
+ end
@@ -0,0 +1,30 @@
1
+ <%= cell("decidim/posts/post_header", post) %>
2
+ <%= cell("decidim/posts/post_attachments", post) %>
3
+ <div class="card__grid-text">
4
+ <% should_truncate = strip_tags(post_body).length > 300 %>
5
+ <div class="content-block__description editor-content" <%= "data-component='accordion'" if should_truncate %>>
6
+ <% if should_truncate %>
7
+ <% seed = SecureRandom.hex(4) %>
8
+ <div id="panel-view-more-<%= seed %>" aria-hidden="true">
9
+ <%= post_body %>
10
+ </div>
11
+ <button class="button button__sm button__text-secondary mt-2" data-controls="panel-view-more-<%= seed %>" aria-expanded="false">
12
+ <span>
13
+ <%= t("view_more", scope: "layouts.decidim.announcements") %>
14
+ </span>
15
+ <%= icon "arrow-down-s-line" %>
16
+ <span>
17
+ <%= t("view_less", scope: "layouts.decidim.announcements") %>
18
+ </span>
19
+ <%= icon "arrow-up-s-line" %>
20
+ </button>
21
+ <% else %>
22
+ <%= post_body %>
23
+ <% end %>
24
+ </div>
25
+ </div>
26
+ <% if post_category == 'survey' %>
27
+ <%= render :survey %>
28
+ <% end %>
29
+ <%= cell("decidim/posts/post_comment", post) %>
30
+ <%= cell("decidim/posts/post_metadata", post) %>
@@ -0,0 +1,68 @@
1
+ <div class="posts__post_survey" id="post_<%= post.id %>_survey">
2
+ <span class="text-right py-2">
3
+ <%= t("decidim.posts.survey.participated", count: post.survey_responses_count) %>
4
+ </span>
5
+ <% post.questions.each do |question| %>
6
+ <% if question.question_type == "single_choice" %>
7
+ <fieldset class="posts__post_survey-question">
8
+ <legend><%= translated_attribute question.title %></legend>
9
+ <% question.answers.each do |answer| %>
10
+ <div class="posts__post_survey-question-answer">
11
+ <div class="posts__post_survey-question-answerInput">
12
+ <%= radio_button_tag "post[questions][#{question.id}]", answer.id, answer.user_answers.find_by(decidim_posts_answer_id: answer.id, decidim_user_id: current_user.id).present?, class: "survey-answer-checkbox", data: { question_id: question.id, answer_id: answer.id } %>
13
+ <%= label_tag "post_questions_#{question.id}_#{answer.id}" do %>
14
+ <%= translated_attribute answer.title %>
15
+ <% end %>
16
+ </div>
17
+ <div class="posts__post_survey-question-answerProgressbar flex justify-between p-1 border border-gray rounded-full shadow-md">
18
+ <div class="w-full bg-background rounded-full">
19
+ <% percentage = post.survey_responses_count.zero? ? 0 : (answer.user_answers.count.to_f / post.survey_responses_count) * 100 %>
20
+ <div id="answer-<%= answer.id %>-progressbar" class="bg-success h-full rounded-full shadow-md" style="width: <%= percentage %>%"></div>
21
+ </div>
22
+ <span class="text-sm shrink-0 px-2">
23
+ <span id="answer-<%= answer.id %>-counter">
24
+ <%= answer.user_answers.count %>
25
+ </span>
26
+ /
27
+ <span id="answer-<%= answer.id %>-total">
28
+ <%= post.survey_responses_count %>
29
+ </span>
30
+ <span class="sr-only">haben geantwortet</span>
31
+ </span>
32
+ </div>
33
+ </div>
34
+ <% end %>
35
+ </fieldset>
36
+ <% elsif question.question_type == "multiple_choice" %>
37
+ <fieldset class="posts__post_survey-question">
38
+ <legend><%= translated_attribute question.title %></legend>
39
+ <% question.answers.each do |answer| %>
40
+ <div class="posts__post_survey-question-answer">
41
+ <div class="posts__post_survey-question-answerInput">
42
+ <%= check_box_tag "post[questions][#{question.id}][#{answer.id}]", answer.id, answer.user_answers.find_by(decidim_posts_answer_id: answer.id, decidim_user_id: current_user.id).present?, class: "survey-answer-checkbox", data: { question_id: question.id, answer_id: answer.id } %>
43
+ <%= label_tag "post_questions_#{question.id}_#{answer.id}" do %>
44
+ <%= translated_attribute answer.title %>
45
+ <% end %>
46
+ </div>
47
+ <div class="posts__post_survey-question-answerProgressbar flex justify-between p-1 border border-gray rounded-full shadow-md">
48
+ <div class="w-full bg-background rounded-full">
49
+ <% percentage = post.survey_responses_count.zero? ? 0 : (answer.user_answers.count.to_f / post.survey_responses_count) * 100 %>
50
+ <div id="answer-<%= answer.id %>-progressbar" class="bg-success h-full rounded-full shadow-md" style="width: <%= percentage %>%"></div>
51
+ </div>
52
+ <span class="text-sm shrink-0 px-2">
53
+ <span id="answer-<%= answer.id %>-counter">
54
+ <%= answer.user_answers.count %>
55
+ </span>
56
+ /
57
+ <span id="answer-<%= answer.id %>-total">
58
+ <%= post.survey_responses_count %>
59
+ </span>
60
+ <span class="sr-only">haben geantwortet</span>
61
+ </span>
62
+ </div>
63
+ </div>
64
+ <% end %>
65
+ </fieldset>
66
+ <% end %>
67
+ <% end %>
68
+ </div>
@@ -0,0 +1,40 @@
1
+ <% if post.attachments.any? %>
2
+ <div class="feeds__attachments py-4 px-2">
3
+
4
+ <div class="flex flex-col items-center">
5
+ <ul id="galleryItems_<%= post.id %>" class="posts__post_gallery flex overflow-hidden">
6
+ <% post.attachments.select { |a| a.url.match(/\.(jpg|jpeg|png|svg|webp|mp4|webm)$/i) }.each_with_index do |attachment, index| %>
7
+ <li class="<%= attachment.url.match(/\.(mp4|webm)$/i) ? 'video' : 'image' %> <%= index.zero? ? 'active' : '' %> aspect-4/3 overflow-hidden flex justify-center items-center" data-src="<%= attachment.url %>">
8
+ <% if attachment.url.match(/\.(mp4|webm)$/i) %>
9
+ <%= video_tag(attachment.url, class: "object-cover w-full h-full", controls: true, preload: "auto") %>
10
+ <% else %>
11
+ <div class="w-full h-full flex justify-center items-center">
12
+ <%= image_tag(attachment.url, class: "max-w-full max-h-full object-contain", alt: translated_attribute(attachment.title), width: '400px', height: '300px' ) %>
13
+ </div>
14
+ <% end %>
15
+ </li>
16
+ <% end %>
17
+ </ul>
18
+ <% if post.attachments.select { |a| a.url.match(/\.(jpg|jpeg|png|svg|webp|mp4|webm)$/i) }.count > 1 %>
19
+ <div id="navDots_<%= post.id %>" class="nav-dots p-1 flex flex-wrap justify-center">
20
+ <% total = post.attachments.select { |a| a.url.match(/\.(jpg|jpeg|png|svg|webp|mp4|webm)$/i) }.count %>
21
+ <% post.attachments.select { |a| a.url.match(/\.(jpg|jpeg|png|svg|webp|mp4|webm)$/i) }.each_with_index do |_, index| %>
22
+ <div class="posts__post_gallery_nav_dot flex justify-center items-center cursor-pointer h-6 w-6" role="button" aria-label="<%= t('decidim.components.posts.gallery.navigation', number: index + 1, total: total) %>" data-target="galleryItems_<%= post.id %>" data-index="<%= index %>">
23
+ <span class="h-2 w-2 rounded-full ring-1 ring-offset-1 ring-feeds-notification <%= index.zero? ? 'bg-feeds-notification' : '' %>"></span>
24
+ </div>
25
+ <% end %>
26
+ </div>
27
+ <% end %>
28
+ </div>
29
+ <% non_media_attachments = post.attachments.reject { |a| a.url.match(/\.(jpg|jpeg|png|svg|webp|mp4|webm)$/i) } %>
30
+ <% if non_media_attachments.any? %>
31
+ <ul class="document-list space-y-2 mt-4">
32
+ <% non_media_attachments.each_with_index do |attachment, index| %>
33
+ <li class="<%= index.even? ? 'bg-feeds-formBody' : '' %> py-1 pl-2 pr-4 flex gap-1 items-center">
34
+ <%= icon "file-2-line" %> <%= link_to File.basename(attachment.url), attachment.url, class: 'text-sm break-all', style: 'overflow-wrap: break-word' %>
35
+ </li>
36
+ <% end %>
37
+ </ul>
38
+ <% end %>
39
+ </div>
40
+ <% end %>
@@ -0,0 +1,17 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Decidim
4
+ module Posts
5
+ class PostAttachmentsCell < Decidim::ViewModel
6
+ include Cell::ViewModel::Partial
7
+ def show
8
+ render :show
9
+ end
10
+
11
+ def post
12
+ model
13
+ end
14
+
15
+ end
16
+ end
17
+ end