Update Bootstrap to v4.1, fix chosen.js and pagedown on multiple sites

This commit is contained in:
Sebastian Serth
2018-10-07 23:55:11 +02:00
parent 4d1cf972e4
commit 7bdb962616
99 changed files with 2758 additions and 472 deletions

View File

@ -8,20 +8,18 @@ window.CodeOcean = {
} }
}; };
$(function() { var ANIMATION_DURATION = 500;
var ANIMATION_DURATION = 500;
$.isController = function(name) { $.isController = function(name) {
return $('.container[data-controller="' + name + '"]').isPresent(); return $('.container[data-controller="' + name + '"]').isPresent();
}; };
$.fn.isPresent = function() { $.fn.isPresent = function() {
return this.length > 0; return this.length > 0;
}; };
$.fn.scrollTo = function(selector) { $.fn.scrollTo = function(selector) {
$(this).animate({ $(this).animate({
scrollTop: $(selector).offset().top - $(this).offset().top + $(this).scrollTop() scrollTop: $(selector).offset().top - $(this).offset().top + $(this).scrollTop()
}, ANIMATION_DURATION); }, ANIMATION_DURATION);
}; };
});

View File

@ -14,7 +14,7 @@ $(document).on('turbolinks:load', function() {
var menu = $(this).parent().find("ul"); var menu = $(this).parent().find("ul");
var menupos = menu.offset(); var menupos = menu.offset();
var newPos; var newPos = 0.0;
if ((menupos.left + menu.width()) + 30 > $(window).width()) { if ((menupos.left + menu.width()) + 30 > $(window).width()) {
newPos = -menu.width(); newPos = -menu.width();
} else { } else {

View File

@ -33,6 +33,7 @@ var CodeOceanEditor = {
<% @config ||= CodeOcean::Config.new(:code_ocean).read(erb: false) %> <% @config ||= CodeOcean::Config.new(:code_ocean).read(erb: false) %>
sendEvents: <%= @config['codeocean_events'] ? @config['codeocean_events']['enabled'] : false %>, sendEvents: <%= @config['codeocean_events'] ? @config['codeocean_events']['enabled'] : false %>,
eventURL: "<%= @config['codeocean_events'] ? events_path : '' %>", eventURL: "<%= @config['codeocean_events'] ? events_path : '' %>",
fileTypeURL: "<%= file_types_path %>",
configureEditors: function () { configureEditors: function () {
@ -43,7 +44,7 @@ configureEditors: function () {
confirmDestroy: function (event) { confirmDestroy: function (event) {
event.preventDefault(); event.preventDefault();
if (confirm($(this).data('message-confirm'))) { if (confirm($(event.target).data('message-confirm'))) {
this.destroyFile(); this.destroyFile();
} }
}, },
@ -63,7 +64,7 @@ configureEditors: function () {
if ($('#output-' + index).isPresent()) { if ($('#output-' + index).isPresent()) {
return $('#output-' + index); return $('#output-' + index);
} else { } else {
var element = $('<pre>').attr('id', 'output-' + index); var element = $('<pre class="mt-2">').attr('id', 'output-' + index);
$('#output').append(element); $('#output').append(element);
return element; return element;
} }
@ -79,13 +80,13 @@ configureEditors: function () {
} }
}, },
getPanelClass: function (result) { getCardClass: function (result) {
if (result.stderr && !result.score) { if (result.stderr && !result.score) {
return 'panel-danger'; return 'card bg-danger text-white';
} else if (result.score < 1) { } else if (result.score < 1) {
return 'panel-warning'; return 'card bg-warning';
} else { } else {
return 'panel-success'; return 'card bg-success text-white';
} }
}, },
@ -107,11 +108,22 @@ configureEditors: function () {
progress_bar.css('width', percentage + '%'); progress_bar.css('width', percentage + '%');
}, },
// The event ready.jstree is fired too early and thus doesn't work.
selectFileInJsTree: function(filetree, file_id) {
if (!filetree.hasClass('jstree-loading')) {
filetree.jstree("deselect_all");
filetree.jstree().select_node(file_id);
} else {
setTimeout(this.selectFileInJsTree.bind(null, filetree, file_id), 250);
}
},
showFirstFile: function() { showFirstFile: function() {
var frame = $('.frame[data-role="main_file"]').isPresent() ? $('.frame[data-role="main_file"]') : $('.frame').first(); var frame = $('.frame[data-role="main_file"]').isPresent() ? $('.frame[data-role="main_file"]') : $('.frame').first();
var file_id = frame.find('.editor').data('file-id'); var file_id = frame.find('.editor').data('file-id');
this.setActiveFile(frame.data('filename'), file_id); this.setActiveFile(frame.data('filename'), file_id);
$('#files').jstree().select_node(file_id); var filetree = $('#files');
this.selectFileInJsTree(filetree, file_id);
this.showFrame(frame); this.showFrame(frame);
this.toggleButtonStates(); this.toggleButtonStates();
}, },
@ -124,11 +136,11 @@ configureEditors: function () {
getProgressBarClass: function (percentage) { getProgressBarClass: function (percentage) {
if (percentage < this.ADEQUATE_PERCENTAGE) { if (percentage < this.ADEQUATE_PERCENTAGE) {
return 'progress-bar progress-bar-striped progress-bar-danger'; return 'progress-bar progress-bar-striped bg-danger';
} else if (percentage < this.SUCCESSFULL_PERCENTAGE) { } else if (percentage < this.SUCCESSFULL_PERCENTAGE) {
return 'progress-bar progress-bar-striped progress-bar-warning'; return 'progress-bar progress-bar-striped bg-warning';
} else { } else {
return 'progress-bar progress-bar-striped progress-bar-success'; return 'progress-bar progress-bar-striped bg-success';
} }
}, },
@ -158,7 +170,7 @@ configureEditors: function () {
category: 'editor_paste', category: 'editor_paste',
data: pasteObject.text, data: pasteObject.text,
exercise_id: $('#editor').data('exercise-id'), exercise_id: $('#editor').data('exercise-id'),
file_id: $(this).data('file-id') file_id: $(event.target).data('file-id')
}); });
} }
}, },
@ -266,6 +278,21 @@ configureEditors: function () {
this.initializeRequestForComments() this.initializeRequestForComments()
}, },
updateEditorModeToFileTypeID: function (editor, fileTypeID) {
var newMode = 'ace/mode/text'
$.ajax(this.fileTypeURL + '/' + fileTypeID, {
dataType: 'json'
}).done(function (data) {
if (data['editor_mode'] !== null) {
newMode = data['editor_mode'];
}
}).fail(_.noop)
.always(function () {
ace.edit(editor).session.setMode(newMode);
});
},
initializeFileTree: function () { initializeFileTree: function () {
$('#files').jstree($('#files').data('entries')); $('#files').jstree($('#files').data('entries'));
$('#files').on('click', 'li.jstree-leaf', function (event) { $('#files').on('click', 'li.jstree-leaf', function (event) {
@ -296,8 +323,8 @@ configureEditors: function () {
handleSideBarToggle: function() { handleSideBarToggle: function() {
$('#sidebar').toggleClass('sidebar-col').toggleClass('sidebar-col-collapsed'); $('#sidebar').toggleClass('sidebar-col').toggleClass('sidebar-col-collapsed');
$('#sidebar-collapsed').toggleClass('hidden'); $('#sidebar-collapsed').toggleClass('d-none');
$('#sidebar-uncollapsed').toggleClass('hidden'); $('#sidebar-uncollapsed').toggleClass('d-none');
}, },
initializeRegexes: function () { initializeRegexes: function () {
@ -369,17 +396,17 @@ configureEditors: function () {
return Modernizr.websockets; return Modernizr.websockets;
}, },
populatePanel: function (panel, result, index) { populateCard: function (card, result, index) {
panel.removeClass('panel-default').addClass(this.getPanelClass(result)); card.addClass(this.getCardClass(result));
panel.find('.panel-title .filename').text(result.filename); card.find('.card-title .filename').text(result.filename);
panel.find('.panel-title .number').text(index + 1); card.find('.card-title .number').text(index + 1);
panel.find('.row .col-sm-9').eq(0).find('.number').eq(0).text(result.passed); card.find('.row .col-sm-9').eq(0).find('.number').eq(0).text(result.passed);
panel.find('.row .col-sm-9').eq(0).find('.number').eq(1).text(result.count); card.find('.row .col-sm-9').eq(0).find('.number').eq(1).text(result.count);
panel.find('.row .col-sm-9').eq(1).find('.number').eq(0).text(parseFloat((result.score * result.weight).toFixed(2))); card.find('.row .col-sm-9').eq(1).find('.number').eq(0).text(parseFloat((result.score * result.weight).toFixed(2)));
panel.find('.row .col-sm-9').eq(1).find('.number').eq(1).text(result.weight); card.find('.row .col-sm-9').eq(1).find('.number').eq(1).text(result.weight);
panel.find('.row .col-sm-9').eq(2).html(result.message); card.find('.row .col-sm-9').eq(2).html(result.message);
if (result.error_messages) panel.find('.row .col-sm-9').eq(3).text(result.error_messages.join(' ')); if (result.error_messages) card.find('.row .col-sm-9').eq(3).text(result.error_messages.join(' '));
//panel.find('.row .col-sm-9').eq(4).find('a').attr('href', '#output-' + index); //card.find('.row .col-sm-9').eq(4).find('a').attr('href', '#output-' + index);
}, },
publishCodeOceanEvent: function (payload) { publishCodeOceanEvent: function (payload) {
@ -556,14 +583,14 @@ configureEditors: function () {
}, },
showOutputBar: function() { showOutputBar: function() {
$('#output_sidebar_collapsed').addClass('hidden'); $('#output_sidebar_collapsed').addClass('d-none');
$('#output_sidebar_uncollapsed').removeClass('hidden'); $('#output_sidebar_uncollapsed').removeClass('d-none');
$('#output_sidebar').removeClass('output-col-collapsed').addClass('output-col'); $('#output_sidebar').removeClass('output-col-collapsed').addClass('output-col');
}, },
hideOutputBar: function() { hideOutputBar: function() {
$('#output_sidebar_collapsed').removeClass('hidden'); $('#output_sidebar_collapsed').removeClass('d-none');
$('#output_sidebar_uncollapsed').addClass('hidden'); $('#output_sidebar_uncollapsed').addClass('d-none');
$('#output_sidebar').removeClass('output-col').addClass('output-col-collapsed'); $('#output_sidebar').removeClass('output-col').addClass('output-col-collapsed');
}, },
@ -572,12 +599,12 @@ configureEditors: function () {
}, },
initializeDescriptionToggle: function() { initializeDescriptionToggle: function() {
$('#exercise-headline').on('click', this.toggleDescriptionPanel.bind(this)); $('#exercise-headline').on('click', this.toggleDescriptionCard.bind(this));
$('a#toggle').on('click', this.toggleDescriptionPanel.bind(this)); $('a#toggle').on('click', this.toggleDescriptionCard.bind(this));
}, },
toggleDescriptionPanel: function() { toggleDescriptionCard: function() {
$('#description-panel').toggleClass('description-panel-collapsed').toggleClass('description-panel'); $('#description-card').toggleClass('description-card-collapsed').toggleClass('description-card');
$('#description-symbol').toggleClass('fa-chevron-down').toggleClass('fa-chevron-right'); $('#description-symbol').toggleClass('fa-chevron-down').toggleClass('fa-chevron-right');
var toggle = $('a#toggle'); var toggle = $('a#toggle');
toggle.text(toggle.text() == toggle.data('hide') ? toggle.data('show') : toggle.data('hide')); toggle.text(toggle.text() == toggle.data('hide') ? toggle.data('show') : toggle.data('hide'));

View File

@ -9,7 +9,7 @@ CodeOceanEditorEvaluation = {
this.clearScoringOutput(); this.clearScoringOutput();
this.createSubmission('#assess', null, function (response) { this.createSubmission('#assess', null, function (response) {
this.showSpinner($('#assess')); this.showSpinner($('#assess'));
$('#score_div').removeClass('hidden'); $('#score_div').removeClass('d-none');
var url = response.score_url; var url = response.score_url;
this.initializeSocketForScoring(url); this.initializeSocketForScoring(url);
}.bind(this)); }.bind(this));
@ -26,9 +26,9 @@ CodeOceanEditorEvaluation = {
printScoringResult: function (result, index) { printScoringResult: function (result, index) {
$('#results').show(); $('#results').show();
var panel = $('#dummies').children().first().clone(); var card = $('#dummies').children().first().clone();
this.populatePanel(panel, result, index); this.populateCard(card, result, index);
$('#results ul').first().append(panel); $('#results ul').first().append(card);
}, },
printScoringResults: function (response) { printScoringResults: function (response) {
@ -60,7 +60,7 @@ CodeOceanEditorEvaluation = {
renderHint: function (object) { renderHint: function (object) {
var hint = object.data || object.hint; var hint = object.data || object.hint;
if (hint) { if (hint) {
$('#hint .panel-body').text(hint); $('#hint .card-body').text(hint);
$('#hint').fadeIn(); $('#hint').fadeIn();
} }
}, },

View File

@ -1,12 +1,12 @@
CodeOceanEditorFlowr = { CodeOceanEditorFlowr = {
isFlowrEnabled: true, isFlowrEnabled: true,
flowrResultHtml: '<div class="panel panel-default"><div id="{{headingId}}" role="tab" class="panel-heading"><h4 class="panel-title"><a data-toggle="collapse" data-parent="#flowrHint" href="#{{collapseId}}" aria-expanded="true" aria-controls="{{collapseId}}"></a></h4></div><div id="{{collapseId}}" role="tabpanel" aria-labelledby="{{headingId}}" class="panel-collapse collapse"><div class="panel-body"></div></div></div>', flowrResultHtml: '<div class="panel panel-default"><div id="{{headingId}}" role="tab" class="panel-heading"><h4 class="panel-title"><a data-toggle="collapse" data-parent="#flowrHint" href="#{{collapseId}}" aria-expanded="true" aria-controls="{{collapseId}}"></a></h4></div><div id="{{collapseId}}" role="tabpanel" aria-labelledby="{{headingId}}" class="panel-collapse collapse"><div class="card-body"></div></div></div>',
handleStderrOutputForFlowr: function () { handleStderrOutputForFlowr: function () {
if (!this.isFlowrEnabled) return; if (!this.isFlowrEnabled) return;
var flowrUrl = $('#flowrHint').data('url'); var flowrUrl = $('#flowrHint').data('url');
var flowrHintBody = $('#flowrHint .panel-body'); var flowrHintBody = $('#flowrHint .card-body');
var queryParameters = { var queryParameters = {
query: this.flowrOutputBuffer query: this.flowrOutputBuffer
}; };
@ -19,8 +19,8 @@ CodeOceanEditorFlowr = {
var resultTile = $(collapsibleTileHtml); var resultTile = $(collapsibleTileHtml);
resultTile.find('h4 > a').text(question.title + ' | Found via ' + question.source); resultTile.find('h4 > a').text(question.title + ' | Found via ' + question.source);
resultTile.find('.panel-body').html(question.body); resultTile.find('.card-body').html(question.body);
resultTile.find('.panel-body').append('<a href="' + question.url + '" class="btn btn-primary btn-block">Open this question</a>'); resultTile.find('.card-body').append('<a href="' + question.url + '" class="btn btn-primary btn-block">Open this question</a>');
flowrHintBody.append(resultTile); flowrHintBody.append(resultTile);
}); });

View File

@ -2,19 +2,19 @@ CodeOceanEditorPrompt = {
prompt: '#prompt', prompt: '#prompt',
showPrompt: function(msg) { showPrompt: function(msg) {
var label = $('#prompt .input-group-addon'); var label = $('#prompt .input-group-text');
var prompt = $(this.prompt); var prompt = $(this.prompt);
label.text(msg.data || label.data('prompt')); label.text(msg.data || label.data('prompt'));
if (prompt.isPresent() && prompt.hasClass('hidden')) { if (prompt.isPresent() && prompt.hasClass('d-none')) {
prompt.removeClass('hidden'); prompt.removeClass('d-none');
} }
$('#prompt input').focus(); $('#prompt input').focus();
}, },
hidePrompt: function() { hidePrompt: function() {
var prompt = $(this.prompt); var prompt = $(this.prompt);
if (prompt.isPresent() && !prompt.hasClass('hidden')) { if (prompt.isPresent() && !prompt.hasClass('d-none')) {
prompt.addClass('hidden'); prompt.addClass('d-none');
} }
}, },

View File

@ -155,7 +155,7 @@ CodeOceanEditorSubmissions = {
$('#stop').data('url', submission.stop_url); $('#stop').data('url', submission.stop_url);
this.running = true; this.running = true;
this.showSpinner($('#run')); this.showSpinner($('#run'));
$('#score_div').addClass('hidden'); $('#score_div').addClass('d-none');
this.toggleButtonStates(); this.toggleButtonStates();
var url = submission.run_url.replace(this.FILENAME_URL_PLACEHOLDER, this.active_file.filename.replace(/#$/,'')); // remove # if it is the last character, this is not part of the filename and just an anchor var url = submission.run_url.replace(this.FILENAME_URL_PLACEHOLDER, this.active_file.filename.replace(/#$/,'')); // remove # if it is the last character, this is not part of the filename and just an anchor
this.initializeSocketForRunning(url); this.initializeSocketForRunning(url);
@ -175,7 +175,7 @@ CodeOceanEditorSubmissions = {
if ($('#test').is(':visible')) { if ($('#test').is(':visible')) {
this.createSubmission('#test', null, function(response) { this.createSubmission('#test', null, function(response) {
this.showSpinner($('#test')); this.showSpinner($('#test'));
$('#score_div').addClass('hidden'); $('#score_div').addClass('d-none');
var url = response.test_url.replace(this.FILENAME_URL_PLACEHOLDER, this.active_file.filename.replace(/#$/,'')); // remove # if it is the last character, this is not part of the filename and just an anchor var url = response.test_url.replace(this.FILENAME_URL_PLACEHOLDER, this.active_file.filename.replace(/#$/,'')); // remove # if it is the last character, this is not part of the filename and just an anchor
this.initializeSocketForTesting(url); this.initializeSocketForTesting(url);
}.bind(this)); }.bind(this));

View File

@ -38,10 +38,10 @@ CodeOceanEditorTurtle = {
showCanvas: function () { showCanvas: function () {
if ($('#turtlediv').isPresent() if ($('#turtlediv').isPresent()
&& this.turtlecanvas.hasClass('hidden')) { && this.turtlecanvas.hasClass('d-none')) {
// initialize two-column layout // initialize two-column layout
$('#output-col1').addClass('col-lg-7 col-md-7 two-column'); $('#output-col1').addClass('col-lg-7 col-md-7 two-column');
this.turtlecanvas.removeClass('hidden'); this.turtlecanvas.removeClass('d-none');
} }
} }

View File

@ -66,10 +66,9 @@ $(document).on('turbolinks:load', function() {
$('#files').append(html); $('#files').append(html);
$('#files li:last select[name*="file_type_id"]').val(getSelectedExecutionEnvironment().file_type_id); $('#files li:last select[name*="file_type_id"]').val(getSelectedExecutionEnvironment().file_type_id);
$('#files li:last select').chosen(window.CodeOcean.CHOSEN_OPTIONS); $('#files li:last select').chosen(window.CodeOcean.CHOSEN_OPTIONS);
$('#files li:last select').remove();
$('#files li:last>div:last').removeClass('in').addClass('show')
$('body, html').scrollTo('#add-file'); $('body, html').scrollTo('#add-file');
// if we collapse the file forms by default, we need to click on the new element in order to open it.
// however, this crashes for more files (if we add several ones by clicking the add button more often), since the elements are probably not correctly added to the files list.
//$('#files li:last>div:first>a>div').click();
// initialize the ace editor for the new textarea. // initialize the ace editor for the new textarea.
// pass the correct index and the last ace editor under the node files. this is the last one, since we just added it. // pass the correct index and the last ace editor under the node files. this is the last one, since we just added it.
@ -93,7 +92,7 @@ $(document).on('turbolinks:load', function() {
var deleteFile = function(event) { var deleteFile = function(event) {
event.preventDefault(); event.preventDefault();
var fileUrl = $(this).data('file-url'); var fileUrl = $(event.target).data('file-url');
if (confirm('<%= I18n.t('shared.confirm_destroy') %>')) { if (confirm('<%= I18n.t('shared.confirm_destroy') %>')) {
var jqxhr = $.ajax({ var jqxhr = $.ajax({
@ -139,9 +138,9 @@ $(document).on('turbolinks:load', function() {
var enableBatchUpdate = function() { var enableBatchUpdate = function() {
$('thead .batch a').on('click', function(event) { $('thead .batch a').on('click', function(event) {
event.preventDefault(); event.preventDefault();
if (!$(this).data('toggled')) { if (!$(event.target).data('toggled')) {
$(this).data('toggled', true); $(event.target).data('toggled', true);
$(this).text($(this).data('text')); $(event.target).text($(event.target).data('text'));
buildCheckboxes(); buildCheckboxes();
} else { } else {
performBatchUpdate(); performBatchUpdate();
@ -199,7 +198,7 @@ $(document).on('turbolinks:load', function() {
var observeFileRoleChanges = function() { var observeFileRoleChanges = function() {
$(document).on('change', 'select[name$="[role]"]', function() { $(document).on('change', 'select[name$="[role]"]', function() {
var is_test_file = $(this).val() === 'teacher_defined_test'; var is_test_file = $(this).val() === 'teacher_defined_test';
var parent = $(this).parents('.panel'); var parent = $(this).parents('.card');
var fields = parent.find('.test-related-fields'); var fields = parent.find('.test-related-fields');
if (is_test_file) { if (is_test_file) {
fields.slideDown(); fields.slideDown();
@ -262,17 +261,13 @@ $(document).on('turbolinks:load', function() {
jqxhr.fail(ajaxError); jqxhr.fail(ajaxError);
} }
if ($.isController('exercises')) { if ($.isController('exercises') || $.isController('submissions')) {
// ignore tags table since it is in the dom before other tables // ignore tags table since it is in the dom before other tables
if ($('table:not(#tags-table)').isPresent()) { if ($('table:not(#tags-table)').isPresent()) {
enableBatchUpdate(); enableBatchUpdate();
} else if ($('.edit_exercise, .new_exercise').isPresent()) { } else if ($('.edit_exercise, .new_exercise').isPresent()) {
execution_environments = $('form').data('execution-environments'); execution_environments = $('form').data('execution-environments');
file_types = $('form').data('file-types'); file_types = $('form').data('file-types');
// new MarkdownEditor('#exercise_instructions');
// new MarkdownEditor('#exercise_description')
// todo: add an ace editor for each file
new PagedownEditor('#exercise_description');
enableInlineFileCreation(); enableInlineFileCreation();
inferFileAttributes(); inferFileAttributes();

View File

@ -14,11 +14,11 @@ $(document).on('turbolinks:load', function() {
var alternative_input = parent.find('.alternative-input'); var alternative_input = parent.find('.alternative-input');
if (alternative_input.attr('disabled')) { if (alternative_input.attr('disabled')) {
$(this).text($(this).data('text-toggled')); $(this).text($(event.target).data('text-toggled'));
original_input.attr('disabled', true).hide(); original_input.attr('disabled', true).hide();
alternative_input.attr('disabled', false).show(); alternative_input.attr('disabled', false).show();
} else { } else {
$(this).text($(this).data('text-initial')); $(this).text($(event.target).data('text-initial'));
alternative_input.attr('disabled', true).hide(); alternative_input.attr('disabled', true).hide();
original_input.attr('disabled', false).show(); original_input.attr('disabled', false).show();
} }
@ -26,5 +26,27 @@ $(document).on('turbolinks:load', function() {
}); });
window.CodeOcean.CHOSEN_OPTIONS = CHOSEN_OPTIONS; window.CodeOcean.CHOSEN_OPTIONS = CHOSEN_OPTIONS;
$('select:visible').chosen(CHOSEN_OPTIONS); chosen_inputs = $('select').filter(function(){
return !$(this).parents('ul').is('#dummies');
});
// enable chosen hook when editing an exercise to update ace code highlighting
if ($.isController('exercises') && $('.edit_exercise, .new_exercise').isPresent()) {
chosen_inputs.filter(function(){
return $(this).attr('id').includes('file_type_id');
}).on('change chosen:ready', function(event, parameter) {
// Set ACE editor mode (for code highlighting) on change of file type and after initialization
editorInstance = $(event.target).closest('.card-body').find('.editor')[0];
selectedFileType = event.target.value;
CodeOceanEditor.updateEditorModeToFileTypeID(editorInstance, selectedFileType);
})
}
chosen_inputs.chosen(CHOSEN_OPTIONS);
});
// Remove some elements before going back to an older site. Otherwise, they might not work.
$(document).on('turbolinks:before-cache', function() {
$('.chosen-container').remove();
$('#wmd-button-row-description').remove();
}); });

View File

@ -9,7 +9,7 @@
}); });
editor.setShowPrintMargin(false); editor.setShowPrintMargin(false);
var session = editor.getSession(); var session = editor.getSession();
session.setMode('markdown'); session.setMode('ace/mode/markdown');
session.setUseWrapMode(true); session.setUseWrapMode(true);
session.setValue($(selector).val()); session.setValue($(selector).val());
}; };

View File

@ -1,10 +0,0 @@
(function() {
var ACE_FILES_PATH = '/assets/ace/';
window.PagedownEditor = function(selector) {
var converter = Markdown.getSanitizingConverter();
var editor = new Markdown.Editor( converter );
editor.run();
};
})();

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,43 @@
//= require markdown.converter
// markdown.editor is slightly adjusted to work with Bootstrap 4.
// Taken from https://github.com/hughevans/pagedown-bootstrap-rails, V2.1.4
//= require markdown.editor
//= require markdown.sanitizer
//= require markdown.extra
renderPagedown = function() {
$(".wmd-output").each(function (i) {
const converter = new Markdown.Converter();
const content = $(this).html();
return $(this).html(converter.makeHtml(content));
})
};
createPagedownEditor = function( selector, context ) {
if (context == null) { context = 'body'; }
return $(selector, context).each(function(i, input) {
if ($(input).data('is_rendered')) {
return;
}
const attr = $(input).attr('id').split('wmd-input')[1];
const converter = new Markdown.Converter();
Markdown.Extra.init(converter);
const help = {
handler() {
window.open('http://daringfireball.net/projects/markdown/syntax');
return false;
},
title: "<%= I18n.t('components.markdown_editor.help', default: 'Markdown Editing Help') %>"
};
const editor = new Markdown.Editor(converter, attr, help);
editor.run();
$('[data-toggle="tooltip"]').tooltip();
return $(input).data('is_rendered', true);
});
};
$(document).on('turbolinks:load', function() {
renderPagedown();
return createPagedownEditor('.wmd-input');
});

View File

@ -1,4 +1,4 @@
$(document).ready(function(){ $(document).on('turbolinks:load', function(){
(function vendorTableSorter(){ (function vendorTableSorter(){
/* /*
SortTable SortTable

View File

@ -49,7 +49,7 @@ $(document).on('turbolinks:load', function() {
var refreshData = function (callback) { var refreshData = function (callback) {
var params = new URLSearchParams(window.location.search.slice(1)); var params = new URLSearchParams(window.location.search.slice(1));
var jqxhr = $.ajax(prefix + '-activity-history.json', { var jqxhr = $.ajax('/statistics/graphs/' + prefix + '-activity-history.json', {
dataType: 'json', dataType: 'json',
data: {from: params.get('from'), to: params.get('to'), interval: params.get('interval')}, data: {from: params.get('from'), to: params.get('to'), interval: params.get('interval')},
method: 'GET' method: 'GET'

View File

@ -101,7 +101,7 @@ $(document).on('turbolinks:load', function() {
}); });
} }
manageGraph('user-activity', 'graphs/user-activity', 10); manageGraph('user-activity', '/statistics/graphs/user-activity', 10);
manageGraph('rfc-activity', 'graphs/rfc-activity', 30); manageGraph('rfc-activity', '/statistics/graphs/rfc-activity', 30);
} }
}); });

View File

@ -5,30 +5,40 @@ $(document).on('turbolinks:load', function() {
var currentSubmission = 0; var currentSubmission = 0;
var active_file = undefined; var active_file = undefined;
var fileTrees = [] var fileTrees = [];
var editor = undefined; var editor = undefined;
var fileTypeById = {} var fileTypeById = {};
var showActiveFile = function() { var showActiveFile = function() {
var session = editor.getSession(); var session = editor.getSession();
var fileType = fileTypeById[active_file.file_type_id] var fileType = fileTypeById[active_file.file_type_id];
session.setMode(fileType.editor_mode); session.setMode(fileType.editor_mode);
session.setTabSize(fileType.indent_size); session.setTabSize(fileType.indent_size);
session.setValue(active_file.content); session.setValue(active_file.content);
session.setUseSoftTabs(true); session.setUseSoftTabs(true);
session.setUseWrapMode(true); session.setUseWrapMode(true);
// The event ready.jstree is fired too early and thus doesn't work.
var selectFileInJsTree = function() {
if (!filetree.hasClass('jstree-loading')) {
filetree.jstree("deselect_all");
filetree.jstree().select_node(active_file.file_id);
} else {
setTimeout(selectFileInJsTree, 250);
}
};
filetree = $(fileTrees[currentSubmission]);
selectFileInJsTree();
// Finally change jstree element to prevent flickering
showFileTree(currentSubmission); showFileTree(currentSubmission);
filetree = $(fileTrees[currentSubmission])
filetree.jstree("deselect_all");
filetree.jstree().select_node(active_file.file_id);
}; };
var initializeFileTree = function() { var initializeFileTree = function() {
$('.files').each(function(index, element) { $('.files').each(function(index, element) {
fileTree = $(element).jstree($(element).data('entries')); fileTree = $(element).jstree($(element).data('entries'));
fileTree.on('click', 'li.jstree-leaf', function() { fileTree.on('click', 'li.jstree-leaf', function() {
var id = parseInt($(this).attr('id')) var id = parseInt($(this).attr('id'));
_.each(files[currentSubmission], function(file) { _.each(files[currentSubmission], function(file) {
if (file.file_id === id) { if (file.file_id === id) {
active_file = file; active_file = file;
@ -42,8 +52,8 @@ $(document).on('turbolinks:load', function() {
var showFileTree = function(index) { var showFileTree = function(index) {
$('.files').hide(); $('.files').hide();
$(fileTrees[index].context).show(); $(fileTrees[index]).show();
} };
if ($.isController('exercises') && $('#timeline').isPresent()) { if ($.isController('exercises') && $('#timeline').isPresent()) {
@ -85,7 +95,7 @@ $(document).on('turbolinks:load', function() {
if (file.name === active_file.name) { if (file.name === active_file.name) {
fileIndex = index; fileIndex = index;
} }
}) });
active_file = currentFiles[fileIndex]; active_file = currentFiles[fileIndex];
showActiveFile(); showActiveFile();
}); });
@ -94,10 +104,10 @@ $(document).on('turbolinks:load', function() {
clearInterval(playInterval); clearInterval(playInterval);
playInterval = undefined; playInterval = undefined;
playButton.find('span.fa').removeClass('fa-pause').addClass('fa-play') playButton.find('span.fa').removeClass('fa-pause').addClass('fa-play')
} };
playButton.on('click', function(event) { playButton.on('click', function(event) {
if (playInterval == undefined) { if (playInterval === undefined) {
playInterval = setInterval(function() { playInterval = setInterval(function() {
if ($.isController('exercises') && $('#timeline').isPresent() && slider.val() < submissions.length - 1) { if ($.isController('exercises') && $('#timeline').isPresent() && slider.val() < submissions.length - 1) {
slider.val(parseInt(slider.val()) + 1); slider.val(parseInt(slider.val()) + 1);
@ -112,7 +122,7 @@ $(document).on('turbolinks:load', function() {
} }
}); });
active_file = files[0][0] active_file = files[0][0];
initializeFileTree(); initializeFileTree();
showActiveFile(); showActiveFile();
} }

View File

@ -28,6 +28,7 @@ span.caret {
.progress-bar { .progress-bar {
line-height: initial; line-height: initial;
min-width: 2em; min-width: 2em;
color: white;
} }
} }
@ -35,7 +36,7 @@ span.caret {
margin-top: 0.5em; margin-top: 0.5em;
} }
.badge { .badge-pill {
font-size: 100%; font-size: 100%;
} }

View File

@ -3,10 +3,14 @@
} }
.dropdown-submenu > .dropdown-menu { .dropdown-submenu > .dropdown-menu {
top: 0; top: -0.2em;
left: 100%; left: 100%;
} }
.dropdown-submenu.open > ul.dropdown-menu {
display: block;
}
.dropdown-submenu > a:after { .dropdown-submenu > a:after {
display: block; display: block;
content: " "; content: " ";
@ -25,11 +29,11 @@
border-left-color: #ffffff; border-left-color: #ffffff;
} }
.dropdown-submenu.pull-left { .dropdown-submenu.float-left {
float: none; float: none;
} }
.dropdown-submenu.pull-left > .dropdown-menu { .dropdown-submenu.float-left > .dropdown-menu {
left: -100%; left: -100%;
margin-left: 10px; margin-left: 10px;
-webkit-border-radius: 6px 0 6px 6px; -webkit-border-radius: 6px 0 6px 6px;

View File

@ -144,7 +144,8 @@ button i.fa-spin {
min-height: 1px; min-height: 1px;
padding-left: 15px; padding-left: 15px;
padding-right: 15px; padding-right: 15px;
box-sizing: border-box box-sizing: border-box;
margin-left: auto;
} }
.output-col-collapsed { .output-col-collapsed {
@ -155,7 +156,8 @@ button i.fa-spin {
min-height: 1px; min-height: 1px;
padding-left: 15px; padding-left: 15px;
padding-right: 15px; padding-right: 15px;
box-sizing: border-box box-sizing: border-box;
margin-left: auto;
} }
.enforce-top-margin { .enforce-top-margin {
@ -166,14 +168,14 @@ button i.fa-spin {
margin-right: 10px !important; margin-right: 10px !important;
} }
.description-panel-collapsed { .description-card-collapsed {
-webkit-transition: width 2s; -webkit-transition: width 2s;
transition: width 2s; transition: width 2s;
height: 0px; height: 0px;
visibility: hidden; visibility: hidden;
} }
.description-panel { .description-card {
height: auto; height: auto;
-webkit-transition: height 2s; -webkit-transition: height 2s;
transition: height 2s; transition: height 2s;

View File

@ -57,10 +57,6 @@ rect.value-bar {
} }
} }
.table-responsive#exercise-list {
max-height: 512px;
}
.exercise-id-tooltip { .exercise-id-tooltip {
position: absolute; position: absolute;
display: none; display: none;

View File

@ -35,11 +35,19 @@ input[type='file'] {
margin: 10px 0 10px 0; margin: 10px 0 10px 0;
} }
.lead.description-panel-collapsed { .lead.description-card-collapsed {
margin: 0; margin: 0;
} }
} }
[data-toggle="collapse"] .fa:before {
content: "\f139";
}
[data-toggle="collapse"].collapsed .fa:before {
content: "\f13a";
}
// Graph Settings // Graph Settings
.axis path { .axis path {

View File

@ -127,7 +127,7 @@
background-color:#f9f9f9 background-color:#f9f9f9
} }
.ace_tooltip { :not(.allow_ace_tooltip) > .ace_tooltip {
display: none !important; display: none !important;
} }

View File

@ -76,8 +76,8 @@ tr.highlight {
grid-gap: 10px; grid-gap: 10px;
> a { > a {
color: #fff; color: #fff !important;
text-decoration: none; text-decoration: none !important;
> div { > div {
border: 2px solid #0055ba; border: 2px solid #0055ba;

View File

@ -38,7 +38,7 @@ class FileTypesController < ApplicationController
end end
def set_editor_modes def set_editor_modes
@editor_modes = Dir.glob('vendor/assets/javascripts/ace/mode-*.js').map do |filename| @editor_modes = Dir.glob('vendor/assets/javascripts/ace/mode-*.js').sort.map do |filename|
name = filename.gsub(/\w+\/|mode-|.js$/, '') name = filename.gsub(/\w+\/|mode-|.js$/, '')
[name, "ace/mode/#{name}"] [name, "ace/mode/#{name}"]
end end

View File

@ -0,0 +1,42 @@
# frozen_string_literal: true
class PagedownFormBuilder < ActionView::Helpers::FormBuilder
def pagedown(method, args)
# Adopt simple form builder to work with form_for
@attribute_name = method
@input_html_options = args[:input_html]
@template.capture do
@template.concat wmd_button_bar
@template.concat wmd_textarea
@template.concat wmd_preview if show_wmd_preview?
end
end
private
def wmd_button_bar
@template.content_tag :div, nil, id: "wmd-button-bar-#{base_id}"
end
def wmd_textarea
@template.text_area @object_name, @attribute_name,
**@input_html_options,
class: 'form-control wmd-input',
id: "wmd-input-#{base_id}"
end
def wmd_preview
@template.content_tag :div, nil,
class: 'wmd-preview',
id: "wmd-preview-#{base_id}"
end
def show_wmd_preview?
@input_html_options[:preview].present?
end
def base_id
options[:pagedown_id_suffix] || @attribute_name
end
end

View File

@ -3,6 +3,6 @@ class ErrorTemplate < ApplicationRecord
has_and_belongs_to_many :error_template_attributes has_and_belongs_to_many :error_template_attributes
def to_s def to_s
"#{id} [#{name}]" name
end end
end end

View File

@ -2,6 +2,6 @@ class ErrorTemplateAttribute < ApplicationRecord
has_and_belongs_to_many :error_template has_and_belongs_to_many :error_template
def to_s def to_s
"#{id} [#{key}]" key
end end
end end

View File

@ -3,17 +3,17 @@
- if model = Kernel.const_get(controller_path.classify) rescue nil - if model = Kernel.const_get(controller_path.classify) rescue nil
- object = model.find_by(id: params[:id]) - object = model.find_by(id: params[:id])
- if model.try(:nested_resource?) - if model.try(:nested_resource?)
li = model.model_name.human(count: 2) li.breadcrumb-item = model.model_name.human(count: 2)
- if object - if object
li = object li.breadcrumb-item = object
- else - else
li = link_to(model.model_name.human(count: 2), send(:"#{model.model_name.collection}_path")) li.breadcrumb-item = link_to(model.model_name.human(count: 2), send(:"#{model.model_name.collection}_path"))
- if object - if object
li = link_to(object, send(:"#{model.model_name.singular}_path", object)) li.breadcrumb-item = link_to(object, send(:"#{model.model_name.singular}_path", object))
li.active li.breadcrumb-item.active
- if I18n.translation_present?("shared.#{params[:action]}") - if I18n.translation_present?("shared.#{params[:action]}")
= t("shared.#{params[:action]}") = t("shared.#{params[:action]}")
- else - else
= t("#{controller_name}.index.#{params[:action]}") = t("#{controller_name}.index.#{params[:action]}")
- else - else
li.active = t("breadcrumbs.#{controller_name}.#{params[:action]}") li.breadcrumb-item.active = t("breadcrumbs.#{controller_name}.#{params[:action]}")

View File

@ -1,6 +1,7 @@
#flash-container #flash-container
#flash.container.fixed_error_messages data-message-failure=t('shared.message_failure') #flash.container.fixed_error_messages data-message-failure=t('shared.message_failure')
- %w[alert danger info notice success warning].each do |severity| - %w[alert danger info notice success warning].each do |severity|
div.alert.flash class="alert-#{{'alert' => 'warning', 'notice' => 'success'}.fetch(severity, severity)}" div.alert.flash class="alert-#{{'alert' => 'warning', 'notice' => 'success'}.fetch(severity, severity)} alert-dismissible fade show"
p id="flash-#{severity}" = flash[severity] p.mb-0 id="flash-#{severity}" = flash[severity]
span.fa.fa-times button type="button" class="close" data-dismiss="alert" aria-label="Close"
span.text-white aria-hidden="true" &times;

View File

@ -1,7 +1,7 @@
li.dropdown li.nav-item.dropdown
a.dropdown-toggle data-toggle='dropdown' href='#' a.nav-link.dropdown-toggle.mx-3 data-toggle='dropdown' href='#'
= t("locales.#{I18n.locale}") = t("locales.#{I18n.locale}")
span.caret span.caret
ul.dropdown-menu role='menu' ul.dropdown-menu.p-0.mt-1 role='menu'
- I18n.available_locales.sort_by { |locale| t("locales.#{locale}") }.each do |locale| - I18n.available_locales.sort_by { |locale| t("locales.#{locale}") }.each do |locale|
li = link_to(t("locales.#{locale}"), url_for(params.permit!.merge(locale: locale))) li = link_to(t("locales.#{locale}"), url_for(params.permit!.merge(locale: locale)), class: 'dropdown-item')

View File

@ -1,14 +1,14 @@
- if current_user.try(:internal_user?) - if current_user.try(:internal_user?)
ul.nav.navbar-nav ul.nav.navbar-nav
li.dropdown li.nav-item.dropdown
a.dropdown-toggle data-toggle='dropdown' href='#' a.nav-link.dropdown-toggle.mx-3 data-toggle='dropdown' href='#'
= t('shared.administration') = t('shared.administration')
span.caret span.caret
ul.dropdown-menu role='menu' ul.dropdown-menu.p-0.mt-1 role='menu'
- if current_user.admin? - if current_user.admin?
li = link_to(t('breadcrumbs.dashboard.show'), admin_dashboard_path, 'data-turbolinks' => "false") li = link_to(t('breadcrumbs.dashboard.show'), admin_dashboard_path, class: 'dropdown-item', 'data-turbolinks' => "false")
li = link_to(t('breadcrumbs.statistics.show'), statistics_path) li = link_to(t('breadcrumbs.statistics.show'), statistics_path, class: 'dropdown-item')
li.divider li.dropdown-divider role='separator'
= render('navigation_submenu', title: t('activerecord.models.exercise.other'), = render('navigation_submenu', title: t('activerecord.models.exercise.other'),
models: [Exercise, ExerciseCollection, ProxyExercise, Tag, Submission], link: exercises_path, cached: true) models: [Exercise, ExerciseCollection, ProxyExercise, Tag, Submission], link: exercises_path, cached: true)
= render('navigation_submenu', title: t('navigation.sections.users'), models: [InternalUser, ExternalUser], = render('navigation_submenu', title: t('navigation.sections.users'), models: [InternalUser, ExternalUser],

View File

@ -1,2 +1,2 @@
- if policy(model).index? - if policy(model).index?
li = link_to(model.model_name.human(count: 2), send(:"#{model.model_name.collection}_path")) li = link_to(model.model_name.human(count: 2), send(:"#{model.model_name.collection}_path"), class: 'dropdown-item')

View File

@ -1,6 +1,6 @@
li.dropdown.dropdown-submenu li.dropdown-submenu
- link = link.nil? ? "#" : link - link = link.nil? ? "#" : link
a href=link class="dropdown-toggle" data-toggle="dropdown" = title a.dropdown-item.dropdown-toggle href=link data-toggle="dropdown" = title
ul class="dropdown-menu" ul.dropdown-menu.p-0
- models.each do |model| - models.each do |model|
= render('navigation_collection_link', model: model, cached: true) = render('navigation_collection_link', model: model, cached: true)

View File

@ -1,19 +1,19 @@
- if current_user - if current_user
li.dropdown li.nav-item.dropdown
a.dropdown-toggle data-toggle='dropdown' href='#' a.nav-link.dropdown-toggle data-toggle='dropdown' href='#'
i.fa.fa-user i.fa.fa-user
= current_user = current_user
span.caret span.caret
ul.dropdown-menu role='menu' ul.dropdown-menu.p-0.mt-1 role='menu'
- if current_user.internal_user? - if current_user.internal_user?
li = link_to(t('consumers.show.link'), current_user.consumer) if current_user.consumer li = link_to(t('consumers.show.link'), current_user.consumer, class: 'dropdown-item') if current_user.consumer
li = link_to(t('internal_users.show.link'), current_user) li = link_to(t('internal_users.show.link'), current_user, class: 'dropdown-item')
li = link_to(t('request_for_comments.index.all'), request_for_comments_path) li = link_to(t('request_for_comments.index.all'), request_for_comments_path, class: 'dropdown-item')
li = link_to(t('request_for_comments.index.get_my_rfc_activity'), my_rfc_activity_path) li = link_to(t('request_for_comments.index.get_my_rfc_activity'), my_rfc_activity_path, class: 'dropdown-item')
li = link_to(t('request_for_comments.index.get_my_comment_requests'), my_request_for_comments_path) li = link_to(t('request_for_comments.index.get_my_comment_requests'), my_request_for_comments_path, class: 'dropdown-item')
- if current_user.internal_user? - if current_user.internal_user?
li = link_to(t('sessions.destroy.link'), sign_out_path, method: :delete) li = link_to(t('sessions.destroy.link'), sign_out_path, method: :delete, class: 'dropdown-item')
- else - else
li = link_to(sign_in_path) do li.nav-item = link_to(sign_in_path, class: 'nav-link') do
i.fa.fa-sign-in i.fa.fa-sign-in
= t('sessions.new.link') = t('sessions.new.link')

View File

@ -9,7 +9,7 @@ h1 = CodeHarborLink.model_name.human(count: 2)
tbody tbody
- @code_harbor_links.each do |code_harbor_link| - @code_harbor_links.each do |code_harbor_link|
tr tr
td = code_harbor_link.oauth2token td = link_to(code_harbor_link.oauth2token, code_harbor_link)
td = link_to(t('shared.show'), code_harbor_link) td = link_to(t('shared.show'), code_harbor_link)
td = link_to(t('shared.edit'), edit_code_harbor_link_path(code_harbor_link)) td = link_to(t('shared.edit'), edit_code_harbor_link_path(code_harbor_link))
td = link_to(t('shared.destroy'), code_harbor_link, data: {confirm: t('shared.confirm_destroy')}, method: :delete) td = link_to(t('shared.destroy'), code_harbor_link, data: {confirm: t('shared.confirm_destroy')}, method: :delete)

View File

@ -12,5 +12,5 @@
= f.label(:file_template_id, t('activerecord.attributes.file.file_template_id')) = f.label(:file_template_id, t('activerecord.attributes.file.file_template_id'))
= f.collection_select(:file_template_id, FileTemplate.all.order(:name), :id, :name, {:include_blank => true}, class: 'form-control') = f.collection_select(:file_template_id, FileTemplate.all.order(:name), :id, :name, {:include_blank => true}, class: 'form-control')
= f.hidden_field(:context_id) = f.hidden_field(:context_id)
.hidden#noTemplateLabel data-text=t('file_template.no_template_label') .d-none#noTemplateLabel data-text=t('file_template.no_template_label')
.actions = render('shared/submit_button', f: f, object: CodeOcean::File.new) .actions = render('shared/submit_button', f: f, object: CodeOcean::File.new)

View File

@ -9,7 +9,7 @@ h1 = Consumer.model_name.human(count: 2)
tbody tbody
- @consumers.each do |consumer| - @consumers.each do |consumer|
tr tr
td = consumer.name td = link_to(consumer.name, consumer)
td = link_to(t('shared.show'), consumer) td = link_to(t('shared.show'), consumer)
td = link_to(t('shared.edit'), edit_consumer_path(consumer)) td = link_to(t('shared.edit'), edit_consumer_path(consumer))
td = link_to(t('shared.destroy'), consumer, data: {confirm: t('shared.confirm_destroy')}, method: :delete) td = link_to(t('shared.destroy'), consumer, data: {confirm: t('shared.confirm_destroy')}, method: :delete)

View File

@ -9,8 +9,9 @@
.form-group .form-group
= f.label(:regex) = f.label(:regex)
= f.text_field(:regex, class: 'form-control', required: true) = f.text_field(:regex, class: 'form-control', required: true)
.help-block == t('error_templates.hints.signature') .help-block.form-text == t('error_templates.hints.signature')
.form-group .form-check.form-group
= f.check_box(:important) label.form-check-label
= f.check_box(:important, class: 'form-check-input')
= t('activerecord.attributes.error_template_attribute.important') = t('activerecord.attributes.error_template_attribute.important')
.actions = render('shared/submit_button', f: f, object: @error_template_attribute) .actions = render('shared/submit_button', f: f, object: @error_template_attribute)

View File

@ -17,9 +17,10 @@ h1 = ErrorTemplateAttribute.model_name.human(count: 2)
span class="fa fa-star" aria-hidden="true" span class="fa fa-star" aria-hidden="true"
- else - else
span class="fa fa-star-o" aria-hidden="true" span class="fa fa-star-o" aria-hidden="true"
td = error_template_attribute.key td = link_to(error_template_attribute.key, error_template_attribute)
td = error_template_attribute.description td = error_template_attribute.description
td = error_template_attribute.regex td
code = error_template_attribute.regex
td = link_to(t('shared.show'), error_template_attribute) td = link_to(t('shared.show'), error_template_attribute)
td = link_to(t('shared.edit'), edit_error_template_attribute_path(error_template_attribute)) td = link_to(t('shared.edit'), edit_error_template_attribute_path(error_template_attribute))
td = link_to(t('shared.destroy'), error_template_attribute, data: {confirm: t('shared.confirm_destroy')}, method: :delete) td = link_to(t('shared.destroy'), error_template_attribute, data: {confirm: t('shared.confirm_destroy')}, method: :delete)

View File

@ -2,7 +2,10 @@ h1
= @error_template_attribute = @error_template_attribute
= render('shared/edit_button', object: @error_template_attribute) = render('shared/edit_button', object: @error_template_attribute)
- [:key, :description, :regex, :important].each do |attribute| - [:key, :description].each do |attribute|
= row(label: "error_template_attribute.#{attribute}", value: @error_template_attribute.send(attribute)) = row(label: "error_template_attribute.#{attribute}", value: @error_template_attribute.send(attribute))
= row(label: "error_template_attribute.key") do
code = @error_template_attribute.key
= row(label: "error_template_attribute.important", value: @error_template_attribute.important)
// todo: used by // todo: used by

View File

@ -9,12 +9,12 @@
.form-group .form-group
= f.label(:signature) = f.label(:signature)
= f.text_field(:signature, class: 'form-control') = f.text_field(:signature, class: 'form-control')
.help-block == t('error_templates.hints.signature') .help-block.form-text == t('error_templates.hints.signature')
.form-group .form-group
= f.label(:description) = f.label(:description)
= f.text_field(:description, class: 'form-control') = f.text_field(:description, class: 'form-control')
.form-group .form-group
= f.label(:hint) = f.label(:hint)
= f.text_field(:hint, class: 'form-control') = f.text_field(:hint, class: 'form-control')
.help-block == t('error_templates.hints.hint_templates') .help-block.form-text == t('error_templates.hints.hint_templates')
.actions = render('shared/submit_button', f: f, object: @error_template) .actions = render('shared/submit_button', f: f, object: @error_template)

View File

@ -11,7 +11,7 @@ h1 = ErrorTemplate.model_name.human(count: 2)
tbody tbody
- @error_templates.each do |error_template| - @error_templates.each do |error_template|
tr tr
td = error_template.name td = link_to(error_template.name, error_template)
td = error_template.description td = error_template.description
td = link_to(error_template.execution_environment) td = link_to(error_template.execution_environment)
td = link_to(t('shared.show'), error_template) td = link_to(t('shared.show'), error_template)

View File

@ -4,10 +4,12 @@ h1
= row(label: 'error_template.name', value: @error_template.name) = row(label: 'error_template.name', value: @error_template.name)
= row(label: 'exercise.execution_environment', value: link_to(@error_template.execution_environment)) = row(label: 'exercise.execution_environment', value: link_to(@error_template.execution_environment))
- [:signature, :description, :hint].each do |attribute| = row(label: "error_template.signature") do
code = @error_template.signature
- [:description, :hint].each do |attribute|
= row(label: "error_template.#{attribute}", value: @error_template.send(attribute)) = row(label: "error_template.#{attribute}", value: @error_template.send(attribute))
h3 h2.mt-4
= t 'error_templates.attributes' = t 'error_templates.attributes'
.table-responsive .table-responsive
@ -27,9 +29,10 @@ h3
span class="fa fa-star" aria-hidden="true" span class="fa fa-star" aria-hidden="true"
- else - else
span class="fa fa-star-o" aria-hidden="true" span class="fa fa-star-o" aria-hidden="true"
td = attribute.key td = link_to(attribute.key, attribute)
td = attribute.description td = attribute.description
td = attribute.regex td
code = attribute.regex
td = link_to(t('shared.show'), attribute) td = link_to(t('shared.show'), attribute)
td = link_to(t('shared.destroy'), attribute_error_template_url(:error_template_attribute_id => attribute.id), :method => :delete) td = link_to(t('shared.destroy'), attribute_error_template_url(:error_template_attribute_id => attribute.id), :method => :delete)
@ -37,4 +40,4 @@ h3
= collection_select({}, :error_template_attribute_id, = collection_select({}, :error_template_attribute_id,
ErrorTemplateAttribute.where.not(id: @error_template.error_template_attributes.select(:id).to_a).order('important DESC', :key), ErrorTemplateAttribute.where.not(id: @error_template.error_template_attributes.select(:id).to_a).order('important DESC', :key),
:id, :key, {include_blank: false}, class: '') :id, :key, {include_blank: false}, class: '')
button.btn.btn-default = t('error_templates.add_attribute') button.btn.btn-outline-primary = t('error_templates.add_attribute')

View File

@ -12,17 +12,17 @@
a.toggle-input data={text_initial: t('shared.new'), text_toggled: t('shared.back')} href='#' = t('shared.new') a.toggle-input data={text_initial: t('shared.new'), text_toggled: t('shared.back')} href='#' = t('shared.new')
.original-input = f.select(:docker_image, @docker_images, {}, class: 'form-control') .original-input = f.select(:docker_image, @docker_images, {}, class: 'form-control')
= f.text_field(:docker_image, class: 'alternative-input form-control', disabled: true) = f.text_field(:docker_image, class: 'alternative-input form-control', disabled: true)
.help-block == t('.hints.docker_image') .help-block.form-text == t('.hints.docker_image')
.form-group .form-group
= f.label(:exposed_ports) = f.label(:exposed_ports)
= f.text_field(:exposed_ports, class: 'form-control', placeholder: '3000, 4000') = f.text_field(:exposed_ports, class: 'form-control', placeholder: '3000, 4000')
.help-block == t('.hints.exposed_ports') .help-block.form-text == t('.hints.exposed_ports')
.form-group .form-group
= f.label(:memory_limit) = f.label(:memory_limit)
= f.number_field(:memory_limit, class: 'form-control', min: DockerClient::MINIMUM_MEMORY_LIMIT, value: f.object.memory_limit || DockerClient::DEFAULT_MEMORY_LIMIT) = f.number_field(:memory_limit, class: 'form-control', min: DockerClient::MINIMUM_MEMORY_LIMIT, value: f.object.memory_limit || DockerClient::DEFAULT_MEMORY_LIMIT)
.checkbox .form-check.mb-3
label label.form-check-label
= f.check_box(:network_enabled) = f.check_box(:network_enabled, class: 'form-check-input')
= t('activerecord.attributes.execution_environment.network_enabled') = t('activerecord.attributes.execution_environment.network_enabled')
.form-group .form-group
= f.label(:permitted_execution_time) = f.label(:permitted_execution_time)
@ -33,11 +33,11 @@
.form-group .form-group
= f.label(:run_command) = f.label(:run_command)
= f.text_field(:run_command, class: 'form-control', placeholder: 'command %{filename}', required: true) = f.text_field(:run_command, class: 'form-control', placeholder: 'command %{filename}', required: true)
.help-block == t('.hints.command') .help-block.form-text == t('.hints.command')
.form-group .form-group
= f.label(:test_command) = f.label(:test_command)
= f.text_field(:test_command, class: 'form-control', placeholder: 'command %{filename}') = f.text_field(:test_command, class: 'form-control', placeholder: 'command %{filename}')
.help-block == t('.hints.command') .help-block.form-text == t('.hints.command')
.form-group .form-group
= f.label(:testing_framework) = f.label(:testing_framework)
= f.select(:testing_framework, @testing_framework_adapters, {include_blank: true}, class: 'form-control') = f.select(:testing_framework, @testing_framework_adapters, {include_blank: true}, class: 'form-control')

View File

@ -15,7 +15,7 @@ h1 = ExecutionEnvironment.model_name.human(count: 2)
tbody tbody
- @execution_environments.each do |execution_environment| - @execution_environments.each do |execution_environment|
tr tr
td = execution_environment.name td = link_to(execution_environment.name, execution_environment)
td = link_to(execution_environment.author, execution_environment.author) td = link_to(execution_environment.author, execution_environment.author)
td = execution_environment.pool_size td = execution_environment.pool_size
td = execution_environment.memory_limit td = execution_environment.memory_limit

View File

@ -5,7 +5,10 @@ h1
= row(label: 'execution_environment.name', value: @execution_environment.name) = row(label: 'execution_environment.name', value: @execution_environment.name)
= row(label: 'execution_environment.user', value: link_to(@execution_environment.author, @execution_environment.author)) = row(label: 'execution_environment.user', value: link_to(@execution_environment.author, @execution_environment.author))
= row(label: 'execution_environment.file_type', value: @execution_environment.file_type.present? ? link_to(@execution_environment.file_type, @execution_environment.file_type) : nil) = row(label: 'execution_environment.file_type', value: @execution_environment.file_type.present? ? link_to(@execution_environment.file_type, @execution_environment.file_type) : nil)
- [:docker_image, :exposed_ports, :memory_limit, :network_enabled, :permitted_execution_time, :pool_size, :run_command, :test_command].each do |attribute| - [:docker_image, :exposed_ports, :memory_limit, :network_enabled, :permitted_execution_time, :pool_size].each do |attribute|
= row(label: "execution_environment.#{attribute}", value: @execution_environment.send(attribute)) = row(label: "execution_environment.#{attribute}", value: @execution_environment.send(attribute))
- [:run_command, :test_command].each do |attribute|
= row(label: "execution_environment.#{attribute}") do
code = @execution_environment.send(attribute)
= row(label: 'execution_environment.testing_framework', value: @testing_framework_adapter.try(:framework_name)) = row(label: 'execution_environment.testing_framework', value: @testing_framework_adapter.try(:framework_name))
= row(label: 'execution_environment.help', value: render_markdown(@execution_environment.help)) = row(label: 'execution_environment.help', value: render_markdown(@execution_environment.help))

View File

@ -2,7 +2,8 @@
form#exercise-selection form#exercise-selection
.form-group .form-group
span.label = t('activerecord.attributes.exercise_collections.exercises') span.badge = t('activerecord.attributes.exercise_collections.exercises')
.mb-2
= collection_select({}, :exercise_ids, exercises, :id, :title, {}, {id: 'add-exercise-list', class: 'form-control', multiple: true}) = collection_select({}, :exercise_ids, exercises, :id, :title, {}, {id: 'add-exercise-list', class: 'form-control', multiple: true})
button.btn.btn-primary#add-exercises = t('exercise_collections.form.add_exercises') button.btn.btn-primary#add-exercises = t('exercise_collections.form.add_exercises')

View File

@ -3,9 +3,10 @@
.form-group .form-group
= f.label(t('activerecord.attributes.exercise_collections.name')) = f.label(t('activerecord.attributes.exercise_collections.name'))
= f.text_field(:name, class: 'form-control', required: true) = f.text_field(:name, class: 'form-control', required: true)
.form-group .form-check.form-group
= f.label(t('activerecord.attributes.exercise_collections.use_anomaly_detection')) label.form-check-label
= f.check_box(:use_anomaly_detection, {class: 'form-control'}) = f.check_box(:use_anomaly_detection, class: 'form-check-input')
= t('activerecord.attributes.exercise_collections.use_anomaly_detection')
.form-group .form-group
= f.label(t('activerecord.attributes.exercise_collections.user')) = f.label(t('activerecord.attributes.exercise_collections.user'))
= f.collection_select(:user_id, InternalUser.order(:name), :id, :name, {}, {class: 'form-control'}) = f.collection_select(:user_id, InternalUser.order(:name), :id, :name, {}, {class: 'form-control'})
@ -26,10 +27,10 @@
td = link_to(t('shared.show'), item.exercise, 'data-turbolinks' => "false") td = link_to(t('shared.show'), item.exercise, 'data-turbolinks' => "false")
td td
a.remove-exercise href='#' = t('shared.destroy') a.remove-exercise href='#' = t('shared.destroy')
.hidden .d-none
= f.collection_select(:exercise_ids, Exercise.all, :id, :title, {}, {id: 'exercise-select', class: 'form-control', multiple: true}) = f.collection_select(:exercise_ids, Exercise.all, :id, :title, {}, {id: 'exercise-select', class: 'form-control', multiple: true})
.exercise-actions .exercise-actions
button.btn.btn-primary type='button' data-toggle='modal' data-target='#add-exercise-modal' = t('exercise_collections.form.add_exercises') button.btn.btn-outline-primary type='button' data-toggle='modal' data-target='#add-exercise-modal' = t('exercise_collections.form.add_exercises')
button.btn.btn-secondary#sort-button type='button' = t('exercise_collections.form.sort_by_title') button.btn.btn-secondary#sort-button type='button' = t('exercise_collections.form.sort_by_title')
.actions = render('shared/submit_button', f: f, object: @exercise_collection) .actions = render('shared/submit_button', f: f, object: @exercise_collection)

View File

@ -7,7 +7,7 @@ h1
= row(label: 'exercise_collections.use_anomaly_detection', value: @exercise_collection.use_anomaly_detection) = row(label: 'exercise_collections.use_anomaly_detection', value: @exercise_collection.use_anomaly_detection)
= row(label: 'exercise_collections.updated_at', value: @exercise_collection.updated_at) = row(label: 'exercise_collections.updated_at', value: @exercise_collection.updated_at)
h4 = t('activerecord.attributes.exercise_collections.exercises') h4.mt-4 = t('activerecord.attributes.exercise_collections.exercises')
.table-responsive#exercise-list .table-responsive#exercise-list
table.table table.table
thead thead
@ -24,5 +24,5 @@ h4 = t('activerecord.attributes.exercise_collections.exercises')
td = exercise_collection_item.position td = exercise_collection_item.position
td = link_to(exercise.title, exercise) td = link_to(exercise.title, exercise)
td = link_to_if(exercise.execution_environment && policy(exercise.execution_environment).show?, exercise.execution_environment, exercise.execution_environment) td = link_to_if(exercise.execution_environment && policy(exercise.execution_environment).show?, exercise.execution_environment, exercise.execution_environment)
td = exercise.user.name td = link_to_if(exercise.user && policy(exercise.user).show?, exercise.user.name, exercise.user)
td = link_to(t('shared.statistics'), statistics_exercise_path(exercise), 'data-turbolinks' => "false") td = link_to(t('shared.statistics'), statistics_exercise_path(exercise), 'data-turbolinks' => "false")

View File

@ -6,7 +6,7 @@ h1 = @exercise_collection
= row(label: 'exercises.statistics.average_worktime', value: @exercise_collection.average_working_time.round(3).to_s + 's') = row(label: 'exercises.statistics.average_worktime', value: @exercise_collection.average_working_time.round(3).to_s + 's')
#graph #graph
#data.hidden(data-working-times=ActiveSupport::JSON.encode(@exercise_collection.collection_statistics) data-average-working-time=@exercise_collection.average_working_time) #data.d-none(data-working-times=ActiveSupport::JSON.encode(@exercise_collection.collection_statistics) data-average-working-time=@exercise_collection.average_working_time)
#legend #legend
- {time: t('exercises.statistics.average_worktime'), - {time: t('exercises.statistics.average_worktime'),
min: 'min. anomaly threshold', min: 'min. anomaly threshold',

View File

@ -3,5 +3,5 @@
| &nbsp; | &nbsp;
a.toggle-input data={text_initial: t('shared.upload_file'), text_toggled: t('shared.back')} href='#' = t('shared.upload_file') 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', rows: 16, style: "display:none;") = form.text_area(attribute, class: 'code-field form-control', rows: 16, style: "display:none;")
= form.file_field(attribute, class: 'alternative-input form-control', disabled: true) = form.file_field(attribute, class: 'alternative-input form-control-file', disabled: true)
= render partial: 'editor_edit', locals: { exercise: @exercise } = render partial: 'editor_edit', locals: { exercise: @exercise }

View File

@ -6,8 +6,7 @@
- hide_rfc_button = @hide_rfc_button || false - hide_rfc_button = @hide_rfc_button || false
#editor.row data-exercise-id=@exercise.id data-message-depleted=t('exercises.editor.depleted') data-message-timeout=t('exercises.editor.timeout', permitted_execution_time: @exercise.execution_environment.permitted_execution_time) data-errors-url=execution_environment_errors_path(exercise.execution_environment) data-submissions-url=submissions_path data-user-id=@current_user.id data-user-external-id=external_user_external_id data-working-times-url=working_times_exercise_path(@exercise) data-intervention-save-url=intervention_exercise_path(@exercise) data-rfc-interventions=show_rfc_interventions data-break-interventions=show_break_interventions data-course_token=@course_token data-search-save-url=search_exercise_path(@exercise) #editor.row data-exercise-id=@exercise.id data-message-depleted=t('exercises.editor.depleted') data-message-timeout=t('exercises.editor.timeout', permitted_execution_time: @exercise.execution_environment.permitted_execution_time) data-errors-url=execution_environment_errors_path(exercise.execution_environment) data-submissions-url=submissions_path data-user-id=@current_user.id data-user-external-id=external_user_external_id data-working-times-url=working_times_exercise_path(@exercise) data-intervention-save-url=intervention_exercise_path(@exercise) data-rfc-interventions=show_rfc_interventions data-break-interventions=show_break_interventions data-course_token=@course_token data-search-save-url=search_exercise_path(@exercise)
div id="sidebar" class=(@exercise.hide_file_tree ? 'sidebar-col-collapsed' : 'sidebar-col') = render('editor_file_tree', exercise: @exercise, files: @files) div id="sidebar" class=(@exercise.hide_file_tree ? 'sidebar-col-collapsed' : 'sidebar-col') = render('editor_file_tree', exercise: @exercise, files: @files)
div id='output_sidebar' class='output-col-collapsed' = render('exercises/editor_output', external_user_id: external_user_id, consumer_id: consumer_id ) div.editor-col.col.p-0 id='frames'
div id='frames' class='editor-col'
#editor-buttons.btn-group.enforce-bottom-margin #editor-buttons.btn-group.enforce-bottom-margin
= render('editor_button', disabled: true, icon: 'fa fa-ban', id: 'dummy', label: t('exercises.editor.dummy')) = render('editor_button', disabled: true, icon: 'fa fa-ban', id: 'dummy', label: t('exercises.editor.dummy'))
= render('editor_button', icon: 'fa fa-desktop', id: 'render', label: t('exercises.editor.render')) = render('editor_button', icon: 'fa fa-desktop', id: 'render', label: t('exercises.editor.render'))
@ -24,6 +23,7 @@
= t('exercises.editor.lastsaved') = t('exercises.editor.lastsaved')
span span
button style="display:none" id="autosave" button style="display:none" id="autosave"
div id='output_sidebar' class='output-col-collapsed' = render('exercises/editor_output', external_user_id: external_user_id, consumer_id: consumer_id )
= render('shared/modal', id: 'comment-modal', title: t('exercises.implement.comment.request'), template: 'exercises/_request_comment_dialogcontent') = render('shared/modal', id: 'comment-modal', title: t('exercises.implement.comment.request'), template: 'exercises/_request_comment_dialogcontent')

View File

@ -1,4 +1,4 @@
button.btn class=local_assigns.fetch(:classes, 'btn-primary btn-sm') *local_assigns.fetch(:data, {}) disabled=local_assigns.fetch(:disabled, false) id=id title=local_assigns[:title] type='button' button.btn class=local_assigns.fetch(:classes, 'btn-primary btn-lg') *local_assigns.fetch(:data, {}) disabled=local_assigns.fetch(:disabled, false) id=id title=local_assigns[:title] type='button'
i.fa.fa-circle-o-notch.fa-spin i.fa.fa-circle-o-notch.fa-spin
i class=icon i class=(label.present? ? icon : "#{icon} m-0")
= label = label

View File

@ -1,5 +1,5 @@
#editor-edit.panel-group.row.original-input data-exercise-id=@exercise.id #editor-edit.original-input data-exercise-id=@exercise.id
#frames #frames
.edit-frame .edit-frame
.editor-content.hidden .editor-content.d-none
.editor .editor.allow_ace_tooltip

View File

@ -1,18 +1,18 @@
div id='sidebar-collapsed' class=(@exercise.hide_file_tree ? '' : 'hidden') div id='sidebar-collapsed' class=(@exercise.hide_file_tree ? '' : 'd-none')
= render('editor_button', classes: 'btn-block btn-primary btn-sm', data: {:'data-toggle' => 'tooltip', :'data-placement' => 'right'}, icon: 'fa fa-plus-square', id: 'sidebar-collapse-collapsed', label:'', title:t('exercises.editor.expand_action_sidebar')) = render('editor_button', classes: 'btn-block btn-primary btn', data: {:'data-toggle' => 'tooltip', :'data-placement' => 'right'}, icon: 'fa fa-plus-square', id: 'sidebar-collapse-collapsed', label:'', title:t('exercises.editor.expand_action_sidebar'))
- if @exercise.allow_file_creation and not @exercise.hide_file_tree? - if @exercise.allow_file_creation and not @exercise.hide_file_tree?
= render('editor_button', classes: 'btn-block btn-primary btn-sm enforce-top-margin', data: {:'data-cause' => 'file', :'data-toggle' => 'tooltip', :'data-placement' => 'right'}, icon: 'fa fa-plus', id: 'create-file-collapsed', label:'', title: t('exercises.editor.create_file')) = render('editor_button', classes: 'btn-block btn-primary btn enforce-top-margin', data: {:'data-cause' => 'file', :'data-toggle' => 'tooltip', :'data-placement' => 'right'}, icon: 'fa fa-plus', id: 'create-file-collapsed', label:'', title: t('exercises.editor.create_file'))
= render('editor_button', classes: 'btn-block btn-primary btn-sm enforce-top-margin', data: {:'data-toggle' => 'tooltip', :'data-placement' => 'right'}, icon: 'fa fa-download', id: 'download-collapsed', label:'', title: t('exercises.editor.download')) = render('editor_button', classes: 'btn-block btn-primary btn enforce-top-margin', data: {:'data-toggle' => 'tooltip', :'data-placement' => 'right'}, icon: 'fa fa-download', id: 'download-collapsed', label:'', title: t('exercises.editor.download'))
= render('editor_button', classes: 'btn-block btn-primary btn-sm enforce-top-margin', data: {:'data-message-confirm' => t('exercises.editor.confirm_start_over'), :'data-url' => reload_exercise_path(@exercise), :'data-toggle' => 'tooltip', :'data-placement' => 'right'}, icon: 'fa fa-history', id: 'start-over-collapsed', label:'', title: t('exercises.editor.start_over')) = render('editor_button', classes: 'btn-block btn-primary btn enforce-top-margin', data: {:'data-message-confirm' => t('exercises.editor.confirm_start_over'), :'data-url' => reload_exercise_path(@exercise), :'data-toggle' => 'tooltip', :'data-placement' => 'right'}, icon: 'fa fa-history', id: 'start-over-collapsed', label:'', title: t('exercises.editor.start_over'))
- if !@course_token.blank? //- if !@course_token.blank?
= render('editor_button', classes: 'btn-block btn-primary btn-sm enforce-top-margin', data: {:'data-toggle' => 'tooltip', :'data-placement' => 'right'}, icon: 'fa fa-search', id: 'sidebar-search-collapsed', label: '', title: t('search.search_in_forum')) = render('editor_button', classes: 'btn-block btn-primary btn enforce-top-margin', data: {:'data-toggle' => 'tooltip', :'data-placement' => 'right'}, icon: 'fa fa-search', id: 'sidebar-search-collapsed', label: '', title: t('search.search_in_forum'))
div id='sidebar-uncollapsed' class=(@exercise.hide_file_tree ? 'hidden' : '') div id='sidebar-uncollapsed' class=(@exercise.hide_file_tree ? 'd-none' : '')
= render('editor_button', classes: 'btn-block btn-primary btn-sm', icon: 'fa fa-minus-square', id: 'sidebar-collapse', label: t('exercises.editor.collapse_action_sidebar')) = render('editor_button', classes: 'btn-block btn-primary btn', icon: 'fa fa-minus-square', id: 'sidebar-collapse', label: t('exercises.editor.collapse_action_sidebar'))
div class=(@exercise.hide_file_tree ? 'hidden' : '') div class=(@exercise.hide_file_tree ? 'd-none' : '')
hr hr
#files data-entries=FileTree.new(files).to_js_tree #files data-entries=FileTree.new(files).to_js_tree
@ -20,11 +20,11 @@ div id='sidebar-uncollapsed' class=(@exercise.hide_file_tree ? 'hidden' : '')
hr hr
- if @exercise.allow_file_creation and not @exercise.hide_file_tree? - if @exercise.allow_file_creation and not @exercise.hide_file_tree?
= render('editor_button', classes: 'btn-block btn-primary btn-sm', data: {:'data-cause' => 'file'}, icon: 'fa fa-plus', id: 'create-file', label: t('exercises.editor.create_file')) = render('editor_button', classes: 'btn-block btn-primary btn', data: {:'data-cause' => 'file'}, icon: 'fa fa-plus', id: 'create-file', label: t('exercises.editor.create_file'))
= render('editor_button', classes: 'btn-block btn-warning btn-sm', data: {:'data-cause' => 'file', :'data-message-confirm' => t('shared.confirm_destroy')}, icon: 'fa fa-times', id: 'destroy-file', label: t('exercises.editor.destroy_file')) = render('editor_button', classes: 'btn-block btn-warning btn', data: {:'data-cause' => 'file', :'data-message-confirm' => t('shared.confirm_destroy')}, icon: 'fa fa-times', id: 'destroy-file', label: t('exercises.editor.destroy_file'))
= render('editor_button', classes: 'btn-block btn-primary btn-sm enforce-top-margin', icon: 'fa fa-download', id: 'download', label: t('exercises.editor.download')) = render('editor_button', classes: 'btn-block btn-primary btn enforce-top-margin', icon: 'fa fa-download', id: 'download', label: t('exercises.editor.download'))
= render('editor_button', classes: 'btn-block btn-primary btn-sm', data: {:'data-message-confirm' => t('exercises.editor.confirm_start_over'), :'data-url' => reload_exercise_path(@exercise)}, icon: 'fa fa-history', id: 'start-over', label: t('exercises.editor.start_over')) = render('editor_button', classes: 'btn-block btn-primary btn', data: {:'data-message-confirm' => t('exercises.editor.confirm_start_over'), :'data-url' => reload_exercise_path(@exercise)}, icon: 'fa fa-history', id: 'start-over', label: t('exercises.editor.start_over'))
//- if !@course_token.blank? //- if !@course_token.blank?
.input-group.enforce-top-margin .input-group.enforce-top-margin

View File

@ -11,5 +11,5 @@
- else - else
= link_to(file.native_file.file.name_with_extension, file.native_file.url) = link_to(file.native_file.file.name_with_extension, file.native_file.url)
- else - else
.editor-content.hidden data-file-id=file.ancestor_id = file.content .editor-content.d-none data-file-id=file.ancestor_id = file.content
.editor data-file-id=file.ancestor_id data-indent-size=file.file_type.indent_size data-mode=file.file_type.editor_mode data-read-only=file.read_only data-allow-auto-completion=exercise.allow_auto_completion.to_s data-id=file.id .editor data-file-id=file.ancestor_id data-indent-size=file.file_type.indent_size data-mode=file.file_type.editor_mode data-read-only=file.read_only data-allow-auto-completion=exercise.allow_auto_completion.to_s data-id=file.id

View File

@ -1,58 +1,61 @@
div id='output_sidebar_collapsed' div id='output_sidebar_collapsed'
= render('editor_button', classes: 'btn-block btn-primary btn-sm', data: {:'data-toggle' => 'tooltip', :'data-placement' => 'left'}, title: t('exercises.editor.expand_output_sidebar'), icon: 'fa fa-plus-square', id: 'toggle-sidebar-output-collapsed', label: '') = render('editor_button', classes: 'btn-block btn-primary btn', data: {:'data-toggle' => 'tooltip', :'data-placement' => 'left'}, title: t('exercises.editor.expand_output_sidebar'), icon: 'fa fa-plus-square', id: 'toggle-sidebar-output-collapsed', label: '')
div id='output_sidebar_uncollapsed' class='hidden col-sm-12 enforce-bottom-margin' data-message-no-output=t('exercises.implement.no_output') div.h-100 id='output_sidebar_uncollapsed' class='d-none col-sm-12 enforce-bottom-margin' data-message-no-output=t('exercises.implement.no_output')
.row .row
= render('editor_button', classes: 'btn-block btn-primary btn-sm', icon: 'fa fa-minus-square', id: 'toggle-sidebar-output', label: t('exercises.editor.collapse_output_sidebar')) = render('editor_button', classes: 'btn-block btn-primary btn', icon: 'fa fa-minus-square', id: 'toggle-sidebar-output', label: t('exercises.editor.collapse_output_sidebar'))
div.enforce-big-top-margin.hidden id='score_div' div.position-absolute.d-flex.mb-1.w-100 style="overflow: scroll; left: 0; bottom: 0; height: calc(100% - 3rem);"
#results div.w-100
h2 = t('exercises.implement.results') div.enforce-big-top-margin.d-none id='score_div'
p.test-count == t('exercises.implement.test_count', count: 0) #results
ul.list-unstyled h2 = t('exercises.implement.results')
ul#dummies.hidden.list-unstyled p.test-count == t('exercises.implement.test_count', count: 0)
li.panel.panel-default ul.list-unstyled
.panel-heading ul#dummies.d-none.list-unstyled
h3.panel-title == t('exercises.implement.file', filename: '', number: 0) li.card.mt-2
.panel-body .card-header
= row(label: 'exercises.implement.passed_tests', value: t('shared.out_of', maximum_value: 0, value: 0).html_safe) h3.card-title.m-0 == t('exercises.implement.file', filename: '', number: 0)
= row(label: 'activerecord.attributes.submission.score', value: t('shared.out_of', maximum_value: 0, value: 0).html_safe) .card-body.bg-white.text-dark
= row(label: 'exercises.implement.feedback') = row(label: 'exercises.implement.passed_tests', value: t('shared.out_of', maximum_value: 0, value: 0).html_safe)
= row(label: 'exercises.implement.error_messages') = row(label: 'activerecord.attributes.submission.score', value: t('shared.out_of', maximum_value: 0, value: 0).html_safe)
/= row(label: 'exercises.implement.output', value: link_to(t('shared.show'), '#')) = row(label: 'exercises.implement.feedback')
#score data-maximum-score=@exercise.maximum_score data-score=@submission.try(:score) = row(label: 'exercises.implement.error_messages')
h4 /= row(label: 'exercises.implement.output', value: link_to(t('shared.show'), '#'))
span == "#{t('activerecord.attributes.submission.score')}:&nbsp;" #score data-maximum-score=@exercise.maximum_score data-score=@submission.try(:score)
span.score h4
.progress span == "#{t('activerecord.attributes.submission.score')}:&nbsp;"
.progress-bar role='progressbar' span.score
.progress
.progress-bar role='progressbar'
br br
- if lti_outcome_service?(@exercise.id, external_user_id, consumer_id) - if lti_outcome_service?(@exercise.id, external_user_id, consumer_id)
p.text-center = render('editor_button', classes: 'btn-lg btn-success', data: {:'data-url' => submit_exercise_path(@exercise)}, icon: 'fa fa-send', id: 'submit', label: t('exercises.editor.submit')) p.text-center = render('editor_button', classes: 'btn-lg btn-success', data: {:'data-url' => submit_exercise_path(@exercise)}, icon: 'fa fa-send', id: 'submit', label: t('exercises.editor.submit'))
- else - else
p.text-center = render('editor_button', classes: 'btn-lg btn-warning-outline', data: {:'data-placement' => 'bottom', :'data-tooltip' => true}, icon: 'fa fa-clock-o', id: 'submit_outdated', label: t('exercises.editor.exercise_deadline_passed'), title: t('exercises.editor.tooltips.exercise_deadline_passed')) p.text-center = render('editor_button', classes: 'btn-lg btn-outline-warning disabled', data: {:'data-placement' => 'bottom', :'data-tooltip' => true}, icon: 'fa fa-clock-o', id: 'submit_outdated', label: t('exercises.editor.exercise_deadline_passed'), title: t('exercises.editor.tooltips.exercise_deadline_passed'))
hr hr
div.enforce-big-top-margin div.enforce-big-top-margin
#turtlediv #turtlediv
canvas#turtlecanvas.hidden width=400 height=400 canvas#turtlecanvas.d-none width=400 height=400
div.enforce-big-top-margin div.enforce-big-top-margin
#hint #hint
.panel.panel-warning .card.bg-warning
.panel-heading = t('exercises.implement.hint') .card-header = t('exercises.implement.hint')
.panel-body .card-body
div.enforce-big-top-margin div.enforce-big-top-margin
#prompt.input-group.hidden #prompt.input-group.d-none
span.input-group-addon data-prompt=t('exercises.editor.input') = t('exercises.editor.input') div.input-group-prepend
input#prompt-input.form-control type='text' span.input-group-text data-prompt=t('exercises.editor.input') = t('exercises.editor.input')
span.input-group-btn input#prompt-input.form-control type='text'
button#prompt-submit.btn.btn-primary type="button" = t('exercises.editor.send') span.input-group-btn
#error-hints button#prompt-submit.btn.btn-primary type="button" = t('exercises.editor.send')
.heading = t('exercises.implement.error_hints.heading') #error-hints
ul.body .heading = t('exercises.implement.error_hints.heading')
#output ul.body
pre = t('exercises.implement.no_output_yet') #output.mt-2
- if CodeOcean::Config.new(:code_ocean).read[:flowr][:enabled] pre = t('exercises.implement.no_output_yet')
#flowrHint.panel.panel-info data-url=CodeOcean::Config.new(:code_ocean).read[:flowr][:url] role='tab' - if CodeOcean::Config.new(:code_ocean).read[:flowr][:enabled]
.panel-heading = 'Gain more insights here' #flowrHint.card.card.text-white.bg-info data-url=CodeOcean::Config.new(:code_ocean).read[:flowr][:url] role='tab'
.panel-body .card-header = 'Gain more insights here'
.card-body

View File

@ -1,41 +1,42 @@
- id = f.object.id - id = f.object.id
li.panel.panel-default li.card.mt-2
.panel-heading role="tab" id="heading" .card-header role="tab" id="heading"
a.file-heading data-toggle="collapse" href="#collapse#{id}" a.file-heading.collapsed data-toggle="collapse" href="#collapse#{id}"
div.clearfix role="button" div.clearfix role="button"
i class="fa" aria-hidden="true"
span = f.object.name span = f.object.name
.panel-collapse.collapse class=('in' if f.object.name.nil?) id="collapse#{id}" role="tabpanel" .card-collapse.collapse class=('in' if f.object.name.nil?) id="collapse#{id}" role="tabpanel"
.panel-body .card-body
- if policy(f.object).destroy? - if policy(f.object).destroy?
.clearfix .clearfix
.btn.btn-warning.btn-sm.pull-right.delete-file data-file-url=code_ocean_file_path(id) = t('shared.destroy') .btn.btn-warning.btn-sm.float-right.delete-file data-file-url=code_ocean_file_path(id) = t('shared.destroy')
.form-group .form-group
= f.label(:name, t('activerecord.attributes.file.name')) = f.label(:name, t('activerecord.attributes.file.name'))
= f.text_field(:name, class: 'form-control') = f.text_field(:name, class: 'form-control')
.form-group .form-group
= f.label(:path, t('activerecord.attributes.file.path')) = f.label(:path, t('activerecord.attributes.file.path'))
= f.text_field(:path, class: 'form-control') = f.text_field(:path, class: 'form-control')
.help-block = t('.hints.path') .help-block.form-text = t('.hints.path')
.form-group .form-group
= f.label(:file_type_id, t('activerecord.attributes.file.file_type_id')) = f.label(:file_type_id, t('activerecord.attributes.file.file_type_id'))
= f.collection_select(:file_type_id, @file_types, :id, :name, {}, class: 'form-control') = f.collection_select(:file_type_id, @file_types, :id, :name, {}, class: 'form-control')
.form-group .form-group
= f.label(:role, t('activerecord.attributes.file.role')) = f.label(:role, t('activerecord.attributes.file.role'))
= f.select(:role, CodeOcean::File::TEACHER_DEFINED_ROLES.map { |role| [t("files.roles.#{role}"), role] }, {include_blank: true}, class: 'form-control') = f.select(:role, CodeOcean::File::TEACHER_DEFINED_ROLES.map { |role| [t("files.roles.#{role}"), role] }, {include_blank: true}, class: 'form-control')
.checkbox .form-check
label label.form-check-label
= f.check_box(:hidden) = f.check_box(:hidden, class: 'form-check-input')
= t('activerecord.attributes.file.hidden') = t('activerecord.attributes.file.hidden')
.checkbox .form-check.mb-3
label label.form-check-label
= f.check_box(:read_only) = f.check_box(:read_only, class: 'form-check-input')
= t('activerecord.attributes.file.read_only') = t('activerecord.attributes.file.read_only')
.test-related-fields style="display: #{f.object.teacher_defined_test? ? 'initial' : 'none'};" .test-related-fields style="display: #{f.object.teacher_defined_test? ? 'initial' : 'none'};"
.form-group .form-group
= f.label(:name, t('activerecord.attributes.file.feedback_message')) = f.label(:name, t('activerecord.attributes.file.feedback_message'))
= f.text_area(:feedback_message, class: 'form-control', maxlength: 255) = f.text_area(:feedback_message, class: 'form-control', maxlength: 255)
.help-block = t('.hints.feedback_message') .help-block.form-text = t('.hints.feedback_message')
.form-group .form-group
= f.label(:role, t('activerecord.attributes.file.weight')) = f.label(:role, t('activerecord.attributes.file.weight'))
= f.number_field(:weight, class: 'form-control', min: 1, step: 'any') = f.number_field(:weight, class: 'form-control', min: 1, step: 'any')

View File

@ -1,14 +1,14 @@
- execution_environments = ExecutionEnvironment.where('file_type_id IS NOT NULL').select(:file_type_id, :id) - execution_environments = ExecutionEnvironment.where('file_type_id IS NOT NULL').select(:file_type_id, :id)
- file_types = FileType.where('file_extension IS NOT NULL').select(:file_extension, :id) - file_types = FileType.where('file_extension IS NOT NULL').select(:file_extension, :id)
= form_for(@exercise, data: {execution_environments: execution_environments, file_types: file_types}, multipart: true) do |f| = form_for(@exercise, data: {execution_environments: execution_environments, file_types: file_types}, multipart: true, builder: PagedownFormBuilder) do |f|
= render('shared/form_errors', object: @exercise) = render('shared/form_errors', object: @exercise)
.form-group .form-group
= f.label(:title) = f.label(:title)
= f.text_field(:title, class: 'form-control', required: true) = f.text_field(:title, class: 'form-control', required: true)
.form-group .form-group
= f.label(:description) = f.label(:description)
= f.pagedown_editor :description = f.pagedown :description, input_html: { preview: true, rows: 10 }
.form-group .form-group
= f.label(:execution_environment_id) = f.label(:execution_environment_id)
= f.collection_select(:execution_environment_id, @execution_environments, :id, :name, {}, class: 'form-control') = f.collection_select(:execution_environment_id, @execution_environments, :id, :name, {}, class: 'form-control')
@ -16,34 +16,34 @@
= f.label(:instructions) = f.label(:instructions)
= f.hidden_field(:instructions) = f.hidden_field(:instructions)
.form-control.markdown .form-control.markdown
.checkbox .form-check
label label.form-check-label
= f.check_box(:public) = f.check_box(:public, class: 'form-check-input')
= t('activerecord.attributes.exercise.public') = t('activerecord.attributes.exercise.public')
.checkbox .form-check
label label.form-check-label
= f.check_box(:hide_file_tree) = f.check_box(:hide_file_tree, class: 'form-check-input')
= t('activerecord.attributes.exercise.hide_file_tree') = t('activerecord.attributes.exercise.hide_file_tree')
.checkbox .form-check
label label.form-check-label
= f.check_box(:allow_file_creation) = f.check_box(:allow_file_creation, class: 'form-check-input')
= t('activerecord.attributes.exercise.allow_file_creation') = t('activerecord.attributes.exercise.allow_file_creation')
.checkbox .form-check.mb-3
label label.form-check-label
= f.check_box(:allow_auto_completion) = f.check_box(:allow_auto_completion, class: 'form-check-input')
= t('activerecord.attributes.exercise.allow_auto_completion') = t('activerecord.attributes.exercise.allow_auto_completion')
.form-group .form-group
= f.label(t('activerecord.attributes.exercise.difficulty')) = f.label(t('activerecord.attributes.exercise.difficulty'))
= f.number_field :expected_difficulty, in: 1..10, step: 1 = f.number_field :expected_difficulty, in: 1..10, step: 1, class: 'form-control'
h2 = t('exercises.form.tags') h2 = t('exercises.form.tags')
ul.list-unstyled.panel-group ul.list-unstyled.card-group
li.panel.panel-default li.card
.panel-heading role="tab" id="heading" .card-header role="tab" id="heading"
a.file-heading data-toggle="collapse" href="#tag-collapse" a.file-heading data-toggle="collapse" href="#tag-collapse"
div.clearfix role="button" div.clearfix role="button"
span = t('exercises.form.click_to_collapse') span = t('exercises.form.click_to_collapse')
.panel-collapse.collapse id="tag-collapse" role="tabpanel" .card-collapse.collapse id="tag-collapse" role="tabpanel"
.table-responsive .table-responsive
table.table#tags-table table.table#tags-table
thead thead
@ -55,15 +55,15 @@
tr tr
td = b.check_box td = b.check_box
td = b.object.tag.name td = b.object.tag.name
td = number_field "tag_factors[#{b.object.tag.id}]", :factor, :value => b.object.factor, in: 1..10, step: 1 td = number_field "tag_factors[#{b.object.tag.id}]", :factor, :value => b.object.factor, in: 1..10, step: 1, class: 'form-control-sm'
h2 = t('activerecord.attributes.exercise.files') h2 = t('activerecord.attributes.exercise.files')
ul#files.list-unstyled.panel-group ul#files.list-unstyled
= f.fields_for :files do |files_form| = f.fields_for :files do |files_form|
= render('file_form', f: files_form) = render('file_form', f: files_form)
a#add-file.btn.btn-default.btn-sm.pull-right href='#' = t('.add_file') a#add-file.btn.btn-secondary.btn-sm.float-right href='#' = t('.add_file')
ul#dummies.hidden = f.fields_for(:files, CodeOcean::File.new, child_index: 'index') do |files_form| ul#dummies.d-none = f.fields_for(:files, CodeOcean::File.new, child_index: 'index') do |files_form|
= render('file_form', f: 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)

View File

@ -2,7 +2,7 @@ h5#rfc_intervention_text style='display: none;' = t('exercises.implement.rfc_int
h5 = t('exercises.implement.comment.question') h5 = t('exercises.implement.comment.question')
textarea.form-control#question(style='resize:none;') textarea.form-control.flex-grow-1#question(style='resize:none;')
p = '' p = ''
/ data-cause='requestComments' is not used here right now, we pass the button #requestComments (not askForCommentsButton) as initiator of the action. / data-cause='requestComments' is not used here right now, we pass the button #requestComments (not askForCommentsButton) as initiator of the action.
/ But if we use this button, it will work since the correct cause is supplied / But if we use this button, it will work since the correct cause is supplied

View File

@ -10,21 +10,21 @@ h1 = "#{@exercise} (external user #{@external_user})"
- file_types.add(ActiveSupport::JSON.encode(file.file_type)) - file_types.add(ActiveSupport::JSON.encode(file.file_type))
- all_files.push(submission.files) - all_files.push(submission.files)
.hidden#data data-submissions=ActiveSupport::JSON.encode(@submissions) data-files=ActiveSupport::JSON.encode(all_files) data-file-types=ActiveSupport::JSON.encode(file_types) .d-none#data data-submissions=ActiveSupport::JSON.encode(@submissions) data-files=ActiveSupport::JSON.encode(all_files) data-file-types=ActiveSupport::JSON.encode(file_types)
#stats-editor.row #stats-editor.row
- index = 0 - index = 0
- all_files.each do |files| - all_files.each do |files|
.files class=(@exercise.hide_file_tree ? 'hidden col-sm-3' : 'col-sm-3') data-index=index data-entries=FileTree.new(files).to_js_tree .files class=(@exercise.hide_file_tree ? 'd-none col-sm-3' : 'col-sm-3') data-index=index data-entries=FileTree.new(files).to_js_tree
- index += 1 - index += 1
div class=(@exercise.hide_file_tree ? 'col-sm-12' : 'col-sm-9') div class=(@exercise.hide_file_tree ? 'col-sm-12' : 'col-sm-9')
#current-file.editor #current-file.editor
.flex-container .flex-container
button.btn.btn-default id='play-button' button.btn.btn-secondary id='play-button'
span.fa.fa-play span.fa.fa-play
#submissions-slider.flex-item #submissions-slider.flex-item
input type='range' orient='horizontal' list='datapoints' min=0 max=@submissions.length-1 value=0 input type='range' orient='horizontal' list='datapoints' min=0 max=@submissions.length-1 value=0 style="width: 100%"
datalist#datapoints datalist#datapoints
- index=0 - index=0
- @submissions.each do |submission| - @submissions.each do |submission|
@ -59,7 +59,7 @@ h1 = "#{@exercise} (external user #{@external_user})"
td = td =
td = @working_times_until[index] if index > 0 td = @working_times_until[index] if index > 0
p = t('.addendum') p = t('.addendum')
.hidden#wtimes data-working_times=ActiveSupport::JSON.encode(@working_times_until); .d-none#wtimes data-working_times=ActiveSupport::JSON.encode(@working_times_until);
div#progress_chart.col-lg-12 div#progress_chart.col-lg-12
.graph-functions-2 .graph-functions-2

View File

@ -8,17 +8,17 @@ h1 = link_to(@exercise, exercise_path(@exercise))
- if @feedbacks.nil? or @feedbacks.size == 0 - if @feedbacks.nil? or @feedbacks.size == 0
.no-feedback = t('user_exercise_feedback.no_feedback') .no-feedback = t('user_exercise_feedback.no_feedback')
ul.list-unstyled.panel-group ul.list-unstyled
- @feedbacks.each do |feedback| - @feedbacks.each do |feedback|
li.panel.panel-default li.card.mt-2
.panel-heading role="tab" id="heading" .card-header role="tab" id="heading"
div.clearfix.feedback-header div.clearfix.feedback-header
span.username = link_to(feedback.user.name, statistics_external_user_exercise_path(id: @exercise.id, external_user_id: feedback.user.id)) span.username = link_to(feedback.user.name, statistics_external_user_exercise_path(id: @exercise.id, external_user_id: feedback.user.id))
- if feedback.anomaly_notification - if feedback.anomaly_notification
i class="fa fa-envelope-o" data-placement="top" data-toggle="tooltip" data-container="body" title=feedback.anomaly_notification.reason i class="fa fa-envelope-o" data-placement="top" data-toggle="tooltip" data-container="body" title=feedback.anomaly_notification.reason
span.date = feedback.created_at span.date = feedback.created_at
.panel-collapse role="tabpanel" .card-collapse role="tabpanel"
.panel-body.feedback .card-body.feedback
.text = feedback.feedback_text .text = feedback.feedback_text
.difficulty = "#{t('user_exercise_feedback.difficulty')} #{feedback.difficulty}" if feedback.difficulty .difficulty = "#{t('user_exercise_feedback.difficulty')} #{feedback.difficulty}" if feedback.difficulty
.worktime = "#{t('user_exercise_feedback.working_time')} #{feedback.user_estimated_worktime}" if feedback.user_estimated_worktime .worktime = "#{t('user_exercise_feedback.working_time')} #{feedback.user_estimated_worktime}" if feedback.user_estimated_worktime

View File

@ -2,13 +2,13 @@
#editor-column.col-md-12 #editor-column.col-md-12
.exercise.clearfix .exercise.clearfix
div div
span.badge.pull-right.score span.badge.badge-pill.float-right.score
h1 id="exercise-headline" h1 id="exercise-headline"
i class="fa fa-chevron-down" id="description-symbol" i class="fa fa-chevron-down" id="description-symbol"
= @exercise.title = @exercise.title
#description-panel.lead.description-panel #description-card.lead.description-card
= render_markdown(@exercise.description) = render_markdown(@exercise.description)
a#toggle href="#" data-show=t('shared.show') data-hide=t('shared.hide') = t('shared.hide') a#toggle href="#" data-show=t('shared.show') data-hide=t('shared.hide') = t('shared.hide')

View File

@ -9,45 +9,43 @@ h1 = Exercise.model_name.human(count: 2)
= f.search_field(:title_cont, class: 'form-control', placeholder: t('activerecord.attributes.exercise.title')) = f.search_field(:title_cont, class: 'form-control', placeholder: t('activerecord.attributes.exercise.title'))
.table-responsive .table-responsive
table.table table.table.mt-4
thead thead
tr tr
th = sort_link(@search, :title, t('activerecord.attributes.exercise.title')) th.p-1 = sort_link(@search, :title, t('activerecord.attributes.exercise.title'))
th = sort_link(@search, :execution_environment_id, t('activerecord.attributes.exercise.execution_environment')) th.p-1 = sort_link(@search, :execution_environment_id, t('activerecord.attributes.exercise.execution_environment'))
th = t('.test_files') th.p-1 = t('.test_files')
th = t('activerecord.attributes.exercise.maximum_score') th.p-1 = t('activerecord.attributes.exercise.maximum_score')
th = t('activerecord.attributes.exercise.tags') th.p-1 = t('activerecord.attributes.exercise.tags')
th = t('activerecord.attributes.exercise.difficulty') th.p-1 = t('activerecord.attributes.exercise.difficulty')
th th.p-1
= t('activerecord.attributes.exercise.public') = t('activerecord.attributes.exercise.public')
- if policy(Exercise).batch_update? - if policy(Exercise).batch_update?
br br
span.batch = link_to(t('shared.batch_update'), '#', 'data-text' => t('shared.update', model: t('activerecord.models.exercise.other'))) span.batch = link_to(t('shared.batch_update'), '#', 'data-text' => t('shared.update', model: t('activerecord.models.exercise.other')))
th colspan=6 = t('shared.actions') th.p-1 colspan=6 = t('shared.actions')
tbody tbody
- @exercises.each do |exercise| - @exercises.each do |exercise|
tr data-id=exercise.id tr data-id=exercise.id
td = exercise.title td.p-1.pt-2 = link_to(exercise.title, exercise, 'data-turbolinks' => "false") if policy(exercise).show?
td = link_to_if(exercise.execution_environment && policy(exercise.execution_environment).show?, exercise.execution_environment, exercise.execution_environment) td.p-1.pt-2 = link_to_if(exercise.execution_environment && policy(exercise.execution_environment).show?, exercise.execution_environment, exercise.execution_environment)
td = exercise.files.teacher_defined_tests.count td.p-1.pt-2 = exercise.files.teacher_defined_tests.count
td = exercise.maximum_score td.p-1.pt-2 = exercise.maximum_score
td = exercise.exercise_tags.count td.p-1.pt-2 = exercise.exercise_tags.count
td = exercise.expected_difficulty td.p-1.pt-2 = exercise.expected_difficulty
td.public data-value=exercise.public? = symbol_for(exercise.public?) td.p-1.pt-2.public data-value=exercise.public? = symbol_for(exercise.public?)
td = link_to(t('shared.edit'), edit_exercise_path(exercise)) if policy(exercise).edit? td.p-1.pt-2 = link_to(t('shared.edit'), edit_exercise_path(exercise)) if policy(exercise).edit?
td = link_to(t('.implement'), implement_exercise_path(exercise)) if policy(exercise).implement? td.p-1.pt-2 = link_to(t('.implement'), implement_exercise_path(exercise)) if policy(exercise).implement?
td = link_to(t('shared.statistics'), statistics_exercise_path(exercise), 'data-turbolinks' => "false") if policy(exercise).statistics? td.p-1.pt-2 = link_to(t('shared.statistics'), statistics_exercise_path(exercise), 'data-turbolinks' => "false") if policy(exercise).statistics?
td td.p-1
.btn-group .btn-group
button.btn.btn-primary-outline.btn-xs.dropdown-toggle data-toggle="dropdown" type="button" = t('shared.actions_button') button.btn.btn-outline-primary.btn-sm.dropdown-toggle data-toggle="dropdown" type="button" = t('shared.actions_button')
span.caret ul.dropdown-menu.float-right role="menu"
span.sr-only Toggle Dropdown li = link_to(t('shared.show'), exercise, 'data-turbolinks' => "false", class: 'dropdown-item') if policy(exercise).show?
ul.dropdown-menu.pull-right role="menu" li = link_to(t('activerecord.models.user_exercise_feedback.other'), feedback_exercise_path(exercise), class: 'dropdown-item') if policy(exercise).feedback?
li = link_to(t('shared.show'), exercise, 'data-turbolinks' => "false") if policy(exercise).show? li = link_to(t('shared.destroy'), exercise, data: {confirm: t('shared.confirm_destroy')}, method: :delete, class: 'dropdown-item') if policy(exercise).destroy?
li = link_to(t('activerecord.models.user_exercise_feedback.other'), feedback_exercise_path(exercise)) if policy(exercise).feedback? li = link_to(t('.clone'), clone_exercise_path(exercise), data: {confirm: t('shared.confirm_destroy')}, method: :post, class: 'dropdown-item') if policy(exercise).clone?
li = link_to(t('shared.destroy'), exercise, data: {confirm: t('shared.confirm_destroy')}, method: :delete) if policy(exercise).destroy?
li = link_to(t('.clone'), clone_exercise_path(exercise), data: {confirm: t('shared.confirm_destroy')}, method: :post) if policy(exercise).clone?
= render('shared/pagination', collection: @exercises) = render('shared/pagination', collection: @exercises)
p = render('shared/new_button', model: Exercise) p = render('shared/new_button', model: Exercise)

View File

@ -12,7 +12,7 @@ h1
= row(label: 'exercise.title', value: @exercise.title) = row(label: 'exercise.title', value: @exercise.title)
= row(label: 'exercise.user', value: link_to_if(policy(@exercise.author).show?, @exercise.author, @exercise.author)) = row(label: 'exercise.user', value: link_to_if(policy(@exercise.author).show?, @exercise.author, @exercise.author))
= row(label: 'exercise.description', value: render_markdown(@exercise.description)) = row(label: 'exercise.description', value: render_markdown(@exercise.description), class: 'm-0')
= row(label: 'exercise.execution_environment', value: link_to_if(policy(@exercise.execution_environment).show?, @exercise.execution_environment, @exercise.execution_environment)) = row(label: 'exercise.execution_environment', value: link_to_if(policy(@exercise.execution_environment).show?, @exercise.execution_environment, @exercise.execution_environment))
/= row(label: 'exercise.instructions', value: render_markdown(@exercise.instructions)) /= row(label: 'exercise.instructions', value: render_markdown(@exercise.instructions))
= row(label: 'exercise.maximum_score', value: @exercise.maximum_score) = row(label: 'exercise.maximum_score', value: @exercise.maximum_score)
@ -20,24 +20,23 @@ h1
= row(label: 'exercise.hide_file_tree', value: @exercise.hide_file_tree?) = row(label: 'exercise.hide_file_tree', value: @exercise.hide_file_tree?)
= row(label: 'exercise.allow_file_creation', value: @exercise.allow_file_creation?) = row(label: 'exercise.allow_file_creation', value: @exercise.allow_file_creation?)
= row(label: 'exercise.allow_auto_completion', value: @exercise.allow_auto_completion?) = row(label: 'exercise.allow_auto_completion', value: @exercise.allow_auto_completion?)
= row(label: 'exercise.embedding_parameters') do
= content_tag(:input, nil, class: 'form-control', readonly: true, value: embedding_parameters(@exercise))
= row(label: 'exercise.difficulty', value: @exercise.expected_difficulty) = row(label: 'exercise.difficulty', value: @exercise.expected_difficulty)
= row(label: 'exercise.tags', value: @exercise.exercise_tags.map{|et| "#{et.tag.name} (#{et.factor})"}.sort.join(", ")) = row(label: 'exercise.tags', value: @exercise.exercise_tags.map{|et| "#{et.tag.name} (#{et.factor})"}.sort.join(", "))
= row(label: 'exercise.embedding_parameters', class: 'mb-4') do
= content_tag(:input, nil, class: 'form-control mb-4', readonly: true, value: embedding_parameters(@exercise))
h2 = t('activerecord.attributes.exercise.files') h2.mt-4 = t('activerecord.attributes.exercise.files')
ul.list-unstyled.panel-group#files ul.list-unstyled#files
- @exercise.files.each do |file| - @exercise.files.each do |file|
li.panel.panel-default li.card.mt-2
.panel-heading role="tab" id="heading" .card-header role="tab" id="heading"
a.file-heading data-toggle="collapse" data-parent="#files" href=".collapse#{file.id}" a.file-heading.collapsed data-toggle="collapse" data-parent="#files" href=".collapse#{file.id}"
div.clearfix role="button" div.clearfix role="button"
i class="fa" aria-hidden="true"
span = file.name_with_extension span = file.name_with_extension
// probably set an icon here that shows that the rows can be collapsed .card-collapse.collapse class="collapse#{file.id}" role="tabpanel"
//span.pull-right.collapse.in class="collapse#{file.id}" &#9788 .card-body
.panel-collapse.collapse class="collapse#{file.id}" role="tabpanel"
.panel-body
- if policy(file).destroy? - if policy(file).destroy?
.clearfix = link_to(t('shared.destroy'), file, class:'btn btn-warning btn-sm pull-right', data: {confirm: t('shared.confirm_destroy')}, method: :delete) .clearfix = link_to(t('shared.destroy'), file, class:'btn btn-warning btn-sm float-right', data: {confirm: t('shared.confirm_destroy')}, method: :delete)
= render('shared/file', file: file) = render('shared/file', file: file)

View File

@ -32,7 +32,7 @@ h1 = @exercise
-working_time = @exercise.average_working_time_for(user.id) or 0 -working_time = @exercise.average_working_time_for(user.id) or 0
-working_time_array.push working_time -working_time_array.push working_time
hr hr
.hidden#data data-working-time=ActiveSupport::JSON.encode(working_time_array) .d-none#data data-working-time=ActiveSupport::JSON.encode(working_time_array)
.graph-functions .graph-functions
div#chart_1 div#chart_1
hr hr

View File

@ -4,9 +4,9 @@ h1 = @user.name
//= row(label: 'external_user.email', value: @user.email) //= row(label: 'external_user.email', value: @user.email)
= row(label: 'external_user.consumer', value: link_to(@user.consumer, @user.consumer)) = row(label: 'external_user.consumer', value: link_to(@user.consumer, @user.consumer))
h4 = link_to(t('.exercise_statistics'), statistics_external_user_path(@user)) h4.mt-4 = link_to(t('.exercise_statistics'), statistics_external_user_path(@user))
h4 = t('.tag_statistics') h4.mt-4 = t('.tag_statistics')
#loading #loading
.spinner .spinner
= t('.loading_tag_statistics') = t('.loading_tag_statistics')

View File

@ -10,7 +10,7 @@ h1 = FileTemplate.model_name.human(count: 2)
tbody tbody
- @file_templates.each do |file_template| - @file_templates.each do |file_template|
tr tr
td = file_template.name td = link_to(file_template.name, file_template)
td = link_to(file_template.file_type, file_type_path(file_template.file_type)) td = link_to(file_template.file_type, file_type_path(file_template.file_type))
td = link_to(t('shared.show'), file_template) td = link_to(t('shared.show'), file_template)
td = link_to(t('shared.edit'), edit_file_template_path(file_template)) td = link_to(t('shared.edit'), edit_file_template_path(file_template))

View File

@ -12,16 +12,16 @@
.form-group .form-group
= f.label(:indent_size) = f.label(:indent_size)
= f.number_field(:indent_size, class: 'form-control', placeholder: 2, required: true) = f.number_field(:indent_size, class: 'form-control', placeholder: 2, required: true)
.checkbox .form-check
label label.form-check-label
= f.check_box(:binary) = f.check_box(:binary, class: 'form-check-input')
= t('activerecord.attributes.file_type.binary') = t('activerecord.attributes.file_type.binary')
.checkbox .form-check
label label.form-check-label
= f.check_box(:executable) = f.check_box(:executable, class: 'form-check-input')
= t('activerecord.attributes.file_type.executable') = t('activerecord.attributes.file_type.executable')
.checkbox .form-check.mb-3
label label.form-check-label
= f.check_box(:renderable) = f.check_box(:renderable, class: 'form-check-input')
= t('activerecord.attributes.file_type.renderable') = t('activerecord.attributes.file_type.renderable')
.actions = render('shared/submit_button', f: f, object: @file_type) .actions = render('shared/submit_button', f: f, object: @file_type)

View File

@ -11,7 +11,7 @@ h1 = FileType.model_name.human(count: 2)
tbody tbody
- @file_types.each do |file_type| - @file_types.each do |file_type|
tr tr
td = file_type.name td = link_to(file_type.name, file_type)
td = link_to(file_type.author, file_type.author) td = link_to(file_type.author, file_type.author)
td = file_type.file_extension td = file_type.file_extension
td = link_to(t('shared.show'), file_type) td = link_to(t('shared.show'), file_type)

View File

@ -0,0 +1 @@
json.extract! @file_type, :id, :name, :editor_mode, :file_extension, :executable, :renderable, :binary

View File

@ -3,15 +3,15 @@
.form-group .form-group
= f.label(:name) = f.label(:name)
= f.text_field(:name, class: 'form-control', required: true) = f.text_field(:name, class: 'form-control', required: true)
.form .form-group
= f.label(:locale) = f.label(:locale)
= f.select(:locale, I18n.available_locales.map { |locale| [t("locales.#{locale}"), locale] }, {}, class: 'form-control') = f.select(:locale, I18n.available_locales.map { |locale| [t("locales.#{locale}"), locale] }, {}, class: 'form-control')
.form-group .form-group
= f.label(:message) = f.label(:message)
= f.text_field(:message, class: 'form-control', placeholder: "'$2' has no method '$1'.", required: true) = f.text_field(:message, class: 'form-control', placeholder: "'$2' has no method '$1'.", required: true)
.help-block = t('.hints.message') .help-block.form-text = t('.hints.message')
.form-group .form-group
= f.label(:regular_expression) = f.label(:regular_expression)
= f.text_field(:regular_expression, class: 'form-control', placeholder: 'undefined method (\w+) for (\w+)', required: true) = f.text_field(:regular_expression, class: 'form-control', placeholder: 'undefined method (\w+) for (\w+)', required: true)
.help-block = t('.hints.regular_expression') .help-block.form-text = t('.hints.regular_expression')
.actions = render('shared/submit_button', f: f, object: @hint) .actions = render('shared/submit_button', f: f, object: @hint)

View File

@ -5,7 +5,7 @@
= f.collection_select(:consumer_id, Consumer.all.sort_by(&:name), :id, :name, {}, class: 'form-control') = f.collection_select(:consumer_id, Consumer.all.sort_by(&:name), :id, :name, {}, class: 'form-control')
.form-group .form-group
= f.label(:email) = f.label(:email)
= f.text_field(:email, class: 'form-control', required: true) = f.email_field(:email, class: 'form-control', required: true)
.form-group .form-group
= f.label(:name) = f.label(:name)
= f.text_field(:name, class: 'form-control', required: true) = f.text_field(:name, class: 'form-control', required: true)

View File

@ -9,4 +9,4 @@ h1 = t('.headline')
= f.label(:password_confirmation) = f.label(:password_confirmation)
= f.password_field(:password_confirmation, class: 'form-control', required: true) = f.password_field(:password_confirmation, class: 'form-control', required: true)
= f.hidden_field(:activation_token) = f.hidden_field(:activation_token)
.actions = submit_tag(t('.submit'), class: 'btn btn-default') .actions = submit_tag(t('.submit'), class: 'btn btn-primary')

View File

@ -3,5 +3,5 @@ h1 = t('.headline')
= form_tag do = form_tag do
.form-group .form-group
= label_tag(:email, t('activerecord.attributes.internal_user.email')) = label_tag(:email, t('activerecord.attributes.internal_user.email'))
= text_field_tag(:email, params[:email], autofocus: true, class: 'form-control', required: true) = email_field_tag(:email, params[:email], autofocus: true, class: 'form-control', required: true)
.actions = submit_tag(t('.submit'), class: 'btn btn-default') .actions = submit_tag(t('.submit'), class: 'btn btn-primary')

View File

@ -12,7 +12,7 @@ h1 = InternalUser.model_name.human(count: 2)
= f.select(:role_eq, User::ROLES.map { |role| [t("users.roles.#{role}"), role] }, {}, class: 'form-control', prompt: t('activerecord.attributes.internal_user.role')) = f.select(:role_eq, User::ROLES.map { |role| [t("users.roles.#{role}"), role] }, {}, class: 'form-control', prompt: t('activerecord.attributes.internal_user.role'))
.table-responsive .table-responsive
table.table table.table.mt-4
thead thead
tr tr
th = t('activerecord.attributes.internal_user.name') th = t('activerecord.attributes.internal_user.name')

View File

@ -9,4 +9,4 @@ h1 = t('.headline')
= f.label(:password_confirmation) = f.label(:password_confirmation)
= f.password_field(:password_confirmation, class: 'form-control', required: true) = f.password_field(:password_confirmation, class: 'form-control', required: true)
= f.hidden_field(:reset_password_token) = f.hidden_field(:reset_password_token)
.actions = submit_tag(t('.submit'), class: 'btn btn-default') .actions = submit_tag(t('.submit'), class: 'btn btn-primary')

View File

@ -13,22 +13,18 @@ html lang='en'
= yield(:head) = yield(:head)
= csrf_meta_tags = csrf_meta_tags
body body
nav.navbar.navbar-default role='navigation' nav.navbar.navbar-dark.bg-dark.navbar-expand-md.mb-4 role='navigation'
.container .container
.navbar-header button.navbar-toggler data-target='#navbar-collapse' data-toggle='collapse' type='button' aria-expanded='false' aria-label='Toggle navigation'
button.navbar-toggle data-target='#navbar-collapse' data-toggle='collapse' type='button' span.navbar-toggler-icon.sr-only
span.sr-only Toggle navigation .navbar-brand
span.icon-bar i.fa.fa-code
span.icon-bar = application_name
span.icon-bar
.navbar-brand
i.fa.fa-code
= application_name
#navbar-collapse.collapse.navbar-collapse #navbar-collapse.collapse.navbar-collapse
= render('navigation', cached: true) = render('navigation', cached: true)
ul.nav.navbar-nav.navbar-right ul.nav.navbar-nav.ml-auto
= render('locale_selector', cached: true) = render('locale_selector', cached: true)
li = link_to(t('shared.help.link'), '#modal-help', data: {toggle: 'modal'}) li.nav-item.mr-3 = link_to(t('shared.help.link'), '#modal-help', data: {toggle: 'modal'}, class: 'nav-link')
= render('session') = render('session')
.container data-controller=controller_name .container data-controller=controller_name
= render('flash') = render('flash')

View File

@ -1,11 +1,11 @@
= form_for(@proxy_exercise, multipart: true) do |f| = form_for(@proxy_exercise, multipart: true, builder: PagedownFormBuilder) do |f|
= render('shared/form_errors', object: @proxy_exercise) = render('shared/form_errors', object: @proxy_exercise)
.form-group .form-group
= f.label(:title) = f.label(:title)
= f.text_field(:title, class: 'form-control', required: true) = f.text_field(:title, class: 'form-control', required: true)
.form-group .form-group
= f.label(:description) = f.label(:description)
= f.pagedown_editor :description = f.pagedown :description, input_html: { preview: true, rows: 10 }
h3 Exercises h3 Exercises
.table-responsive .table-responsive

View File

@ -6,30 +6,30 @@ h1 = ProxyExercise.model_name.human(count: 2)
= f.search_field(:title_cont, class: 'form-control', placeholder: t('activerecord.attributes.proxy_exercise.title')) = f.search_field(:title_cont, class: 'form-control', placeholder: t('activerecord.attributes.proxy_exercise.title'))
.table-responsive .table-responsive
table.table table.table.mt-4
thead thead
tr tr
th = sort_link(@search, :title, t('activerecord.attributes.proxy_exercise.title')) th.p-1 = sort_link(@search, :title, t('activerecord.attributes.proxy_exercise.title'))
th = t('activerecord.attributes.exercise.token') th.p-1 = t('activerecord.attributes.exercise.token')
th = t('activerecord.attributes.proxy_exercise.files_count') th.p-1 = t('activerecord.attributes.proxy_exercise.files_count')
th colspan=6 = t('shared.actions') th.p-1 colspan=6 = t('shared.actions')
tbody tbody
- @proxy_exercises.each do |proxy_exercise| - @proxy_exercises.each do |proxy_exercise|
tr data-id=proxy_exercise.id tr data-id=proxy_exercise.id
td = link_to(proxy_exercise.title,proxy_exercise) td.p-1.pt-2 = link_to(proxy_exercise.title,proxy_exercise)
td = proxy_exercise.token td.p-1.pt-2 = proxy_exercise.token
td = proxy_exercise.count_files td.p-1.pt-2 = proxy_exercise.count_files
td = link_to(t('shared.edit'), edit_proxy_exercise_path(proxy_exercise)) if policy(proxy_exercise).edit? td.p-1.pt-2 = link_to(t('shared.edit'), edit_proxy_exercise_path(proxy_exercise)) if policy(proxy_exercise).edit?
td td.p-1
.btn-group .btn-group
button.btn.btn-primary-outline.btn-xs.dropdown-toggle data-toggle="dropdown" type="button" = t('shared.actions_button') button.btn.btn-outline-primary.btn-sm.dropdown-toggle data-toggle="dropdown" type="button" = t('shared.actions_button')
span.caret span.caret
span.sr-only Toggle Dropdown span.sr-only Toggle Dropdown
ul.dropdown-menu.pull-right role="menu" ul.dropdown-menu.float-right role="menu"
li = link_to(t('shared.show'), proxy_exercise, 'data-turbolinks' => "false") if policy(proxy_exercise).show? li = link_to(t('shared.show'), proxy_exercise, 'data-turbolinks' => "false", class: 'dropdown-item') if policy(proxy_exercise).show?
li = link_to(t('shared.destroy'), proxy_exercise, data: {confirm: t('shared.confirm_destroy')}, method: :delete) if policy(proxy_exercise).destroy? li = link_to(t('shared.destroy'), proxy_exercise, data: {confirm: t('shared.confirm_destroy')}, method: :delete, class: 'dropdown-item') if policy(proxy_exercise).destroy?
li = link_to(t('.clone'), clone_proxy_exercise_path(proxy_exercise), data: {confirm: t('shared.confirm_destroy')}, method: :post) if policy(proxy_exercise).clone? li = link_to(t('.clone'), clone_proxy_exercise_path(proxy_exercise), data: {confirm: t('shared.confirm_destroy')}, method: :post, class: 'dropdown-item') if policy(proxy_exercise).clone?
= render('shared/pagination', collection: @proxy_exercises) = render('shared/pagination', collection: @proxy_exercises)
p = render('shared/new_button', model: ProxyExercise) p = render('shared/new_button', model: ProxyExercise)

View File

@ -14,7 +14,8 @@ h1
= row(label: 'proxy_exercise.files_count', value: @exercises.count) = row(label: 'proxy_exercise.files_count', value: @exercises.count)
= row(label: 'exercise.description', value: @proxy_exercise.description) = row(label: 'exercise.description', value: @proxy_exercise.description)
= row(label: 'exercise.token', value: @proxy_exercise.token) = row(label: 'exercise.token', value: @proxy_exercise.token)
h3 Exercises
h2.mt-4 Exercises
.table-responsive .table-responsive
table.table table.table
thead thead

View File

@ -1,9 +1,8 @@
br hr
h4 Admin Menu h5.mt-4 Admin Menu
h5 ul.text
ul li = link_to "User's current status of this exercise", statistics_external_user_exercise_path(id: @request_for_comment.exercise_id, external_user_id: @request_for_comment.user_id)
li = link_to "User's current status of this exercise", statistics_external_user_exercise_path(id: @request_for_comment.exercise_id, external_user_id: @request_for_comment.user_id) li = link_to "All exercises of this user", statistics_external_user_path(id: @request_for_comment.user_id)
li = link_to "All exercises of this user", statistics_external_user_path(id: @request_for_comment.user_id) ul.text
ul li = link_to "Implement the exercise yourself", implement_exercise_path(id: @request_for_comment.exercise_id)
li = link_to "Implement the exercise yourself", implement_exercise_path(id: @request_for_comment.exercise_id) li = link_to "Show the exercise", exercise_path(id: @request_for_comment.exercise_id)
li = link_to "Show the exercise", exercise_path(id: @request_for_comment.exercise_id)

View File

@ -4,4 +4,4 @@ button.btn.btn-primary#mark-as-solved-button = t('request_for_comments.mark_as_s
p = t('request_for_comments.write_a_thank_you_node') p = t('request_for_comments.write_a_thank_you_node')
textarea#thank-you-note textarea#thank-you-note
button.btn.btn-primary#send-thank-you-note = t('request_for_comments.send_thank_you_note') button.btn.btn-primary#send-thank-you-note = t('request_for_comments.send_thank_you_note')
button.btn.btn-default#cancel-thank-you-note = t('request_for_comments.cancel_thank_you_note') button.btn.btn-secondary#cancel-thank-you-note = t('request_for_comments.cancel_thank_you_note')

View File

@ -9,7 +9,7 @@ h1 = RequestForComment.model_name.human(count: 2)
= f.select(:solved_not_eq, [[t('request_for_comments.show_all'), 2], [t('request_for_comments.show_unsolved'), 1], [t('request_for_comments.show_solved'), 0]]) = f.select(:solved_not_eq, [[t('request_for_comments.show_all'), 2], [t('request_for_comments.show_unsolved'), 1], [t('request_for_comments.show_solved'), 0]])
.table-responsive .table-responsive
table.table.sortable table.table.sortable.mt-4
thead thead
tr tr
th th
@ -39,7 +39,7 @@ h1 = RequestForComment.model_name.human(count: 2)
- else - else
td = '-' td = '-'
td = request_for_comment.comments_count td = request_for_comment.comments_count
td = request_for_comment.user.displayname td = link_to_if(request_for_comment.user && policy(request_for_comment.user).show?, request_for_comment.user.displayname, request_for_comment.user)
td = t('shared.time.before', time: distance_of_time_in_words_to_now(request_for_comment.created_at)) td = t('shared.time.before', time: distance_of_time_in_words_to_now(request_for_comment.created_at))
td = t('shared.time.before', time: distance_of_time_in_words_to_now(request_for_comment.last_comment.nil? ? request_for_comment.updated_at : request_for_comment.last_comment)) td = t('shared.time.before', time: distance_of_time_in_words_to_now(request_for_comment.last_comment.nil? ? request_for_comment.updated_at : request_for_comment.last_comment))

View File

@ -25,7 +25,7 @@
</div> </div>
<div class="question"> <div class="question">
<h5> <h5 class="mt-4">
<%= t('activerecord.attributes.request_for_comments.question')%> <%= t('activerecord.attributes.request_for_comments.question')%>
</h5> </h5>
<div class="text"> <div class="text">
@ -42,7 +42,7 @@
<div class="testruns"> <div class="testruns">
<% output_runs = testruns.select { |run| run.cause == 'run' } %> <% output_runs = testruns.select { |run| run.cause == 'run' } %>
<% if output_runs.size > 0 %> <% if output_runs.size > 0 %>
<h5><%= t('request_for_comments.runtime_output') %></h5> <h5 class="mt-4"><%= t('request_for_comments.runtime_output') %></h5>
<div class="collapsed testrun-output text"> <div class="collapsed testrun-output text">
<span class="fa fa-chevron-down collapse-button"></span> <span class="fa fa-chevron-down collapse-button"></span>
<% output_runs.each do |testrun| %> <% output_runs.each do |testrun| %>
@ -63,7 +63,7 @@
<% assess_runs = testruns.select { |run| run.cause == 'assess' } %> <% assess_runs = testruns.select { |run| run.cause == 'assess' } %>
<% if assess_runs.size > 0 %> <% if assess_runs.size > 0 %>
<h5><%= t('request_for_comments.test_results') %></h5> <h5 class="mt-4"><%= t('request_for_comments.test_results') %></h5>
<div class="testrun-assess-results"> <div class="testrun-assess-results">
<% assess_runs.each do |testrun| %> <% assess_runs.each do |testrun| %>
<div class="testrun-container"> <div class="testrun-container">
@ -86,7 +86,7 @@
<hr> <hr>
<div class="howto"> <div class="howto">
<h5> <h5 class="mt-4">
<%= t('request_for_comments.howto_title') %> <%= t('request_for_comments.howto_title') %>
</h5> </h5>
<div class="text"> <div class="text">
@ -96,7 +96,7 @@
</div> </div>
</div> </div>
<div class="hidden sanitizer"></div> <div class="d-none sanitizer"></div>
<!-- <!--
do not put a carriage return in the line below. it will be present in the presentation of the source code, otherwise. do not put a carriage return in the line below. it will be present in the presentation of the source code, otherwise.
also, all settings from the rails model needed for the editor configuration in the JavaScript are attached to the editor as data attributes here. also, all settings from the rails model needed for the editor configuration in the JavaScript are attached to the editor as data attributes here.
@ -205,16 +205,16 @@ also, all settings from the rails model needed for the editor configuration in t
<div class="comment-header"> \ <div class="comment-header"> \
<div class="comment-username">' + preprocess(comment.username) + '</div> \ <div class="comment-username">' + preprocess(comment.username) + '</div> \
<div class="comment-date">' + comment.date + '</div> \ <div class="comment-date">' + comment.date + '</div> \
<div class="comment-updated' + (comment.updated ? '' : ' hidden') + '"> \ <div class="comment-updated' + (comment.updated ? '' : ' d-none') + '"> \
<i class="fa fa-pencil" aria-hidden="true"></i> \ <i class="fa fa-pencil" aria-hidden="true"></i> \
<%= t('request_for_comments.comment_edited') %> \ <%= t('request_for_comments.comment_edited') %> \
</div> \ </div> \
</div> \ </div> \
<div class="comment-content">' + commentText + '</div> \ <div class="comment-content">' + commentText + '</div> \
<textarea class="comment-editor">' + commentText + '</textarea> \ <textarea class="comment-editor">' + commentText + '</textarea> \
<div class="comment-actions' + (comment.editable ? '' : ' hidden') + '"> \ <div class="comment-actions' + (comment.editable ? '' : ' d-none') + '"> \
<button class="action-edit btn btn-xs btn-warning"><%= t('shared.edit') %></button> \ <button class="action-edit btn btn-sm btn-warning"><%= t('shared.edit') %></button> \
<button class="action-delete btn btn-xs btn-danger"><%= t('shared.destroy') %></button> \ <button class="action-delete btn btn-sm btn-danger"><%= t('shared.destroy') %></button> \
</div> \ </div> \
</div>'; </div>';
}); });
@ -380,7 +380,7 @@ also, all settings from the rails model needed for the editor configuration in t
$(target).popover('show'); $(target).popover('show');
$(target).on('mouseleave', function () { $(target).on('mouseleave', function () {
$(this).off('mouseleave'); $(this).off('mouseleave');
$(this).popover('destroy'); $(this).popover('dispose');
}); });
} }
@ -440,7 +440,7 @@ also, all settings from the rails model needed for the editor configuration in t
deleteButton.show(); deleteButton.show();
commentContent.show(); commentContent.show();
commentEditor.hide(); commentEditor.hide();
commentUpdated.removeClass('hidden'); commentUpdated.removeClass('d-none');
}); });
} else { } else {
button.text('<%= t('comments.save_update') %>'); button.text('<%= t('comments.save_update') %>');

View File

@ -3,13 +3,14 @@ h1 = t('.headline')
= form_tag(sessions_path) do = form_tag(sessions_path) do
.form-group .form-group
= label_tag(:email, t('activerecord.attributes.internal_user.email')) = label_tag(:email, t('activerecord.attributes.internal_user.email'))
= text_field_tag(:email, params[:email], autofocus: true, class: 'form-control', required: true) = email_field_tag(:email, params[:email], autofocus: true, class: 'form-control', required: true)
.form-group .form-group
= label_tag(:password, t('activerecord.attributes.internal_user.password')) = label_tag(:password, t('activerecord.attributes.internal_user.password'))
= password_field_tag(:password, nil, class: 'form-control', required: true) = password_field_tag(:password, nil, class: 'form-control', required: true)
.checkbox .form-check.form-group
label label.form-check-label
= check_box_tag(:remember_me) // Set values 1 and true/false explicit to allow passing a custom HTML class
= check_box_tag(:remember_me, 1, true, class: 'form-check-input')
= t('.remember_me') = t('.remember_me')
span.pull-right = link_to(t('.forgot_password'), forgot_password_path) span.float-right = link_to(t('.forgot_password'), forgot_password_path)
.actions = submit_tag(t('.link'), class: 'btn btn-default') .actions = submit_tag(t('.link'), class: 'btn btn-primary')

View File

@ -1,3 +1,3 @@
// default value for fetch will always be evaluated even if it is not returned // default value for fetch will always be evaluated even if it is not returned
- link_target = local_assigns.fetch(:path, false) || send(:"edit_#{object.class.name.underscore}_path", object) - link_target = local_assigns.fetch(:path, false) || send(:"edit_#{object.class.name.underscore}_path", object)
= link_to(t('shared.edit'), link_target, class: 'btn btn-default pull-right') = link_to(t('shared.edit'), link_target, class: 'btn btn-secondary float-right')

View File

@ -5,6 +5,6 @@
= row(label: 'file.hidden', value: file.hidden) = row(label: 'file.hidden', value: file.hidden)
= row(label: 'file.read_only', value: file.read_only) = row(label: 'file.read_only', value: file.read_only)
- if file.teacher_defined_test? - if file.teacher_defined_test?
= row(label: 'file.feedback_message', value: render_markdown(file.feedback_message)) = row(label: 'file.feedback_message', value: render_markdown(file.feedback_message), class: 'm-0')
= row(label: 'file.weight', value: file.weight) = row(label: 'file.weight', value: file.weight)
= row(label: 'file.content', value: file.native_file? ? link_to(file.native_file.file.filename, file.native_file.url) : code_tag(file.content)) = row(label: 'file.content', value: file.native_file? ? link_to(file.native_file.file.filename, file.native_file.url) : code_tag(file.content))

View File

@ -1,11 +1,9 @@
.well .card.card-body.bg-light
= search_form_for(@search, class: 'clearfix filter-form form-inline') do |f| = search_form_for(@search, class: 'clearfix filter-form form-inline') do |f|
= yield(f) = yield(f)
.btn-group.pull-right .btn-group.ml-auto
button.btn.btn-default.btn-sm type='submit' = t('shared.apply_filters') button.btn.btn-primary type='submit' = t('shared.apply_filters')
button.btn.btn-default.btn-sm.dropdown-toggle data-toggle='dropdown' type='button' button.btn.btn-primary.dropdown-toggle data-toggle='dropdown' type='button'
span.caret
span.sr-only Toggle Dropdown
ul.dropdown-menu role='menu' ul.dropdown-menu role='menu'
li li
a href=request.path = t('shared.reset_filters') a.dropdown-item href=request.path = t('shared.reset_filters')

View File

@ -2,10 +2,10 @@
.modal-dialog class=local_assigns[:classes] .modal-dialog class=local_assigns[:classes]
.modal-content .modal-content
.modal-header .modal-header
h4#modal-title.modal-title = title
button.close data-dismiss='modal' type='button' button.close data-dismiss='modal' type='button'
span aria-hidden=true &times; span aria-hidden=true &times;
span.sr-only Close span.sr-only Close
h4#modal-title.modal-title = title
.modal-body .modal-body
- if local_assigns.has_key?(:body) - if local_assigns.has_key?(:body)
= body = body

View File

@ -1,3 +1,2 @@
- if (pagination = will_paginate(collection, container: false)).present? - if (pagination = will_paginate(collection, container: false, renderer: WillPaginate::ActionView::Bootstrap4LinkRenderer)).present?
.text-center ul.pagination.justify-content-center = pagination
ul.pagination = pagination

View File

@ -1 +1 @@
= f.submit(class: 'btn btn-default', value: t(object.new_record? ? 'shared.create' : 'shared.update', model: object.class.model_name.human)) = f.submit(class: 'btn btn-primary', value: t(object.new_record? ? 'shared.create' : 'shared.update', model: object.class.model_name.human))

View File

@ -10,11 +10,11 @@
h1 = t('.user_activity') h1 = t('.user_activity')
a href=statistics_graphs_user_activity_history_path = t('.history') a href=statistics_graphs_user_activity_history_path = t('.history')
.spinner .spinner
.graph#user-activity .graph.mb-5#user-activity
.group .group
.title .title
h1 = t('.rfc_activity') h1 = t('.rfc_activity')
a href=statistics_graphs_rfc_activity_history_path = t('.history') a href=statistics_graphs_rfc_activity_history_path = t('.history')
.spinner .spinner
.graph#rfc-activity .graph.mb-5#rfc-activity

View File

@ -9,7 +9,7 @@ h1 = Submission.model_name.human(count: 2)
= f.select(:cause_eq, Submission.select(:cause).distinct.map(&:cause).sort, class: 'form-control', prompt: t('activerecord.attributes.submission.cause')) = f.select(:cause_eq, Submission.select(:cause).distinct.map(&:cause).sort, class: 'form-control', prompt: t('activerecord.attributes.submission.cause'))
.table-responsive .table-responsive
table.table table.table.mt-4
thead thead
tr tr
th = sort_link(@search, :exercise_id, t('activerecord.attributes.submission.exercise')) th = sort_link(@search, :exercise_id, t('activerecord.attributes.submission.exercise'))

View File

@ -1,3 +1,10 @@
- content_for :head do
// Force a full page reload, see https://github.com/turbolinks/turbolinks/issues/326.
Otherwise, code might not be highlighted correctly (race condition)
meta name='turbolinks-visit-control' content='reload'
= javascript_include_tag(asset_path('highlight.min.js', type: :javascript))
= stylesheet_link_tag(asset_path('highlight-default.css', type: :stylesheet))
h1 = @submission h1 = @submission
= row(label: 'submission.exercise', value: link_to(@submission.exercise, @submission.exercise)) = row(label: 'submission.exercise', value: link_to(@submission.exercise, @submission.exercise))
@ -5,9 +12,9 @@ h1 = @submission
= row(label: 'submission.cause', value: t("submissions.causes.#{@submission.cause}")) = row(label: 'submission.cause', value: t("submissions.causes.#{@submission.cause}"))
= row(label: 'submission.score', value: @submission.score) = row(label: 'submission.score', value: @submission.score)
h2 = t('activerecord.attributes.submission.files') h2.mt-4 = t('activerecord.attributes.submission.files')
ul.list-unstyled ul.list-unstyled
- @files.each do |file| - @files.each do |file|
li.panel.panel-default li.card.mt-2
.panel-body = render('shared/file', file: file) .card-body = render('shared/file', file: file)

View File

@ -4,7 +4,7 @@ h1 = @submission
= row(label: 'submission.score', value: @submission.score) = row(label: 'submission.score', value: @submission.score)
= row(label: '.siblings', value: @submission.siblings.count) = row(label: '.siblings', value: @submission.siblings.count)
h2 = t('.history') h2.mt-4 = t('.history')
.table-responsive .table-responsive
table.table table.table

View File

@ -1,6 +1,6 @@
= form_for(@uef) do |f| = form_for(@uef) do |f|
div div
span.badge.pull-right.score span.badge.badge-pill.float-right.score
h1 id="exercise-headline" h1 id="exercise-headline"
= t('activerecord.models.user_exercise_feedback.one') + " " = t('activerecord.models.user_exercise_feedback.one') + " "
@ -8,16 +8,16 @@
= render('shared/form_errors', object: @uef) = render('shared/form_errors', object: @uef)
h4 h4
== t('user_exercise_feedback.description') == t('user_exercise_feedback.description')
#description-panel.lead.description-panel #description-card.lead.description-card
u = t('activerecord.attributes.exercise.description') u = t('activerecord.attributes.exercise.description')
= render_markdown(@exercise.description) = render_markdown(@exercise.description)
.form-group .form-group
= f.text_area(:feedback_text, class: 'form-control', required: true, :rows => "10") = f.text_area(:feedback_text, class: 'form-control', required: true, :rows => "10")
h4 = t('user_exercise_feedback.difficulty') h4 = t('user_exercise_feedback.difficulty')
= f.collection_radio_buttons :difficulty, @texts, :first, :last, html_options={class: "radio-inline"} do |b| = f.collection_radio_buttons :difficulty, @texts, :first, :last, html_options={class: "form-check-inline"} do |b|
= b.label(:class => 'radio') { b.radio_button + b.text } = b.label(:class => 'form-check') { b.radio_button + b.text }
h4 = t('user_exercise_feedback.working_time') h4 = t('user_exercise_feedback.working_time')
= f.collection_radio_buttons :user_estimated_worktime, @times, :first, :last, html_options={class: "radio-inline"} do |b| = f.collection_radio_buttons :user_estimated_worktime, @times, :first, :last, html_options={class: "form-check-inline"} do |b|
= b.label(:class => 'radio') { b.radio_button + b.text } = b.label(:class => 'form-check') { b.radio_button + b.text }
= f.hidden_field(:exercise_id, :value => @exercise.id) = f.hidden_field(:exercise_id, :value => @exercise.id)
.actions = render('shared/submit_button', f: f, object: @uef) .actions = render('shared/submit_button', f: f, object: @uef)