mumuki-laboratory 5.7.0 → 5.8.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (67) hide show
  1. checksums.yaml +4 -4
  2. data/app/assets/javascripts/application/codemirror-builder.js +16 -8
  3. data/app/assets/javascripts/application/codemirror.js +7 -9
  4. data/app/assets/javascripts/application/discussions.js +20 -12
  5. data/app/assets/javascripts/application/multiple-files.js +222 -0
  6. data/app/assets/javascripts/application/submission.js +1 -0
  7. data/app/assets/stylesheets/application/modules/_discussion.scss +12 -0
  8. data/app/assets/stylesheets/application/modules/_editor.scss +13 -0
  9. data/app/controllers/discussions_controller.rb +10 -0
  10. data/app/controllers/discussions_messages_controller.rb +15 -3
  11. data/app/helpers/application_helper.rb +1 -1
  12. data/app/helpers/discussions_helper.rb +13 -5
  13. data/app/helpers/multiple_file_editor_helper.rb +9 -0
  14. data/app/models/application_record.rb +5 -0
  15. data/app/models/concerns/with_assignments.rb +3 -21
  16. data/app/models/discussion.rb +1 -1
  17. data/app/models/exam.rb +12 -11
  18. data/app/models/exercise.rb +1 -1
  19. data/app/models/guide.rb +1 -1
  20. data/app/models/message.rb +5 -1
  21. data/app/models/stats.rb +4 -29
  22. data/app/models/user.rb +6 -0
  23. data/app/views/discussions/_message.html.erb +3 -0
  24. data/app/views/errors/forbidden.html.erb +1 -1
  25. data/app/views/layouts/_authoring.html.erb +5 -0
  26. data/app/views/layouts/_copyright.html.erb +2 -0
  27. data/app/views/layouts/_social_media.html.erb +4 -0
  28. data/app/views/layouts/application.html.erb +4 -6
  29. data/app/views/layouts/embedded.html.erb +27 -0
  30. data/app/views/layouts/exercise_inputs/editors/_multiple_files.html.erb +8 -3
  31. data/config/routes.rb +3 -1
  32. data/db/migrate/20180802190437_add_approved_to_messages.rb +5 -0
  33. data/lib/mumuki/laboratory/controllers/dynamic_errors.rb +6 -1
  34. data/lib/mumuki/laboratory/controllers/notifications.rb +3 -2
  35. data/lib/mumuki/laboratory/exceptions.rb +1 -0
  36. data/lib/mumuki/laboratory/exceptions/blocked_forum_error.rb +2 -0
  37. data/lib/mumuki/laboratory/locales/en.yml +3 -1
  38. data/lib/mumuki/laboratory/locales/es.yml +3 -2
  39. data/lib/mumuki/laboratory/locales/pt.yml +3 -2
  40. data/lib/mumuki/laboratory/mumukit/directives.rb +6 -5
  41. data/lib/mumuki/laboratory/status/submission/pending.rb +1 -5
  42. data/lib/mumuki/laboratory/status/submission/running.rb +1 -1
  43. data/lib/mumuki/laboratory/status/submission/submission.rb +0 -1
  44. data/lib/mumuki/laboratory/version.rb +1 -1
  45. data/spec/controllers/discussions_controller_spec.rb +1 -0
  46. data/spec/controllers/exercise_solutions_controller_spec.rb +1 -1
  47. data/spec/controllers/organizations_api_controller_spec.rb +1 -1
  48. data/spec/dummy/db/schema.rb +2 -1
  49. data/spec/factories/api_client_factory.rb +3 -3
  50. data/spec/factories/assignments_factory.rb +1 -1
  51. data/spec/factories/chapter_factory.rb +1 -1
  52. data/spec/factories/course_factory.rb +5 -5
  53. data/spec/factories/discussion_factory.rb +2 -2
  54. data/spec/factories/exercise_factory.rb +24 -26
  55. data/spec/factories/guide_factory.rb +3 -3
  56. data/spec/factories/login_settings_factory.rb +1 -1
  57. data/spec/factories/message_factory.rb +1 -1
  58. data/spec/factories/organization_factory.rb +9 -9
  59. data/spec/factories/topic_factory.rb +1 -1
  60. data/spec/features/choose_organization_spec.rb +49 -42
  61. data/spec/models/exercise_spec.rb +3 -27
  62. data/spec/models/query_spec.rb +1 -1
  63. data/spec/models/question_spec.rb +2 -2
  64. data/spec/models/stats_spec.rb +2 -9
  65. data/spec/models/user_spec.rb +13 -0
  66. metadata +8 -3
  67. data/lib/mumuki/laboratory/status/submission/unknown.rb +0 -11
