mumuki-laboratory 7.6.2 → 7.8.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (126) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +203 -2
  3. data/Rakefile +9 -0
  4. data/app/assets/javascripts/mumuki_laboratory/application.js +0 -1
  5. data/app/assets/javascripts/mumuki_laboratory/application/alias-modes.js +1 -1
  6. data/app/assets/javascripts/mumuki_laboratory/application/assets-loader.js +1 -1
  7. data/app/assets/javascripts/mumuki_laboratory/application/bridge.js +82 -47
  8. data/app/assets/javascripts/mumuki_laboratory/application/button.js +90 -1
  9. data/app/assets/javascripts/mumuki_laboratory/application/codemirror-builder.js +28 -25
  10. data/app/assets/javascripts/mumuki_laboratory/application/codemirror.js +8 -9
  11. data/app/assets/javascripts/mumuki_laboratory/application/confirmation.js +2 -2
  12. data/app/assets/javascripts/mumuki_laboratory/application/console.js +41 -43
  13. data/app/assets/javascripts/mumuki_laboratory/application/csrf-token.js +9 -12
  14. data/app/assets/javascripts/mumuki_laboratory/application/custom-editor.js +46 -8
  15. data/app/assets/javascripts/mumuki_laboratory/application/discussions.js +15 -16
  16. data/app/assets/javascripts/mumuki_laboratory/application/editors.js +104 -0
  17. data/app/assets/javascripts/mumuki_laboratory/application/elipsis.js +5 -4
  18. data/app/assets/javascripts/mumuki_laboratory/application/exercise.js +32 -0
  19. data/app/assets/javascripts/mumuki_laboratory/application/inputs.js +4 -2
  20. data/app/assets/javascripts/mumuki_laboratory/application/interval.js +2 -4
  21. data/app/assets/javascripts/mumuki_laboratory/application/kids.js +74 -37
  22. data/app/assets/javascripts/mumuki_laboratory/application/load-analytics.js +1 -1
  23. data/app/assets/javascripts/mumuki_laboratory/application/load-error-svg.js +1 -1
  24. data/app/assets/javascripts/mumuki_laboratory/application/messages.js +2 -2
  25. data/app/assets/javascripts/mumuki_laboratory/application/multiple-choice.js +1 -1
  26. data/app/assets/javascripts/mumuki_laboratory/application/multiple-scenarios.js +3 -6
  27. data/app/assets/javascripts/mumuki_laboratory/application/pin.js +3 -5
  28. data/app/assets/javascripts/mumuki_laboratory/application/progress.js +27 -6
  29. data/app/assets/javascripts/mumuki_laboratory/application/results-renderer.js +60 -0
  30. data/app/assets/javascripts/mumuki_laboratory/application/speech-bubble-renderer.js +12 -5
  31. data/app/assets/javascripts/mumuki_laboratory/application/submission.js +122 -55
  32. data/app/assets/javascripts/mumuki_laboratory/application/submissions-store.js +93 -0
  33. data/app/assets/javascripts/mumuki_laboratory/application/sync-mode.js +75 -0
  34. data/app/assets/javascripts/mumuki_laboratory/application/timer.js +5 -6
  35. data/app/assets/javascripts/mumuki_laboratory/application/tooltip.js +1 -1
  36. data/app/assets/javascripts/mumuki_laboratory/application/upload.js +1 -1
  37. data/app/assets/javascripts/mumuki_laboratory/application/user.js +1 -1
  38. data/app/assets/stylesheets/mumuki_laboratory/application/_modules.scss +1 -0
  39. data/app/assets/stylesheets/mumuki_laboratory/application/modules/_discussion.scss +43 -5
  40. data/app/assets/stylesheets/mumuki_laboratory/application/modules/_gs-board.scss +3 -0
  41. data/app/assets/stylesheets/mumuki_laboratory/application/modules/_kids.scss +3 -4
  42. data/app/assets/stylesheets/mumuki_laboratory/application/modules/_kindergarten.scss +55 -0
  43. data/app/controllers/application_controller.rb +1 -0
  44. data/app/controllers/assets_controller.rb +2 -0
  45. data/app/controllers/concerns/with_authorization.rb +4 -0
  46. data/app/controllers/concerns/with_user_discussion_validation.rb +14 -0
  47. data/app/controllers/discussions_controller.rb +6 -14
  48. data/app/controllers/discussions_messages_controller.rb +10 -1
  49. data/app/controllers/exercise_solutions_controller.rb +4 -2
  50. data/app/helpers/application_helper.rb +9 -5
  51. data/app/helpers/discussions_helper.rb +37 -23
  52. data/app/helpers/exercise_input_helper.rb +1 -1
  53. data/app/helpers/{locale_helper.rb → globals_helper.rb} +6 -2
  54. data/app/helpers/icons_helper.rb +3 -3
  55. data/app/mailers/user_mailer.rb +24 -11
  56. data/app/views/book/show.html.erb +1 -1
  57. data/app/views/book_discussions/index.html.erb +3 -3
  58. data/app/views/discussions/_message.html.erb +20 -8
  59. data/app/views/discussions/index.html.erb +0 -1
  60. data/app/views/discussions/new.html.erb +33 -0
  61. data/app/views/discussions/show.html.erb +18 -46
  62. data/app/views/exercise_solutions/_contextualization_results_container.html.erb +1 -1
  63. data/app/views/exercise_solutions/_results_title.html.erb +2 -2
  64. data/app/views/exercises/_read_only.html.erb +33 -6
  65. data/app/views/exercises/show.html.erb +2 -0
  66. data/app/views/layouts/_copyright.html.erb +1 -1
  67. data/app/views/layouts/_discussions.html.erb +21 -3
  68. data/app/views/layouts/_main.html.erb +1 -2
  69. data/app/views/layouts/_progress.html.erb +1 -1
  70. data/app/views/layouts/_progress_bar.html.erb +7 -1
  71. data/app/views/layouts/_test_results.html.erb +1 -1
  72. data/app/views/layouts/application.html.erb +1 -1
  73. data/app/views/layouts/exercise_inputs/editors/_custom.html.erb +1 -1
  74. data/app/views/layouts/exercise_inputs/forms/_kids_form.html.erb +1 -1
  75. data/app/views/layouts/exercise_inputs/forms/_problem_form.html.erb +1 -1
  76. data/app/views/layouts/exercise_inputs/layouts/_input_bottom.html.erb +1 -1
  77. data/app/views/layouts/exercise_inputs/layouts/_input_kindergarten.html.erb +40 -0
  78. data/app/views/layouts/exercise_inputs/layouts/{_input_kids.html.erb → _input_primary.html.erb} +1 -1
  79. data/app/views/layouts/exercise_inputs/layouts/_input_right.html.erb +1 -1
  80. data/app/views/layouts/modals/_kids_context.html.erb +1 -8
  81. data/app/views/user_mailer/1st_reminder.html.erb +1 -1
  82. data/app/views/user_mailer/1st_reminder.text.erb +1 -1
  83. data/app/views/user_mailer/2nd_reminder.html.erb +1 -1
  84. data/app/views/user_mailer/2nd_reminder.text.erb +1 -1
  85. data/app/views/user_mailer/3rd_reminder.html.erb +1 -1
  86. data/app/views/user_mailer/3rd_reminder.text.erb +1 -1
  87. data/app/views/user_mailer/no_submissions_reminder.html.erb +1 -1
  88. data/app/views/user_mailer/no_submissions_reminder.text.erb +1 -1
  89. data/config/routes.rb +2 -1
  90. data/lib/mumuki/laboratory/controllers.rb +1 -0
  91. data/lib/mumuki/laboratory/controllers/incognito_mode.rb +28 -0
  92. data/lib/mumuki/laboratory/controllers/results_rendering.rb +1 -2
  93. data/lib/mumuki/laboratory/locales/en.yml +14 -6
  94. data/lib/mumuki/laboratory/locales/es-CL.yml +292 -0
  95. data/lib/mumuki/laboratory/locales/es.yml +13 -5
  96. data/lib/mumuki/laboratory/locales/pt.yml +12 -6
  97. data/lib/mumuki/laboratory/version.rb +1 -1
  98. data/spec/controllers/confirmations_controller_spec.rb +1 -1
  99. data/spec/controllers/discussions_messages_controller_spec.rb +73 -0
  100. data/spec/controllers/exercise_solutions_controller_spec.rb +41 -6
  101. data/spec/dummy/db/schema.rb +13 -1
  102. data/spec/features/chapter_spec.rb +17 -0
  103. data/spec/features/discussion_flow_spec.rb +190 -0
  104. data/spec/features/exercise_flow_spec.rb +48 -3
  105. data/spec/features/home_public_flow_spec.rb +16 -0
  106. data/spec/features/menu_bar_spec.rb +88 -7
  107. data/spec/helpers/breadcrumbs_helper_spec.rb +1 -1
  108. data/spec/javascripts/bridge-spec.js +5 -0
  109. data/spec/javascripts/csrf-token-spec.js +7 -0
  110. data/spec/javascripts/editors-spec.js +54 -0
  111. data/spec/javascripts/elipsis-spec.js +25 -0
  112. data/spec/javascripts/exercise-spec.js +22 -0
  113. data/spec/javascripts/global-spec.js +6 -0
  114. data/spec/javascripts/results-renderers-spec.js +17 -0
  115. data/spec/javascripts/spec-helper.js +34 -0
  116. data/spec/javascripts/speech-bubble-renderer-spec.js +11 -0
  117. data/spec/javascripts/submissions-store-spec.js +44 -0
  118. data/spec/javascripts/sync-mode-spec.js +15 -0
  119. data/spec/javascripts/timeout-spec.js +5 -0
  120. data/spec/javascripts/timer-spec.js +5 -0
  121. data/spec/mailers/user_mailer_spec.rb +18 -3
  122. data/spec/teaspoon_env.rb +193 -0
  123. metadata +50 -11
  124. data/app/helpers/version_helper.rb +0 -5
  125. data/app/views/layouts/modals/_new_discussion.html.erb +0 -27
  126. data/vendor/assets/javascripts/hotjar.js +0 -8
