mumuki-laboratory 6.5.1 → 6.6.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (36) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +22 -7
  3. data/app/assets/javascripts/application/bridge.js +16 -4
  4. data/app/assets/javascripts/application/characters.js +1 -1
  5. data/app/assets/javascripts/application/kids.js +1 -1
  6. data/app/assets/javascripts/application/messages.js +2 -6
  7. data/app/assets/javascripts/application/submission.js +1 -1
  8. data/app/assets/stylesheets/application/modules/_kids.scss +9 -3
  9. data/app/controllers/application_controller.rb +6 -0
  10. data/app/controllers/discussions_controller.rb +12 -2
  11. data/app/controllers/exercises_controller.rb +2 -2
  12. data/app/controllers/guides_controller.rb +2 -6
  13. data/app/controllers/topics_controller.rb +5 -0
  14. data/app/controllers/users_controller.rb +1 -1
  15. data/app/helpers/discussions_helper.rb +3 -1
  16. data/app/helpers/editor_tabs_helper.rb +2 -2
  17. data/app/helpers/email_helper.rb +1 -1
  18. data/app/helpers/exercise_input_helper.rb +1 -1
  19. data/app/helpers/links_helper.rb +1 -1
  20. data/app/helpers/menu_bar_helper.rb +2 -2
  21. data/app/views/discussions/_message.html.erb +1 -1
  22. data/app/views/discussions/show.html.erb +1 -1
  23. data/app/views/layouts/exercise_inputs/forms/_playground_form.html.erb +1 -1
  24. data/config/routes.rb +7 -2
  25. data/lib/mumuki/laboratory/locales/pt.yml +1 -1
  26. data/lib/mumuki/laboratory/version.rb +1 -1
  27. data/spec/controllers/discussions_controller_spec.rb +38 -1
  28. data/spec/dummy/db/schema.rb +10 -1
  29. data/spec/dummy/public/character/animations.json +4 -1
  30. data/spec/features/dynamic_exam_spec.rb +4 -4
  31. data/spec/features/exercise_flow_spec.rb +8 -8
  32. data/spec/features/progressive_tips_spec.rb +1 -1
  33. data/spec/features/topic_flow_spec.rb +27 -0
  34. data/spec/helpers/email_helper_spec.rb +1 -1
  35. data/spec/helpers/exercise_input_helper_spec.rb +6 -1
  36. metadata +7 -4
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 34cf29985f59254c42f3424acc37371405dfc99696bc97f065f889d58a161477
4
- data.tar.gz: 387f9dc632a9920109c764cb5f55e6c5042492efb53b3881d725930e873e4688
3
+ metadata.gz: 0cd1851406b33a479d3c82a2fda1d82c4614bcd11104b577595b1578bc432eab
4
+ data.tar.gz: 3a28bd64ebdec590b2763bca2f70bd9ff1e85708d49f76b59b9314d7318a1dc4
5
5
  SHA512:
6
- metadata.gz: ea187bb009e1de73dd3a34205f47dcbcfb20a86968b731cba8a1142972c1d99dab03b90b97a947f1cc719918371a6963daf542852ffb909838a0d08e050acd5a
7
- data.tar.gz: 744214eb8614ee95cfa679f338f49352fb450917d5afc6b5728a1d4aefd759c40159eed8a09714e11376ca74d68783f2eb65d975dad970c64cefc20156004ac9
6
+ metadata.gz: 22711361229161b1b1cb626516ff7462943b971fda7434fda012da775a9cdc645d7f5be92ad8f3d9f35a588e43a028e1141b8913a6d5a4b7ee891123eccc9da0
7
+ data.tar.gz: ef931680911d4bfe9de176c7ee54e7692bcfc34260cf415132c14d2c8ec404274a723b1ecd51927b6b465670ae71d4bedd02bb48be1d596843d002e566113db9
data/README.md CHANGED
@@ -177,14 +177,20 @@ which are granted to be safe and stable.
177
177
  ### Methods
178
178
 
179
179
  * `mumuki.bridge.Laboratory`
180
- * `mumuki.kids.registerBlocksAreaScaler`
181
- * `mumuki.kids.registerStateScaler`
182
- * `mumuki.kids.restart`
183
- * `mumuki.kids.scaleBlocksArea`
184
- * `mumuki.kids.scaleState`
185
- * `mumuki.kids.showResult`
180
+ * `.runTests`
181
+ * `mumuki.kids`
182
+ * `registerBlocksAreaScaler`
183
+ * `registerStateScaler`
184
+ * `restart`
185
+ * `scaleBlocksArea`
186
+ * `scaleState`
187
+ * `showResult`
186
188
  * `mumuki.locale`