data/config/routes.rb CHANGED
@@ -15,7 +15,9 @@ Rails.application.routes.draw do
15
15
  concerns :debatable, controller: 'book_discussions', only: :index
16
16
 
17
17
  resources :discussions, only: [] do
18
- resources :messages, only: [:create, :destroy], controller: 'discussions_messages'
18
+ resources :messages, only: [:create, :destroy], controller: 'discussions_messages' do
19
+ post :approve, on: :member
20
+ end
19
21
  end
20
22
 
21
23
  resources :book, only: [:show]
@@ -0,0 +1,5 @@
1
+ class AddApprovedToMessages < ActiveRecord::Migration[5.1]
2
+ def change
3
+ add_column :messages, :approved, :boolean, default: false
4
+ end
5
+ end
@@ -12,6 +12,7 @@ module Mumuki::Laboratory::Controllers::DynamicErrors
12
12
  rescue_from Mumuki::Laboratory::ForbiddenError, with: :forbidden
13
13
  rescue_from Mumuki::Laboratory::UnauthorizedError, with: :unauthorized
14
14
  rescue_from Mumuki::Laboratory::GoneError, with: :gone
15
+ rescue_from Mumuki::Laboratory::BlockedForumError, with: :blocked_forum
15
16
  end
16
17
 
17
18
  def not_found
@@ -29,7 +30,11 @@ module Mumuki::Laboratory::Controllers::DynamicErrors
29
30
 
30
31
  def forbidden
31
32
  Rails.logger.info "Access to organization #{Organization.current} was forbidden to user #{current_user} with permissions #{current_user.permissions}"
32
- render 'errors/forbidden', status: 403
33
+ render 'errors/forbidden', status: 403, locals: { explanation: :forbidden_explanation }
34
+ end
35
+
36
+ def blocked_forum
37
+ render 'errors/forbidden', status: 403, locals: { explanation: :blocked_forum_explanation }
33
38
  end
34
39
 
35
40
  def gone
@@ -1,6 +1,7 @@
1
1
  module Mumuki::Laboratory::Controllers::Notifications
2
+
2
3
  def user_notifications_path
3
- "#{user_path}##{notifications_path}"
4
+ user_path(anchor: current_notifications_tab)
4
5
  end
5
6
 
6
7
  def has_notifications?
@@ -13,7 +14,7 @@ module Mumuki::Laboratory::Controllers::Notifications
13
14
 
14
15
  private
15
16
 
16
- def notifications_path
17
+ def current_notifications_tab
17
18
  has_messages? ? 'messages' : 'discussions'
18
19
  end
19
20
 
@@ -2,3 +2,4 @@ require_relative './exceptions/forbidden_error'
2
2
  require_relative './exceptions/gone_error'
3
3
  require_relative './exceptions/not_found_error'
4
4
  require_relative './exceptions/unauthorized_error'
5
+ require_relative './exceptions/blocked_forum_error'
@@ -0,0 +1,2 @@
1
+ class Mumuki::Laboratory::BlockedForumError < StandardError
2
+ end
@@ -20,6 +20,7 @@ en:
20
20
  authoring_note: This guide's content was developed by %{authors} and <a href="%{collaborators}" target="_blank">many others</a>, under the terms of <a href="https://creativecommons.org/licenses/by-sa/4.0/" target="_blank">Creative Commons License Share-Alike, 4.0</a>
21
21
  back_to_mumuki: Back to Mumuki!
22
22
  bibliotheca: Bibliotheca
23
+ blocked_forum_explanation: You are not allowed to see this content. <br> Are you in the middle of an exam right now?
23
24
  cancel_subscription: Cancel your subscription.