@@ -23,31 +23,32 @@ module DiscussionsHelper
23
23
  fixed_fa_icon 'comment', text: text
24
24
  end
25
25
 
26
- def discussions_link(item, path, html_options=nil, organization=Organization.current)
27
- link_to item, path, html_options if organization.forum_enabled?
26
+ def discussions_link(item, path, html_options = nil)
27
+ return unless current_user&.can_discuss_here?
28
+ link_to item, path, html_options
28
29
  end
29
30
 
30
- def item_discussion_path(discussion, params={})
31
+ def item_discussion_path(discussion, params = {})
31
32
  polymorphic_path([discussion.item, discussion], params)
32
33
  end
33
34
 
34
- def item_discussions_path(item, params={})
35
+ def item_discussions_path(item, params = {})
35
36
  polymorphic_path([item, :discussions], params)
36
37
  end
37
38
 
38
39
  def solve_discussion_params_for(user)
39
40
  if user&.moderator_here?
40
- { status: :pending_review, sort: :created_at_asc }
41
+ {status: :pending_review, sort: :responses_count_asc, requires_moderator_response: true}
41
42
  else
42
- { status: :opened, sort: :created_at_asc }
43
+ {status: :opened, sort: :responses_count_desc}
43
44
  end
44
45
  end
45
46
 
