mumuki-laboratory 9.12.1 → 9.13.0

Sign up to get free protection for your applications and to get access to all the features.
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>