24
25
  chapter: Chapter
25
26
  chapter_finished_html: You have finished %{chapter}!
@@ -88,6 +89,7 @@ en:
88
89
  history: History
89
90
  home: Home
90
91
  initial_state: Initial board
92
+ insert_file_name: Insert a file name
91
93
  invitation: Invitation to course
92
94
  invitation_for: 'Join %{course}'
93
95
  item_not_accessible: "You are not permitted to access this content"
@@ -115,6 +117,7 @@ en:
115
117
  moderator: Moderator
116
118
  mumuki_catchphrase: Improve your programming skills
117
119
  mumuki_short_description: Mumuki is a simple, open and collaborative platform for sharing and solving programming exercises. It is aimed to help people with learning and teaching programing languages and paradigms
120
+ my_doubts: My doubts
118
121
  my_submissions: My Submissions
119
122
  name: Name
120
123
  navigation_continue: 'Next: %{sibling}'
@@ -185,7 +188,6 @@ en:
185
188
  start_lesson: Start this lesson!
186
189
  start_practicing: Start Practicing!
187
190
  start_using_mumuki: Start using Mumuki!
188
- stats: Some stats
189
191
  status: Status
190
192
  stay_here: I want to stay here
191
193
  stop_emails?: Want to stop getting emails?
@@ -21,6 +21,7 @@ es:
21
21
  authoring_note: Esta guía fue desarrollada por %{authors} y <a href="%{collaborators}" target="_blank">muchas personas más</a>, bajo los términos de la <a href="https://creativecommons.org/licenses/by-sa/4.0/" target="_blank">Licencia Creative Commons Compartir-Igual, 4.0</a>.
22
22
  back_to_mumuki: ¡Volvé a Mumuki!
23
23
  bibliotheca: Biblioteca
24
+ blocked_forum_explanation: Es decir que no tenés autorización para ver este contenido. <br> ¿Puede que estés en medio de un examen?
24
25
  cancel_subscription: Cancelá tu subscripción.
25
26
  chapter: Capítulo
26
27
  chapter_finished_html: ¡Terminaste %{chapter}! ¡Felicitaciones!
@@ -98,6 +99,7 @@ es:
98
99
  hour: hora
99
100
  hours: horas
100
101
  initial_state: Tablero inicial
102
+ insert_file_name: Ingresa un nombre de archivo
101
103
  invitation: Invitación a curso
102
104
  invitation_for: 'Unirse a %{course}'
103
105
  item_not_accessible: "No tenés permisos para acceder a este contenido"
@@ -128,6 +130,7 @@ es:
128
130
  moderator: Moderador
129
131
  mumuki_catchphrase: Aprendé a programar
130
132
  mumuki_short_description: Mumuki es la plataforma libre y gratuita para aprender a programar, de la práctica a la teoría y en tu idioma
133
+ my_doubts: Mis consultas
131
134
  name: Nombre
132
135
  navigation_continue: 'Siguiente %{kind}: %{sibling}'
133
136
  navigation_next: Siguiente %{kind}
@@ -208,7 +211,6 @@ es:
208
211
  start_lesson: ¡Comenzá esta lección!
209
212
  start_practicing: ¡Empezá ahora!
210
213
  start_using_mumuki: ¡Empezá a usar Mumuki!
211
- stats: Tu Progreso
212
214
  status: Estado
213
215
  stay_here: No, quiero quedarme acá
214
216
  stop_emails?: ¿Querés dejar de recibir estos mails?
@@ -235,7 +237,6 @@ es:
235
237
  unauthorized_explanation: ¡Ups! Esto es lo que se conoce como %{error}, es decir que no iniciaste sesión.
236
238
  uncategorized: No categorizado
237
239
  undo_upvote: No es útil
238
- unknown: no resuelto
239
240
  unmeet_expectations: Objetivos que no se cumplieron
240
241
  unsubscribe: Dejar de recibir notificaciones
241
242
  unsubscribed_successfully: Te desuscribiste exitosamente de los recordatorios.