46
47
  def default_discussions_params
47
- { status: :solved, sort: :upvotes_count_desc }
48
+ {status: :solved, sort: :upvotes_count_desc}
48
49
  end
49
50
 
50
- def user_avatar(user, image_class='')
51
+ def user_avatar(user, image_class = '')
51
52
  image_tag user.profile_picture, height: 40, class: "img-circle #{image_class}"
52
53
  end
53
54
 
@@ -67,7 +68,7 @@ module DiscussionsHelper
67
68
  %Q{
68
69
  <span class="discussion-icon fa-stack fa-xs">
69
70
  <i class="fa fa-comment-o fa-stack-2x"></i>
70
- <i class="fa fa-stack-1x">#{discussion.messages.size}</i>
71
+ <i class="fa fa-stack-1x">#{discussion.validated_messages_count}</i>
71
72
  </span>
72
73
  }.html_safe
73
74
  end
@@ -91,22 +92,16 @@ module DiscussionsHelper
91
92
  end
92
93
 
93
94
  def new_discussion_link(teaser_text, link_text)
94
- return '' unless Organization.current.can_create_discussions?(current_user)
95
-
96
95
  %Q{
97
96
  <h4>
98
97
  <span>#{t(teaser_text)}</span>
99
- <a>
100
- <span class="discussion-create">
101
- #{t(link_text)}
102
- </span>
103
- </a>
98
+ #{link_to t(link_text), new_exercise_discussion_path(@debatable, anchor: 'new-discussion-description-container') }
104
99
  </h4>
105
100
  }.html_safe
