mumuki-laboratory 9.8.0 → 9.10.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (48) hide show
  1. checksums.yaml +4 -4
  2. data/app/assets/javascripts/mumuki_laboratory/application/messages.js +1 -30
  3. data/app/assets/stylesheets/mumuki_laboratory/application/modules/_discussion.scss +32 -34
  4. data/app/assets/stylesheets/mumuki_laboratory/application/modules/_editor.scss +3 -0
  5. data/app/controllers/messages_controller.rb +2 -5
  6. data/app/controllers/users_controller.rb +5 -1
  7. data/app/helpers/application_helper.rb +4 -0
  8. data/app/helpers/discussions_helper.rb +11 -4
  9. data/app/helpers/exam_registration_helper.rb +0 -4
  10. data/app/helpers/icons_helper.rb +1 -1
  11. data/app/helpers/menu_bar_helper.rb +1 -1
  12. data/app/helpers/messages_helper.rb +4 -8
  13. data/app/helpers/profile_helper.rb +4 -0
  14. data/app/helpers/user_discussions_helper.rb +38 -0
  15. data/app/views/discussions/new.html.erb +1 -1
  16. data/app/views/layouts/_copyright.html.erb +1 -1
  17. data/app/views/layouts/_discussions.html.erb +1 -37
  18. data/app/views/layouts/_discussions_list.html.erb +38 -0
  19. data/app/views/layouts/_messages.html.erb +1 -7
  20. data/app/views/layouts/exercise_inputs/forms/_interactive_form.html.erb +1 -1
  21. data/app/views/layouts/exercise_inputs/forms/_problem_form.html.erb +1 -1
  22. data/app/views/user_mailer/1st_reminder.html.erb +1 -1
  23. data/app/views/user_mailer/1st_reminder.text.erb +1 -1
  24. data/app/views/user_mailer/2nd_reminder.html.erb +1 -1
  25. data/app/views/user_mailer/2nd_reminder.text.erb +1 -1
  26. data/app/views/user_mailer/3rd_reminder.html.erb +1 -1
  27. data/app/views/user_mailer/3rd_reminder.text.erb +1 -1
  28. data/app/views/user_mailer/certificate.html.erb +1 -1
  29. data/app/views/user_mailer/certificate.text.erb +1 -1
  30. data/app/views/user_mailer/no_submissions_reminder.html.erb +1 -1
  31. data/app/views/user_mailer/no_submissions_reminder.text.erb +1 -1
  32. data/app/views/users/_basic_profile_fields.html.erb +30 -0
  33. data/app/views/users/_profile_fields.html.erb +1 -20
  34. data/app/views/users/discussions.html.erb +19 -11
  35. data/app/views/users/messages.html.erb +1 -1
  36. data/config/routes.rb +0 -1
  37. data/lib/mumuki/laboratory/locales/en.yml +12 -6
  38. data/lib/mumuki/laboratory/locales/es-CL.yml +13 -6
  39. data/lib/mumuki/laboratory/locales/es.yml +13 -7
  40. data/lib/mumuki/laboratory/locales/pt.yml +11 -5
  41. data/lib/mumuki/laboratory/version.rb +1 -1
  42. data/spec/controllers/organizations_api_controller_spec.rb +6 -1
  43. data/spec/controllers/students_api_controller_spec.rb +1 -1
  44. data/spec/dummy/db/schema.rb +3 -1
  45. data/spec/features/not_found_public_flow_spec.rb +1 -1
  46. data/spec/features/profile_flow_spec.rb +1 -1
  47. metadata +116 -114
  48. data/app/views/messages/errors.html.erb +0 -1
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: cf699468e8c7bb82501af07ac560eb1a4d0eeb76e132de4471313601b305b0b8
4
- data.tar.gz: 2dc39959670941c08b30d163905bf76b45ed1bcd2c22a73904124717847de23b
3
+ metadata.gz: fb8cf028976b13658a28a48a6bf235276d1732ce1256a463cd348b8d915fcba7
4
+ data.tar.gz: 990a6400b5f4fa76f7f17cd5dc97575c9cb475bc7588a15d4ae8102d434583b7
5
5
  SHA512:
6
- metadata.gz: 72ddf7aa48d50a43378a814d009f3f535905cbbb070fd159a38210b4f7d1204d391bb7c57fa2a469eef0931840f3876b9c068e54f815a69bc06e3342b7cba8c2
7
- data.tar.gz: 747b4074fcd68c4763ca6d539e8bb6b6f16ba4888e8c5289f4cd4dfc2748a3da0bbd097048860a6f59a2affe0e73c4a9fc8510e9f78dbfcf1bb7b78171e91058
6
+ metadata.gz: 371681f22a60126bad63d241eee30f52f5a8c674077648a2bad413f15375fb704276af6149fc04046f0fc6d72d1562f76ccfcb1ec4d013a7bdcd4480dd817766
7
+ data.tar.gz: 40875a8558cb07951d09ca67bfe2c10405b9fb75d225ff1afd9f4ede4803ba87b9a1460a8aae18268027c3e4380b9a00d20eb9d03d8c2d5561d4ee023453d4a6
@@ -19,6 +19,7 @@ mumuki.load(() => {
19
19
  $('.pending-messages-filter').removeClass('pending-messages-filter');
20
20
  $('button.btn-submit').removeClass('disabled');
21
21
  $('.pending-messages-text').remove();
22
+ $("a[data-bs-target='#messages']")[0].click();
22
23
  },
23
24
  readMessages: function (url) {
24
25
  Chat.tokenRequest({
@@ -47,35 +48,6 @@ mumuki.load(() => {
47
48
  setMessagesInterval: function () {
48
49
  mumuki.setInterval(Chat.getMessages, 60000);
49
50
  },
50
- submitMessagesForm: function (url, readUrl, errorUrl) {
51
- var $container = $('.mu-view-messages');
52
-
53
- function renderHTML(data) {
54
- $container.empty();
55
- $container.html(data);
56
- $("a[data-bs-target='#messages']").click();
57
- }
58
-
59
- function success(data) {
60
- renderHTML(data);
61
- Chat.readMessages(readUrl);
62
- }
63
-
64
- function error(_xhr) {
65
- Chat.tokenRequest({
66
- url: errorUrl,
67
- success: renderHTML,
68
- xhrFields: {withCredentials: true}
69
- });
70
- }
71
-
72
- $.ajax({
73
- url: url,
74
- success: success,
75
- error: error,
76
- xhrFields: {withCredentials: true}
77
- });
78
- },
79
51
  openNewMessageModal: function () {
80
52
  Chat.$newMessageModal().modal({backdrop: false, keyboard: false});
81
53
  Chat.$body().addClass("new-message-modal-open");
@@ -90,4 +62,3 @@ mumuki.load(() => {
90
62
  mumuki.Chat = Chat;
91
63
 
92
64
  });
93
-
@@ -50,36 +50,28 @@ $moderator-badge-color: #dd9900;
50
50
  }
51
51
  }
52
52
 
53
- .discussion-pagination {
54
- display: flex;
55
- flex-direction: row;
56
- align-items: center;
57
- justify-content: center;
58
- }
59
-
60
53
  .discussion-language-icon {
61
- float: right;
62
- margin-right: 10px;
63
- margin-top: 6px;
64
- -ms-transform: scale(1.4, 1.4);
65
- -webkit-transform: scale(1.4, 1.4);
66
- transform: scale(1.4, 1.4);
54
+ display: inline;
55
+ padding-right: 5px;
67
56
  }
68
57
 
69
- .discussion-icon {
70
- -ms-transform: scale(0.8, 0.8);
71
- -webkit-transform: scale(0.8, 0.8);
72
- transform: scale(0.8, 0.8);
73
- margin-left: 5px;
74
- float: right;
75
- .fa-stack-1x {
76
- -ms-transform: scale(0.9, 0.9);
77
- -webkit-transform: scale(0.9, 0.9);
78
- transform: scale(0.9, 0.9);
58
+ .discussion-validated-messages-count {
59
+ .fa-check {
60
+ font-size: calc(#{$font-size-base} * 0.5);
61
+ position: absolute;
62
+ margin-left: -13px;
63
+ margin-top: 10px;
79
64
  }
80
65
  }
81
66
  }
82
67
 
68
+ .discussion-pagination {
69
+ display: flex;
70
+ flex-direction: row;
71
+ align-items: center;
72
+ justify-content: center;
73
+ }
74
+
83
75
  .discussions-no-filtered-results {
84
76
  padding: 20px;
85
77
  border: 1px solid $discussion-toolbar-border-color;
@@ -96,7 +88,7 @@ $moderator-badge-color: #dd9900;
96
88
  }
97
89
 
98
90
  .discussion-status-icon {
99
- padding-right: 10px;
91
+ padding-left: 2px;
100
92
  }
101
93
 
102
94
  .discussion-row {
@@ -106,11 +98,26 @@ $moderator-badge-color: #dd9900;
106
98
  font-size: 18px;
107
99
  font-weight: bold;
108
100
  }
101
+ .discussion-title-icons {
102
+ display: flex;
103
+ flex-shrink: 0;
104
+ align-items: center;
105
+ font-size: calc(#{$font-size-base} * 0.95);
106
+
107
+ > * {
108
+ padding-left: 20px;
109
+ }
110
+ }
109
111
  .discussion-description {
110
112
  overflow: hidden;
111
113
  display: block;
112
114
  text-overflow: ellipsis;
113
115
  white-space: nowrap;
116
+ margin-bottom: 20px;
117
+ font-size: calc(#{$font-size-base} * 0.95);
118
+ }
119
+ .discussion-initiator {
120
+ font-size: calc(#{$font-size-base} * 0.85);
114
121
  }
115
122
  }
116
123
 
@@ -308,6 +315,7 @@ $moderator-badge-color: #dd9900;
308
315
  border: 1px solid $discussion-message-border-color;
309
316
  border-radius: 3px;
310
317
  position: relative;
318
+ margin-bottom: 20px;
311
319
  .discussion-message-bubble-header {
312
320
  background-color: $mu-color-highlight-background;
313
321
  height: 40px;
@@ -400,13 +408,3 @@ $moderator-badge-color: #dd9900;
400
408
  font-weight: normal;
401
409
  }
402
410
  }
403
-
404
- .discussion-moderator-access {
405
- margin-right: 20px;
406
- display: flex;
407
- flex-direction: column;
408
- align-items: center;
409
- .moderator-initials {
410
- font-size: 14px;
411
- }
412
- }
@@ -115,3 +115,6 @@ body.fullscreen {
115
115
  }
116
116
  }
117
117
 
118
+ .pending-messages-text {
119
+ top: auto;
120
+ }
@@ -2,7 +2,8 @@ class MessagesController < AjaxController
2
2
  before_action :set_exercise!, only: [:create, :read_messages]
3
3
 
4
4
  def index
5
- render json: {has_messages: has_messages?, messages_count: messages_count}
5
+ pending_messages = current_user.unread_messages
6
+ render json: {has_messages: pending_messages.present?, messages_count: pending_messages.count}
6
7
  end
7
8
 
8
9
  def read_messages
@@ -15,10 +16,6 @@ class MessagesController < AjaxController
15
16
  redirect_to @exercise
16
17
  end
17
18
 
18
- def errors
19
- render 'messages/errors', layout: false
20
- end
21
-
22
19
  private
23
20
 
24
21
  def set_exercise!
@@ -27,7 +27,7 @@ class UsersController < ApplicationController
27
27
  end
28
28
 
29
29
  def discussions
30
- @watched_discussions = current_user.watched_discussions_in_organization
30
+ @watched_discussions = current_user.watched_discussions_in_organization.scoped_query_by(discussion_filter_params).unread_first
31
31
  end
32
32
 
33
33
  def activity
@@ -71,4 +71,8 @@ class UsersController < ApplicationController
71
71
  nil
72
72
  end
73
73
  end
74
+
75
+ def discussion_filter_params
76
+ @filter_params ||= params.permit([:page])
77
+ end
74
78
  end
@@ -48,4 +48,8 @@ module ApplicationHelper
48
48
  def notification_preview_for(target)
49
49
  render "notifications/#{target.class.name.underscore}", { target: target }
50
50
  end
51
+
52
+ def current_time_zone_html
53
+ %Q{(<span class="select-date-timezone">#{Organization.current.time_zone}</span>)}.html_safe
54
+ end
51
55
  end
@@ -60,11 +60,18 @@ module DiscussionsHelper
60
60
  }.html_safe
61
61
  end
62
62
 
63
- def discussion_messages_icon(discussion)
63
+ def discussion_messages_count(discussion)
64
64
  %Q{
65
- <span class="discussion-icon fa-stack fa-xs">
66
- <i class="far fa-comment fa-stack-2x"></i>
67
- <i class="fas fa-stack-1x">#{discussion.validated_messages_count}</i>
65
+ <span class="discussion-messages-count">
66
+ #{fa_icon :comments, type: :regular, text: discussion.messages_count}
67
+ </span>
68
+ }.html_safe
69
+ end
70
+
71
+ def discussion_validated_messages_count(discussion)
72
+ %Q{
73
+ <span class="discussion-validated-messages-count">
74
+ #{fa_icon :comment, type: :regular}#{fa_icon :check, text: discussion.validated_messages_count}
68
75
  </span>
69
76
  }.html_safe
70
77
  end
@@ -6,8 +6,4 @@ module ExamRegistrationHelper
6
6
  { icon: :info_circle, class: :info, t: :exam_registration_explanation_html }
7
7
  end
8
8
  end
9
-
10
- def current_time_zone_html
11
- %Q{(<span class="select-date-timezone">#{Organization.current&.time_zone}</span>)}.html_safe
12
- end
13
9
  end
@@ -50,6 +50,6 @@ module IconsHelper
50
50
  end
51
51
 
52
52
  def icon_for_read(read)
53
- tag('i', class: "fa#{read ? 'r' : 's'} fa-envelope")
53
+ tag('i', class: "fa#{read ? 'r' : 's'} fa-envelope#{read ? '-open' : ''}")
54
54
  end
55
55
  end
@@ -48,6 +48,6 @@ module MenuBarHelper
48
48
  end
49
49
 
50
50
  def menu_link_to_faqs
51
- li_tag menu_item('question', :faqs, faqs_path)
51
+ li_tag menu_item(:info, :faqs_abbreviated, faqs_path)
52
52
  end
53
53
  end
@@ -1,8 +1,4 @@
1
1
  module MessagesHelper
2
- def messages_url(exercise)
3
- exercise.messages_url_for(current_user) if current_user?
4
- end
5
-
6
2
  def hidden_pending(assignment)
7
3
  assignment.pending_messages? ? '' : 'd-none'
8
4
  end
@@ -15,11 +11,11 @@ module MessagesHelper
15
11
  'pending-messages-filter' if assignment.pending_messages?
16
12
  end
17
13
 
18
- def read_messages_caption(assignment)
19
- assignment.pending_messages? ? :read_messages : :exit
20
- end
21
-
22
14
  def sender_class(message)
23
15
  message.blank? || message.from_user?(current_user) ? 'self' : 'other'
24
16
  end
17
+
18
+ def staleness_class(message)
19
+ 'mu-stale' if message.stale?
20
+ end
25
21
  end
@@ -10,4 +10,8 @@ module ProfileHelper
10
10
  def save_edit_profile_button(form)
11
11
  form.submit t(:save), class: 'btn btn-complementary mu-edit-profile-btn'
12
12
  end
13
+
14
+ def show_verified_full_name_notice?(user, organization)
15
+ user.has_verified_full_name? && organization.private?
16
+ end
13
17
  end
@@ -0,0 +1,38 @@
1
+ module UserDiscussionsHelper
2
+ def user_discussions_table_title(discussion, user, last_read)
3
+ %Q{
4
+ <tr></tr>
5
+ <thead>
6
+ <tr>
7
+ <td class="#{last_read.nil? ? '' : 'pt-5'}" colspan="4">
8
+ <strong>#{discussion.read_by?(user) ? t(:discussions_read) : t(:discussions_unread)}</strong>
9
+ </td>
10
+ </tr>
11
+ </thead>
12
+ }.html_safe
13
+ end
14
+
15
+ def user_discussions_table_header
16
+ %Q{
17
+ <tr class="fw-bold">
18
+ <td></td>
19
+ <td>#{t(:exercise)}</td>
20
+ <td>#{t(:discussion_created_by)}</td>
21
+ <td>#{t(:last_message)}</td>
22
+ </tr>
23
+ }.html_safe
24
+ end
25
+
26
+ def user_discussions_table_item(discussion, user)
27
+ %Q{
28
+ <tr>
29
+ <td class="text-center">
30
+ #{icon_for_read(discussion.read_by?(user))}
31
+ </td>
32
+ <td>#{link_to discussion.item.name, item_discussion_path(discussion)}</td>
33
+ <td>#{discussion_user_name discussion.initiator}</td>
34
+ <td>#{t(:time_since, time: time_ago_in_words(discussion.last_message_date))}</td>
35
+ </tr>
36
+ }.html_safe
37
+ end
38
+ end
@@ -28,6 +28,6 @@
28
28
  </div>
29
29
  </div>
30
30
  </div>
31
- <%= f.submit t(:save), class: 'btn btn-complementary w-100 discussion-new-message-button' %>
31
+ <%= f.submit t(:publish_discussion), class: 'btn btn-complementary w-100 discussion-new-message-button' %>
32
32
  <% end %>
33
33
  <% end %>
@@ -1,2 +1,2 @@
1
- &copy; 2015-<%= DateTime.now.year %>
1
+ &copy; 2015-<%= Time.current.year %>
2
2
  <a href="http://mumuki.org/" class="mu-org-link"><span class="da da-mumuki-circle"></span> Mumuki</a>
@@ -40,43 +40,7 @@
40
40
  </span>
41
41
  </div>
42
42
  <% else %>
43
- <div class="discussions">
44
- <% @filtered_discussions.each do |discussion| %>
45
- <%= link_to exercise_discussion_path(discussion.exercise.id, discussion) do %>
46
- <div class="discussion">
47
- <div class="discussion-row">
48
- <%= discussion_messages_icon(discussion) %>
49
- <% unless @debatable.respond_to? :language %>
50
- <div class="discussion-language-icon">
51
- <%= language_icon(discussion.exercise.language) %>
52
- </div>
53
- <% end %>
54
- <div>
55
- <div class="discussion-title">
56
- <span class="discussion-status-icon">
57
- <%= discussion_status_fa_icon(discussion) %>
58
- </span>
59
- <span class="d-none d-lg-inline"><%= discussion.exercise.guide.name %> -</span>
60
- <span><%= discussion.exercise.name %></span>
61
- <% if discussion.current_responsible_visible_for?(current_user) %>
62
- <div class="float-end discussion-moderator-access" >
63
- <%= profile_picture_for(discussion.responsible_moderator_by, height: 32) %>
64
- <span class="moderator-initials">
65
- <%= discussion.responsible_moderator_by.name_initials %>
66
- </span>
67
- </div>
68
- <% end %>
69
-
70
- </div>
71
- <span class="discussion-description">
72
- <%= discussion.description %>
73
- </span>
74
- </div>
75
- </div>
76
- </div>
77
- <% end %>
78
- <% end %>
79
- </div>
43
+ <%= render partial: 'layouts/discussions_list' %>
80
44
 
81
45
  <div class="discussion-pagination">
82
46
  <%= paginate @filtered_discussions, nav_class: 'pagination' %>
@@ -0,0 +1,38 @@
1
+ <div class="discussions">
2
+ <% @filtered_discussions.each do |discussion| %>
3
+ <%= link_to exercise_discussion_path(discussion.exercise.id, discussion), class: 'discussion-link' do %>
4
+ <div class="discussion">
5
+ <div class="discussion-row">
6
+ <div class="d-flex justify-content-between">
7
+ <div class="discussion-title">
8
+ <div class="discussion-language-icon">
9
+ <%= language_icon(discussion.exercise.language) %>
10
+ </div>
11
+ <span class="d-none d-lg-inline"><%= discussion.exercise.guide.name %> -</span>
12
+ <span><%= discussion.exercise.name %></span>
13
+ <% if discussion.current_responsible_visible_for?(current_user) %>
14
+ <span class="badge ms-1 bg-dark">
15
+ <%= responsible_moderator_text_for(discussion, current_user)%>
16
+ </span>
17
+ <% end %>
18
+ </div>
19
+ <div class="discussion-title-icons">
20
+ <%= discussion_messages_count(discussion) %>
21
+ <%= discussion_validated_messages_count(discussion) %>
22
+ </div>
23
+ </div>
24
+ <span class="discussion-description">
25
+ <%= discussion.description %>
26
+ </span>
27
+ <span class="discussion-initiator">
28
+ <strong><%= discussion_user_name discussion.initiator %></strong>
29
+ <%= t(:asked_time_since, time: time_ago_in_words(discussion.created_at)) %>
30
+ <span class="discussion-status-icon">
31
+ <%= discussion_status_fa_icon(discussion) %>
32
+ </span>
33
+ </span>
34
+ </div>
35
+ </div>
36
+ <% end %>
37
+ <% end %>
38
+ </div>