@@ -21,6 +21,7 @@ pt:
21
21
  authoring_note: 'Este guia foi desenvolvido por %{authors} e <a href="%{collaborators}" target="_blank"> muitas outras pessoas </a>, nos termos do <a href = "https://creativecommons.org/licenses/by-sa/4.0/" target="_blank"> Creative Commons License Share-Equal, 4.0 </a>.'
22
22
  back_to_mumuki: Voltei para Mumuki!
23
23
  bibliotheca: Biblioteca
24
+ blocked_forum_explanation: Você não tem permissão para ver este conteúdo. <br> Você está no meio de um exame agora?
24
25
  chapter: Capítulo
25
26
  chapter_finished_html: Você terminou %{chapter}! Parabéns!
26
27
  chapter_number: Capítulo %{number}
@@ -96,6 +97,7 @@ pt:
96
97
  hour: hora
97
98
  hours: horas
98
99
  initial_state: Tabuleiro inicial
100
+ insert_file_name: Insira um nome de arquivo
99
101
  invitation: Convite para o curso
100
102
  invitation_for: Participe %{curso}
101
103
  item_not_accessible: Você não tem permissões para acessar este conteúdo
@@ -124,6 +126,7 @@ pt:
124
126
  moderator: Moderador
125
127
  mumuki_catchphrase: Aprendi a programar
126
128
  mumuki_short_description: Mumuki é a plataforma gratuita e gratuita para aprender a programar, desde a prática até a teoria e no seu idioma
129
+ my_doubts: Minhas duvidas
127
130
  name: Nome
128
131
  navigation_continue: "Próximo %{kind}: %{sibling}"
129
132
  navigation_next: Próximo %{kind}
@@ -198,7 +201,6 @@ pt:
198
201
  sort: Ordenar
199
202
  start_lesson: Comece esta lição!
200
203
  start_practicing: Comece agora!
201
- stats: Seu progresso
202
204
  status: Estado
203
205
  stay_here: Não, eu quero ficar aqui
204
206
  submission: solução
@@ -223,7 +225,6 @@ pt:
223
225
  try_again: Opa, algo deu errado em Mumuki. Reinicie o exercício, aguarde alguns segundos e tente novamente.
224
226
  unauthorized_explanation: Opa! Isso é o que é conhecido como %{error}, ou seja, você não fez logon.
225
227
  uncategorized: Não classificado
226
- unknown: não resolvido
227
228
  unmeet_expectations: Objetivos que não foram atendidos
228
229
  unsubscribe: Cancelar subscrição
229
230
  upload_solution: Carregar solução
@@ -1,15 +1,16 @@
1
1
  require 'mumukit/directives'
2
2
 
3
3
  class Mumukit::Directives::Sections < Mumukit::Directives::Directive
4
- def build(section, content)
5
- "#{comment_type.comment "<#{section}#"}#{content}#{comment_type.comment "##{section}>"}"
6
- end
7
-
8
4
  def join(sections)
9
5
  file_declarations, file_references = sections.map do |section, content|
10
6
  [build(section, content), interpolate(section)]
11
7
  end.transpose
12
- "#{file_declarations.join "\n"}\n#{build 'content', file_references.join("\n")}"
8
+
9
+ file_declarations.join "\n"
10
+ end
11
+
12
+ def build(section, content)
13
+ "#{comment_type.comment "<#{section}#"}#{content}#{comment_type.comment "##{section}>"}"
13
14
  end
14
15
 
15
16
  def interpolate(section)
@@ -1,11 +1,7 @@
1
1
  module Mumuki::Laboratory::Status::Submission::Pending
2
2
  extend Mumuki::Laboratory::Status::Submission
3
3
 
4
- def self.group
5
- Mumuki::Laboratory::Status::Submission::Unknown
6
- end
7
-
8
4
  def self.iconize
9
- {class: :info, type: 'clock-o'}
5
+ {class: :muted, type: :circle}
10
6
  end
11
7
  end
@@ -2,7 +2,7 @@ module Mumuki::Laboratory::Status::Submission::Running
2
2
  extend Mumuki::Laboratory::Status::Submission
3
3
 
4
4
  def self.group
