mumuki-laboratory 9.12.1 → 9.13.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (37) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +3 -2
  3. data/app/controllers/application_controller.rb +7 -3
  4. data/app/controllers/book_controller.rb +6 -0
  5. data/app/controllers/chapters_controller.rb +9 -0
  6. data/app/controllers/discussions_controller.rb +9 -0
  7. data/app/controllers/exercises_controller.rb +5 -0
  8. data/app/controllers/faqs_controller.rb +4 -0
  9. data/app/controllers/lessons_controller.rb +9 -0
  10. data/app/controllers/users_controller.rb +4 -0
  11. data/app/helpers/concerns/with_student_path_navigation.rb +1 -1
  12. data/app/helpers/content_view_helper.rb +8 -0
  13. data/app/helpers/discussions_helper.rb +1 -1
  14. data/app/helpers/links_helper.rb +5 -1
  15. data/app/helpers/menu_bar_helper.rb +2 -3
  16. data/app/helpers/overlapped_buttons_helper.rb +2 -1
  17. data/app/views/book/show.html.erb +16 -7
  18. data/app/views/chapters/show.html.erb +1 -0
  19. data/app/views/discussions/_new_message.html.erb +1 -1
  20. data/app/views/discussions/show.html.erb +5 -5
  21. data/app/views/exercise_solutions/_results.html.erb +1 -1
  22. data/app/views/guides/_guide.html.erb +2 -2
  23. data/app/views/layouts/_progress_bar.html.erb +1 -0
  24. data/app/views/layouts/_progress_listing.html.erb +1 -0
  25. data/app/views/layouts/application.html.erb +4 -0
  26. data/app/views/layouts/exercise_inputs/forms/_kids_form.html.erb +1 -1
  27. data/app/views/layouts/exercise_inputs/forms/_problem_form.html.erb +6 -2
  28. data/app/views/layouts/exercise_inputs/read_only_editors/_hidden.html.erb +0 -0
  29. data/app/views/layouts/exercise_inputs/read_only_editors/_upload.html.erb +0 -0
  30. data/lib/mumuki/laboratory/controllers.rb +1 -0
  31. data/lib/mumuki/laboratory/controllers/authorization.rb +5 -1
  32. data/lib/mumuki/laboratory/controllers/validate_access_mode.rb +15 -0
  33. data/lib/mumuki/laboratory/version.rb +1 -1
  34. data/spec/features/menu_bar_spec.rb +3 -2
  35. data/spec/features/not_found_private_flow_spec.rb +1 -1
  36. data/spec/features/read_only_flow_spec.rb +920 -0
  37. metadata +13 -14
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 67eb631fe5c878f8151a859f82bcffc8225e087e59669f600e08ff66cc9034c0
4
- data.tar.gz: ab208353d092d920d65d899ad84aecb58f7f89e15ecae80f6869e09e0df85b1e
3
+ metadata.gz: 331d677d1f93dd36a842d9b39bd78a364e3cfdfe6b87711a52b8dbd0fdd3c678
4
+ data.tar.gz: 159c5b4bf7abb35721da25fea0f11c861fe2df4243c505a4bc8b3c6f523f6207
5
5
  SHA512:
6
- metadata.gz: 9b33f896c2ae44fee775b055f2dfcd965280452a3ef073a19a8650d0ca4c7e66c223247a28e5d126bc7a8dda256ae74683488d2090f796d97aa390a63bc3c00a
7
- data.tar.gz: 21b1a86962e2edfcb17e09a96ae9983cf89828251cf283b3a0dd2004a8b98b09d495cf75b2995793d02d138aba5eaaba9cda6bf4c8f22e722881a0a312f94c87
6
+ metadata.gz: b575ed92e419931d69aae5b043f7e29a792c9d9180746e1be3c6bd1261b29394d656a5d97231a6f419181c97daf0dd01b40637c62ab7cc35ee52fd31f84de2b6
7
+ data.tar.gz: 84a684078c62aa1f15c070a3af71f82c6afce2ea657c0b8de0017954c2d020e7a3eb9c5014031746440bfd44b363386cbae0d8f1d6a9e58ee16cb2cb7368257e
data/README.md CHANGED
@@ -559,15 +559,16 @@ Before using the API, you must create an `ApiClient` using `rails c`, which will
559
559
 
