merge with master
This commit is contained in:
BIN
app/assets/.DS_Store
vendored
BIN
app/assets/.DS_Store
vendored
Binary file not shown.
BIN
app/assets/javascripts/.DS_Store
vendored
BIN
app/assets/javascripts/.DS_Store
vendored
Binary file not shown.
@@ -21,3 +21,7 @@
|
||||
//= require turbolinks
|
||||
//= require_tree ../../../lib
|
||||
//= require_tree .
|
||||
//= require bootstrap_pagedown
|
||||
//= require markdown.converter
|
||||
//= require markdown.sanitizer
|
||||
//= require markdown.editor
|
@@ -14,21 +14,18 @@ $(function() {
|
||||
var REMEMBER_TAB = false;
|
||||
var AUTOSAVE_INTERVAL = 15 * 1000;
|
||||
var REQUEST_FOR_COMMENTS_DELAY = 3 * 60 * 1000;
|
||||
var NONE = 0;
|
||||
var WEBSOCKET = 1;
|
||||
var SERVER_SEND_EVENT = 2;
|
||||
|
||||
var editors = [];
|
||||
var editor_for_file = new Map();
|
||||
var regex_for_language = new Map();
|
||||
var tracepositions_regex;
|
||||
var resetTurtle = true;
|
||||
|
||||
var active_file = undefined;
|
||||
var active_frame = undefined;
|
||||
var running = false;
|
||||
var qa_api = undefined;
|
||||
var output_mode_is_streaming = true;
|
||||
var runmode = NONE;
|
||||
|
||||
var websocket,
|
||||
turtlescreen,
|
||||
@@ -63,18 +60,6 @@ $(function() {
|
||||
$('#output pre').remove();
|
||||
};
|
||||
|
||||
var closeEventSource = function(event) {
|
||||
event.target.close();
|
||||
hideSpinner();
|
||||
running = false;
|
||||
toggleButtonStates();
|
||||
|
||||
if (event.type === 'error' || JSON.parse(event.data).code !== 200) {
|
||||
ajaxError();
|
||||
showTab(0);
|
||||
}
|
||||
};
|
||||
|
||||
var collectFiles = function() {
|
||||
var editable_editors = _.filter(editors, function(editor) {
|
||||
return !editor.getReadOnly();
|
||||
@@ -153,8 +138,8 @@ $(function() {
|
||||
// This is the case, since it is set via a call to ancestor_id on the model, which returns either file_id if set, or id if it is not set.
|
||||
// therefore the else part is not needed any longer...
|
||||
|
||||
// if we have an file_id set (the file is a copy of a teacher supplied given file)
|
||||
if (file_id_old != null){
|
||||
// if we have an file_id set (the file is a copy of a teacher supplied given file) and the new file-ids are present in the response
|
||||
if (file_id_old != null && data.files){
|
||||
// if we find file_id_old (this is the reference to the base file) in the submission, this is the match
|
||||
for(var j = 0; j< data.files.length; j++){
|
||||
if(data.files[j].file_id == file_id_old){
|
||||
@@ -188,44 +173,8 @@ $(function() {
|
||||
});
|
||||
};
|
||||
|
||||
var evaluateCode = function(url, streamed, callback) {
|
||||
(streamed ? evaluateCodeWithStreamedResponse : evaluateCodeWithoutStreamedResponse)(url, callback);
|
||||
};
|
||||
|
||||
var evaluateCodeWithStreamedResponse = function(url, onmessageFunction) {
|
||||
initWebsocketConnection(url, onmessageFunction);
|
||||
|
||||
// TODO only init turtle when required
|
||||
initTurtle();
|
||||
|
||||
// TODO reimplement via websocket messsages
|
||||
/*var event_source = new EventSource(url);
|
||||
event_source.addEventListener('hint', renderHint);
|
||||
event_source.addEventListener('info', storeContainerInformation);
|
||||
|
||||
if ($('#flowrHint').isPresent()) {
|
||||
event_source.addEventListener('output', handleStderrOutputForFlowr);
|
||||
event_source.addEventListener('close', handleStderrOutputForFlowr);
|
||||
}
|
||||
|
||||
if (qa_api) {
|
||||
event_source.addEventListener('close', handleStreamedResponseForCodePilot);
|
||||
}*/
|
||||
};
|
||||
|
||||
var handleStreamedResponseForCodePilot = function(event) {
|
||||
qa_api.executeCommand('syncOutput', [chunkBuffer]);
|
||||
chunkBuffer = [{streamedResponse: true}];
|
||||
}
|
||||
|
||||
var evaluateCodeWithoutStreamedResponse = function(url, callback) {
|
||||
var jqxhr = ajax({
|
||||
method: 'GET',
|
||||
url: url
|
||||
});
|
||||
jqxhr.always(hideSpinner);
|
||||
jqxhr.done(callback);
|
||||
jqxhr.fail(ajaxError);
|
||||
var evaluateCode = function(url, callback) {
|
||||
initWebsocketConnection(url, callback);
|
||||
};
|
||||
|
||||
var fileActionsAvailable = function() {
|
||||
@@ -521,10 +470,6 @@ $(function() {
|
||||
}, REQUEST_FOR_COMMENTS_DELAY);
|
||||
};
|
||||
|
||||
var isActiveFileBinary = function() {
|
||||
return 'binary' in active_frame.data();
|
||||
};
|
||||
|
||||
var isActiveFileExecutable = function() {
|
||||
return 'executable' in active_frame.data();
|
||||
};
|
||||
@@ -574,21 +519,6 @@ $(function() {
|
||||
panel.find('.row .col-sm-9').eq(4).find('a').attr('href', '#output-' + index);
|
||||
};
|
||||
|
||||
var chunkBuffer = [{streamedResponse: true}];
|
||||
|
||||
var printChunk = function(event) {
|
||||
var output = JSON.parse(event.data);
|
||||
if (output) {
|
||||
printOutput(output, true, 0);
|
||||
// send test response to QA
|
||||
// we are expecting an array of outputs:
|
||||
if (qa_api) {
|
||||
chunkBuffer.push(output);
|
||||
}
|
||||
} else {
|
||||
resetOutputTab();
|
||||
}
|
||||
};
|
||||
|
||||
var resetOutputTab = function() {
|
||||
clearOutput();
|
||||
@@ -769,14 +699,13 @@ $(function() {
|
||||
var runCode = function(event) {
|
||||
event.preventDefault();
|
||||
if ($('#run').is(':visible')) {
|
||||
runmode = WEBSOCKET;
|
||||
createSubmission(this, null, function(response) {
|
||||
$('#stop').data('url', response.stop_url);
|
||||
running = true;
|
||||
showSpinner($('#run'));
|
||||
toggleButtonStates();
|
||||
var url = response.run_url.replace(FILENAME_URL_PLACEHOLDER, active_file.filename);
|
||||
evaluateCode(url, true, function(evt) { parseCanvasMessage(evt.data, true); });
|
||||
evaluateCode(url, function(evt) { parseCanvasMessage(evt.data, true); });
|
||||
});
|
||||
}
|
||||
};
|
||||
@@ -807,11 +736,10 @@ $(function() {
|
||||
|
||||
var scoreCode = function(event) {
|
||||
event.preventDefault();
|
||||
runmode = SERVER_SEND_EVENT;
|
||||
createSubmission(this, null, function(response) {
|
||||
showSpinner($('#assess'));
|
||||
var url = response.score_url;
|
||||
evaluateCode(url, true, handleScoringResponse);
|
||||
evaluateCode(url, handleScoringResponse);
|
||||
});
|
||||
};
|
||||
|
||||
@@ -917,31 +845,11 @@ $(function() {
|
||||
|
||||
var stopCode = function(event) {
|
||||
event.preventDefault();
|
||||
if ($('#stop').is(':visible')) {
|
||||
if(runmode == WEBSOCKET){
|
||||
killWebsocketAndContainer();
|
||||
} else if (runmode == SERVER_SEND_EVENT) {
|
||||
stopCodeServerSendEvent(event);
|
||||
}
|
||||
runmode = NONE;
|
||||
if (isActiveFileStoppable()) {
|
||||
killWebsocketAndContainer();
|
||||
}
|
||||
};
|
||||
|
||||
var stopCodeServerSendEvent = function(event){
|
||||
var jqxhr = ajax({
|
||||
data: {
|
||||
container_id: $('#stop').data('container').id
|
||||
},
|
||||
url: $('#stop').data('url')
|
||||
});
|
||||
jqxhr.always(function() {
|
||||
hideSpinner();
|
||||
running = false;
|
||||
toggleButtonStates();
|
||||
});
|
||||
jqxhr.fail(ajaxError);
|
||||
};
|
||||
|
||||
var killWebsocketAndContainer = function() {
|
||||
if (websocket.readyState != WebSocket.OPEN) {
|
||||
return;
|
||||
@@ -949,28 +857,17 @@ $(function() {
|
||||
websocket.send(JSON.stringify({cmd: 'exit'}));
|
||||
websocket.flush();
|
||||
websocket.close();
|
||||
|
||||
if(turtlescreen != null){
|
||||
resetTurtle = true;
|
||||
}
|
||||
|
||||
hideSpinner();
|
||||
running = false;
|
||||
toggleButtonStates();
|
||||
hidePrompt();
|
||||
}
|
||||
|
||||
// todo set this from websocket command, required to e.g. stop container
|
||||
var storeContainerInformation = function(event) {
|
||||
var container_information = JSON.parse(event.data);
|
||||
$('#stop').data('container', container_information);
|
||||
|
||||
if (_.size(container_information.port_bindings) > 0) {
|
||||
$.flash.info({
|
||||
icon: ['fa', 'fa-exchange'],
|
||||
text: _.map(container_information.port_bindings, function(key, value) {
|
||||
var url = window.location.protocol + '//' + window.location.hostname + ':' + key;
|
||||
return $('#run').data('message-network').replace('%{port}', value).replace(/%{address}/g, url);
|
||||
}).join('\n')
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
var storeTab = function(event) {
|
||||
localStorage.tab = $(event.target).parent().index();
|
||||
};
|
||||
@@ -990,7 +887,7 @@ $(function() {
|
||||
createSubmission(this, null, function(response) {
|
||||
showSpinner($('#test'));
|
||||
var url = response.test_url.replace(FILENAME_URL_PLACEHOLDER, active_file.filename);
|
||||
evaluateCode(url, true, handleTestResponse);
|
||||
evaluateCode(url, handleTestResponse);
|
||||
});
|
||||
}
|
||||
};
|
||||
@@ -1027,10 +924,11 @@ $(function() {
|
||||
// clear canvas
|
||||
// turtlecanvas.getContext("2d").clearRect(0, 0, turtlecanvas.width, turtlecanvas.height);
|
||||
|
||||
if(resetTurtle) {
|
||||
turtlescreen = new Turtle(websocket, turtlecanvas);
|
||||
if ($('#run').isPresent()) {
|
||||
$('#run').bind('click', hideCanvas);
|
||||
}
|
||||
showCanvas();
|
||||
resetTurtle = false;
|
||||
}
|
||||
};
|
||||
|
||||
var initPrompt = function() {
|
||||
@@ -1058,10 +956,12 @@ $(function() {
|
||||
printWebsocketOutput(msg);
|
||||
break;
|
||||
case 'turtle':
|
||||
initTurtle();
|
||||
showCanvas();
|
||||
handleTurtleCommand(msg);
|
||||
break;
|
||||
case 'turtlebatch':
|
||||
initTurtle();
|
||||
showCanvas();
|
||||
handleTurtlebatchCommand(msg);
|
||||
break;
|
||||
|
55
app/assets/javascripts/editor_edit.js
Normal file
55
app/assets/javascripts/editor_edit.js
Normal file
@@ -0,0 +1,55 @@
|
||||
$(function() {
|
||||
var ACE_FILES_PATH = '/assets/ace/';
|
||||
var THEME = 'ace/theme/textmate';
|
||||
|
||||
var configureEditors = function() {
|
||||
_.each(['modePath', 'themePath', 'workerPath'], function(attribute) {
|
||||
ace.config.set(attribute, ACE_FILES_PATH);
|
||||
});
|
||||
};
|
||||
|
||||
var initializeEditors = function() {
|
||||
$('.editor').each(function(index, element) {
|
||||
var editor = ace.edit(element);
|
||||
|
||||
var document = editor.getSession().getDocument();
|
||||
// insert pre-existing code into editor. we have to use insertLines, otherwise the deltas are not properly added
|
||||
var file_id = $(element).data('file-id');
|
||||
var content = $('.editor-content[data-file-id=' + file_id + ']');
|
||||
|
||||
document.insertLines(0, content.text().split(/\n/));
|
||||
// remove last (empty) that is there by default line
|
||||
document.removeLines(document.getLength() - 1, document.getLength() - 1);
|
||||
editor.setReadOnly($(element).data('read-only') !== undefined);
|
||||
editor.setShowPrintMargin(false);
|
||||
editor.setTheme(THEME);
|
||||
|
||||
var textarea = $('textarea[id="exercise_files_attributes_'+index+'_content"]');
|
||||
var content = textarea.val();
|
||||
|
||||
if (content != undefined)
|
||||
{
|
||||
editor.getSession().setValue(content);
|
||||
editor.getSession().on('change', function(){
|
||||
textarea.val(editor.getSession().getValue());
|
||||
});
|
||||
}
|
||||
|
||||
editor.commands.bindKey("ctrl+alt+0", null);
|
||||
var session = editor.getSession();
|
||||
session.setMode($(element).data('mode'));
|
||||
session.setTabSize($(element).data('indent-size'));
|
||||
session.setUseSoftTabs(true);
|
||||
session.setUseWrapMode(true);
|
||||
|
||||
var file_id = $(element).data('id');
|
||||
}
|
||||
)};
|
||||
|
||||
if ($('#editor-edit').isPresent()) {
|
||||
configureEditors();
|
||||
initializeEditors();
|
||||
$('.frame').show();
|
||||
}
|
||||
});
|
||||
|
@@ -173,9 +173,10 @@ $(function() {
|
||||
} else if ($('.edit_exercise, .new_exercise').isPresent()) {
|
||||
execution_environments = $('form').data('execution-environments');
|
||||
file_types = $('form').data('file-types');
|
||||
// new MarkdownEditor('#exercise_instructions');
|
||||
new MarkdownEditor('#exercise_description');
|
||||
// new MarkdownEditor('#exercise_instructions');
|
||||
// new MarkdownEditor('#exercise_description')
|
||||
// todo: add an ace editor for each file
|
||||
new PagedownEditor('#exercise_description');
|
||||
|
||||
enableInlineFileCreation();
|
||||
inferFileAttributes();
|
||||
|
16
app/assets/javascripts/markdown_ace_editor.js
Normal file
16
app/assets/javascripts/markdown_ace_editor.js
Normal file
@@ -0,0 +1,16 @@
|
||||
(function() {
|
||||
var ACE_FILES_PATH = '/assets/ace/';
|
||||
|
||||
window.MarkdownEditor = function(selector) {
|
||||
ace.config.set('modePath', ACE_FILES_PATH);
|
||||
var editor = ace.edit($(selector).next()[0]);
|
||||
editor.on('change', function() {
|
||||
$(selector).val(editor.getValue());
|
||||
});
|
||||
editor.setShowPrintMargin(false);
|
||||
var session = editor.getSession();
|
||||
session.setMode('markdown');
|
||||
session.setUseWrapMode(true);
|
||||
session.setValue($(selector).val());
|
||||
};
|
||||
})();
|
@@ -1,16 +0,0 @@
|
||||
(function() {
|
||||
var ACE_FILES_PATH = '/assets/ace/';
|
||||
|
||||
window.MarkdownEditor = function(selector) {
|
||||
ace.config.set('modePath', ACE_FILES_PATH);
|
||||
var editor = ace.edit($(selector).next()[0]);
|
||||
editor.on('change', function() {
|
||||
$(selector).val(editor.getValue());
|
||||
});
|
||||
editor.setShowPrintMargin(false);
|
||||
var session = editor.getSession();
|
||||
session.setMode('markdown');
|
||||
session.setUseWrapMode(true);
|
||||
session.setValue($(selector).val());
|
||||
};
|
||||
})();
|
10
app/assets/javascripts/pagedown.js
Normal file
10
app/assets/javascripts/pagedown.js
Normal file
@@ -0,0 +1,10 @@
|
||||
(function() {
|
||||
var ACE_FILES_PATH = '/assets/ace/';
|
||||
|
||||
window.PagedownEditor = function(selector) {
|
||||
var converter = Markdown.getSanitizingConverter();
|
||||
var editor = new Markdown.Editor( converter );
|
||||
|
||||
editor.run();
|
||||
};
|
||||
})();
|
@@ -14,4 +14,6 @@
|
||||
*= require_tree ../../../lib
|
||||
*= require_tree ../../../vendor/assets/stylesheets/
|
||||
*= require_self
|
||||
*/
|
||||
*= require bootstrap_pagedown
|
||||
*= require markdown
|
||||
*/
|
||||
|
@@ -224,7 +224,7 @@ class ExercisesController < ApplicationController
|
||||
if lti_outcome_service?
|
||||
transmit_lti_score
|
||||
else
|
||||
redirect_to_lti_return_path
|
||||
redirect_after_submit
|
||||
end
|
||||
end
|
||||
|
||||
@@ -232,7 +232,7 @@ class ExercisesController < ApplicationController
|
||||
::NewRelic::Agent.add_custom_parameters({ submission: @submission.id, normalized_score: @submission.normalized_score })
|
||||
response = send_score(@submission.normalized_score)
|
||||
if response[:status] == 'success'
|
||||
redirect_to_lti_return_path
|
||||
redirect_after_submit
|
||||
else
|
||||
respond_to do |format|
|
||||
format.html { redirect_to(implement_exercise_path(@submission.exercise)) }
|
||||
@@ -245,4 +245,28 @@ class ExercisesController < ApplicationController
|
||||
def update
|
||||
update_and_respond(object: @exercise, params: exercise_params)
|
||||
end
|
||||
|
||||
def redirect_after_submit
|
||||
Rails.logger.debug('Score ' + @submission.normalized_score.to_s)
|
||||
if @submission.normalized_score == 1.0
|
||||
# if user has an own rfc, redirect to it and message him to clean up and accept the answer.
|
||||
|
||||
# else: show open rfc for same exercise
|
||||
if rfc = RequestForComment.unsolved.where(exercise_id: @submission.exercise).order("RANDOM()").first
|
||||
|
||||
# set a message that informs the user that his score was perfect and help in RFC is greatly appreciated.
|
||||
flash[:notice] = I18n.t('exercises.submit.full_score_redirect_to_rfc')
|
||||
flash.keep(:notice)
|
||||
|
||||
respond_to do |format|
|
||||
format.html { redirect_to(rfc) }
|
||||
format.json { render(json: {redirect: url_for(rfc)}) }
|
||||
end
|
||||
|
||||
return
|
||||
end
|
||||
end
|
||||
redirect_to_lti_return_path
|
||||
end
|
||||
|
||||
end
|
||||
|
@@ -1,5 +1,5 @@
|
||||
module ApplicationHelper
|
||||
APPLICATION_NAME = 'Code Ocean'
|
||||
APPLICATION_NAME = 'CodeOcean'
|
||||
|
||||
def application_name
|
||||
APPLICATION_NAME
|
||||
|
@@ -4,16 +4,12 @@ class RequestForComment < ActiveRecord::Base
|
||||
belongs_to :exercise
|
||||
belongs_to :file, class_name: 'CodeOcean::File'
|
||||
|
||||
before_create :set_requested_timestamp
|
||||
scope :unsolved, -> { where(solved: [false, nil]) }
|
||||
|
||||
def self.last_per_user(n = 5)
|
||||
from("(#{row_number_user_sql}) as request_for_comments").where("row_number <= ?", n)
|
||||
end
|
||||
|
||||
def set_requested_timestamp
|
||||
self.requested_at = Time.now
|
||||
end
|
||||
|
||||
# not used right now, finds the last submission for the respective user and exercise.
|
||||
# might be helpful to check whether the exercise has been solved in the meantime.
|
||||
def last_submission
|
||||
|
@@ -8,7 +8,7 @@
|
||||
- if current_user.admin?
|
||||
li = link_to(t('breadcrumbs.dashboard.show'), admin_dashboard_path)
|
||||
li.divider
|
||||
- models = [ExecutionEnvironment, Exercise, Consumer, CodeHarborLink, ExternalUser, FileType, FileTemplate, InternalUser, Submission].sort_by { |model| model.model_name.human(count: 2) }
|
||||
- models = [ExecutionEnvironment, Exercise, Consumer, CodeHarborLink, ExternalUser, FileType, FileTemplate, InternalUser].sort_by { |model| model.model_name.human(count: 2) }
|
||||
- models.each do |model|
|
||||
- if policy(model).index?
|
||||
li = link_to(model.model_name.human(count: 2), send(:"#{model.model_name.collection}_path"))
|
||||
|
@@ -2,5 +2,5 @@
|
||||
= form.label(attribute, label)
|
||||
|
|
||||
a.toggle-input data={text_initial: t('shared.upload_file'), text_toggled: t('shared.back')} href='#' = t('shared.upload_file')
|
||||
= form.text_area(attribute, class: 'code-field form-control original-input', rows: 16)
|
||||
= form.text_area(attribute, class: 'code-field form-control original-input', rows: 16, style: "display:none;")
|
||||
= form.file_field(attribute, class: 'alternative-input form-control', disabled: true)
|
||||
|
@@ -1,9 +1,9 @@
|
||||
h5 =t('exercises.implement.comment.others')
|
||||
pre#other-comments
|
||||
|
||||
h5 =t('exercises.implement.comment.addyours')
|
||||
|
||||
textarea.form-control(style='resize:none;')
|
||||
#otherComments
|
||||
h5 =t('exercises.implement.comment.others')
|
||||
pre#otherCommentsTextfield
|
||||
p = ''
|
||||
button#addCommentButton.btn.btn-block.btn-primary(type='button') =t('exercises.implement.comment.addComment')
|
||||
button#removeAllButton.btn.btn-block.btn-warning(type='button') =t('exercises.implement.comment.removeAllOnLine')
|
5
app/views/exercises/_editor_edit.html.slim
Normal file
5
app/views/exercises/_editor_edit.html.slim
Normal file
@@ -0,0 +1,5 @@
|
||||
#editor-edit.panel-group.row data-exercise-id=@exercise.id
|
||||
#frames
|
||||
.frame
|
||||
.editor-content.hidden
|
||||
.editor
|
@@ -1,4 +1,5 @@
|
||||
- id = f.object.id
|
||||
|
||||
li.panel.panel-default
|
||||
.panel-heading role="tab" id="heading"
|
||||
a.file-heading data-toggle="collapse" data-parent="#files" href="#collapse#{id}"
|
||||
@@ -37,3 +38,4 @@ li.panel.panel-default
|
||||
= f.label(:role, t('activerecord.attributes.file.weight'))
|
||||
= f.number_field(:weight, class: 'form-control', min: 1, step: 'any')
|
||||
= render('code_field', attribute: :content, form: f, label: t('activerecord.attributes.file.content'))
|
||||
= render partial: 'editor_edit', locals: { exercise: @exercise }
|
@@ -8,8 +8,7 @@
|
||||
= f.text_field(:title, class: 'form-control', required: true)
|
||||
.form-group
|
||||
= f.label(:description)
|
||||
= f.hidden_field(:description)
|
||||
.form-control.markdown
|
||||
= f.pagedown_editor :description
|
||||
.form-group
|
||||
= f.label(:execution_environment_id)
|
||||
= f.collection_select(:execution_environment_id, @execution_environments, :id, :name, {}, class: 'form-control')
|
||||
@@ -33,7 +32,9 @@
|
||||
ul#files.list-unstyled.panel-group
|
||||
= f.fields_for :files do |files_form|
|
||||
= render('file_form', f: files_form)
|
||||
|
||||
a#add-file.btn.btn-default.btn-sm.pull-right href='#' = t('.add_file')
|
||||
ul#dummies.hidden = f.fields_for(:files, CodeOcean::File.new, child_index: 'index') do |files_form|
|
||||
= render('file_form', f: files_form)
|
||||
.actions = render('shared/submit_button', f: f, object: @exercise)
|
||||
|
||||
.actions = render('shared/submit_button', f: f, object: @exercise)
|
@@ -38,7 +38,7 @@
|
||||
/ #output-col1.col-sm-12
|
||||
#output-col1
|
||||
// todo set to full width if turtle isnt used
|
||||
#prompt.input-group.hidden
|
||||
#prompt.input-group.hidden.col-lg-7.col-md-7.two-column
|
||||
span.input-group-addon data-prompt=t('exercises.editor.input') = t('exercises.editor.input')
|
||||
input#prompt-input.form-control type='text'
|
||||
span.input-group-btn
|
||||
|
@@ -23,10 +23,6 @@
|
||||
<%= f.label :file_id %><br>
|
||||
<%= f.number_field :file_id %>
|
||||
</div>
|
||||
<div class="field">
|
||||
<%= f.label :requested_at %><br>
|
||||
<%= f.datetime_select :requested_at %>
|
||||
</div>
|
||||
<div class="field">
|
||||
<%= f.label :user_type %><br>
|
||||
<%= f.text_field :user_type %>
|
||||
|
@@ -1,4 +1,4 @@
|
||||
json.array!(@request_for_comments) do |request_for_comment|
|
||||
json.extract! request_for_comment, :id, :user_id, :exercise_id, :file_id, :requested_at, :user_type
|
||||
json.extract! request_for_comment, :id, :user_id, :exercise_id, :file_id, :user_type
|
||||
json.url request_for_comment_url(request_for_comment, format: :json)
|
||||
end
|
||||
|
@@ -8,7 +8,7 @@
|
||||
<%= user.displayname %> | <%= @request_for_comment.created_at.localtime %>
|
||||
</p>
|
||||
<h5>
|
||||
<u><%= t('activerecord.attributes.exercise.description') %>:</u> "<%= render_markdown(@request_for_comment.exercise.description) %>"
|
||||
<u><%= t('activerecord.attributes.exercise.description') %>:</u> <%= render_markdown(@request_for_comment.exercise.description) %>
|
||||
</h5>
|
||||
|
||||
<h5>
|
||||
@@ -162,9 +162,10 @@ also, all settings from the rails model needed for the editor configuration in t
|
||||
if (hasCommentsInRow(editor, row)) {
|
||||
var rowComments = getCommentsForRow(editor, row);
|
||||
var comments = _.pluck(rowComments, 'text').join('\n');
|
||||
commentModal.find('#other-comments').text(comments);
|
||||
commentModal.find('#otherComments').show();
|
||||
commentModal.find('#otherCommentsTextfield').text(comments);
|
||||
} else {
|
||||
commentModal.find('#other-comments').text('none');
|
||||
commentModal.find('#otherComments').hide();
|
||||
}
|
||||
|
||||
commentModal.find('#addCommentButton').off('click');
|
||||
|
@@ -1 +1 @@
|
||||
json.extract! @request_for_comment, :id, :user_id, :exercise_id, :file_id, :requested_at, :created_at, :updated_at, :user_type, :solved
|
||||
json.extract! @request_for_comment, :id, :user_id, :exercise_id, :file_id, :created_at, :updated_at, :user_type, :solved
|
||||
|
Reference in New Issue
Block a user