5
- Mumuki::Laboratory::Status::Submission::Unknown
5
+ Mumuki::Laboratory::Status::Submission::Pending
6
6
  end
7
7
 
8
8
  def self.iconize
@@ -2,7 +2,6 @@ module Mumuki::Laboratory::Status::Submission
2
2
  include Mumuki::Laboratory::Status
3
3
  end
4
4
 
5
- require_relative './unknown'
6
5
  require_relative './pending'
7
6
  require_relative './running'
8
7
  require_relative './passed'
@@ -1,5 +1,5 @@
1
1
  module Mumuki
2
2
  module Laboratory
3
- VERSION = '5.7.0'
3
+ VERSION = '5.8.0'
4
4
  end
5
5
  end
@@ -6,6 +6,7 @@ describe DiscussionsController, organization_workspace: :test do
6
6
  let(:exercise_params) { {debatable_class: 'Exercise', exercise_id: exercise.id} }
7
7
 
8
8
  before { set_current_user! user }
9
+ before { Organization.current.tap { |it| it.forum_enabled = true }.save! }
9
10
 
10
11
  describe 'post' do
11
12
  before { allow_any_instance_of(DiscussionsController).to receive(:discussion_params).and_return title: 'A title' }
@@ -28,7 +28,7 @@ describe ExerciseSolutionsController, organization_workspace: :test do
28
28
  let(:files) { problem.files_for(user) }
29
29
 
30
30
  it { expect(response.status).to eq 200 }
31
- it { expect(Assignment.last.solution).to eq("/*<a_file.css#*/a css content/*#a_file.css>*/\n/*<a_file.js#*/a js content/*#a_file.js>*/\n/*<content#*//*...a_file.css...*/\n/*...a_file.js...*//*#content>*/") }
31
+ it { expect(Assignment.last.solution).to eq("/*<a_file.css#*/a css content/*#a_file.css>*/\n/*<a_file.js#*/a js content/*#a_file.js>*/") }
32
32
  it { expect(files.count).to eq 2 }
33
33
  it { expect(files[0]).to have_attributes(name: 'a_file.css', content: 'a css content') }
34
34
  it { expect(files[0].highlight_mode).to eq 'css' }
@@ -33,7 +33,7 @@ describe Api::OrganizationsController, type: :controller, organization_workspace
33
33
 
34
34
  it { check_status! 200 }
35
35
 
36
- it { expect(body[:organizations].map { |it| it[:name] }).to eq %w(base public dot.org private another_private) }
36
+ it { expect(body[:organizations].map { |it| it[:name] }).to contain_exactly(*%w(base public dot.org private another_private)) }
37
37
  end
38
38
  context 'with non-wildcard permissions' do
39
39
  before { get :index }
@@ -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: 20180704150839) do
13
+ ActiveRecord::Schema.define(version: 20180802190437) do
14
14
 
15
15
  # These are extensions that must be enabled in order to support this database
16
16
  enable_extension "plpgsql"
@@ -235,6 +235,7 @@ ActiveRecord::Schema.define(version: 20180704150839) do
235
235
  t.datetime "updated_at"
236
236
  t.boolean "read", default: false
237
237
  t.integer "discussion_id"
238
+ t.boolean "approved", default: false
238
239
  end
239
240
 
240
241
  create_table "organizations", id: :serial, force: :cascade do |t|
@@ -1,11 +1,11 @@
1
1
  FactoryBot.define do
2
2
  factory :api_client do |t|
3
3
  transient do
4
- role :janitor
5
- grant 'test/*'
4
+ role { :janitor }
5
+ grant { 'test/*' }
6
6
  end
7
7
 