560
560
  Before using the API, take a look to the roles hierarchy:
561
561
 
562
- ![roles hierarchy](https://yuml.me/diagram/plain/class/[Admin]%5E-[Janitor],[Admin]%5E-[Moderator],%20[Janitor]%5E-[Headmaster],%20[Headmaster]%5E-[Teacher],%20[Teacher]%5E-[Student],%20,%20[Admin]%5E-[Editor],%20[Editor]%5E-[Writer],%20[Owner]%5E-[Admin]).
562
+ ![roles hierarchy](https://yuml.me/diagram/plain/class/[Admin]%5E-[Janitor],[Admin]%5E-[Forum%20Supervisor],[Forum%20Supervisor]%5E-[Moderator],[Janitor]%5E-[Headmaster],[Headmaster]%5E-[Teacher],[Teacher]%5E-[Student],[Student]%5E-[Ex%20Student],[Admin]%5E-[Editor],[Editor]%5E-[Writer],[Owner]%5E-[Admin]).
563
563
 
564
564
  Permissions are bound to a scope, that states in which context the operation can be performed. Scopes are simply two-level contexts, expressed as slugss `<first>/<second>`, without any explicit semantic. They exact meaning depends on the role:
565
565
 
566
+ * ex_student: `organization/course`
566
567
  * student: `organization/course`
567
568
  * teacher and headmaster: `organization/course`
568
569
  * writer and editor: `organization/content`
569
570
  * janitor: `organization/_`
570
- * moderator: `organization/_`
571
+ * moderator and forum_supervisor: `organization/_`
571
572
  * admin: `_/_`
572
573
  * owner: `_/_`
573
574
 
@@ -24,7 +24,6 @@ class ApplicationController < ActionController::Base
24
24
  before_action :redirect_to_proper_context!, if: :immersive_context_wrong?
25
25
 
26
26
  before_action :authorize_if_private!
27
- before_action :validate_active_organization!
28
27
  before_action :validate_user_profile!, if: :current_user?
29
28
  before_action :validate_accepted_role_terms!, if: :current_user?
30
29
 
@@ -43,7 +42,8 @@ class ApplicationController < ActionController::Base
43
42
  :current_immersive_organizations,
44
43
  :theme_stylesheet_url,
45
44
  :extension_javascript_url,
46
- :current_immersive_path
45
+ :current_immersive_path,
46
+ :current_access_mode
47
47
 
48
48
  add_flash_types :info
49
49
 
@@ -72,7 +72,7 @@ class ApplicationController < ActionController::Base
72
72
 
73
73
  def validate_active_organization!
74
74
  return if current_user&.teacher_here?
75
- Organization.current.validate_active!
75
+ Organization.current.validate_active_for! current_user
76
76
  end
77
77
 
78
78
  # required by Mumukit::Login
@@ -160,4 +160,8 @@ class ApplicationController < ActionController::Base
160
160
  def leave_organization!
161
161
  Mumukit::Platform::Organization.leave!
162
162
  end
163
+
164
+ def current_access_mode
165
+ Organization.current.access_mode(current_user)
166
+ end
163
167
  end
@@ -4,4 +4,10 @@ class BookController < ApplicationController
4
4
  @book = Organization.current.book
5
5
  @exams = Organization.current.accessible_exams_for current_user
6
6
  end
7
+
8
+ private
9
+
10
+ def authorization_minimum_role
11
+ :ex_student
12
+ end
7
13
  end
@@ -3,6 +3,7 @@ require 'addressable/uri'
3
3
  # It acts as a guide container in monolesson contexts
4
4
  class ChaptersController < GuideContainerController
5
5
  include Mumuki::Laboratory::Controllers::ImmersiveNavigation
6
+ include Mumuki::Laboratory::Controllers::ValidateAccessMode
6
7
 
7
8
  def subject
8
9
  @chapter ||= Chapter.find_by(id: params[:id])
@@ -14,4 +15,12 @@ class ChaptersController < GuideContainerController
14
15
  @monolesson = subject.monolesson
15
16
  @guide = @monolesson&.guide
16
17
  end
18
+
19
+ def authorization_minimum_role
20
+ :ex_student
21
+ end
22
+
23
+ def subject_container
24
+ subject.topic
25
+ end
17
26
  end
@@ -7,6 +7,7 @@ class DiscussionsController < ApplicationController
7
7
  before_action :discussion_filter_params, only: :index
8
8
  before_action :read_discussion, only: :show
9
9
  before_action :authorize_moderator!, only: [:responsible]
10
+ before_action :validate_access_mode!
10
11
 
11
12
  helper_method :discussion_filter_params
12
13
 
@@ -99,4 +100,12 @@ class DiscussionsController < ApplicationController
99
100
  def discussion_filter_params
100
101
  @filter_params ||= params.permit(Discussion.permitted_query_params)
101
102
  end
103
+
104
+ def authorization_minimum_role
105
+ :ex_student
106
+ end
107
+
108
+ def validate_access_mode!
109
+ current_access_mode.validate_discuss_here! subject
110
+ end
102
111
  end
@@ -2,6 +2,7 @@ class ExercisesController < ApplicationController
2
2
  include Mumuki::Laboratory::Controllers::Content
3
3
  include Mumuki::Laboratory::Controllers::ExerciseSeed
4
4
  include Mumuki::Laboratory::Controllers::ImmersiveNavigation
5
+ include Mumuki::Laboratory::Controllers::ValidateAccessMode
5
6
 
6
7
  before_action :set_guide!, only: :show
7
8
  before_action :set_assignment!, only: :show, if: :current_user?
@@ -50,4 +51,8 @@ class ExercisesController < ApplicationController
50
51
  :guide_id, :number,
51
52
  :layout, :expectations_yaml)
52
53
  end
54
+
55
+ def authorization_minimum_role
56
+ :ex_student
57
+ end
53
58
  end
@@ -1,4 +1,8 @@
1
1
  class FAQsController < ApplicationController
2
+ def authorization_minimum_role
3
+ :ex_student
4
+ end
5
+
2
6
  def index
3
7
  @faqs = Organization.current.faqs_html
4
8
  raise Mumuki::Domain::NotFoundError unless @faqs.present?
@@ -1,9 +1,18 @@
1
1
  class LessonsController < GuideContainerController
2
2
  include Mumuki::Laboratory::Controllers::ImmersiveNavigation
3
+ include Mumuki::Laboratory::Controllers::ValidateAccessMode
3
4
 
4
5
  private
5
6
 
6
7
  def subject
7
8
  @lesson ||= Lesson.find_by(id: params[:id])
8
9
  end
10
+
11
+ def authorization_minimum_role
12
+ :ex_student
13
+ end
14
+
15
+ def subject_container
16
+ subject.guide
17
+ end
9
18
  end
@@ -108,4 +108,8 @@ class UsersController < ApplicationController
108
108
  def discussion_filter_params
109
109
  @filter_params ||= params.permit([:page])
110
110
  end
111
+
112
+ def authorization_minimum_role
113
+ :ex_student
114
+ end
111
115
  end
@@ -9,7 +9,7 @@ module WithStudentPathNavigation
9
9
  end
10
10
 
11
11
  def next_exercise_button(exercise)
12
- next_button(exercise) || next_button(exercise.guide.lesson)
12
+ next_button(exercise) || next_button(exercise.guide.lesson) if show_content_element?
13
13
  end
14
14
 
15
15
  def close_modal_button
@@ -7,6 +7,14 @@ module ContentViewHelper
7
7
  content.name
8
8
  end
9
9
 
10
+ def show_content?(content)
11
+ current_access_mode.show_content?(content)
12
+ end
13
+
14
+ def show_content_element?
15
+ current_access_mode.show_content_element?
16
+ end
17
+
10
18
  private
11
19
 
12
20
  def content_type_number(content)
@@ -8,7 +8,7 @@ module DiscussionsHelper
8
8
  end
9
9
 
10
10
  def solve_discussions_link
11
- discussions_link others_discussions_icon(t(:solve_doubts)), discussions_path(solve_discussion_params_for(current_user)), class: 'dropdown-item'
11
+ discussions_link others_discussions_icon(t(:solve_doubts)), discussions_path(solve_discussion_params_for(current_user)), class: 'dropdown-item' if current_access_mode.resolve_discussions_here?
12
12
  end
13
13
 
14
14
  def user_discussions_link
@@ -71,7 +71,11 @@ module LinksHelper
71
71
  end
72
72
 
73
73
  def faqs_enabled_here?
74
- Organization.current.faqs.present?
74
+ current_access_mode.faqs_here?
75
+ end
76
+
77
+ def profile_enabled_here?
78
+ current_access_mode.profile_here?
75
79
  end
76
80
 
77
81
  private
@@ -1,7 +1,6 @@
1
1
  module MenuBarHelper
2
2
  def menu_bar_links
3
3
  [
4
- menu_link_to_profile,
5
4
  menu_link_to_classroom,
6
5
  menu_link_to_bibliotheca,
7
6
  solve_discussions_link,
@@ -18,7 +17,7 @@ module MenuBarHelper
18
17
  end
19
18
 
20
19
  def menu_link_to_profile
21
- menu_item('user', :my_account, user_path)
20
+ li_tag menu_item('user', :my_account, user_path)
22
21
  end
23
22
 
24
23
  def menu_link_to_classroom
@@ -36,7 +35,7 @@ module MenuBarHelper
36
35
  end
37
36
 
38
37
  def logout_menu_link
39
- li_tag menu_item('sign-out-alt', :sign_out, logout_path(origin: url_for))
38
+ li_tag menu_item('sign-out-alt', :sign_out, logout_path(origin: url_for, organization: Organization.current))
40
39
  end
41
40
 
42
41
  def menu_item(icon, name, url, css_class = nil, **translation_params)
@@ -16,7 +16,8 @@ module OverlappedButtonsHelper
16
16
  {class: 'mu-content-toolbar-item mu-restart-guide',
17
17
  data: {confirm: t(:confirm_restart)}, method: :delete, 'data-bs-placement': 'top'})
18
18
 
19
- link_to overlapped_button_icon(:undo), guide_progress_path(guide), all_options
19
+ link_to overlapped_button_icon(:undo), guide_progress_path(guide), all_options if show_content_element?
20
+
20
21
  end
21
22
 
22
23
  private
@@ -6,16 +6,25 @@
6
6
  <%= render partial: 'book/header' %>
7
7
 
8
8
  <% @book.next_lesson_for(current_user).try do |lesson| %>
9
- <div class="actions">
10
- <a href="<%= lesson_path(lesson) %>" class="btn btn-complementary">
11
- <%= t book_practice_key_for(current_user) %>
12
- </a>
13
- </div>
9
+ <% if show_content_element? %>
10
+ <div class="actions">
11
+ <a href="<%= lesson_path(lesson) %>" class="btn btn-complementary">
12
+ <%= t book_practice_key_for(current_user) %>
13
+ </a>
14
+ </div>
15
+ <% else %>
16
+ <br>
17
+ <% end %>
14
18
  <% end %>
15
19
 
16
- <h2><%= t(:chapters) %></h2>
20
+ <% if show_content?(@book) %>
21
+ <h2><%= t(:chapters) %></h2>
22
+ <% end %>
17
23
 
18
24
  <% @book.chapter_visibilities_in(current_workspace).each do |it, enabled| %>
25
+
26
+ <% next unless show_content?(it.topic) %>
27
+
19
28
  <div class="chapter-container">
20
29
  <div class="chapter <%= enabled ? '' : 'mu-locked' %>">
21
30
  <h3><%= it.number %>. <%= link_to_path_element it, mode: :plain %></h3>
@@ -24,7 +33,7 @@
24
33
  </div>
25
34
  </div>
26
35
 
27
- <% unless enabled %>
36
+ <% unless enabled %>
28
37
  <div class="text-center mu-lock">
29
38
  <i class="fas fa-lock fa-5x"></i>
30
39
  <p><%= t :locked_content %></p>
@@ -28,6 +28,7 @@
28
28
  <h3><%= t(:lessons) %></h3>
29
29
 
30
30
  <% @chapter.lessons.includes(guide: :exercises).each do |lesson| %>
31
+ <% next unless show_content?(lesson.guide) %>
31
32
  <h4><%= lesson.number %>. <%= link_to_path_element lesson, mode: :plain %></h4>
32
33
  <%= render partial: 'layouts/progress_listing', locals: { guide: lesson.guide } %>
33
34
  <% end %>
@@ -1,5 +1,5 @@
1
1
  <%= form_for [@discussion, Message.new] do |f| %>
2
- <%= render layout: 'discussions/message_container', locals: {user: user} do %>
2
+ <%= render layout: 'discussions/message_container', locals: { user: user } do %>
3
3
  <div class="discussion-message-bubble">
4
4
  <div class="discussion-message-bubble-header">
5
5
  <div class="discussion-message-bubble-title">
@@ -4,7 +4,7 @@
4
4
 
5
5
  <div>
6
6
  <div class="discussion-context">
7
- <%= render partial: 'exercises/read_only', locals: {exercise: @debatable} %>
7
+ <%= render partial: 'exercises/read_only', locals: { exercise: @debatable } %>
8
8
  </div>
9
9
  </div>
10
10
 
@@ -13,7 +13,7 @@
13
13
 
14
14
  <div class="d-flex flex-wrap">
15
15
  <h3 class="flex-grow-1 me-3"><%= t :messages %></h3>
16
- <% if current_user && @discussion.persisted? %>
16
+ <% if current_user && @discussion.persisted? && current_access_mode.show_discussion_element? %>
17
17
  <span class="d-flex">
18
18
  <% if @discussion.can_toggle_responsible?(current_user) %>
19
19
  <div class="discussion-responsible me-1">
@@ -40,10 +40,10 @@
40
40
  <% if @discussion.has_messages? %>
41
41
  <div class="discussion-messages">
42
42
  <% if @discussion.description.present? %>
43
- <%= render partial: 'discussions/description_message', locals: {discussion: @discussion} %>
43
+ <%= render partial: 'discussions/description_message', locals: { discussion: @discussion } %>
44
44
  <% end %>
45
45
  <% @discussion.visible_messages.each do |message| %>
46
- <%= render partial: 'discussions/message', locals: {user: message.sender_user, message: message} %>
46
+ <%= render partial: 'discussions/message', locals: { user: message.sender_user, message: message } %>
47
47
  <% end %>
48
48
  <% if @discussion.commentable_by?(current_user) %>
49
49
  <hr class="message-divider">
@@ -51,7 +51,7 @@
51
51
  </div>
52
52
  <% end %>
53
53
 
54
- <%= render partial: 'discussions/new_message', locals: {user: current_user} if @discussion.commentable_by?(current_user) %>
54
+ <%= render partial: 'discussions/new_message', locals: { user: current_user } if @discussion.commentable_by?(current_user) && current_access_mode.show_discussion_element? %>
55
55
 
56
56
  <% end %>
57
57
 
@@ -13,7 +13,7 @@
13
13
  <% unless assignment.manual_evaluation_comment? %>
14
14
  <%= render layout: 'exercise_solutions/assistant_rules_box', locals: {assignment: assignment } do %>
15
15
  <%= render partial: 'exercise_solutions/contextualization_results_body', locals: {contextualization: assignment} %>
16
- <% if should_render_need_help_dropdown?(assignment) %>
16
+ <% if should_render_need_help_dropdown?(assignment) && current_access_mode.show_discussion_element? %>
17
17
  <div class="notify-problem-box">
18
18
  <div class="dropdown">
19
19
  <%= link_to fa_icon(:'question-circle', text: t(:need_help)), "", {'data-bs-toggle': 'dropdown'} %>
@@ -28,7 +28,7 @@
28
28
  </div>
29
29
  <% end %>
30
30
 
31
- <% if !@stats.try(:done?) && @next_exercise %>
31
+ <% if !@stats.try(:done?) && @next_exercise && show_content_element? %>
32
32
  <div class="text-box">
33
33
  <div class="actions">
34
34
  <%= link_to t(lesson_practice_key_for(@stats)), exercise_path(@next_exercise), class: 'btn btn-complementary' %>
@@ -36,7 +36,7 @@
36
36
  </div>
37
37
  <% end %>
38
38
 
39
- <% if @stats&.done? %>
39
+ <% if @stats&.done? && show_content_element? %>
40
40
  <div class="text-box">
41
41
  <div class="actions">
42
42
  <%= next_lesson_button @guide %>
@@ -1,6 +1,7 @@
1
1
  <div class="progress-list-flex">
2
2
  <% assignments = guide.assignments_for(current_user) %>
3
3
  <% assignments.each do |assignment| %>
4
+ <% next unless show_content?(assignment.exercise) %>
4
5
  <% exercise = assignment.exercise %>
5
6
  <a
6
7
  <%= turbolinks_enable_for exercise %>
@@ -1,5 +1,6 @@
1
1
  <ul class="progress-listing">
2
2
  <% guide.assignments_for(current_user).each do |assignment| %>
3
+ <% next unless show_content?(assignment.exercise) %>
3
4
  <% cache [assignment.exercise, assignment.status, Organization.current.name, current_user?] do %>
4
5
  <li <%= turbolinks_enable_for(assignment.exercise) %>>
5
6
  <%= assignment_status_icon assignment %>
@@ -20,6 +20,10 @@
20
20
  <%= profile_picture_for current_user %>
21
21
  </div>
22
22
  <ul class="dropdown-menu dropdown-menu-end" aria-labelledby="profileDropdown">
23
+ <% if profile_enabled_here? %>
24
+ <%= menu_link_to_profile %>
25
+ <li class="dropdown-divider"></li>
26
+ <% end %>
23
27
  <%= menu_bar_list_items %>
24
28
  <% if any_menu_bar_links? %>
25
29
  <li class="dropdown-divider"></li>
@@ -4,7 +4,7 @@
4
4
  <%= render_exercise_input_editor f, exercise %>
5
5
 
6
6
  <div class="actions mu-submit-button mu-kids-submit-button">
7
- <%= render_submit_button(@assignment) %>
7
+ <%= render_submit_button(@assignment) if current_access_mode.submit_solutions_here? %>
8
8
  </div>
9
9
  <div class="actions mu-kids-reset-button"></div>
10
10
  <img class="mu-kids-compass-rose" src="/compass_rose.svg">
@@ -34,11 +34,15 @@
34
34
  remote: true,
35
35
  html: {role: 'form', class: "new_solution mu-editor mu-editor-overlap mu-form #{pending_messages_filter(@assignment)}"}) do |f| %>
36
36
  <div class="mb-3">
37
- <%= render_exercise_input_editor f, exercise %>
37
+ <% if current_access_mode.submit_solutions_here? %>
38
+ <%= render_exercise_input_editor f, exercise %>
39
+ <% else %>
40
+ <%= render_exercise_read_only_editor exercise, @assignment.solution %>
41
+ <% end %>
38
42
  </div>
39
43
 
40
44
  <div class="actions mu-submit-button">
41
- <%= render_submit_button(@assignment) %>
45
+ <%= render_submit_button(@assignment) if current_access_mode.submit_solutions_here? %>
42
46
  </div>
43
47
  <% end %>
44
48
  </div>