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();
};
};
$.fn.isPresent = function() {
$.fn.isPresent = function() {
return this.length > 0;
};
};
$.fn.scrollTo = function(selector) {
$.fn.scrollTo = function(selector) {
$(this).animate({
scrollTop: $(selector).offset().top - $(this).offset().top + $(this).scrollTop()
}, ANIMATION_DURATION);
};
});
};

View File

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

View File

@ -33,6 +33,7 @@ var CodeOceanEditor = {
<% @config ||= CodeOcean::Config.new(:code_ocean).read(erb: false) %>
sendEvents: <%= @config['codeocean_events'] ? @config['codeocean_events']['enabled'] : false %>,
eventURL: "<%= @config['codeocean_events'] ? events_path : '' %>",
fileTypeURL: "<%= file_types_path %>",
configureEditors: function () {
@ -43,7 +44,7 @@ configureEditors: function () {
confirmDestroy: function (event) {
event.preventDefault();
if (confirm($(this).data('message-confirm'))) {
if (confirm($(event.target).data('message-confirm'))) {
this.destroyFile();
}
},
@ -63,7 +64,7 @@ configureEditors: function () {
if ($('#output-' + index).isPresent()) {
return $('#output-' + index);
} else {
var element = $('<pre>').attr('id', 'output-' + index);
var element = $('<pre class="mt-2">').attr('id', 'output-' + index);
$('#output').append(element);
return element;
}
@ -79,13 +80,13 @@ configureEditors: function () {
}
},
getPanelClass: function (result) {
getCardClass: function (result) {
if (result.stderr && !result.score) {
return 'panel-danger';
return 'card bg-danger text-white';
} else if (result.score < 1) {
return 'panel-warning';
return 'card bg-warning';
} else {
return 'panel-success';
return 'card bg-success text-white';
}
},
@ -107,11 +108,22 @@ configureEditors: function () {
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() {
var frame = $('.frame[data-role="main_file"]').isPresent() ? $('.frame[data-role="main_file"]') : $('.frame').first();
var file_id = frame.find('.editor').data('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.toggleButtonStates();
},
@ -124,11 +136,11 @@ configureEditors: function () {
getProgressBarClass: function (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) {
return 'progress-bar progress-bar-striped progress-bar-warning';
return 'progress-bar progress-bar-striped bg-warning';
} 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',
data: pasteObject.text,
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()
},
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 () {
$('#files').jstree($('#files').data('entries'));
$('#files').on('click', 'li.jstree-leaf', function (event) {
@ -296,8 +323,8 @@ configureEditors: function () {
handleSideBarToggle: function() {
$('#sidebar').toggleClass('sidebar-col').toggleClass('sidebar-col-collapsed');
$('#sidebar-collapsed').toggleClass('hidden');
$('#sidebar-uncollapsed').toggleClass('hidden');
$('#sidebar-collapsed').toggleClass('d-none');
$('#sidebar-uncollapsed').toggleClass('d-none');
},
initializeRegexes: function () {
@ -369,17 +396,17 @@ configureEditors: function () {
return Modernizr.websockets;
},
populatePanel: function (panel, result, index) {
panel.removeClass('panel-default').addClass(this.getPanelClass(result));
panel.find('.panel-title .filename').text(result.filename);
panel.find('.panel-title .number').text(index + 1);
panel.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);
panel.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);
panel.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(' '));
//panel.find('.row .col-sm-9').eq(4).find('a').attr('href', '#output-' + index);
populateCard: function (card, result, index) {
card.addClass(this.getCardClass(result));
card.find('.card-title .filename').text(result.filename);
card.find('.card-title .number').text(index + 1);
card.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(1).text(result.count);
card.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(1).text(result.weight);
card.find('.row .col-sm-9').eq(2).html(result.message);
if (result.error_messages) card.find('.row .col-sm-9').eq(3).text(result.error_messages.join(' '));
//card.find('.row .col-sm-9').eq(4).find('a').attr('href', '#output-' + index);
},
publishCodeOceanEvent: function (payload) {
@ -556,14 +583,14 @@ configureEditors: function () {
},
showOutputBar: function() {
$('#output_sidebar_collapsed').addClass('hidden');
$('#output_sidebar_uncollapsed').removeClass('hidden');
$('#output_sidebar_collapsed').addClass('d-none');
$('#output_sidebar_uncollapsed').removeClass('d-none');
$('#output_sidebar').removeClass('output-col-collapsed').addClass('output-col');
},
hideOutputBar: function() {
$('#output_sidebar_collapsed').removeClass('hidden');
$('#output_sidebar_uncollapsed').addClass('hidden');
$('#output_sidebar_collapsed').removeClass('d-none');
$('#output_sidebar_uncollapsed').addClass('d-none');
$('#output_sidebar').removeClass('output-col').addClass('output-col-collapsed');
},
@ -572,12 +599,12 @@ configureEditors: function () {
},
initializeDescriptionToggle: function() {
$('#exercise-headline').on('click', this.toggleDescriptionPanel.bind(this));
$('a#toggle').on('click', this.toggleDescriptionPanel.bind(this));
$('#exercise-headline').on('click', this.toggleDescriptionCard.bind(this));
$('a#toggle').on('click', this.toggleDescriptionCard.bind(this));
},
toggleDescriptionPanel: function() {
$('#description-panel').toggleClass('description-panel-collapsed').toggleClass('description-panel');
toggleDescriptionCard: function() {
$('#description-card').toggleClass('description-card-collapsed').toggleClass('description-card');
$('#description-symbol').toggleClass('fa-chevron-down').toggleClass('fa-chevron-right');
var toggle = $('a#toggle');
toggle.text(toggle.text() == toggle.data('hide') ? toggle.data('show') : toggle.data('hide'));

View File

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

View File

@ -1,12 +1,12 @@
CodeOceanEditorFlowr = {
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 () {
if (!this.isFlowrEnabled) return;
var flowrUrl = $('#flowrHint').data('url');
var flowrHintBody = $('#flowrHint .panel-body');
var flowrHintBody = $('#flowrHint .card-body');
var queryParameters = {
query: this.flowrOutputBuffer
};
@ -19,8 +19,8 @@ CodeOceanEditorFlowr = {
var resultTile = $(collapsibleTileHtml);
resultTile.find('h4 > a').text(question.title + ' | Found via ' + question.source);
resultTile.find('.panel-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').html(question.body);
resultTile.find('.card-body').append('<a href="' + question.url + '" class="btn btn-primary btn-block">Open this question</a>');
flowrHintBody.append(resultTile);
});

View File

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

View File

@ -155,7 +155,7 @@ CodeOceanEditorSubmissions = {
$('#stop').data('url', submission.stop_url);
this.running = true;
this.showSpinner($('#run'));
$('#score_div').addClass('hidden');
$('#score_div').addClass('d-none');
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
this.initializeSocketForRunning(url);
@ -175,7 +175,7 @@ CodeOceanEditorSubmissions = {
if ($('#test').is(':visible')) {
this.createSubmission('#test', null, function(response) {
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
this.initializeSocketForTesting(url);
}.bind(this));

View File

@ -38,10 +38,10 @@ CodeOceanEditorTurtle = {
showCanvas: function () {
if ($('#turtlediv').isPresent()
&& this.turtlecanvas.hasClass('hidden')) {
&& this.turtlecanvas.hasClass('d-none')) {
// initialize two-column layout
$('#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 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').remove();
$('#files li:last>div:last').removeClass('in').addClass('show')
$('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.
// 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) {
event.preventDefault();
var fileUrl = $(this).data('file-url');
var fileUrl = $(event.target).data('file-url');
if (confirm('<%= I18n.t('shared.confirm_destroy') %>')) {
var jqxhr = $.ajax({
@ -139,9 +138,9 @@ $(document).on('turbolinks:load', function() {
var enableBatchUpdate = function() {
$('thead .batch a').on('click', function(event) {
event.preventDefault();
if (!$(this).data('toggled')) {
$(this).data('toggled', true);
$(this).text($(this).data('text'));
if (!$(event.target).data('toggled')) {
$(event.target).data('toggled', true);
$(event.target).text($(event.target).data('text'));
buildCheckboxes();
} else {
performBatchUpdate();
@ -199,7 +198,7 @@ $(document).on('turbolinks:load', function() {
var observeFileRoleChanges = function() {
$(document).on('change', 'select[name$="[role]"]', function() {
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');
if (is_test_file) {
fields.slideDown();
@ -262,17 +261,13 @@ $(document).on('turbolinks:load', function() {
jqxhr.fail(ajaxError);
}
if ($.isController('exercises')) {
if ($.isController('exercises') || $.isController('submissions')) {
// ignore tags table since it is in the dom before other tables
if ($('table:not(#tags-table)').isPresent()) {
enableBatchUpdate();
} else if ($('.edit_exercise, .new_exercise').isPresent()) {
execution_environments = $('form').data('execution-environments');
file_types = $('form').data('file-types');
// new MarkdownEditor('#exercise_instructions');
// new MarkdownEditor('#exercise_description')
// todo: add an ace editor for each file
new PagedownEditor('#exercise_description');
enableInlineFileCreation();
inferFileAttributes();

View File

@ -14,11 +14,11 @@ $(document).on('turbolinks:load', function() {
var alternative_input = parent.find('.alternative-input');
if (alternative_input.attr('disabled')) {
$(this).text($(this).data('text-toggled'));
$(this).text($(event.target).data('text-toggled'));
original_input.attr('disabled', true).hide();
alternative_input.attr('disabled', false).show();
} else {
$(this).text($(this).data('text-initial'));
$(this).text($(event.target).data('text-initial'));
alternative_input.attr('disabled', true).hide();
original_input.attr('disabled', false).show();
}
@ -26,5 +26,27 @@ $(document).on('turbolinks:load', function() {
});
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);
var session = editor.getSession();
session.setMode('markdown');
session.setMode('ace/mode/markdown');
session.setUseWrapMode(true);
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(){
/*
SortTable

View File

@ -49,7 +49,7 @@ $(document).on('turbolinks:load', function() {
var refreshData = function (callback) {
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',
data: {from: params.get('from'), to: params.get('to'), interval: params.get('interval')},
method: 'GET'

View File

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

View File

@ -5,30 +5,40 @@ $(document).on('turbolinks:load', function() {
var currentSubmission = 0;
var active_file = undefined;
var fileTrees = []
var fileTrees = [];
var editor = undefined;
var fileTypeById = {}
var fileTypeById = {};
var showActiveFile = function() {
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.setTabSize(fileType.indent_size);
session.setValue(active_file.content);
session.setUseSoftTabs(true);
session.setUseWrapMode(true);
showFileTree(currentSubmission);
filetree = $(fileTrees[currentSubmission])
// 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);
};
var initializeFileTree = function() {
$('.files').each(function(index, element) {
fileTree = $(element).jstree($(element).data('entries'));
fileTree.on('click', 'li.jstree-leaf', function() {
var id = parseInt($(this).attr('id'))
var id = parseInt($(this).attr('id'));
_.each(files[currentSubmission], function(file) {
if (file.file_id === id) {
active_file = file;
@ -42,8 +52,8 @@ $(document).on('turbolinks:load', function() {
var showFileTree = function(index) {
$('.files').hide();
$(fileTrees[index].context).show();
}
$(fileTrees[index]).show();
};
if ($.isController('exercises') && $('#timeline').isPresent()) {
@ -85,7 +95,7 @@ $(document).on('turbolinks:load', function() {
if (file.name === active_file.name) {
fileIndex = index;
}
})
});
active_file = currentFiles[fileIndex];
showActiveFile();
});
@ -94,10 +104,10 @@ $(document).on('turbolinks:load', function() {
clearInterval(playInterval);
playInterval = undefined;
playButton.find('span.fa').removeClass('fa-pause').addClass('fa-play')
}
};
playButton.on('click', function(event) {
if (playInterval == undefined) {
if (playInterval === undefined) {
playInterval = setInterval(function() {
if ($.isController('exercises') && $('#timeline').isPresent() && slider.val() < submissions.length - 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();
showActiveFile();
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -38,7 +38,7 @@ class FileTypesController < ApplicationController
end
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, "ace/mode/#{name}"]
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
def to_s
"#{id} [#{name}]"
name
end
end

View File

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

View File

@ -3,17 +3,17 @@
- if model = Kernel.const_get(controller_path.classify) rescue nil
- object = model.find_by(id: params[:id])
- if model.try(:nested_resource?)
li = model.model_name.human(count: 2)
li.breadcrumb-item = model.model_name.human(count: 2)
- if object
li = object
li.breadcrumb-item = object
- 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
li = link_to(object, send(:"#{model.model_name.singular}_path", object))
li.active
li.breadcrumb-item = link_to(object, send(:"#{model.model_name.singular}_path", object))
li.breadcrumb-item.active
- if I18n.translation_present?("shared.#{params[:action]}")
= t("shared.#{params[:action]}")
- else
= t("#{controller_name}.index.#{params[:action]}")
- 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.fixed_error_messages data-message-failure=t('shared.message_failure')
- %w[alert danger info notice success warning].each do |severity|
div.alert.flash class="alert-#{{'alert' => 'warning', 'notice' => 'success'}.fetch(severity, severity)}"
p id="flash-#{severity}" = flash[severity]
span.fa.fa-times
div.alert.flash class="alert-#{{'alert' => 'warning', 'notice' => 'success'}.fetch(severity, severity)} alert-dismissible fade show"
p.mb-0 id="flash-#{severity}" = flash[severity]
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
a.dropdown-toggle data-toggle='dropdown' href='#'
li.nav-item.dropdown
a.nav-link.dropdown-toggle.mx-3 data-toggle='dropdown' href='#'
= t("locales.#{I18n.locale}")
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|
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?)
ul.nav.navbar-nav
li.dropdown
a.dropdown-toggle data-toggle='dropdown' href='#'
li.nav-item.dropdown
a.nav-link.dropdown-toggle.mx-3 data-toggle='dropdown' href='#'
= t('shared.administration')
span.caret
ul.dropdown-menu role='menu'
ul.dropdown-menu.p-0.mt-1 role='menu'
- if current_user.admin?
li = link_to(t('breadcrumbs.dashboard.show'), admin_dashboard_path, 'data-turbolinks' => "false")
li = link_to(t('breadcrumbs.statistics.show'), statistics_path)
li.divider
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, class: 'dropdown-item')
li.dropdown-divider role='separator'
= render('navigation_submenu', title: t('activerecord.models.exercise.other'),
models: [Exercise, ExerciseCollection, ProxyExercise, Tag, Submission], link: exercises_path, cached: true)
= render('navigation_submenu', title: t('navigation.sections.users'), models: [InternalUser, ExternalUser],

View File

@ -1,2 +1,2 @@
- 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
a href=link class="dropdown-toggle" data-toggle="dropdown" = title
ul class="dropdown-menu"
a.dropdown-item.dropdown-toggle href=link data-toggle="dropdown" = title
ul.dropdown-menu.p-0
- models.each do |model|
= render('navigation_collection_link', model: model, cached: true)

View File

@ -1,19 +1,19 @@
- if current_user
li.dropdown
a.dropdown-toggle data-toggle='dropdown' href='#'
li.nav-item.dropdown
a.nav-link.dropdown-toggle data-toggle='dropdown' href='#'
i.fa.fa-user
= current_user
span.caret
ul.dropdown-menu role='menu'
ul.dropdown-menu.p-0.mt-1 role='menu'
- if current_user.internal_user?
li = link_to(t('consumers.show.link'), current_user.consumer) if current_user.consumer
li = link_to(t('internal_users.show.link'), current_user)
li = link_to(t('request_for_comments.index.all'), request_for_comments_path)
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_comment_requests'), my_request_for_comments_path)
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, class: 'dropdown-item')
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, class: 'dropdown-item')
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?
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
li = link_to(sign_in_path) do
li.nav-item = link_to(sign_in_path, class: 'nav-link') do
i.fa.fa-sign-in
= t('sessions.new.link')

View File

@ -9,7 +9,7 @@ h1 = CodeHarborLink.model_name.human(count: 2)
tbody
- @code_harbor_links.each do |code_harbor_link|
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.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)

View File

@ -12,5 +12,5 @@
= 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.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)

View File

@ -9,7 +9,7 @@ h1 = Consumer.model_name.human(count: 2)
tbody
- @consumers.each do |consumer|
tr
td = consumer.name
td = link_to(consumer.name, consumer)
td = link_to(t('shared.show'), 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)

View File

@ -9,8 +9,9 @@
.form-group
= f.label(:regex)
= f.text_field(:regex, class: 'form-control', required: true)
.help-block == t('error_templates.hints.signature')
.form-group
= f.check_box(:important)
.help-block.form-text == t('error_templates.hints.signature')
.form-check.form-group
label.form-check-label
= f.check_box(:important, class: 'form-check-input')
= t('activerecord.attributes.error_template_attribute.important')
.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"
- else
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.regex
td
code = error_template_attribute.regex
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.destroy'), error_template_attribute, data: {confirm: t('shared.confirm_destroy')}, method: :delete)

View File

@ -2,7 +2,10 @@ h1
= @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.key") do
code = @error_template_attribute.key
= row(label: "error_template_attribute.important", value: @error_template_attribute.important)
// todo: used by

View File

@ -9,12 +9,12 @@
.form-group
= f.label(:signature)
= 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
= f.label(:description)
= f.text_field(:description, class: 'form-control')
.form-group
= f.label(:hint)
= 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)

View File

@ -11,7 +11,7 @@ h1 = ErrorTemplate.model_name.human(count: 2)
tbody
- @error_templates.each do |error_template|
tr
td = error_template.name
td = link_to(error_template.name, error_template)
td = error_template.description
td = link_to(error_template.execution_environment)
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: '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))
h3
h2.mt-4
= t 'error_templates.attributes'
.table-responsive
@ -27,9 +29,10 @@ h3
span class="fa fa-star" aria-hidden="true"
- else
span class="fa fa-star-o" aria-hidden="true"
td = attribute.key
td = link_to(attribute.key, attribute)
td = attribute.description
td = attribute.regex
td
code = attribute.regex
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)
@ -37,4 +40,4 @@ h3
= collection_select({}, :error_template_attribute_id,
ErrorTemplateAttribute.where.not(id: @error_template.error_template_attributes.select(:id).to_a).order('important DESC', :key),
: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')
.original-input = f.select(:docker_image, @docker_images, {}, class: 'form-control')
= 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
= f.label(:exposed_ports)
= 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
= 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)
.checkbox
label
= f.check_box(:network_enabled)
.form-check.mb-3
label.form-check-label
= f.check_box(:network_enabled, class: 'form-check-input')
= t('activerecord.attributes.execution_environment.network_enabled')
.form-group
= f.label(:permitted_execution_time)
@ -33,11 +33,11 @@
.form-group
= f.label(:run_command)
= 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
= f.label(:test_command)
= 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
= f.label(:testing_framework)
= 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
- @execution_environments.each do |execution_environment|
tr
td = execution_environment.name
td = link_to(execution_environment.name, execution_environment)
td = link_to(execution_environment.author, execution_environment.author)
td = execution_environment.pool_size
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.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)
- [: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))
- [: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.help', value: render_markdown(@execution_environment.help))

View File

@ -2,7 +2,8 @@
form#exercise-selection
.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})
button.btn.btn-primary#add-exercises = t('exercise_collections.form.add_exercises')

View File

@ -3,9 +3,10 @@
.form-group
= f.label(t('activerecord.attributes.exercise_collections.name'))
= f.text_field(:name, class: 'form-control', required: true)
.form-group
= f.label(t('activerecord.attributes.exercise_collections.use_anomaly_detection'))
= f.check_box(:use_anomaly_detection, {class: 'form-control'})
.form-check.form-group
label.form-check-label
= f.check_box(:use_anomaly_detection, class: 'form-check-input')
= t('activerecord.attributes.exercise_collections.use_anomaly_detection')
.form-group
= f.label(t('activerecord.attributes.exercise_collections.user'))
= 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
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})
.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')
.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.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.table
thead
@ -24,5 +24,5 @@ h4 = t('activerecord.attributes.exercise_collections.exercises')
td = exercise_collection_item.position
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 = 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")

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')
#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
- {time: t('exercises.statistics.average_worktime'),
min: 'min. anomaly threshold',

View File

@ -3,5 +3,5 @@
| &nbsp;
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.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 }

View File

@ -6,8 +6,7 @@
- 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)
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 id='frames' class='editor-col'
div.editor-col.col.p-0 id='frames'
#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', icon: 'fa fa-desktop', id: 'render', label: t('exercises.editor.render'))
@ -24,6 +23,7 @@
= t('exercises.editor.lastsaved')
span
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')

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 class=icon
i class=(label.present? ? icon : "#{icon} m-0")
= 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
.edit-frame
.editor-content.hidden
.editor
.editor-content.d-none
.editor.allow_ace_tooltip

View File

@ -1,18 +1,18 @@
div id='sidebar-collapsed' class=(@exercise.hide_file_tree ? '' : 'hidden')
= 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'))
div id='sidebar-collapsed' class=(@exercise.hide_file_tree ? '' : 'd-none')
= 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?
= 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-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'))
- 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-download', id: 'download-collapsed', label:'', title: t('exercises.editor.download'))
= 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?
= 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' : '')
= 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'))
div id='sidebar-uncollapsed' class=(@exercise.hide_file_tree ? 'd-none' : '')
= 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
#files data-entries=FileTree.new(files).to_js_tree
@ -20,11 +20,11 @@ div id='sidebar-uncollapsed' class=(@exercise.hide_file_tree ? 'hidden' : '')
hr
- 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-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-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', 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-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 enforce-top-margin', icon: 'fa fa-download', id: 'download', label: t('exercises.editor.download'))
= 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?
.input-group.enforce-top-margin

View File

@ -11,5 +11,5 @@
- else
= link_to(file.native_file.file.name_with_extension, file.native_file.url)
- 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

View File

@ -1,19 +1,21 @@
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: '')
div id='output_sidebar_uncollapsed' class='hidden col-sm-12 enforce-bottom-margin' data-message-no-output=t('exercises.implement.no_output')
= 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.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
= 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);"
div.w-100
div.enforce-big-top-margin.d-none id='score_div'
#results
h2 = t('exercises.implement.results')
p.test-count == t('exercises.implement.test_count', count: 0)
ul.list-unstyled
ul#dummies.hidden.list-unstyled
li.panel.panel-default
.panel-heading
h3.panel-title == t('exercises.implement.file', filename: '', number: 0)
.panel-body
ul#dummies.d-none.list-unstyled
li.card.mt-2
.card-header
h3.card-title.m-0 == t('exercises.implement.file', filename: '', number: 0)
.card-body.bg-white.text-dark
= row(label: 'exercises.implement.passed_tests', value: t('shared.out_of', maximum_value: 0, value: 0).html_safe)
= row(label: 'activerecord.attributes.submission.score', value: t('shared.out_of', maximum_value: 0, value: 0).html_safe)
= row(label: 'exercises.implement.feedback')
@ -30,29 +32,30 @@ div id='output_sidebar_uncollapsed' class='hidden col-sm-12 enforce-bottom-margi
- 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'))
- 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
div.enforce-big-top-margin
#turtlediv
canvas#turtlecanvas.hidden width=400 height=400
canvas#turtlecanvas.d-none width=400 height=400
div.enforce-big-top-margin
#hint
.panel.panel-warning
.panel-heading = t('exercises.implement.hint')
.panel-body
.card.bg-warning
.card-header = t('exercises.implement.hint')
.card-body
div.enforce-big-top-margin
#prompt.input-group.hidden
span.input-group-addon data-prompt=t('exercises.editor.input') = t('exercises.editor.input')
#prompt.input-group.d-none
div.input-group-prepend
span.input-group-text data-prompt=t('exercises.editor.input') = t('exercises.editor.input')
input#prompt-input.form-control type='text'
span.input-group-btn
button#prompt-submit.btn.btn-primary type="button" = t('exercises.editor.send')
#error-hints
.heading = t('exercises.implement.error_hints.heading')
ul.body
#output
#output.mt-2
pre = t('exercises.implement.no_output_yet')
- if CodeOcean::Config.new(:code_ocean).read[:flowr][:enabled]
#flowrHint.panel.panel-info data-url=CodeOcean::Config.new(:code_ocean).read[:flowr][:url] role='tab'
.panel-heading = 'Gain more insights here'
.panel-body
#flowrHint.card.card.text-white.bg-info data-url=CodeOcean::Config.new(:code_ocean).read[:flowr][:url] role='tab'
.card-header = 'Gain more insights here'
.card-body

View File

@ -1,41 +1,42 @@
- id = f.object.id
li.panel.panel-default
.panel-heading role="tab" id="heading"
a.file-heading data-toggle="collapse" href="#collapse#{id}"
li.card.mt-2
.card-header role="tab" id="heading"
a.file-heading.collapsed data-toggle="collapse" href="#collapse#{id}"
div.clearfix role="button"
i class="fa" aria-hidden="true"
span = f.object.name
.panel-collapse.collapse class=('in' if f.object.name.nil?) id="collapse#{id}" role="tabpanel"
.panel-body
.card-collapse.collapse class=('in' if f.object.name.nil?) id="collapse#{id}" role="tabpanel"
.card-body
- if policy(f.object).destroy?
.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
= f.label(:name, t('activerecord.attributes.file.name'))
= f.text_field(:name, class: 'form-control')
.form-group
= f.label(:path, t('activerecord.attributes.file.path'))
= f.text_field(:path, class: 'form-control')
.help-block = t('.hints.path')
.help-block.form-text = t('.hints.path')
.form-group
= 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')
.form-group
= 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')
.checkbox
label
= f.check_box(:hidden)
.form-check
label.form-check-label
= f.check_box(:hidden, class: 'form-check-input')
= t('activerecord.attributes.file.hidden')
.checkbox
label
= f.check_box(:read_only)
.form-check.mb-3
label.form-check-label
= f.check_box(:read_only, class: 'form-check-input')
= t('activerecord.attributes.file.read_only')
.test-related-fields style="display: #{f.object.teacher_defined_test? ? 'initial' : 'none'};"
.form-group
= f.label(:name, t('activerecord.attributes.file.feedback_message'))
= 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
= f.label(:role, t('activerecord.attributes.file.weight'))
= 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)
- 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)
.form-group
= f.label(:title)
= f.text_field(:title, class: 'form-control', required: true)
.form-group
= f.label(:description)
= f.pagedown_editor :description
= f.pagedown :description, input_html: { preview: true, rows: 10 }
.form-group
= f.label(:execution_environment_id)
= f.collection_select(:execution_environment_id, @execution_environments, :id, :name, {}, class: 'form-control')
@ -16,34 +16,34 @@
= f.label(:instructions)
= f.hidden_field(:instructions)
.form-control.markdown
.checkbox
label
= f.check_box(:public)
.form-check
label.form-check-label
= f.check_box(:public, class: 'form-check-input')
= t('activerecord.attributes.exercise.public')
.checkbox
label
= f.check_box(:hide_file_tree)
.form-check
label.form-check-label
= f.check_box(:hide_file_tree, class: 'form-check-input')
= t('activerecord.attributes.exercise.hide_file_tree')
.checkbox
label
= f.check_box(:allow_file_creation)
.form-check
label.form-check-label
= f.check_box(:allow_file_creation, class: 'form-check-input')
= t('activerecord.attributes.exercise.allow_file_creation')
.checkbox
label
= f.check_box(:allow_auto_completion)
.form-check.mb-3
label.form-check-label
= f.check_box(:allow_auto_completion, class: 'form-check-input')
= t('activerecord.attributes.exercise.allow_auto_completion')
.form-group
= 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')
ul.list-unstyled.panel-group
li.panel.panel-default
.panel-heading role="tab" id="heading"
ul.list-unstyled.card-group
li.card
.card-header role="tab" id="heading"
a.file-heading data-toggle="collapse" href="#tag-collapse"
div.clearfix role="button"
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.table#tags-table
thead
@ -55,15 +55,15 @@
tr
td = b.check_box
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')
ul#files.list-unstyled.panel-group
ul#files.list-unstyled
= f.fields_for :files do |files_form|
= render('file_form', f: files_form)
a#add-file.btn.btn-default.btn-sm.pull-right href='#' = t('.add_file')
ul#dummies.hidden = f.fields_for(:files, CodeOcean::File.new, child_index: 'index') do |files_form|
a#add-file.btn.btn-secondary.btn-sm.float-right href='#' = t('.add_file')
ul#dummies.d-none = f.fields_for(:files, CodeOcean::File.new, child_index: 'index') do |files_form|
= render('file_form', f: files_form)
.actions = render('shared/submit_button', f: f, object: @exercise)

View File

@ -2,7 +2,7 @@ h5#rfc_intervention_text style='display: none;' = t('exercises.implement.rfc_int
h5 = t('exercises.implement.comment.question')
textarea.form-control#question(style='resize:none;')
textarea.form-control.flex-grow-1#question(style='resize:none;')
p = ''
/ 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

View File

@ -10,21 +10,21 @@ h1 = "#{@exercise} (external user #{@external_user})"
- file_types.add(ActiveSupport::JSON.encode(file.file_type))
- 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
- index = 0
- 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
div class=(@exercise.hide_file_tree ? 'col-sm-12' : 'col-sm-9')
#current-file.editor
.flex-container
button.btn.btn-default id='play-button'
button.btn.btn-secondary id='play-button'
span.fa.fa-play
#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
- index=0
- @submissions.each do |submission|
@ -59,7 +59,7 @@ h1 = "#{@exercise} (external user #{@external_user})"
td =
td = @working_times_until[index] if index > 0
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
.graph-functions-2

View File

@ -8,17 +8,17 @@ h1 = link_to(@exercise, exercise_path(@exercise))
- if @feedbacks.nil? or @feedbacks.size == 0
.no-feedback = t('user_exercise_feedback.no_feedback')
ul.list-unstyled.panel-group
ul.list-unstyled
- @feedbacks.each do |feedback|
li.panel.panel-default
.panel-heading role="tab" id="heading"
li.card.mt-2
.card-header role="tab" id="heading"
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))
- if feedback.anomaly_notification
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
.panel-collapse role="tabpanel"
.panel-body.feedback
.card-collapse role="tabpanel"
.card-body.feedback
.text = feedback.feedback_text
.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

View File

@ -2,13 +2,13 @@
#editor-column.col-md-12
.exercise.clearfix
div
span.badge.pull-right.score
span.badge.badge-pill.float-right.score
h1 id="exercise-headline"
i class="fa fa-chevron-down" id="description-symbol"
= @exercise.title
#description-panel.lead.description-panel
#description-card.lead.description-card
= render_markdown(@exercise.description)
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'))
.table-responsive
table.table
table.table.mt-4
thead
tr
th = sort_link(@search, :title, t('activerecord.attributes.exercise.title'))
th = sort_link(@search, :execution_environment_id, t('activerecord.attributes.exercise.execution_environment'))
th = t('.test_files')
th = t('activerecord.attributes.exercise.maximum_score')
th = t('activerecord.attributes.exercise.tags')
th = t('activerecord.attributes.exercise.difficulty')
th
th.p-1 = sort_link(@search, :title, t('activerecord.attributes.exercise.title'))
th.p-1 = sort_link(@search, :execution_environment_id, t('activerecord.attributes.exercise.execution_environment'))
th.p-1 = t('.test_files')
th.p-1 = t('activerecord.attributes.exercise.maximum_score')
th.p-1 = t('activerecord.attributes.exercise.tags')
th.p-1 = t('activerecord.attributes.exercise.difficulty')
th.p-1
= t('activerecord.attributes.exercise.public')
- if policy(Exercise).batch_update?
br
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
- @exercises.each do |exercise|
tr data-id=exercise.id
td = exercise.title
td = 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 = exercise.maximum_score
td = exercise.exercise_tags.count
td = exercise.expected_difficulty
td.public data-value=exercise.public? = symbol_for(exercise.public?)
td = 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 = link_to(t('shared.statistics'), statistics_exercise_path(exercise), 'data-turbolinks' => "false") if policy(exercise).statistics?
td.p-1.pt-2 = link_to(exercise.title, exercise, 'data-turbolinks' => "false") if policy(exercise).show?
td.p-1.pt-2 = link_to_if(exercise.execution_environment && policy(exercise.execution_environment).show?, exercise.execution_environment, exercise.execution_environment)
td.p-1.pt-2 = exercise.files.teacher_defined_tests.count
td.p-1.pt-2 = exercise.maximum_score
td.p-1.pt-2 = exercise.exercise_tags.count
td.p-1.pt-2 = exercise.expected_difficulty
td.p-1.pt-2.public data-value=exercise.public? = symbol_for(exercise.public?)
td.p-1.pt-2 = link_to(t('shared.edit'), edit_exercise_path(exercise)) if policy(exercise).edit?
td.p-1.pt-2 = link_to(t('.implement'), implement_exercise_path(exercise)) if policy(exercise).implement?
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
button.btn.btn-primary-outline.btn-xs.dropdown-toggle data-toggle="dropdown" type="button" = t('shared.actions_button')
span.caret
span.sr-only Toggle Dropdown
ul.dropdown-menu.pull-right role="menu"
li = link_to(t('shared.show'), exercise, 'data-turbolinks' => "false") if policy(exercise).show?
li = link_to(t('activerecord.models.user_exercise_feedback.other'), feedback_exercise_path(exercise)) if policy(exercise).feedback?
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?
button.btn.btn-outline-primary.btn-sm.dropdown-toggle data-toggle="dropdown" type="button" = t('shared.actions_button')
ul.dropdown-menu.float-right role="menu"
li = link_to(t('shared.show'), exercise, 'data-turbolinks' => "false", class: 'dropdown-item') if policy(exercise).show?
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.destroy'), exercise, data: {confirm: t('shared.confirm_destroy')}, method: :delete, class: 'dropdown-item') if policy(exercise).destroy?
li = link_to(t('.clone'), clone_exercise_path(exercise), data: {confirm: t('shared.confirm_destroy')}, method: :post, class: 'dropdown-item') if policy(exercise).clone?
= render('shared/pagination', collection: @exercises)
p = render('shared/new_button', model: Exercise)

View File

@ -12,7 +12,7 @@ h1
= 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.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.instructions', value: render_markdown(@exercise.instructions))
= 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.allow_file_creation', value: @exercise.allow_file_creation?)
= 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.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|
li.panel.panel-default
.panel-heading role="tab" id="heading"
a.file-heading data-toggle="collapse" data-parent="#files" href=".collapse#{file.id}"
li.card.mt-2
.card-header role="tab" id="heading"
a.file-heading.collapsed data-toggle="collapse" data-parent="#files" href=".collapse#{file.id}"
div.clearfix role="button"
i class="fa" aria-hidden="true"
span = file.name_with_extension
// probably set an icon here that shows that the rows can be collapsed
//span.pull-right.collapse.in class="collapse#{file.id}" &#9788
.panel-collapse.collapse class="collapse#{file.id}" role="tabpanel"
.panel-body
.card-collapse.collapse class="collapse#{file.id}" role="tabpanel"
.card-body
- 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)

View File

@ -32,7 +32,7 @@ h1 = @exercise
-working_time = @exercise.average_working_time_for(user.id) or 0
-working_time_array.push working_time
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
div#chart_1
hr

View File

@ -4,9 +4,9 @@ h1 = @user.name
//= row(label: 'external_user.email', value: @user.email)
= 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
.spinner
= t('.loading_tag_statistics')

View File

@ -10,7 +10,7 @@ h1 = FileTemplate.model_name.human(count: 2)
tbody
- @file_templates.each do |file_template|
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(t('shared.show'), file_template)
td = link_to(t('shared.edit'), edit_file_template_path(file_template))

View File

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

View File

@ -11,7 +11,7 @@ h1 = FileType.model_name.human(count: 2)
tbody
- @file_types.each do |file_type|
tr
td = file_type.name
td = link_to(file_type.name, file_type)
td = link_to(file_type.author, file_type.author)
td = file_type.file_extension
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
= f.label(:name)
= f.text_field(:name, class: 'form-control', required: true)
.form
.form-group
= f.label(:locale)
= f.select(:locale, I18n.available_locales.map { |locale| [t("locales.#{locale}"), locale] }, {}, class: 'form-control')
.form-group
= f.label(:message)
= 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
= f.label(:regular_expression)
= 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)

View File

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

View File

@ -9,4 +9,4 @@ h1 = t('.headline')
= f.label(:password_confirmation)
= f.password_field(:password_confirmation, class: 'form-control', required: true)
= 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-group
= label_tag(:email, t('activerecord.attributes.internal_user.email'))
= text_field_tag(:email, params[:email], autofocus: true, class: 'form-control', required: true)
.actions = submit_tag(t('.submit'), class: 'btn btn-default')
= email_field_tag(:email, params[:email], autofocus: true, class: 'form-control', required: true)
.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'))
.table-responsive
table.table
table.table.mt-4
thead
tr
th = t('activerecord.attributes.internal_user.name')

View File

@ -9,4 +9,4 @@ h1 = t('.headline')
= f.label(:password_confirmation)
= f.password_field(:password_confirmation, class: 'form-control', required: true)
= 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)
= csrf_meta_tags
body
nav.navbar.navbar-default role='navigation'
nav.navbar.navbar-dark.bg-dark.navbar-expand-md.mb-4 role='navigation'
.container
.navbar-header
button.navbar-toggle data-target='#navbar-collapse' data-toggle='collapse' type='button'
span.sr-only Toggle navigation
span.icon-bar
span.icon-bar
span.icon-bar
button.navbar-toggler data-target='#navbar-collapse' data-toggle='collapse' type='button' aria-expanded='false' aria-label='Toggle navigation'
span.navbar-toggler-icon.sr-only
.navbar-brand
i.fa.fa-code
= application_name
#navbar-collapse.collapse.navbar-collapse
= render('navigation', cached: true)
ul.nav.navbar-nav.navbar-right
ul.nav.navbar-nav.ml-auto
= 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')
.container data-controller=controller_name
= 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)
.form-group
= f.label(:title)
= f.text_field(:title, class: 'form-control', required: true)
.form-group
= f.label(:description)
= f.pagedown_editor :description
= f.pagedown :description, input_html: { preview: true, rows: 10 }
h3 Exercises
.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'))
.table-responsive
table.table
table.table.mt-4
thead
tr
th = sort_link(@search, :title, t('activerecord.attributes.proxy_exercise.title'))
th = t('activerecord.attributes.exercise.token')
th = t('activerecord.attributes.proxy_exercise.files_count')
th colspan=6 = t('shared.actions')
th.p-1 = sort_link(@search, :title, t('activerecord.attributes.proxy_exercise.title'))
th.p-1 = t('activerecord.attributes.exercise.token')
th.p-1 = t('activerecord.attributes.proxy_exercise.files_count')
th.p-1 colspan=6 = t('shared.actions')
tbody
- @proxy_exercises.each do |proxy_exercise|
tr data-id=proxy_exercise.id
td = link_to(proxy_exercise.title,proxy_exercise)
td = proxy_exercise.token
td = 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(proxy_exercise.title,proxy_exercise)
td.p-1.pt-2 = proxy_exercise.token
td.p-1.pt-2 = proxy_exercise.count_files
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
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.sr-only Toggle Dropdown
ul.dropdown-menu.pull-right role="menu"
li = link_to(t('shared.show'), proxy_exercise, 'data-turbolinks' => "false") 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('.clone'), clone_proxy_exercise_path(proxy_exercise), data: {confirm: t('shared.confirm_destroy')}, method: :post) if policy(proxy_exercise).clone?
ul.dropdown-menu.float-right role="menu"
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, 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, class: 'dropdown-item') if policy(proxy_exercise).clone?
= render('shared/pagination', collection: @proxy_exercises)
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: 'exercise.description', value: @proxy_exercise.description)
= row(label: 'exercise.token', value: @proxy_exercise.token)
h3 Exercises
h2.mt-4 Exercises
.table-responsive
table.table
thead

View File

@ -1,9 +1,8 @@
br
h4 Admin Menu
h5
ul
hr
h5.mt-4 Admin Menu
ul.text
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)
ul
ul.text
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)

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')
textarea#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]])
.table-responsive
table.table.sortable
table.table.sortable.mt-4
thead
tr
th
@ -39,7 +39,7 @@ h1 = RequestForComment.model_name.human(count: 2)
- else
td = '-'
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.last_comment.nil? ? request_for_comment.updated_at : request_for_comment.last_comment))

View File

@ -25,7 +25,7 @@
</div>
<div class="question">
<h5>
<h5 class="mt-4">
<%= t('activerecord.attributes.request_for_comments.question')%>
</h5>
<div class="text">
@ -42,7 +42,7 @@
<div class="testruns">
<% output_runs = testruns.select { |run| run.cause == 'run' } %>
<% 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">
<span class="fa fa-chevron-down collapse-button"></span>
<% output_runs.each do |testrun| %>
@ -63,7 +63,7 @@
<% assess_runs = testruns.select { |run| run.cause == 'assess' } %>
<% 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">
<% assess_runs.each do |testrun| %>
<div class="testrun-container">
@ -86,7 +86,7 @@
<hr>
<div class="howto">
<h5>
<h5 class="mt-4">
<%= t('request_for_comments.howto_title') %>
</h5>
<div class="text">
@ -96,7 +96,7 @@
</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.
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-username">' + preprocess(comment.username) + '</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> \
<%= t('request_for_comments.comment_edited') %> \
</div> \
</div> \
<div class="comment-content">' + commentText + '</div> \
<textarea class="comment-editor">' + commentText + '</textarea> \
<div class="comment-actions' + (comment.editable ? '' : ' hidden') + '"> \
<button class="action-edit btn btn-xs btn-warning"><%= t('shared.edit') %></button> \
<button class="action-delete btn btn-xs btn-danger"><%= t('shared.destroy') %></button> \
<div class="comment-actions' + (comment.editable ? '' : ' d-none') + '"> \
<button class="action-edit btn btn-sm btn-warning"><%= t('shared.edit') %></button> \
<button class="action-delete btn btn-sm btn-danger"><%= t('shared.destroy') %></button> \
</div> \
</div>';
});
@ -380,7 +380,7 @@ also, all settings from the rails model needed for the editor configuration in t
$(target).popover('show');
$(target).on('mouseleave', function () {
$(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();
commentContent.show();
commentEditor.hide();
commentUpdated.removeClass('hidden');
commentUpdated.removeClass('d-none');
});
} else {
button.text('<%= t('comments.save_update') %>');

View File

@ -3,13 +3,14 @@ h1 = t('.headline')
= form_tag(sessions_path) do
.form-group
= 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
= label_tag(:password, t('activerecord.attributes.internal_user.password'))
= password_field_tag(:password, nil, class: 'form-control', required: true)
.checkbox
label
= check_box_tag(:remember_me)
.form-check.form-group
label.form-check-label
// 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')
span.pull-right = link_to(t('.forgot_password'), forgot_password_path)
.actions = submit_tag(t('.link'), class: 'btn btn-default')
span.float-right = link_to(t('.forgot_password'), forgot_password_path)
.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
- 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.read_only', value: file.read_only)
- 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.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|
= yield(f)
.btn-group.pull-right
button.btn.btn-default.btn-sm type='submit' = t('shared.apply_filters')
button.btn.btn-default.btn-sm.dropdown-toggle data-toggle='dropdown' type='button'
span.caret
span.sr-only Toggle Dropdown
.btn-group.ml-auto
button.btn.btn-primary type='submit' = t('shared.apply_filters')
button.btn.btn-primary.dropdown-toggle data-toggle='dropdown' type='button'
ul.dropdown-menu role='menu'
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-content
.modal-header
h4#modal-title.modal-title = title
button.close data-dismiss='modal' type='button'
span aria-hidden=true &times;
span.sr-only Close
h4#modal-title.modal-title = title
.modal-body
- if local_assigns.has_key?(:body)
= body

View File

@ -1,3 +1,2 @@
- if (pagination = will_paginate(collection, container: false)).present?
.text-center
ul.pagination = pagination
- if (pagination = will_paginate(collection, container: false, renderer: WillPaginate::ActionView::Bootstrap4LinkRenderer)).present?
ul.pagination.justify-content-center = 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')
a href=statistics_graphs_user_activity_history_path = t('.history')
.spinner
.graph#user-activity
.graph.mb-5#user-activity
.group
.title
h1 = t('.rfc_activity')
a href=statistics_graphs_rfc_activity_history_path = t('.history')
.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'))
.table-responsive
table.table
table.table.mt-4
thead
tr
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
= 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.score', value: @submission.score)
h2 = t('activerecord.attributes.submission.files')
h2.mt-4 = t('activerecord.attributes.submission.files')
ul.list-unstyled
- @files.each do |file|
li.panel.panel-default
.panel-body = render('shared/file', file: file)
li.card.mt-2
.card-body = render('shared/file', file: file)

View File

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

View File

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