187
189
  * `mumuki.MultipleScenarios`
190
+ * `scenarios`
191
+ * `currentScenarioIndex`
192
+ * `resetIndicators`
193
+ * `updateIndicators`
188
194
  * `mumuki.version`
189
195
 
190
196
  ### Kids Call order
@@ -194,6 +200,14 @@ which are granted to be safe and stable.
194
200
  2. Laboratory Kids Layout Initialization
195
201
  3. Runner Editor HTML
196
202
 
203
+ ## Transparent Navigation API Docs
204
+
205
+ In order to be able to link content, laboratory exposes slug-based routes that will redirect to the actual
206
+ content URL in the current organization transparently:
207
+
208
+ * `GET <organization-url>/topics/<organization>/<repository>`
209
+ * `GET <organization-url>/guides/<organization>/<repository>`
210
+ * `GET <organization-url>/exercies/<organization>/<repository>/<bibliotheca-id>`
197
211
 
198
212
  ## REST API Docs
199
213
 
@@ -201,7 +215,7 @@ Before using the API, you must create an `ApiClient` using `rails c`, which will
201
215
 
202
216
  Before using the API, take a look to the roles hierarchy:
203
217
 
204
- ![roles hierarchy](https://yuml.me/diagram/plain/class/[Owner]%5E-[Janitor],%20[Janitor]%5E-[Headmaster],%20[Headmaster]%5E-[Teacher],%20[Teacher]%5E-[Student],%20,%20[Owner]%5E-[Editor],%20[Editor]%5E-[Writer]).
218
+ ![roles hierarchy](https://yuml.me/diagram/plain/class/[Owner]%5E-[Janitor],[Owner]%5E-[Moderator],%20[Janitor]%5E-[Headmaster],%20[Headmaster]%5E-[Teacher],%20[Teacher]%5E-[Student],%20,%20[Owner]%5E-[Editor],%20[Editor]%5E-[Writer]).
205
219
 
206
220
  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:
207
221
 
@@ -209,6 +223,7 @@ Permissions are bound to a scope, that states in which context the operation can
209
223
  * teacher and headmaster: `organization/course`
210
224
  * writer and editor: `organization/content`
211
225
  * janitor: `organization/_`
226
+ * moderator: `organization/_`
212
227
  * owner: `_/_`
213
228
 
214
229
  ### Users
@@ -37,16 +37,28 @@ var mumuki = mumuki || {};
37
37
  });
38
38
 
