diff --git a/.gitignore b/.gitignore
index d39c25e5..4a862a70 100644
--- a/.gitignore
+++ b/.gitignore
@@ -5,6 +5,7 @@
/config/sendmail.yml
/config/smtp.yml
/config/*.production.yml
+/config/*.staging.yml
/coverage
/log
/public/assets
diff --git a/.travis.yml b/.travis.yml
index 623b56dc..7a64549e 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -15,7 +15,14 @@ before_script:
cache: bundler
language: ruby
rvm:
+
+## - 2.1.5
+## - 2.2.1
+# - 2.3.1
+#script: bundle exec rspec --color --format documentation --require spec_helper --require rails_helper --tag ~docker
+
- 2.1.5
- 2.2.1
- 2.3.1
-script: bundle exec rspec --tag ~docker
+script: bundle exec rspec --require spec_helper --require rails_helper --tag ~docker
+
diff --git a/Gemfile b/Gemfile
index acc4c133..0f49702d 100644
--- a/Gemfile
+++ b/Gemfile
@@ -5,8 +5,8 @@ gem 'bcrypt', '~> 3.1.7'
gem 'bootstrap-will_paginate'
gem 'carrierwave'
gem 'coffee-rails', '~> 4.0.0'
-gem 'concurrent-ruby', '~> 1.0.0'
-gem 'concurrent-ruby-ext', '~> 1.0.0', platform: :ruby
+gem 'concurrent-ruby', '~> 1.0.1'
+gem 'concurrent-ruby-ext', '~> 1.0.1', platform: :ruby
gem 'docker-api','~> 1.25.0', require: 'docker'
gem 'factory_girl_rails', '~> 4.0'
gem 'forgery'
@@ -28,6 +28,8 @@ gem 'rubytree'
gem 'sass-rails', '~> 4.0.3'
gem 'sdoc', '~> 0.4.0', group: :doc
gem 'slim'
+gem 'bootstrap_pagedown'
+gem 'pagedown-rails', '~> 1.1.4'
gem 'sorcery'
gem 'thread_safe'
gem 'turbolinks'
diff --git a/Gemfile.lock b/Gemfile.lock
index 3a7e03d2..198d157a 100644
--- a/Gemfile.lock
+++ b/Gemfile.lock
@@ -48,6 +48,8 @@ GEM
debug_inspector (>= 0.0.1)
bootstrap-will_paginate (0.0.10)
will_paginate
+ bootstrap_pagedown (1.1.0)
+ rails (>= 3.2)
builder (3.2.2)
byebug (8.2.2)
capistrano (3.3.5)
@@ -94,10 +96,9 @@ GEM
coffee-script-source
execjs
coffee-script-source (1.10.0)
- concurrent-ruby (1.0.0)
- concurrent-ruby (1.0.0-java)
- concurrent-ruby-ext (1.0.0)
- concurrent-ruby (~> 1.0.0)
+ concurrent-ruby (1.0.2)
+ concurrent-ruby-ext (1.0.2)
+ concurrent-ruby (~> 1.0.2)
d3-rails (3.5.11)
railties (>= 3.1)
database_cleaner (1.5.1)
@@ -175,6 +176,8 @@ GEM
multi_json (~> 1.3)
multi_xml (~> 0.5)
rack (>= 1.2, < 3)
+ pagedown-rails (1.1.4)
+ railties (> 3.1)
parser (2.3.0.6)
ast (~> 2.2)
pg (0.18.4)
@@ -358,6 +361,7 @@ DEPENDENCIES
better_errors
binding_of_caller
bootstrap-will_paginate
+ bootstrap_pagedown
byebug
capistrano (~> 3.3.0)
capistrano-rails
@@ -368,8 +372,8 @@ DEPENDENCIES
carrierwave
codeclimate-test-reporter
coffee-rails (~> 4.0.0)
- concurrent-ruby (~> 1.0.0)
- concurrent-ruby-ext (~> 1.0.0)
+ concurrent-ruby (~> 1.0.1)
+ concurrent-ruby-ext (~> 1.0.1)
d3-rails
database_cleaner
docker-api (~> 1.25.0)
@@ -385,6 +389,7 @@ DEPENDENCIES
newrelic_rpm
nokogiri
nyan-cat-formatter
+ pagedown-rails (~> 1.1.4)
pg
pry-byebug
puma (~> 2.15.3)
diff --git a/app/assets/.DS_Store b/app/assets/.DS_Store
deleted file mode 100644
index 29fe5375..00000000
Binary files a/app/assets/.DS_Store and /dev/null differ
diff --git a/app/assets/javascripts/.DS_Store b/app/assets/javascripts/.DS_Store
deleted file mode 100644
index 5008ddfc..00000000
Binary files a/app/assets/javascripts/.DS_Store and /dev/null differ
diff --git a/app/assets/javascripts/application.js b/app/assets/javascripts/application.js
index a78c7a67..ebdcc08d 100644
--- a/app/assets/javascripts/application.js
+++ b/app/assets/javascripts/application.js
@@ -21,3 +21,7 @@
//= require turbolinks
//= require_tree ../../../lib
//= require_tree .
+//= require bootstrap_pagedown
+//= require markdown.converter
+//= require markdown.sanitizer
+//= require markdown.editor
\ No newline at end of file
diff --git a/app/assets/javascripts/editor.js.erb b/app/assets/javascripts/editor.js.erb
index 2d73bbf0..b2f69fcf 100644
--- a/app/assets/javascripts/editor.js.erb
+++ b/app/assets/javascripts/editor.js.erb
@@ -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;
diff --git a/app/assets/javascripts/editor_edit.js b/app/assets/javascripts/editor_edit.js
new file mode 100644
index 00000000..b1251cf9
--- /dev/null
+++ b/app/assets/javascripts/editor_edit.js
@@ -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();
+ }
+});
+
diff --git a/app/assets/javascripts/exercises.js b/app/assets/javascripts/exercises.js
index 81da8cf8..9d85b4f1 100644
--- a/app/assets/javascripts/exercises.js
+++ b/app/assets/javascripts/exercises.js
@@ -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();
diff --git a/app/assets/javascripts/markdown_ace_editor.js b/app/assets/javascripts/markdown_ace_editor.js
new file mode 100644
index 00000000..42e566fe
--- /dev/null
+++ b/app/assets/javascripts/markdown_ace_editor.js
@@ -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());
+ };
+})();
\ No newline at end of file
diff --git a/app/assets/javascripts/markdown_editor.js b/app/assets/javascripts/markdown_editor.js
deleted file mode 100644
index 91292da1..00000000
--- a/app/assets/javascripts/markdown_editor.js
+++ /dev/null
@@ -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());
- };
-})();
diff --git a/app/assets/javascripts/pagedown.js b/app/assets/javascripts/pagedown.js
new file mode 100644
index 00000000..b48c2ae6
--- /dev/null
+++ b/app/assets/javascripts/pagedown.js
@@ -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();
+ };
+})();
\ No newline at end of file
diff --git a/app/assets/stylesheets/application.css b/app/assets/stylesheets/application.css
index 622f35c1..47163008 100644
--- a/app/assets/stylesheets/application.css
+++ b/app/assets/stylesheets/application.css
@@ -14,4 +14,6 @@
*= require_tree ../../../lib
*= require_tree ../../../vendor/assets/stylesheets/
*= require_self
- */
+ *= require bootstrap_pagedown
+ *= require markdown
+*/
diff --git a/app/controllers/exercises_controller.rb b/app/controllers/exercises_controller.rb
index 45fd04d9..75451eb3 100644
--- a/app/controllers/exercises_controller.rb
+++ b/app/controllers/exercises_controller.rb
@@ -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
diff --git a/app/helpers/application_helper.rb b/app/helpers/application_helper.rb
index f11da84b..844a87c4 100644
--- a/app/helpers/application_helper.rb
+++ b/app/helpers/application_helper.rb
@@ -1,5 +1,5 @@
module ApplicationHelper
- APPLICATION_NAME = 'Code Ocean'
+ APPLICATION_NAME = 'CodeOcean'
def application_name
APPLICATION_NAME
diff --git a/app/models/request_for_comment.rb b/app/models/request_for_comment.rb
index 63d932fc..4be7d325 100644
--- a/app/models/request_for_comment.rb
+++ b/app/models/request_for_comment.rb
@@ -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
diff --git a/app/views/application/_navigation.html.slim b/app/views/application/_navigation.html.slim
index 8aa289d3..b9663d3f 100644
--- a/app/views/application/_navigation.html.slim
+++ b/app/views/application/_navigation.html.slim
@@ -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"))
diff --git a/app/views/exercises/_code_field.html.slim b/app/views/exercises/_code_field.html.slim
index 3ad55f16..5273a693 100644
--- a/app/views/exercises/_code_field.html.slim
+++ b/app/views/exercises/_code_field.html.slim
@@ -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)
diff --git a/app/views/exercises/_comment_dialogcontent.html.slim b/app/views/exercises/_comment_dialogcontent.html.slim
index b14c988f..0d89bea3 100644
--- a/app/views/exercises/_comment_dialogcontent.html.slim
+++ b/app/views/exercises/_comment_dialogcontent.html.slim
@@ -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')
\ No newline at end of file
diff --git a/app/views/exercises/_editor_edit.html.slim b/app/views/exercises/_editor_edit.html.slim
new file mode 100644
index 00000000..810653d2
--- /dev/null
+++ b/app/views/exercises/_editor_edit.html.slim
@@ -0,0 +1,5 @@
+#editor-edit.panel-group.row data-exercise-id=@exercise.id
+ #frames
+ .frame
+ .editor-content.hidden
+ .editor
\ No newline at end of file
diff --git a/app/views/exercises/_file_form.html.slim b/app/views/exercises/_file_form.html.slim
index 7bb4cd27..796c6722 100644
--- a/app/views/exercises/_file_form.html.slim
+++ b/app/views/exercises/_file_form.html.slim
@@ -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 }
\ No newline at end of file
diff --git a/app/views/exercises/_form.html.slim b/app/views/exercises/_form.html.slim
index 968540af..a640f0c2 100644
--- a/app/views/exercises/_form.html.slim
+++ b/app/views/exercises/_form.html.slim
@@ -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)
\ No newline at end of file
diff --git a/app/views/exercises/implement.html.slim b/app/views/exercises/implement.html.slim
index 0c5e109b..1feab189 100644
--- a/app/views/exercises/implement.html.slim
+++ b/app/views/exercises/implement.html.slim
@@ -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
diff --git a/app/views/request_for_comments/_form.html.erb b/app/views/request_for_comments/_form.html.erb
index 4d4494ce..81f6ed65 100644
--- a/app/views/request_for_comments/_form.html.erb
+++ b/app/views/request_for_comments/_form.html.erb
@@ -23,10 +23,6 @@
<%= f.label :file_id %>
<%= f.number_field :file_id %>
-