mumuki-laboratory 5.7.0 → 5.8.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.
- checksums.yaml +4 -4
- data/app/assets/javascripts/application/codemirror-builder.js +16 -8
- data/app/assets/javascripts/application/codemirror.js +7 -9
- data/app/assets/javascripts/application/discussions.js +20 -12
- data/app/assets/javascripts/application/multiple-files.js +222 -0
- data/app/assets/javascripts/application/submission.js +1 -0
- data/app/assets/stylesheets/application/modules/_discussion.scss +12 -0
- data/app/assets/stylesheets/application/modules/_editor.scss +13 -0
- data/app/controllers/discussions_controller.rb +10 -0
- data/app/controllers/discussions_messages_controller.rb +15 -3
- data/app/helpers/application_helper.rb +1 -1
- data/app/helpers/discussions_helper.rb +13 -5
- data/app/helpers/multiple_file_editor_helper.rb +9 -0
- data/app/models/application_record.rb +5 -0
- data/app/models/concerns/with_assignments.rb +3 -21
- data/app/models/discussion.rb +1 -1
- data/app/models/exam.rb +12 -11
- data/app/models/exercise.rb +1 -1
- data/app/models/guide.rb +1 -1
- data/app/models/message.rb +5 -1
- data/app/models/stats.rb +4 -29
- data/app/models/user.rb +6 -0
- data/app/views/discussions/_message.html.erb +3 -0
- data/app/views/errors/forbidden.html.erb +1 -1
- data/app/views/layouts/_authoring.html.erb +5 -0
- data/app/views/layouts/_copyright.html.erb +2 -0
- data/app/views/layouts/_social_media.html.erb +4 -0
- data/app/views/layouts/application.html.erb +4 -6
- data/app/views/layouts/embedded.html.erb +27 -0
- data/app/views/layouts/exercise_inputs/editors/_multiple_files.html.erb +8 -3
- data/config/routes.rb +3 -1
- data/db/migrate/20180802190437_add_approved_to_messages.rb +5 -0
- data/lib/mumuki/laboratory/controllers/dynamic_errors.rb +6 -1
- data/lib/mumuki/laboratory/controllers/notifications.rb +3 -2
- data/lib/mumuki/laboratory/exceptions.rb +1 -0
- data/lib/mumuki/laboratory/exceptions/blocked_forum_error.rb +2 -0
- data/lib/mumuki/laboratory/locales/en.yml +3 -1
- data/lib/mumuki/laboratory/locales/es.yml +3 -2
- data/lib/mumuki/laboratory/locales/pt.yml +3 -2
- data/lib/mumuki/laboratory/mumukit/directives.rb +6 -5
- data/lib/mumuki/laboratory/status/submission/pending.rb +1 -5
- data/lib/mumuki/laboratory/status/submission/running.rb +1 -1
- data/lib/mumuki/laboratory/status/submission/submission.rb +0 -1
- data/lib/mumuki/laboratory/version.rb +1 -1
- data/spec/controllers/discussions_controller_spec.rb +1 -0
- data/spec/controllers/exercise_solutions_controller_spec.rb +1 -1
- data/spec/controllers/organizations_api_controller_spec.rb +1 -1
- data/spec/dummy/db/schema.rb +2 -1
- data/spec/factories/api_client_factory.rb +3 -3
- data/spec/factories/assignments_factory.rb +1 -1
- data/spec/factories/chapter_factory.rb +1 -1
- data/spec/factories/course_factory.rb +5 -5
- data/spec/factories/discussion_factory.rb +2 -2
- data/spec/factories/exercise_factory.rb +24 -26
- data/spec/factories/guide_factory.rb +3 -3
- data/spec/factories/login_settings_factory.rb +1 -1
- data/spec/factories/message_factory.rb +1 -1
- data/spec/factories/organization_factory.rb +9 -9
- data/spec/factories/topic_factory.rb +1 -1
- data/spec/features/choose_organization_spec.rb +49 -42
- data/spec/models/exercise_spec.rb +3 -27
- data/spec/models/query_spec.rb +1 -1
- data/spec/models/question_spec.rb +2 -2
- data/spec/models/stats_spec.rb +2 -9
- data/spec/models/user_spec.rb +13 -0
- metadata +8 -3
- data/lib/mumuki/laboratory/status/submission/unknown.rb +0 -11
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 7ca436cc42496e79bddfaff35a982a8f86dfbac83a5899152e6870526e8319eb
|
|
4
|
+
data.tar.gz: 60d05b8275511ab5f817fb6a2d6c10e756a7d362d9937ea071345262d4a088ee
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 688fd91f7f9ffa2534d164742bb32bdf595ccd9e70638b9e84ba8011b0f5deced7b4154a699788b905577e45d33e4f0af3e53f8945dc3f69296e2545a0fb18e6
|
|
7
|
+
data.tar.gz: 4a6795662d811fc676d3ea281a6862dd1a4c811bd5255bf97f76ef008ddf341af011f30077ca3eab2b8c38f35359a3ef56ae992571350bba80f276a3a27393b5
|
|
@@ -29,9 +29,6 @@ var mumuki = mumuki || {};
|
|
|
29
29
|
};
|
|
30
30
|
|
|
31
31
|
CodeMirrorBuilder.prototype = {
|
|
32
|
-
createEditor: function (customOptions) {
|
|
33
|
-
return CodeMirror.fromTextArea(this.textarea, Object.assign({}, codeMirrorDefaults, customOptions));
|
|
34
|
-
},
|
|
35
32
|
setupEditor: function () {
|
|
36
33
|
this.editor = this.createEditor({
|
|
37
34
|
lineNumbers: true,
|
|
@@ -47,6 +44,8 @@ var mumuki = mumuki || {};
|
|
|
47
44
|
}
|
|
48
45
|
}
|
|
49
46
|
});
|
|
47
|
+
|
|
48
|
+
return this;
|
|
50
49
|
},
|
|
51
50
|
setupSimpleEditor: function () {
|
|
52
51
|
this.editor = this.createEditor({
|
|
@@ -59,21 +58,30 @@ var mumuki = mumuki || {};
|
|
|
59
58
|
}
|
|
60
59
|
}
|
|
61
60
|
});
|
|
61
|
+
|
|
62
|
+
return this;
|
|
62
63
|
},
|
|
63
|
-
setupLanguage: function () {
|
|
64
|
-
var
|
|
65
|
-
if (
|
|
64
|
+
setupLanguage: function (language) {
|
|
65
|
+
var highlightMode = language || this.$textarea.data('editor-language');
|
|
66
|
+
if (highlightMode === 'dynamic') {
|
|
66
67
|
mumuki.page.dynamicEditors.push(this.editor);
|
|
67
68
|
} else {
|
|
68
|
-
this.editor.setOption('mode',
|
|
69
|
+
this.editor.setOption('mode', highlightMode);
|
|
69
70
|
this.editor.refresh();
|
|
70
71
|
}
|
|
72
|
+
|
|
73
|
+
return this;
|
|
71
74
|
},
|
|
72
|
-
|
|
75
|
+
setupMinLines: function (minLines) {
|
|
73
76
|
this.editor.setOption('minLines', minLines);
|
|
77
|
+
|
|
78
|
+
return this;
|
|
74
79
|
},
|
|
75
80
|
build: function () {
|
|
76
81
|
return this.editor;
|
|
82
|
+
},
|
|
83
|
+
createEditor: function (customOptions) {
|
|
84
|
+
return CodeMirror.fromTextArea(this.textarea, Object.assign({}, codeMirrorDefaults, customOptions));
|
|
77
85
|
}
|
|
78
86
|
};
|
|
79
87
|
|
|
@@ -2,16 +2,15 @@ var mumuki = mumuki || {};
|
|
|
2
2
|
|
|
3
3
|
(function (mumuki) {
|
|
4
4
|
function createCodeMirrors() {
|
|
5
|
-
|
|
5
|
+
return $(".editor").map(function (index, textarea) {
|
|
6
6
|
var $textarea = $("#solution_content");
|
|
7
|
-
var builder = new mumuki.editor.CodeMirrorBuilder(textarea);
|
|
8
|
-
builder.setupEditor();
|
|
9
|
-
builder.setupOptions($textarea.data('lines'));
|
|
10
|
-
builder.setupLanguage();
|
|
11
|
-
return builder.build();
|
|
12
|
-
});
|
|
13
7
|
|
|
14
|
-
|
|
8
|
+
return new mumuki.editor.CodeMirrorBuilder(textarea)
|
|
9
|
+
.setupEditor()
|
|
10
|
+
.setupMinLines($textarea.data('lines'))
|
|
11
|
+
.setupLanguage()
|
|
12
|
+
.build();
|
|
13
|
+
});
|
|
15
14
|
}
|
|
16
15
|
|
|
17
16
|
function onSelectUpdateCodeMirror() {
|
|
@@ -62,7 +61,6 @@ var mumuki = mumuki || {};
|
|
|
62
61
|
}
|
|
63
62
|
|
|
64
63
|
mumuki.editor = mumuki.editor || {};
|
|
65
|
-
mumuki.editor.setupCodeMirrors = setEditorLanguage;
|
|
66
64
|
mumuki.editor.toggleFullscreen = toggleFullscreen;
|
|
67
65
|
mumuki.editor.indentWithSpaces = indentWithSpaces;
|
|
68
66
|
mumuki.editor.syncContent = syncContent;
|
|
@@ -18,10 +18,11 @@ mumuki.load(function () {
|
|
|
18
18
|
var $textarea = $("#new-discussion-message");
|
|
19
19
|
var textarea = $textarea[0];
|
|
20
20
|
if(!textarea) return;
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
21
|
+
|
|
22
|
+
new mumuki.editor.CodeMirrorBuilder(textarea)
|
|
23
|
+
.setupSimpleEditor()
|
|
24
|
+
.setupMinLines($textarea.data('lines'))
|
|
25
|
+
.build();
|
|
25
26
|
}
|
|
26
27
|
|
|
27
28
|
createNewMessageEditor();
|
|
@@ -31,6 +32,16 @@ mumuki.load(function () {
|
|
|
31
32
|
spans.toggleClass('hidden');
|
|
32
33
|
},
|
|
33
34
|
token: new mumuki.CsrfToken(),
|
|
35
|
+
tokenRequest: function (data) {
|
|
36
|
+
return $.ajax(Forum.token.newRequest(data))
|
|
37
|
+
},
|
|
38
|
+
discussionPost: function (url) {
|
|
39
|
+
return Forum.tokenRequest({
|
|
40
|
+
url: url,
|
|
41
|
+
method: 'POST',
|
|
42
|
+
xhrFields: {withCredentials: true}
|
|
43
|
+
})
|
|
44
|
+
},
|
|
34
45
|
discussionSubscription: function (url) {
|
|
35
46
|
Forum.discussionPostAndToggle(url, $subscriptionSpans)
|
|
36
47
|
},
|
|
@@ -38,15 +49,12 @@ mumuki.load(function () {
|
|
|
38
49
|
Forum.discussionPostAndToggle(url, $upvoteSpans)
|
|
39
50
|
},
|
|
40
51
|
discussionPostAndToggle: function (url, elem) {
|
|
41
|
-
Forum.
|
|
42
|
-
url: url,
|
|
43
|
-
method: 'POST',
|
|
44
|
-
success: Forum.toggleButton(elem),
|
|
45
|
-
xhrFields: {withCredentials: true}
|
|
46
|
-
})
|
|
52
|
+
Forum.discussionPost(url).done(Forum.toggleButton(elem))
|
|
47
53
|
},
|
|
48
|
-
|
|
49
|
-
|
|
54
|
+
discussionMessageToggleApprove : function (url, elem) {
|
|
55
|
+
Forum.discussionPost(url).done(function () {
|
|
56
|
+
elem.toggleClass("selected");
|
|
57
|
+
})
|
|
50
58
|
}
|
|
51
59
|
};
|
|
52
60
|
|
|
@@ -0,0 +1,222 @@
|
|
|
1
|
+
mumuki.load(() => {
|
|
2
|
+
class File {
|
|
3
|
+
static get NAME_CLASS() { return '.file-name'; }
|
|
4
|
+
static get DELETE_BUTTON_CLASS() { return '.delete-file-button'; }
|
|
5
|
+
|
|
6
|
+
constructor(tab, editor) {
|
|
7
|
+
this.tab = tab;
|
|
8
|
+
this.editor = editor;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
initialize(name) {
|
|
12
|
+
this.name = name;
|
|
13
|
+
this.unselect();
|
|
14
|
+
|
|
15
|
+
return this;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
get name() {
|
|
19
|
+
return this.tab.find(File.NAME_CLASS).text();
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
set name(name) {
|
|
23
|
+
return this.tab.find(File.NAME_CLASS).text(name);
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
get isSelected() {
|
|
27
|
+
return this.tab.hasClass("active");
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
setUpOnRemove(handler) {
|
|
31
|
+
this.tab.find(File.DELETE_BUTTON_CLASS).click(() => {
|
|
32
|
+
handler(this);
|
|
33
|
+
});
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
remove() {
|
|
37
|
+
const wasSelected = this.isSelected;
|
|
38
|
+
|
|
39
|
+
this.tab.remove();
|
|
40
|
+
this.editor.remove();
|
|
41
|
+
|
|
42
|
+
return wasSelected;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
select() {
|
|
46
|
+
this._selectElement(this.tab);
|
|
47
|
+
this._selectElement(this.editor);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
unselect() {
|
|
51
|
+
this._unselectElement(this.tab);
|
|
52
|
+
this._unselectElement(this.editor);
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
_selectElement(element) {
|
|
56
|
+
element.addClass('active');
|
|
57
|
+
element.addClass('in');
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
_unselectElement(element) {
|
|
61
|
+
element.removeClass('active');
|
|
62
|
+
element.removeClass('in');
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
class MultipleFileEditor {
|
|
67
|
+
constructor(tabsContainer, editorsContainer) {
|
|
68
|
+
this.tabsContainer = tabsContainer;
|
|
69
|
+
this.editorsContainer = editorsContainer;
|
|
70
|
+
|
|
71
|
+
this.MAX_TABS = 5;
|
|
72
|
+
|
|
73
|
+
this._addFileButton = this.tabsContainer.siblings('.add-file-button');
|
|
74
|
+
this._updateButtonsVisibility();
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
get files() {
|
|
78
|
+
const editors = this.editors;
|
|
79
|
+
|
|
80
|
+
return this.tabs.map((i, tab) => {
|
|
81
|
+
return new File($(tab), $(editors[i]));
|
|
82
|
+
});
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
get tabs() {
|
|
86
|
+
return this.tabsContainer.children();
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
get editors() {
|
|
90
|
+
return this.editorsContainer.find('.file-editor')
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
get highlightModes() {
|
|
94
|
+
return this._getDataFromHiddenInput('#highlight-modes')
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
get locales() {
|
|
98
|
+
return this._getDataFromHiddenInput('#multifile-locales')
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
setUpAddFile() {
|
|
102
|
+
this._addFileButton.click(() => {
|
|
103
|
+
this._addFile();
|
|
104
|
+
});
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
setUpDeleteFiles() {
|
|
108
|
+
this.files.each((i, file) => {
|
|
109
|
+
this.setUpDeleteFile(file);
|
|
110
|
+
});
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
setUpDeleteFile(file) {
|
|
114
|
+
file.setUpOnRemove(this._deleteFile.bind(this));
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
_addFile() {
|
|
118
|
+
const name = prompt(this.locales.insert_file_name);
|
|
119
|
+
const alreadyExists = this.files.toArray().some(it => it.name === name);
|
|
120
|
+
if (!name.length || !name.includes('.') || alreadyExists) return;
|
|
121
|
+
|
|
122
|
+
const id = `editor-file-${this._getFilesCount()}`;
|
|
123
|
+
this.tabsContainer.append(this._createTab(name, id));
|
|
124
|
+
this.editors.parent().last().append(this._createEditor(name, id));
|
|
125
|
+
const file = this.files.last().get(0).initialize(name);
|
|
126
|
+
this.setUpDeleteFile(file);
|
|
127
|
+
|
|
128
|
+
this._updateButtonsVisibility();
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
_deleteFile(file) {
|
|
132
|
+
const index = this.files.toArray()
|
|
133
|
+
.map((file) => file.name)
|
|
134
|
+
.indexOf(file.name);
|
|
135
|
+
const previousIndex = Math.max(index - 1, 0);
|
|
136
|
+
|
|
137
|
+
const wasSelected = file.remove();
|
|
138
|
+
if (wasSelected) this.files[previousIndex].select();
|
|
139
|
+
|
|
140
|
+
this._updateButtonsVisibility();
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
_updateButtonsVisibility() {
|
|
144
|
+
const filesCount = this._getFilesCount();
|
|
145
|
+
const deleteButtons = this.tabs.find(File.DELETE_BUTTON_CLASS);
|
|
146
|
+
|
|
147
|
+
this._setVisibility(this._addFileButton, filesCount < this.MAX_TABS);
|
|
148
|
+
this._setVisibility(deleteButtons, filesCount > 1);
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
_createTab(name, id) {
|
|
152
|
+
const tab = this.tabs.last().clone();
|
|
153
|
+
tab.attr('data-target', `#${id}`);
|
|
154
|
+
|
|
155
|
+
return tab;
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
_createEditor(name, id) {
|
|
159
|
+
const editor = this.editors.last().clone();
|
|
160
|
+
editor.attr('id', id);
|
|
161
|
+
editor.find('.CodeMirror').remove();
|
|
162
|
+
|
|
163
|
+
const textarea = editor.children().first();
|
|
164
|
+
this._setUpTextArea(textarea, `solution_content[${name}]`, `solution[content[${name}]]`);
|
|
165
|
+
|
|
166
|
+
const highlightMode = this._getHighlightModeFor(name);
|
|
167
|
+
const codeMirrorEditor = new mumuki.editor.CodeMirrorBuilder(textarea.get(0))
|
|
168
|
+
.setupEditor()
|
|
169
|
+
.setupMinLines(textarea.data('lines'))
|
|
170
|
+
.setupLanguage(highlightMode)
|
|
171
|
+
.build();
|
|
172
|
+
|
|
173
|
+
codeMirrorEditor.on("change", (event) => {
|
|
174
|
+
textarea.val(event.getValue());
|
|
175
|
+
});
|
|
176
|
+
|
|
177
|
+
const solutionTextArea = $('.new_solution').find('textarea').last();
|
|
178
|
+
this._setUpTextArea(solutionTextArea, '', '');
|
|
179
|
+
|
|
180
|
+
return editor;
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
_getFilesCount() {
|
|
184
|
+
return this.files.length;
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
_setUpTextArea(textarea, id, name) {
|
|
188
|
+
textarea.attr('id', id);
|
|
189
|
+
textarea.attr('name', name);
|
|
190
|
+
textarea.text('');
|
|
191
|
+
textarea.val('');
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
_getHighlightModeFor(name) {
|
|
195
|
+
const extension = name.split('.').pop();
|
|
196
|
+
const language = this.highlightModes.find((it) => it.extension === extension);
|
|
197
|
+
|
|
198
|
+
return language && language.highlight_mode || extension;
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
_setVisibility(element, isVisible) {
|
|
202
|
+
if (isVisible) element.show(); else element.hide();
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
_getDataFromHiddenInput(name) {
|
|
206
|
+
return JSON.parse($(name).val());
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
|
|
211
|
+
const setUpTabsBehavior = () => {
|
|
212
|
+
const tabsContainer = $('.nav-tabs');
|
|
213
|
+
if (!tabsContainer.length) return;
|
|
214
|
+
const editorsContainer = $('.tab-content');
|
|
215
|
+
|
|
216
|
+
const multipleFileEditor = new MultipleFileEditor(tabsContainer, editorsContainer);
|
|
217
|
+
multipleFileEditor.setUpAddFile();
|
|
218
|
+
multipleFileEditor.setUpDeleteFiles();
|
|
219
|
+
};
|
|
220
|
+
|
|
221
|
+
$(document).ready(setUpTabsBehavior);
|
|
222
|
+
});
|
|
@@ -329,6 +329,18 @@ summary.discussion-summary {
|
|
|
329
329
|
}
|
|
330
330
|
.actions {
|
|
331
331
|
float: right;
|
|
332
|
+
a {
|
|
333
|
+
margin-left: 5px;
|
|
334
|
+
}
|
|
335
|
+
.discussion-message-approved {
|
|
336
|
+
cursor: pointer;
|
|
337
|
+
text-decoration: none;
|
|
338
|
+
&.selected {
|
|
339
|
+
i {
|
|
340
|
+
color: $brand-success;
|
|
341
|
+
}
|
|
342
|
+
}
|
|
343
|
+
}
|
|
332
344
|
i {
|
|
333
345
|
color: #aaaaaa
|
|
334
346
|
}
|
|
@@ -88,6 +88,7 @@ body.fullscreen {
|
|
|
88
88
|
width: 100%;
|
|
89
89
|
|
|
90
90
|
li {
|
|
91
|
+
position: relative;
|
|
91
92
|
width: 100%;
|
|
92
93
|
white-space: nowrap;
|
|
93
94
|
overflow: hidden;
|
|
@@ -99,6 +100,12 @@ body.fullscreen {
|
|
|
99
100
|
margin-right: 0;
|
|
100
101
|
padding: 5px 10px;
|
|
101
102
|
}
|
|
103
|
+
.delete-file-button {
|
|
104
|
+
position: absolute;
|
|
105
|
+
right: 16px;
|
|
106
|
+
top: 12px;
|
|
107
|
+
cursor: pointer;
|
|
108
|
+
}
|
|
102
109
|
&.active {
|
|
103
110
|
a {
|
|
104
111
|
background-color: #f7f7f7;
|
|
@@ -114,5 +121,11 @@ body.fullscreen {
|
|
|
114
121
|
}
|
|
115
122
|
}
|
|
116
123
|
}
|
|
124
|
+
|
|
125
|
+
.add-file-button {
|
|
126
|
+
margin-left: 16px;
|
|
127
|
+
margin-right: 16px;
|
|
128
|
+
cursor: pointer;
|
|
129
|
+
}
|
|
117
130
|
}
|
|
118
131
|
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
class DiscussionsController < AjaxController
|
|
2
2
|
include Mumuki::Laboratory::Controllers::Content
|
|
3
3
|
|
|
4
|
+
before_action :validate_forum_enabled!
|
|
5
|
+
before_action :validate_not_in_exam!
|
|
4
6
|
before_action :set_debatable, except: [:subscription]
|
|
5
7
|
before_action :authenticate!, only: [:update, :create]
|
|
6
8
|
before_action :discussion_filter_params, only: :index
|
|
@@ -62,4 +64,12 @@ class DiscussionsController < AjaxController
|
|
|
62
64
|
def discussion_filter_params
|
|
63
65
|
@filter_params ||= params.permit(Discussion.permitted_query_params)
|
|
64
66
|
end
|
|
67
|
+
|
|
68
|
+
def validate_forum_enabled!
|
|
69
|
+
raise Mumuki::Laboratory::NotFoundError unless Organization.current.forum_enabled?
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
def validate_not_in_exam!
|
|
73
|
+
raise Mumuki::Laboratory::BlockedForumError if current_user&.currently_in_exam?
|
|
74
|
+
end
|
|
65
75
|
end
|