39
39
  Laboratory.prototype = {
40
- runLocalTests: function (solution) {
40
+
41
+ // ==========
42
+ // Public API
43
+ // ==========
44
+
45
+ // Runs tests for the current exercise using the given submission
46
+ // content.
47
+ runTests: function(content) {
48
+ return this._submitSolution({ solution: content });
49
+ },
50
+
51
+ // ===========
52
+ // Private API
53
+ // ===========
54
+
55
+ _submitSolution: function (solution) {
41
56
  if(lastSubmissionFinishedSuccessfully() && sameAsLastSolution(solution)){
42
57
  return $.Deferred().resolve(lastSubmission.result);
43
58
  } else {
44
59
  return sendNewSolution(solution);
45
60
  }
46
61
  },
47
- runTests: function(content) {
48
- return this.runLocalTests({ solution: content });
49
- }
50
62
  };
51
63
 
52
64
  mumuki.bridge = {
@@ -9,7 +9,7 @@ mumuki.load(() => {
9
9
  });
10
10
 
11
11
  function placeKidsAnimations() {
12
- placeAnimation('.mu-kids-character-animation', 'jump');
12
+ placeAnimation('.mu-kids-character-animation', 'talk');
13
13
  placeAnimation('.mu-kids-character-context', 'context');
14
14
  }
15
15
 
@@ -120,7 +120,7 @@ mumuki.load(function () {
120
120
  mumuki.kids._hideMessageOnCharacterBubble();
121
121
  var $bubble = mumuki.kids._getCharacterBubble();
122
122
  Object.keys(mumuki.kids.resultAction).forEach($bubble.removeClass.bind($bubble));
123
- mumuki.presenterCharacter.playAnimation('jump', mumuki.kids._getCharaterImage());
123
+ mumuki.presenterCharacter.playAnimation('talk', mumuki.kids._getCharaterImage());
124
124
  },
125
125
 
126
126
  // ===========
@@ -1,7 +1,4 @@
1
- var mumuki = mumuki || {};
2
-
3
- (function (mumuki) {
4
-
1
+ mumuki.load(function () {
5
2
  var Chat = {
6
3
  $body: function () {
7
4
  return $('body')
@@ -92,6 +89,5 @@ var mumuki = mumuki || {};
92
89
  Chat.setMessagesInterval();
93
90
  mumuki.Chat = Chat;
94
91
 
95
- }(mumuki));
96
-
92
+ })
97
93
 
@@ -90,7 +90,7 @@ var mumuki = mumuki || {};
90
90
  mumuki.editor.syncContent();
91
91
  var solution = getContent();
92
92
 
93
- bridge.runLocalTests(solution).done(function (data) {
93
+ bridge._submitSolution(solution).done(function (data) {
94
94
  resultsBox.success(data, submitButton);
95
95
  }).fail(function () {
96
96
  resultsBox.error(submitButton);
@@ -4,6 +4,7 @@ $kids-characters-height: 120px;
4
4
  $kids-speech-arrow-size: 10px;
5
5
  $kids-speech-border-color: lighten($mu-color-disabled, 20%);
6
6
  $kids-speech-background-color: #f4f6fb;
7
+ $kids-scrollbar-background-color: #d7d9dd;
7
8
 
8
9
  $kids-speech-border: 1px solid $kids-speech-border-color;
9
10
 
@@ -94,6 +95,10 @@ $kids-speech-tabs-width: 40px;
94
95
 
95
96
  }
96
97
 
98
+ .mu-kids-character-animation {
99
+ width: 120px;
100
+ }
101
+
97
102
  .mu-kids-character {
98
103
  position: relative;
99
104
  display: flex;
@@ -358,12 +363,13 @@ $kids-speech-tabs-width: 40px;
358
363
  ry: 10px !important;
359
364
  }
360
365
  .blocklyScrollbarHandle {
361
- display: none;
362
- fill: $kids-speech-background-color !important;
366
+ fill: $kids-scrollbar-background-color !important;
363
367
  stroke: $kids-speech-border-color !important;
364
368
  stroke-width: 1px !important;
365
369
  }
366
- .blocklyScrollbarVertical,
370
+ .blocklyMainWorkspaceScrollbar {
371
+ display: none;
372
+ }
367
373
  .blocklyScrollbarHorizontal {
368
374
  display: none;
369
375
  }
@@ -54,6 +54,12 @@ class ApplicationController < ActionController::Base
54
54
  login_form.button_html I18n.t(:sign_in), options[:class]
55
55
  end
56
56
 
57
+ # redirects to the usage in the current organization for the given content
58
+ # or raises a not found error if unused
59
+ def redirect_to_usage(content)
60
+ raise Mumuki::Domain::NotFoundError unless content.usage_in_organization.try { |usage| redirect_to usage }
61
+ end
62
+
57
63
  private
58
64
 
59
65
  def login_settings
@@ -1,8 +1,14 @@
1
1
  class DiscussionsController < ApplicationController
2
2
  include Mumuki::Laboratory::Controllers::Content
3
3
 
4
- before_action :validate_forum_enabled!
4
+ # discussions are not enabled for all organitions nor all users
5
+ # there is no need to validate forum existance when creating; next validation is stronger
6
+ before_action :validate_forum_enabled!, except: :create
7
+ before_action :validate_can_create_discusssion!, only: :create
8
+
9
+ # users are not allowed to access discussions during exams
5
10
  before_action :validate_not_in_exam!
11
+
6
12
  before_action :set_debatable, except: [:subscription]
7
13
  before_action :authenticate!, only: [:update, :create]
8
14
  before_action :discussion_filter_params, only: :index
@@ -41,7 +47,7 @@ class DiscussionsController < ApplicationController
41
47
  private
42
48
 
43
49
  def current_content_discussions
44
- @debatable.discussions
50
+ @debatable.discussions_in_organization
45
51
  end
46
52
 
47
53
  def set_debatable
@@ -69,6 +75,10 @@ class DiscussionsController < ApplicationController
69
75
  raise Mumuki::Domain::NotFoundError unless Organization.current.forum_enabled?
70
76
  end
71
77
 
78
+ def validate_can_create_discusssion!
79
+ raise Mumuki::Domain::NotFoundError unless Organization.current.can_create_discussions?(current_user)
80
+ end
81
+
72
82
  def validate_not_in_exam!
73
83
  raise Mumuki::Domain::BlockedForumError if current_user&.currently_in_exam?
74
84
  end
@@ -12,8 +12,8 @@ class ExercisesController < ApplicationController
12
12
  enable_embedded_rendering
13
13
  end
14
14
 
15
- def show_by_slug
16
- redirect_to Guide.by_slug_parts!(params).exercises.find_by!(bibliotheca_id: params[:bibliotheca_id])
15
+ def show_transparently
16
+ redirect_to Exercise.find_transparently!(params)
17
17
  end
18
18
 
19
19
  private
@@ -3,11 +3,7 @@ class GuidesController < ApplicationController
3
3
  redirect_to_usage Guide.find_by!(id: params[:id])
4
4
  end
5
5
 
6
- def show_by_slug
7
- redirect_to_usage Guide.by_slug_parts!(params)
8
- end
9
-
10
- def redirect_to_usage(guide)
11
- raise Mumuki::Domain::NotFoundError unless guide.usage_in_organization.try { |usage| redirect_to usage }
6
+ def show_transparently
7
+ redirect_to_usage Guide.find_transparently!(params)
12
8
  end
13
9
  end
@@ -0,0 +1,5 @@
1
+ class TopicsController < ApplicationController
2
+ def show_transparently
3
+ redirect_to_usage Topic.find_transparently!(params)
4
+ end
5
+ end
@@ -4,7 +4,7 @@ class UsersController < ApplicationController
4
4
 
5
5
  def show
6
6
  @messages = current_user.messages.to_a
7
- @watched_discussions = current_user.watched_discussions
7
+ @watched_discussions = current_user.watched_discussions_in_organization
8
8
  end
9
9
 
10
10
  def update
@@ -32,7 +32,7 @@ module DiscussionsHelper
32
32
  end
33
33
 
34
34
  def solve_discussion_params_for(user)
35
- if user&.moderator?
35
+ if user&.moderator_here?
36
36
  { status: :pending_review, sort: :created_at_asc }
37
37
  else
38
38
  { status: :opened, sort: :created_at_asc }
@@ -87,6 +87,8 @@ module DiscussionsHelper
87
87
  end
88
88
 
89
89
  def new_discussion_link(teaser_text, link_text)
90
+ return '' unless Organization.current.can_create_discussions?(current_user)
91
+
90
92
  %Q{
91
93
  <h4>
92
94
  <span>#{t(teaser_text)}</span>
@@ -3,8 +3,8 @@ module EditorTabsHelper
3
3
  "<li role='presentation'> <a data-target='#visible-extra' aria-controls='visible-extra' role='tab' data-toggle='tab' class='editor-tab'>#{fa_icon 'code'} #{t 'activerecord.attributes.exercise.extra'}</a> </li>".html_safe
4
4
  end
5
5
 
6
- def console_tab
7
- "<li role='presentation'>
6
+ def console_tab(active: false)
7
+ "<li role='presentation' class='#{'active' if active}'>
8
8
  <a data-target='#console' aria-controls='console' tabindex='0' role='tab' data-toggle='tab' class='editor-tab'>
9
9
  #{fa_icon 'terminal'}#{t :console }
10
10
  </a>
@@ -8,7 +8,7 @@ module EmailHelper
8
8
 
9
9
  #{t :status}: #{assignment.status}
10
10
 
11
- See #{exercise_by_slug_url(assignment.exercise.slug_parts)}
11
+ See #{transparent_exercise_url(assignment.exercise.transparent_params)}
12
12
  EOM
13
13
  end
14
14
 
@@ -44,7 +44,7 @@ module ExerciseInputHelper
44
44
  end
45
45
 
46
46
  def should_render_need_help_dropdown?(assignment, organization = Organization.current)
47
- !assignment.passed? && organization.ask_for_help_enabled?
47
+ !assignment.passed? && organization.ask_for_help_enabled?(assignment.submitter)
48
48
  end
49
49
 
50
50
  def render_submit_button(assignment)
@@ -76,6 +76,6 @@ module LinksHelper
76
76
  end
77
77
 
78
78
  def url_for_bibliotheca_guide(guide)
79
- "#{url_for_application(:bibliotheca)}/#/guides/#{guide.slug}"
79
+ "#{url_for_application(:bibliotheca_ui)}/#/guides/#{guide.slug}"
80
80
  end
81
81
  end
@@ -13,11 +13,11 @@ module MenuBarHelper
13
13
  end
14
14
 
15
15
  def link_to_classroom
16
- link_to_application 'graduation-cap', :classroom, :teacher_here?
16
+ link_to_application 'graduation-cap', :classroom_ui, :teacher_here?
17
17
  end
18
18
 
19
19
  def link_to_bibliotheca
20
- link_to_application :book, :bibliotheca, :writer?
20
+ link_to_application :book, :bibliotheca_ui, :writer?
21
21
  end
22
22
 
23
23
  def link_to_application(icon, app_name, minimal_permissions)
@@ -6,7 +6,7 @@
6
6
  <span class="message-date">
7
7
  <%= t(:time_since, time: time_ago_in_words(message.created_at)) %>
8
8
  </span>
9
- <% if user.moderator? %>
9
+ <% if user.moderator_here? %>
10
10
  <%= fa_icon(:star, {'data-toggle': 'tooltip', title: (t :moderator), class: 'moderator-star'}) %>
11
11
  <% end %>
12
12
  <% if message.authorized? current_user %>
@@ -5,7 +5,7 @@
5
5
 
6
6
  <div>
7
7
  <div class="row">
8
- <% if current_user && !current_user.moderator? %>
8
+ <% if current_user && !current_user.moderator_here? %>
9
9
  <div class="mu-inline-block-right hidden-xs discussion-user-menu">
10
10
  <h3>
11
11
  <% if @discussion.subscribable? %>
@@ -2,7 +2,7 @@
2
2
 
3
3
  <% if exercise.extra_visible? %>
4
4
  <ul class="nav nav-tabs" role="tablist">
5
- <%= console_tab %>
5
+ <%= console_tab active: true %>
6
6
  <% if exercise.extra_visible? %>
7
7
  <%= extra_code_tab %>
8
8
  <% end %>
data/config/routes.rb CHANGED
@@ -58,10 +58,15 @@ Rails.application.routes.draw do
58
58
  get '/messages/errors' => 'messages#errors'
59
59
 
60
60
  # Routes by slug
61
- get '/guides/:organization/:repository' => 'guides#show_by_slug', as: :guide_by_slug
62
- get '/exercises/:organization/:repository/:bibliotheca_id' => 'exercises#show_by_slug', as: :exercise_by_slug
61
+ get '/guides/:organization/:repository' => 'guides#show_transparently', as: :transparent_guide
62
+ get '/topics/:organization/:repository' => 'topics#show_transparently', as: :transparent_topic
63
+ get '/exercises/:organization/:repository/:bibliotheca_id' => 'exercises#show_transparently', as: :transparent_exercise
64
+
65
+ # Join to course
63
66
  get '/join/:code' => 'invitations#show', as: :invitation
64
67
  post '/join/:code' => 'invitations#join'
68
+
69
+ # Notification subscriptions
65
70
  get '/user/unsubscribe' => 'users#unsubscribe'
66
71
  put '/user' => 'users#update', as: :update_user
67
72
 
@@ -107,7 +107,7 @@ pt:
107
107
  joining: Bem-vindo ao curso <strong> %{curso} </strong>!
108
108
  kids_default_success: Você fez muito bem!
109
109
  language: Linguagem
110
- languages: Idiomas
110
+ languages: Linguagens
111
111
  last_name: Sobrenome
112
112
  last_submission_date: Última solução
113
113
  latest_exercises: Últimos exercícios
@@ -1,5 +1,5 @@
1
1
  module Mumuki
2
2
  module Laboratory
3
- VERSION = '6.5.1'
3
+ VERSION = '6.6.0'
4
4
  end
5
5
  end
@@ -1,13 +1,31 @@
1
1
  require 'spec_helper'
2
2
 
3
3
  describe DiscussionsController, organization_workspace: :test do
4
- let(:user) { create(:user) }
4
+ let(:user) { create(:user, permissions: Mumukit::Auth::Permissions.parse(student: '*')) }
5
5
  let(:exercise) { create(:exercise) }
6
6
  let(:exercise_params) { {debatable_class: 'Exercise', exercise_id: exercise.id} }
7
7
 
8
8
  before { set_current_user! user }
9
9
  before { Organization.current.tap { |it| it.forum_enabled = true }.save! }
10
10
 
11
+ describe 'show' do
12
+ let(:another_orga) { create(:organization, {name: 'another_orga'}) }
13
+
14
+ describe 'a discussion from the current orga' do
15
+ let(:discussion) { create(:discussion, {organization: Organization.current}) }
16
+ before {get :show, params: exercise_params.merge({id: discussion.id})}
17
+
18
+ it {expect(response.status).to eq 200}
19
+ end
20
+
21
+ describe 'a discussion from another orga' do
22
+ let(:discussion) { create(:discussion, {organization: another_orga}) }
23
+ before {get :show, params: exercise_params.merge({id: discussion.id})}
24
+
25
+ it {expect(response.status).to eq 404}
26
+ end
27
+ end
28
+
11
29
  describe 'post' do
12
30
  before { allow_any_instance_of(DiscussionsController).to receive(:discussion_params).and_return title: 'A title' }
13
31
  before { post :create, params: exercise_params }
@@ -24,4 +42,23 @@ describe DiscussionsController, organization_workspace: :test do
24
42
  it { expect { Discussion.find(@discussion_id) }.to raise_exception(ActiveRecord::RecordNotFound) }
25
43
  end
26
44
  end
45
+
46
+ describe 'when the minimal role for discussions is teacher' do
47
+ before { Organization.current.tap { |it| it.forum_discussions_minimal_role = 'teacher' }.save! }
48
+
49
+ describe 'and the user does not match the minimal role' do
50
+ before { post :create, params: exercise_params }
51
+
52
+ it { expect(response.status).to eq 404 }
53
+ end
54
+
55
+ describe 'and the user matches the minimal role' do
56
+ let(:user) { create(:user, permissions: Mumukit::Auth::Permissions.parse(teacher: '*')) }
57
+ before { allow_any_instance_of(DiscussionsController).to receive(:discussion_params).and_return title: 'A title' }
58
+ before { post :create, params: exercise_params }
59
+
60
+ it { expect(response.status).to eq 302 }
61
+ it { expect(exercise.discussions.size).to eq 1 }
62
+ end
63
+ end
27
64
  end
@@ -10,7 +10,7 @@
10
10
  #
11
11
  # It's strongly recommended that you check this file into your version control system.
12
12
 
13
- ActiveRecord::Schema.define(version: 20190123180147) do
13
+ ActiveRecord::Schema.define(version: 20190404181724) do
14
14
 
15
15
  # These are extensions that must be enabled in order to support this database
16
16
  enable_extension "plpgsql"
@@ -105,8 +105,10 @@ ActiveRecord::Schema.define(version: 20190123180147) do
105
105
  t.text "query_results"
106
106
  t.text "manual_evaluation_comment"
107
107
  t.integer "upvotes_count", default: 0
108
+ t.bigint "organization_id"
108
109
  t.index ["initiator_id"], name: "index_discussions_on_initiator_id"
109
110
  t.index ["item_type", "item_id"], name: "index_discussions_on_item_type_and_item_id"
111
+ t.index ["organization_id"], name: "index_discussions_on_organization_id"
110
112
  end
111
113
 
112
114
  create_table "exam_authorizations", force: :cascade do |t|
@@ -167,6 +169,7 @@ ActiveRecord::Schema.define(version: 20190123180147) do
167
169
  t.text "free_form_editor_source"
168
170
  t.text "teacher_info"
169
171
  t.text "choices"
172
+ t.text "settings"
170
173
  t.index ["guide_id"], name: "index_exercises_on_guide_id"
171
174
  t.index ["language_id"], name: "index_exercises_on_language_id"
172
175
  end
@@ -191,6 +194,7 @@ ActiveRecord::Schema.define(version: 20190123180147) do
191
194
  t.text "teacher_info"
192
195
  t.text "sources"
193
196
  t.text "learn_more"
197
+ t.text "settings"
194
198
  t.index ["name"], name: "index_guides_on_name"
195
199
  t.index ["slug"], name: "index_guides_on_slug", unique: true
196
200
  end
@@ -332,4 +336,9 @@ ActiveRecord::Schema.define(version: 20190123180147) do
332
336
  t.index ["uid"], name: "index_users_on_uid", unique: true
333
337
  end
334
338
 
339
+ add_foreign_key "chapters", "topics"
340
+ add_foreign_key "complements", "guides"
341
+ add_foreign_key "exams", "guides"
342
+ add_foreign_key "lessons", "guides"
343
+ add_foreign_key "organizations", "books"
335
344
  end
@@ -7,7 +7,10 @@
7
7
  "jump",
8
8
  "success2_l",
9
9
  "success_l"
10
- ]
10
+ ],
11
+ "actions": {
12
+ "talk": "jump"
13
+ }
11
14
  }
12
15
  },
13
16
  "extra": {
@@ -17,7 +17,7 @@ feature 'Dynamic Exam', organization_workspace: :test do
17
17
 
18
18
  context 'not logged user' do
19
19
  scenario 'visit exercise by slug' do
20
- visit "/exercises/#{problem.slug}"
20
+ visit "/exercises/#{problem.transparent_id}"
21
21
 
22
22
  expect(page).to have_text('do f = some_string')
23
23
  end
@@ -25,14 +25,14 @@ feature 'Dynamic Exam', organization_workspace: :test do
25
25
 
26
26
 
27
27
  context 'logged user' do
28
- scenario 'visit exercise by slug' do
28
+ scenario 'visit exercise by transparent_id' do
29
29
  set_current_user! user
30
- visit "/exercises/#{problem.slug}"
30
+ visit "/exercises/#{problem.transparent_id}"
31
31
 
32
32
  expect(page).to have_text('do f = some_other_string')
33
33
 
34
34
  set_current_user! user2
35
- visit "/exercises/#{problem.slug}"
35
+ visit "/exercises/#{problem.transparent_id}"
36
36
 
37
37
  expect(page).to have_text('do f = some_string')
38
38
  end
@@ -29,13 +29,13 @@ feature 'Exercise Flow', organization_workspace: :test do
29
29
  before { reindex_current_organization! }
30
30
 
31
31
  context 'inexistent exercise' do
32
- scenario 'visit exercise by slug, not in path' do
33
- visit "/exercises/#{exercise_not_in_path.slug}"
32
+ scenario 'visit exercise transparently, not in path' do
33
+ visit "/exercises/#{exercise_not_in_path.transparent_id}"
34
34
  expect(page).to have_text('You may have mistyped the address or the page may have moved')
35
35
  end
36
36
 
37
- scenario 'visit exercise by slug, unknown exercise' do
38
- visit '/exercises/an_exercise_slug'
37
+ scenario 'visit exercise transparently, unknown exercise' do
38
+ visit '/exercises/an_exercise_transparent_id'
39
39
  expect(page).to have_text('You may have mistyped the address or the page may have moved')
40
40
  end
41
41
 
@@ -51,8 +51,8 @@ feature 'Exercise Flow', organization_workspace: :test do
51
51
  end
52
52
 
53
53
  context 'not logged user' do
54
- scenario 'visit exercise by slug' do
55
- visit "/exercises/#{problem_1.slug}"
54
+ scenario 'visit exercise transparently' do
55
+ visit "/exercises/#{problem_1.transparent_id}"
56
56
 
57
57
  expect(page).to have_text('Succ1')
58
58
  expect(page).to_not have_text('Console')
@@ -82,8 +82,8 @@ feature 'Exercise Flow', organization_workspace: :test do
82
82
  before { set_current_user! user }
83
83
  let(:writer) { create(:user, permissions: {student: 'private/*', writer: 'private/*'}) }
84
84
 
85
- scenario 'visit exercise by slug' do
86
- visit "/exercises/#{problem_1.slug}"
85
+ scenario 'visit exercise transparently' do
86
+ visit "/exercises/#{problem_1.transparent_id}"
87
87
 
88
88
  expect(page).to have_text('Succ1')
89
89
  expect(page).to have_text('Console')
@@ -23,7 +23,7 @@ feature 'Progressive Tips', organization_workspace: :test do
23
23
 
24
24
  scenario '2 failed submissions' do
25
25
  assignment.update! attempts_count: 2
26
- visit "/exercises/#{problem.slug}"
26
+ visit "/exercises/#{problem.transparent_id}"
27
27
 
28
28
  expect(page).to have_text('Try this')
29
29
  expect(page).to_not have_text('Try that')
@@ -0,0 +1,27 @@
1
+ require 'spec_helper'
2
+
3
+ feature 'Topic Flow', organization_workspace: :test do
4
+ let(:user) { create(:user) }
5
+ let(:problem) { build(:problem) }
6
+ let(:chapter) { create(:chapter, name: 'Functional Programming', lessons: [ create(:lesson, exercises: [ problem ]) ]) }
7
+
8
+ let!(:topic_in_path) { chapter.topic }
9
+ let(:topic_not_in_path) { create(:topic, name: 'Logic Programming') }
10
+
11
+ before { reindex_current_organization! }
12
+ before { set_current_user! user }
13
+
14
+ scenario 'visit topic in path transparently' do
15
+ visit "/topics/#{topic_in_path.transparent_id}"
16
+
17
+ expect(page).to have_text('Functional Programming')
18
+ expect(page).to have_text('Content')
19
+ end
20
+
21
+ scenario 'visit topic in path not transparently' do
22
+ visit "/topics/#{topic_not_in_path.transparent_id}"
23
+
24
+ expect(page).to_not have_text('Logic Programming')
25
+ expect(page).to_not have_text('Content Programming')
26
+ end
27
+ end
@@ -9,6 +9,6 @@ describe EmailHelper, organization_workspace: :test do
9
9
  let(:exercise) { create(:problem, name: 'An Exercise', guide: guide, number: 2) }
10
10
  let(:assignment) { exercise.submit_solution!(user, content: 'foo') }
11
11
 
12
- it { expect(assignment_help_email_body assignment).to eq "Exercise: An Exercise\n\nSolution:\nfoo\n\nStatus: failed\n\nSee http://test.localmumuki.io/exercises/#{exercise.slug}\n" }
12
+ it { expect(assignment_help_email_body assignment).to eq "Exercise: An Exercise\n\nSolution:\nfoo\n\nStatus: failed\n\nSee http://test.localmumuki.io/exercises/#{exercise.transparent_id}\n" }
13
13
  end
14
14
  end
@@ -25,7 +25,8 @@ describe ApplicationHelper, organization_workspace: :test do
25
25
  end
26
26
 
27
27
  describe 'should_render_need_help_dropdown?' do
28
- let(:assignment) { create(:assignment) }
28
+ let(:student) { create(:user, permissions: Mumukit::Auth::Permissions.parse(student: "*")) }
29
+ let(:assignment) { create(:assignment, submitter: student) }
29
30
 
30
31
  context 'when the orga has a community link' do
31
32
  let(:organization) { create(:organization, name: 'myorg', community_link: 'com_link') }
@@ -39,6 +40,10 @@ describe ApplicationHelper, organization_workspace: :test do
39
40
  let(:organization) { create(:organization, name: 'myorg', forum_enabled: true) }
40
41
  it { expect(should_render_need_help_dropdown? assignment, organization).to be true }
41
42
  end
43
+ context 'when forum enabled but the minimal role for discussions is teacher' do
44
+ let(:organization) { create(:organization, name: 'myorg', forum_enabled: true, forum_discussions_minimal_role: 'teacher' ) }
45
+ it { expect(should_render_need_help_dropdown? assignment, organization).to be false }
46
+ end
42
47
  context 'when ask for help is not enabled' do
43
48
  it { expect(should_render_need_help_dropdown? assignment).to be false }
44
49
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: mumuki-laboratory
3
3
  version: !ruby/object:Gem::Version
4
- version: 6.5.1
4
+ version: 6.6.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Franco Bulgarelli
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2019-03-18 00:00:00.000000000 Z
11
+ date: 2019-04-12 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rails
@@ -30,14 +30,14 @@ dependencies:
30
30
  requirements:
31
31
  - - "~>"
32
32
  - !ruby/object:Gem::Version
33
- version: 6.5.1
33
+ version: 6.6.1
34
34
  type: :runtime
35
35
  prerelease: false
36
36
  version_requirements: !ruby/object:Gem::Requirement
37
37
  requirements:
38
38
  - - "~>"
39
39
  - !ruby/object:Gem::Version
40
- version: 6.5.1
40
+ version: 6.6.1
41
41
  - !ruby/object:Gem::Dependency
42
42
  name: mumukit-login
43
43
  requirement: !ruby/object:Gem::Requirement
@@ -275,6 +275,7 @@ files:
275
275
  - app/controllers/lessons_controller.rb
276
276
  - app/controllers/login_controller.rb
277
277
  - app/controllers/messages_controller.rb
278
+ - app/controllers/topics_controller.rb
278
279
  - app/controllers/users_controller.rb
279
280
  - app/helpers/application_helper.rb
280
281
  - app/helpers/assets_helper.rb
@@ -520,6 +521,7 @@ files:
520
521
  - spec/features/profile_flow_spec.rb
521
522
  - spec/features/progressive_tips_spec.rb
522
523
  - spec/features/standard_flow_spec.rb
524
+ - spec/features/topic_flow_spec.rb
523
525
  - spec/helpers/application_helper_spec.rb
524
526
  - spec/helpers/breadcrumbs_helper_spec.rb
525
527
  - spec/helpers/email_helper_spec.rb
@@ -662,6 +664,7 @@ test_files:
662
664
  - spec/features/progressive_tips_spec.rb
663
665
  - spec/features/menu_bar_spec.rb
664
666
  - spec/features/home_public_flow_spec.rb
667
+ - spec/features/topic_flow_spec.rb
665
668
  - spec/features/chapter_spec.rb
666
669
  - spec/features/complements_flow_spec.rb
667
670
  - spec/features/dynamic_exam_spec.rb