106
101
  end
107
102
 
108
103
  def discussion_count_for_status(status, discussions)
109
- discussions.scoped_query_by(discussion_filter_params, :status).by_status(status).count
104
+ discussions.scoped_query_by(discussion_filter_params, excluded_params: [:status], excluded_methods: [:page]).by_status(status).count
110
105
  end
111
106
 
112
107
  def discussions_reset_query_link
@@ -117,8 +112,10 @@ module DiscussionsHelper
117
112
  Mumuki::Domain::Status::Discussion::STATUSES
118
113
  end
119
114
 
115
+ #TODO: this one uses a long method chain in order to take advantage of eager load
116
+ # Delegate it once again when polymorphic association is removed
120
117
  def discussions_languages(discussions)
121
- discussions.map { |it| it.language.name }.uniq
118
+ @languages ||= discussions.map { |it| it.exercise.language.name }.uniq
122
119
  end
123
120
 
124
121
  def discussion_status_filter_link(status, discussions)
@@ -132,14 +129,14 @@ module DiscussionsHelper
132
129
 
133
130
  def discussion_status_filter(status, discussions_count)
134
131
  %Q{
135
- #{discussion_status_fa_icon(status)}
132
+ #{discussion_status_fa_icon(status)}
136
133
  <span>
137
134
  #{t("#{status}_count", count: discussions_count)}
138
135
  </span>
139
136
  }.html_safe
140
137
  end
141
138
 
142
- def discussion_dropdown_filter(label, filters, &block)
139
+ def discussion_dropdown_filter(label, filters, can_select_all = false, &block)
143
140
  if filters.present?