8
- description "foo"
8
+ description { "foo" }
9
9
  user {
10
10
  create :user,
11
11
  first_name: 'foo',
@@ -1,6 +1,6 @@
1
1
  FactoryBot.define do
2
2
  factory :assignment do
3
- status :pending
3
+ status { :pending }
4
4
  exercise
5
5
  submitter { create(:user) }
6
6
  end
@@ -5,7 +5,7 @@ FactoryBot.define do
5
5
  book { Organization.current.book rescue nil }
6
6
 
7
7
  transient do
8
- lessons []
8
+ lessons { [] }
9
9
  name { Faker::Lorem.sentence(3) }
10
10
  end
11
11
 
@@ -1,9 +1,9 @@
1
1
  FactoryBot.define do
2
2
  factory :course do
3
- period '2016'
4
- shifts %w(morning)
5
- days %w(monday wednesday)
6
- description 'test'
7
- organization_id 1
3
+ period { '2016' }
4
+ shifts { %w(morning) }
5
+ days { %w(monday wednesday) }
6
+ description { 'test' }
7
+ organization_id { 1 }
8
8
  end
9
9
  end
@@ -1,7 +1,7 @@
1
1
  FactoryBot.define do
2
2
  factory :discussion do
3
- title 'A discussion'
4
- description 'A discussion description'
3
+ title { 'A discussion' }
4
+ description { 'A discussion description' }
5
5
  initiator { create(:user) }
6
6
  item { create(:exercise) }
7
7
  end
@@ -4,27 +4,27 @@ FactoryBot.define do
4
4
  sequence(:name) { |n| "lang#{n}" }
5
5
 
6
6
  runner_url { Faker::Internet.url }
7
- queriable true
7
+ queriable { true }
8
8
  end
9
9
 
10
10
  factory :haskell, parent: :language do
11
- name 'haskell'
11
+ name { 'haskell' }
12
12
  end
13
13
 
14
14
  factory :bash, parent: :language do
15
- name 'bash'
16
- triable true
17
- stateful_console true
15
+ name { 'bash' }
16
+ triable { true }
17
+ stateful_console { true }
18
18
  end
19
19
 
20
20
  factory :text_language, parent: :language do
21
- name 'text'
21
+ name { 'text' }
22
22
  end
23
23
 
24
24
  factory :gobstones, parent: :language do
25
- name 'gobstones'
26
- extension 'gbs'
27
- queriable false
25
+ name { 'gobstones' }
26
+ extension { 'gbs' }
27
+ queriable { false }
28
28
  end
29
29
 
30
30
  factory :exercise_base do
@@ -32,44 +32,42 @@ FactoryBot.define do
32
32
  sequence(:bibliotheca_id) { |n| n }
33
33
  sequence(:number) { |n| n }
34
34
 
35
- locale :en
35
+ locale { :en }
36
36
  guide
37
37
  end
38
38
 
39
39
  factory :challenge, parent: :exercise_base do
40
- layout 'input_right'
40
+ layout { 'input_right' }
41
41
  end
42
42
 
43
43
  factory :reading, class: Reading, parent: :exercise_base do
44
- name 'A reading'
45
- description 'Simple reading'
44
+ name { 'A reading' }
45
+ description { 'Simple reading' }
46
46
  end
47
47
 
48
48
  factory :problem, class: Problem, parent: :challenge do
49
- name 'A problem'
50
- description 'Simple problem'
51
- test 'dont care'
49
+ name { 'A problem' }
50
+ description { 'Simple problem' }
51
+ test { 'dont care' }
52
52
  end
53
53
 
54
54
  factory :interactive, class: Interactive, parent: :challenge do
55
- name 'An interactive problem'
56
- description 'Simple interactive problem'
57
- goal :query_passes
55
+ name { 'An interactive problem' }
56
+ description { 'Simple interactive problem' }
57
+ goal { :query_passes }
58
58
  language { create(:bash) }
59
59
  end
60
60
 
61
61
  factory :playground, class: Playground, parent: :challenge do
62
- name 'A Playground'
63
- description 'Simple playground'
62
+ name { 'A Playground' }
63
+ description { 'Simple playground' }
64
64
  end
65
65
 
66
66
  factory :exercise, parent: :problem
67
67
 
68
68
  factory :x_equal_5_exercise, parent: :exercise do
69
- test <<-EOT
70
- describe "x" $ do
71
- it "should be equal 5" $ do
72
- x `shouldBe` 5
73
- EOT
69
+ test { 'describe "x" $ do
70
+ it "should be equal 5" $ do
71
+ x `shouldBe` 5' }
74
72
  end
75
73
  end