144
141
  %Q{
145
142
  <div class="dropdown discussions-toolbar-filter">
@@ -147,6 +144,7 @@ module DiscussionsHelper
147
144
  #{t label} #{fa_icon :'caret-down', class: 'fa-xs'}
148
145
  </a>
149
146
  <ul class="dropdown-menu" aria-labelledby="dropdown-#{label}">
147
+ #{discussion_filter_unselect_item(label, can_select_all)}
150
148
  #{discussion_filter_list(label, filters, &block)}
151
149
  </ul>
152
150
  </div>
@@ -159,7 +157,15 @@ module DiscussionsHelper
159
157
  end
160
158
 
161
159
  def discussion_filter_item(label, filter, &block)
162
- content_tag(:li, discussion_filter_link(label, filter, &block), class: "#{'selected' if discussion_filter_selected?(label, filter)}")
160
+ content_tag(:li, discussion_filter_link(label, filter, &block), class: ('selected' if discussion_filter_selected?(label, filter)))
161
+ end
162
+
163
+ def discussion_filter_unselect_item(label, can_select_all)
164
+ if can_select_all
165
+ content_tag(:li,
166
+ link_to(t(:all), discussion_filter_params_without_page.except(label)),
167
+ class: ('selected' unless discussion_filter_params.include?(label)))
168
+ end
163
169
  end
164
170
 
165
171
  def discussion_filter_selected?(label, filter)
@@ -167,10 +173,18 @@ module DiscussionsHelper
167
173
  end
168
174
 
169
175
  def discussion_filter_link(label, filter, &block)
170
- link_to capture(filter, &block), discussion_filter_params.merge(Hash[label, filter])
176
+ link_to capture(filter, &block), discussion_filter_params_without_page.merge(Hash[label, filter])
171
177
  end
172
178
 
173
179
  def discussion_info(discussion)
174
- "#{t(:time_since, time: time_ago_in_words(discussion.created_at))} · #{t(:message_count, count: discussion.messages.size)}"
180
+ "#{t(:time_since, time: time_ago_in_words(discussion.created_at))} · #{t(:message_count, count: discussion.messages.size)}"
181
+ end
182
+
183
+ def discussion_filter_params_without_page
184
+ discussion_filter_params.except(:page)
185
+ end
186
+
187
+ def should_show_approved_for?(user, message)
188
+ !user&.moderator_here? && message.approved? && !message.from_moderator?
175
189
  end
176
190
  end
@@ -75,7 +75,7 @@ module ExerciseInputHelper
75
75
 
76
76
  def render_custom_editor(exercise, read_only=false)
77
77
  custom_editor_tag = "mu-#{exercise.language}-custom-editor"
78
- "<#{custom_editor_tag} class='#{custom_editor_tag}' #{custom_editor_read_only if read_only}> </#{custom_editor_tag}>".html_safe
78
+ "<#{custom_editor_tag} id='#{custom_editor_tag}' class='#{custom_editor_tag}' #{custom_editor_read_only if read_only}> </#{custom_editor_tag}>".html_safe
79
79
  end
80
80
 
81
81
  def custom_editor_read_only
@@ -1,10 +1,14 @@
1
- module LocaleHelper
2
- def locale_tags
1
+ module GlobalsHelper
2
+ def globals_tags
3
3
  %Q{
4
4
  <script type="text/javascript">
5
5
  window.mumukiLocale = #{raw Organization.current.locale_json};
6
6
  mumuki.locale = '#{Organization.current.locale}';
7
7
  moment.locale('#{Organization.current.locale}');
8
+
9
+ mumuki.incognitoUser = #{current_incognito_user?};
10
+
11
+ mumuki.version = '#{Mumuki::Laboratory::VERSION}';
8
12
  </script>
9
13
  }.html_safe
10
14
  end
@@ -3,7 +3,7 @@ module IconsHelper
3
3
  fa_icon *icon_for(status_like.to_submission_status)
4
4
  end
5
5
 
6
- def fixed_fa_icon(name, options={})
6
+ def fixed_fa_icon(name, options = {})
7
7
  fa_icon name, options.merge(class: 'fa-fw fixed-icon')
8
8
  end
9
9
 
@@ -47,12 +47,12 @@ module IconsHelper
47
47
  iconizable.iconize[:type].to_s
48
48
  end
49
49
 
50
- def label_for_contextualization(contextualization)
50
+ def label_for_contextualization(contextualization, **options)
51
51
  iconized = contextualization.iconize
52
52
  %Q{
53
53
  <span class="text-#{iconized[:class]} status-label">
54
54
  #{fa_icon "#{iconized[:type]}"}
55
- <span>#{t contextualization.visible_status}</span>
55
+ <span class="#{options[:class]}">#{t contextualization.visible_status}</span>
56
56
  </span>
57
57
  }.html_safe
58
58
  end
@@ -1,24 +1,37 @@
1
1
  class UserMailer < ApplicationMailer
2
+ def welcome_email(user, organization)
3
+ with_locale(user, organization) do
4
+ organization_name = organization.display_name || t(:your_new_organization)
5
+ build_email t(:welcome, name: organization_name), { inline: organization.welcome_email_template }
6
+ end
7
+ end
2
8
 
3
9
  def we_miss_you_reminder(user, cycles)
4
- send_reminder! user, :we_miss_you, "#{cycles.ordinalize}_reminder"
10
+ with_locale(user) do
11
+ build_email t(:we_miss_you), "#{cycles.ordinalize}_reminder"
12
+ end
5
13
  end
6
14
 
7
15
  def no_submissions_reminder(user)
8
- send_reminder! user, :start_using_mumuki, "no_submissions_reminder"
16
+ with_locale(user) do
17
+ build_email t(:start_using_mumuki), 'no_submissions_reminder'
18
+ end
9
19
  end
10
20
 
11
- private
12
-
13
- def send_reminder!(user, subject, template_name)
21
+ def with_locale(user, organization = nil, &block)
14
22
  @user = user
15
23
  @unsubscribe_code = User.unsubscription_verifier.generate(user.id)
16
- @organization = user.last_organization
24
+ @organization = organization || user.last_organization
17
25
 
18
- I18n.with_locale(@organization.locale) do
19
- mail to: user.email,
20
- subject: t(subject),
21
- template_name: template_name
22
- end
26
+ I18n.with_locale(@organization.locale, &block)
27
+ end
28
+
29
+ private
30
+
31
+ def build_email(subject, template)
32
+ mail to: @user.email,
33
+ subject: subject,
34
+ content_type: 'text/html',
35
+ body: render(template)
23
36
  end
24
37
  end
@@ -36,7 +36,7 @@
36
36
  <div class="chapter-container">
37
37
  <div class="chapter <%= enabled ? '' : 'mu-locked' %>">
38
38
  <h3><%= it.number %>. <%= link_to_path_element it, mode: :plain %></h3>
39
- <div class="text-box" <%= 'aria-label=""' unless enabled %>%>
39
+ <div class="text-box" <%= 'aria-label=""' unless enabled %>>
40
40
  <%= it.description_teaser_html %>
41
41
  </div>
42
42
  </div>
@@ -1,9 +1,9 @@
1
1
  <%= content_for :breadcrumbs do %>
2
- <%= home_breadcrumb %>
2
+ <%= header_breadcrumbs %> <%= breadcrumb_list_item(t(:discussions), 'last') %>
3
3
  <% end %>
4
4
 
5
5
  <%= content_for :extra_filters do %>
6
- <%= discussion_dropdown_filter :language, discussions_languages(@discussions) do |language_filter| %>
6
+ <%= discussion_dropdown_filter :language, discussions_languages(@discussions), true do |language_filter| %>
7
7
  <%= language_filter.capitalize %>
8
8
  <% end %>
9
9
  <% end %>
@@ -18,6 +18,6 @@
18
18
  <% if @discussions.empty? %>
19
19
  <h4><%= t :no_questions %></h4>
20
20
  <% else %>
21
- <%= render partial: 'layouts/discussions' %>
21
+ <%= render partial: 'layouts/discussions' %>
22
22
  <% end %>
23
23
  </div>
@@ -7,16 +7,28 @@
7
7
  <%= t(:time_since, time: time_ago_in_words(message.created_at)) %>
8
8
  </span>
9
9
  <% if user.moderator_here? %>
10
- <%= fa_icon(:star, {'data-toggle': 'tooltip', title: (t :moderator), class: 'moderator-star'}) %>
10
+ <%= fa_icon(:star, 'data-toggle': 'tooltip', title: (t :moderator), class: 'moderator-star') %>
11
11
  <% end %>
12
- <% if message.authorized? current_user %>
13
- <span class="actions">
14
- <a class="discussion-message-approved <%= 'selected' if message.approved? %>" onclick="mumuki.Forum.discussionMessageToggleApprove('<%= approve_discussion_message_url(@discussion, message) %>', $(this))">
15
- <%= fa_icon(:check, class: 'fa-xs') %>
16
- </a>
12
+ <span class="actions">
13
+ <% if message.authorized? current_user %>
14
+ <% if current_user&.moderator_here? %>
15
+ <a class="discussion-message-approved <%= 'selected' if message.approved? %>" onclick="mumuki.Forum.discussionMessageToggleApprove('<%= approve_discussion_message_url(@discussion, message) %>', $(this))">
16
+ <%= fa_icon(:check, class: 'fa-xs') %>
17
+ </a>
18
+ <% if message.from_initiator? %>
19
+ <a class="discussion-message-not-actually-a-question <%= 'selected' if message.not_actually_a_question? %>" onclick="mumuki.Forum.discussionMessageToggleNotActuallyAQuestion('<%= question_discussion_message_url(@discussion, message) %>', $(this))">
20
+ <%= fa_icon('question-circle-o', class: 'fa-xs') %>
21
+ </a>
22
+ <% end %>
23
+ <% end %>
17
24
  <%= link_to fa_icon('trash-o'), discussion_message_path(@discussion, message), method: :delete, data: { confirm: t(:are_you_sure, action: t(:destroy_message)) } %>
18
- </span>
19
- <% end %>
25
+ <% end %>
26
+ <% if should_show_approved_for?(current_user, message) %>
27
+ <span class="discussion-message-approved selected">
28
+ <%= fa_icon(:check, class: 'fa-xs', 'data-toggle': 'tooltip', title: (t :approved_message)) %>
29
+ </span>
30
+ <% end %>
31
+ </span>
20
32
  </div>
21
33
  </div>
22
34
  <div class="discussion-message-bubble-content">
@@ -24,7 +24,6 @@
24
24
  <% end %>
25
25
 
26
26
  <%= content_for :no_container do %>
27
- <%= render partial: 'layouts/modals/new_discussion', locals: {debatable: @debatable} %>
28
27
  <% end if current_user? %>
29
28
 
30
29
 
@@ -0,0 +1,33 @@
1
+ <%= content_for :breadcrumbs do %>
2
+ <%= breadcrumbs_for_discussion @discussion, @debatable %>
3
+ <% end %>
4
+
5
+ <%= form_for [@debatable, @discussion] do |f| %>
6
+ <div>
7
+ <div class="discussion-context">
8
+ <%= render partial: 'exercises/read_only', locals: {exercise: @debatable} %>
9
+ </div>
10
+ </div>
11
+
12
+ <hr class="message-divider">
13
+
14
+ <%= render layout: 'discussions/message_container', locals: {user: @discussion.initiator} do %>
15
+ <div class="discussion-message-bubble" id="new-discussion-description-container">
16
+ <div class="discussion-message-bubble-header">
17
+ <div class="discussion-message-bubble-title">
18
+ <%= @discussion.initiator.name %>
19
+ </div>
20
+ </div>
21
+ <div class="discussion-message-bubble-content">
22
+ <div class="container-fluid">
23
+ <div class="row">
24
+ <div class="discussion-new-message-content">
25
+ <%= f.editor :description, '', {id: 'new-discussion-message', class: 'form-control', placeholder: t(:discussion_description_placeholder)} %>
26
+ </div>
27
+ </div>
28
+ </div>
29
+ </div>
30
+ </div>
31
+ <%= f.submit t(:save), class: 'btn btn-success btn-block discussion-new-message-button' %>
32
+ <% end %>
33
+ <% end %>
@@ -3,55 +3,25 @@
3
3
  <% end %>
4
4
 
5
5
  <div>
6
- <div class="row">
7
- <% if current_user %>
8
- <div class="mu-inline-block-right hidden-xs discussion-user-menu">
9
- <h3>
10
- <% if @discussion.subscribable? %>
11
- <a class="discussion-subscription" onclick="mumuki.Forum.discussionSubscription('<%= subscription_discussion_url(@discussion) %>')">
12
- <%= fa_icon(:eye, class: 'fa-xs') %>
13
- <%= span_toggle t(:subscribe), t(:unsubscribe), current_user.subscribed_to?(@discussion) %>
14
- </a>
15
- <% end %>
16
- <% if @discussion.solved? %>
17
- <a class="discussion-upvote" onclick="mumuki.Forum.discussionUpvote('<%= upvote_discussion_url(@discussion) %>')">
18
- <%= fa_icon(:star, class: 'fa-xs') %>
19
- <%= span_toggle t(:upvote), t(:undo_upvote), current_user.upvoted?(@discussion) %>
20
- </a>
21
- <% end %>
22
- </h3>
23
- </div>
24
- <% end %>
25
- <div class="mu-inline-block-left">
26
- <div class="discussion-header">
27
- <h3 class="discussion-title">
28
- <%= @discussion.title %>
29
- </h3>
30
- </div>
31
- <div class="discussion-description">
32
- <%= label_for_contextualization(@discussion) %> ·
33
- <span class="discussion-info">
34
- <span class="discussion-initiator-name">
35
- <%= @discussion.initiator.name %>
36
- </span>
37
- <%= discussion_info(@discussion) %>
38
- </span>
39
- </div>
40
- </div>
41
- </div>
42
-
43
6
  <div class="discussion-context">
44
7
  <%= render partial: 'exercises/read_only', locals: {exercise: @debatable} %>
45
8
  </div>
46
-
47
9
  </div>
48
10
 
49
11
  <% if @discussion.has_messages? || @discussion.commentable_by?(current_user) %>
50
12
  <hr class="message-divider">
51
13
 
52
- <h3>
53
- <%= t :messages %>
54
- </h3>
14
+ <div>
15
+ <h3>
16
+ <%= t :messages %>
17
+ <% if @discussion.last_moderator_access_visible_for?(current_user) %>
18
+ <small class="pull-right">
19
+ <span><%= t :last_seen, name: @discussion.last_moderator_access_by.full_name %></span>
20
+ <span><%= t :time_since, time: time_ago_in_words(@discussion.last_moderator_access_at) %></span>
21
+ </small>
22
+ <% end %>
23
+ </h3>
24
+ </div>
55
25
 
56
26
  <% if @discussion.has_messages? %>
57
27
  <div class="discussion-messages">
@@ -71,8 +41,10 @@
71
41
 
72
42
  <% end %>
73
43
 
74
- <div class="discussion-actions">
75
- <% @discussion.reachable_statuses_for(current_user).each do |status| %>
76
- <%= discussion_update_status_button(status) %>
77
- <% end if current_user %>
78
- </div>
44
+ <% if current_user&.moderator_here? %>
45
+ <div class="discussion-actions">
46
+ <% @discussion.reachable_statuses_for(current_user).each do |status| %>
47
+ <%= discussion_update_status_button(status) %>
48
+ <% end %>
49
+ </div>
50
+ <% end %>
@@ -1,4 +1,4 @@
1
- <div class="<%= "bs-callout bs-callout-#{icon_class_for contextualization}" %>">
1
+ <div class="<%= "bs-callout bs-callout-#{icon_class_for contextualization.submission_status}" %>">
2
2
  <%= render partial: 'exercise_solutions/results_title', locals: {contextualization: contextualization} %>
3
3
  <%= yield %>
4
4
  </div>