From d07b4f436e37628b24c602a9f7eee86489c448e9 Mon Sep 17 00:00:00 2001 From: Sebastian Serth Date: Tue, 5 May 2020 15:34:17 +0200 Subject: [PATCH] Add more actions to show exercise --- app/assets/javascripts/exercises.js.erb | 734 ++++++++++++------------ app/views/exercises/show.html.slim | 14 +- 2 files changed, 381 insertions(+), 367 deletions(-) diff --git a/app/assets/javascripts/exercises.js.erb b/app/assets/javascripts/exercises.js.erb index e24f0b7e..000637e6 100644 --- a/app/assets/javascripts/exercises.js.erb +++ b/app/assets/javascripts/exercises.js.erb @@ -1,397 +1,399 @@ -$(document).on('turbolinks:load', function() { - // ruby part adds the relative_url_root, if it is set. - var ACE_FILES_PATH = '<%= (defined? Rails.application.config.relative_url_root) && Rails.application.config.relative_url_root != nil && Rails.application.config.relative_url_root != "" ? Rails.application.config.relative_url_root : "" %>' + '/assets/ace/'; - var THEME = 'ace/theme/textmate'; +$(document).on('turbolinks:load', function () { + // ruby part adds the relative_url_root, if it is set. + var ACE_FILES_PATH = '<%= (defined? Rails.application.config.relative_url_root) && Rails.application.config.relative_url_root != nil && Rails.application.config.relative_url_root != "" ? Rails.application.config.relative_url_root : "" %>' + '/assets/ace/'; + var THEME = 'ace/theme/textmate'; - var TAB_KEY_CODE = 9; + var TAB_KEY_CODE = 9; - var execution_environments; - var file_types; + var execution_environments; + var file_types; + var configureEditors = function () { + _.each(['modePath', 'themePath', 'workerPath'], function (attribute) { + ace.config.set(attribute, ACE_FILES_PATH); + }); + }; - var configureEditors = function() { - _.each(['modePath', 'themePath', 'workerPath'], function(attribute) { - ace.config.set(attribute, ACE_FILES_PATH); - }); - }; + var initializeEditor = function (index, element) { + var editor = ace.edit(element); - var initializeEditor = function(index, element) { - var editor = ace.edit(element); + var document = editor.getSession().getDocument(); + // insert pre-existing code into editor. we have to use insertLines, otherwise the deltas are not properly added + var file_id = $(element).data('file-id'); + var content = $('.editor-content[data-file-id=' + file_id + ']'); - var document = editor.getSession().getDocument(); - // insert pre-existing code into editor. we have to use insertLines, otherwise the deltas are not properly added - var file_id = $(element).data('file-id'); - var content = $('.editor-content[data-file-id=' + file_id + ']'); + document.insertLines(0, content.text().split(/\n/)); + // remove last (empty) that is there by default line + document.removeLines(document.getLength() - 1, document.getLength() - 1); + editor.setReadOnly($(element).data('read-only') !== undefined); + editor.setShowPrintMargin(false); + editor.setTheme(THEME); - document.insertLines(0, content.text().split(/\n/)); - // remove last (empty) that is there by default line - document.removeLines(document.getLength() - 1, document.getLength() - 1); - editor.setReadOnly($(element).data('read-only') !== undefined); - editor.setShowPrintMargin(false); - editor.setTheme(THEME); + var textarea = $('textarea[id="exercise_files_attributes_' + index + '_content"]'); + var content = textarea.val(); - var textarea = $('textarea[id="exercise_files_attributes_'+index+'_content"]'); - var content = textarea.val(); + if (content != undefined) { + editor.getSession().setValue(content); + editor.getSession().on('change', function () { + textarea.val(editor.getSession().getValue()); + }); + } - if (content != undefined) - { - editor.getSession().setValue(content); - editor.getSession().on('change', function(){ - textarea.val(editor.getSession().getValue()); - }); + editor.commands.bindKey("ctrl+alt+0", null); + var session = editor.getSession(); + session.setMode($(element).data('mode')); + session.setTabSize($(element).data('indent-size')); + session.setUseSoftTabs(true); + session.setUseWrapMode(true); } - editor.commands.bindKey("ctrl+alt+0", null); - var session = editor.getSession(); - session.setMode($(element).data('mode')); - session.setTabSize($(element).data('indent-size')); - session.setUseSoftTabs(true); - session.setUseWrapMode(true); - } + var initializeEditors = function () { + // initialize ace editors for all code textareas in the dom except the last one. The last one is the dummy area for new files, which is cloned when needed. + // this one must NOT! be initialized. + $('.editor:not(:last)').each(initializeEditor) + }; - var initializeEditors = function() { - // initialize ace editors for all code textareas in the dom except the last one. The last one is the dummy area for new files, which is cloned when needed. - // this one must NOT! be initialized. - $('.editor:not(:last)').each(initializeEditor) - }; - - var addFileForm = function(event) { - event.preventDefault(); - var element = $('#dummies').children().first().clone(); - - // the timestamp is used here, since it is most probably unique. This is strange, but was originally designed that way. - var latestTextAreaIndex = new Date().getTime(); - var html = $('
').append(element).html().replace(/index/g, latestTextAreaIndex); - $('#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>div:last').removeClass('in').addClass('show') - $('body, html').scrollTo('#add-file'); - - // 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. - initializeEditor(latestTextAreaIndex, $('#files .editor').last()[0]); - }; - - var removeFileForm = function(fileUrl) { - // validate fileUrl - var matches = fileUrl.match(/files\/(\d+)/); - if (matches) { - // select the file form based on the delete button - var fileForm = $('*[data-file-url="' + fileUrl + '"]').parent().parent().parent(); - fileForm.remove(); - - // now remove the hidden input representing the file - var fileId = matches[1]; - var input = $('input[type="hidden"][value="' + fileId + '"]') - input.remove() - } - } - - var deleteFile = function(event) { - event.preventDefault(); - var fileUrl = $(event.target).data('file-url'); - - if (confirm('<%= I18n.t('shared.confirm_destroy') %>')) { - var jqxhr = $.ajax({ - // normal file path (without json) would destroy the context object (the exercise) as well, due to redirection - // to the context after the :destroy action. - contentType: 'Application/json', - url: fileUrl + '.json', - method: 'DELETE' - }); - jqxhr.done(function () { - removeFileForm(fileUrl) - }); - jqxhr.fail(ajaxError); - } - } - - var ajaxError = function(error) { - $.flash.danger({ - text: $('#flash').data('message-failure') - }); - Sentry.captureException(JSON.stringify(error)); - }; - - var buildCheckboxes = function() { - $('tbody tr').each(function(index, element) { - var td = $('td.public', element); - var checkbox = $('', { - checked: td.data('value'), - type: 'checkbox' - }); - td.on('click', function(event) { + var addFileForm = function (event) { event.preventDefault(); - checkbox.prop('checked', !checkbox.prop('checked')); - }); - td.html(checkbox); - }); - }; + var element = $('#dummies').children().first().clone(); - var discardFile = function(event) { - event.preventDefault(); - $(this).parents('li').remove(); - }; + // the timestamp is used here, since it is most probably unique. This is strange, but was originally designed that way. + var latestTextAreaIndex = new Date().getTime(); + var html = $('
').append(element).html().replace(/index/g, latestTextAreaIndex); + $('#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>div:last').removeClass('in').addClass('show') + $('body, html').scrollTo('#add-file'); - var enableBatchUpdate = function() { - $('thead .batch a').on('click', function(event) { - event.preventDefault(); - if (!$(event.target).data('toggled')) { - $(event.target).data('toggled', true); - $(event.target).text($(event.target).data('text')); - buildCheckboxes(); - } else { - performBatchUpdate(); - } - }); - }; + // 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. + initializeEditor(latestTextAreaIndex, $('#files .editor').last()[0]); + }; - var enableInlineFileCreation = function() { - $('#add-file').on('click', addFileForm); - $('#files').on('click', 'li .discard-file', discardFile); - $('form.edit_exercise, form.new_exercise').on('submit', function() { - $('#dummies').html(''); - }); - $('.delete-file').on('click', deleteFile); - }; + var removeFileForm = function (fileUrl) { + // validate fileUrl + var matches = fileUrl.match(/files\/(\d+)/); + if (matches) { + // select the file form based on the delete button + var fileForm = $('*[data-file-url="' + fileUrl + '"]').parent().parent().parent(); + fileForm.remove(); - var findFileTypeByFileExtension = function(file_extension) { - return _.find(file_types, function(file_type) { - return file_type.file_extension === file_extension; - }) || {}; - }; - - var getSelectedExecutionEnvironment = function() { - return _.find(execution_environments, function(execution_environment) { - return execution_environment.id === parseInt($('#exercise_execution_environment_id').val()); - }) || {}; - }; - - var highlightCode = function() { - $('pre code').each(function(index, element) { - hljs.highlightBlock(element); - }); - }; - - var inferFileAttributes = function() { - $(document).on('change', 'input[type="file"]', function() { - var filename = $(this).val().split(/\\|\//g).pop(); - var file_extension = '.' + filename.split('.')[1]; - var file_type = findFileTypeByFileExtension(file_extension); - var name = filename.split('.')[0]; - var parent = $(this).parents('li'); - parent.find('input[name*="name"]').val(name); - parent.find('select[name*="file_type_id"]').val(file_type.id).trigger('chosen:updated'); - }); - }; - - var insertTabAtCursor = function(textarea) { - var selection_start = textarea.get(0).selectionStart; - var selection_end = textarea.get(0).selectionEnd; - textarea.val(textarea.val().substring(0, selection_start) + "\t" + textarea.val().substring(selection_end)); - textarea.get(0).selectionStart = selection_start + 1; - textarea.get(0).selectionEnd = selection_start + 1; - }; - - var observeFileRoleChanges = function() { - $(document).on('change', 'select[name$="[role]"]', function() { - var is_test_file = $(this).val() === 'teacher_defined_test'; - var parent = $(this).parents('.card'); - var fields = parent.find('.test-related-fields'); - if (is_test_file) { - fields.slideDown(); - } else { - fields.slideUp(); - parent.find('[name$="[feedback_message]"]').val(''); - parent.find('[name$="[weight]"]').val(1); - } - }); - }; - - var old_execution_environment = $('#exercise_execution_environment_id').val(); - var observeExecutionEnvironment = function() { - $('#exercise_execution_environment_id').on('change', function(){ - new_execution_environment = $('#exercise_execution_environment_id').val(); - - if(new_execution_environment == '' && !$('#exercise_unpublished').prop('checked')){ - if(confirm('<%= I18n.t('exercises.form.unpublish_warning') %>')){ - $('#exercise_unpublished').prop('checked', true); - } else { - return $('#exercise_execution_environment_id').val(old_execution_environment).trigger("chosen:updated"); + // now remove the hidden input representing the file + var fileId = matches[1]; + var input = $('input[type="hidden"][value="' + fileId + '"]') + input.remove() } - } - old_execution_environment = new_execution_environment; - }); - }; + } - var observeUnpublishedState = function() { - $('#exercise_unpublished').on('change', function(){ - if($('#exercise_unpublished').prop('checked')){ - if(!confirm('<%= I18n.t('exercises.form.unpublish_warning') %>')){ - $('#exercise_unpublished').prop('checked', false); - } - } else if($('#exercise_execution_environment_id').val() == '') { - alert('<%= I18n.t('exercises.form.no_execution_environment_selected') %>'); - $('#exercise_unpublished').prop('checked', true); - } - }) - }; - - var observeExportButtons = function(){ - $('.export-start').on('click', function(e){ - e.preventDefault(); - $('#export-modal').modal({ - height: 250 - }); - $('#export-modal').modal('show'); - exportExerciseStart($(this).data().exerciseId); - }); - $('body').on('click', '.export-retry-button', function(){ - exportExerciseStart($(this).data().exerciseId); - }); - $('body').on('click', '.export-action', function(){ - exportExerciseConfirm($(this).data().exerciseId); - }); - } - - var exportExerciseStart = function(exerciseID) { - var $exerciseDiv = $('#export-exercise'); - var $messageDiv = $exerciseDiv.children('.export-message'); - var $actionsDiv = $exerciseDiv.children('.export-exercise-actions'); - - $messageDiv.removeClass('export-failure'); - - $messageDiv.html('<%= I18n.t('exercises.export_codeharbor.checking_codeharbor') %>'); - $actionsDiv.html('
'); - - return $.ajax({ - type: 'POST', - url: '/exercises/' + exerciseID + '/export_external_check', - dataType: 'json', - success: function(response) { - $messageDiv.html(response.message); - return $actionsDiv.html(response.actions); - }, - error: function(a, b, c) { - return alert('error:' + c); - } - }); - }; - - var exportExerciseConfirm = function(exerciseID) { - var $exerciseDiv = $('#export-exercise'); - var $messageDiv = $exerciseDiv.children('.export-message'); - var $actionsDiv = $exerciseDiv.children('.export-exercise-actions'); - - return $.ajax({ - type: 'POST', - url: '/exercises/' + exerciseID + '/export_external_confirm', - dataType: 'json', - success: function(response) { - $messageDiv.html(response.message) - $actionsDiv.html(response.actions); - - if(response.status == 'success') { - $messageDiv.addClass('export-success'); - setTimeout((function() { - $('#export-modal').modal('hide'); - $messageDiv.html('').removeClass('export-success'); - }), 3000); - } else { - $messageDiv.addClass('export-failure'); - } - }, - error: function(a, b, c) { - return alert('error:' + c); - } - }); - }; - - var overrideTextareaTabBehavior = function() { - $('.form-group textarea[name$="[content]"]').on('keydown', function(event) { - if (event.which === TAB_KEY_CODE) { + var deleteFile = function (event) { event.preventDefault(); - insertTabAtCursor($(this)); - } - }); - }; + var fileUrl = $(event.target).data('file-url'); - var performBatchUpdate = function() { - var jqxhr = $.ajax({ - data: { - exercises: _.map($('tbody tr'), function(element) { - return { - id: $(element).data('id'), - public: $('.public input', element).prop('checked') - }; + if (confirm('<%= I18n.t('shared.confirm_destroy') %>')) { + var jqxhr = $.ajax({ + // normal file path (without json) would destroy the context object (the exercise) as well, due to redirection + // to the context after the :destroy action. + contentType: 'Application/json', + url: fileUrl + '.json', + method: 'DELETE' + }); + jqxhr.done(function () { + removeFileForm(fileUrl) + }); + jqxhr.fail(ajaxError); + } + } + + var ajaxError = function (error) { + $.flash.danger({ + text: $('#flash').data('message-failure') + }); + Sentry.captureException(JSON.stringify(error)); + }; + + var buildCheckboxes = function () { + $('tbody tr').each(function (index, element) { + var td = $('td.public', element); + var checkbox = $('', { + checked: td.data('value'), + type: 'checkbox' + }); + td.on('click', function (event) { + event.preventDefault(); + checkbox.prop('checked', !checkbox.prop('checked')); + }); + td.html(checkbox); + }); + }; + + var discardFile = function (event) { + event.preventDefault(); + $(this).parents('li').remove(); + }; + + var enableBatchUpdate = function () { + $('thead .batch a').on('click', function (event) { + event.preventDefault(); + if (!$(event.target).data('toggled')) { + $(event.target).data('toggled', true); + $(event.target).text($(event.target).data('text')); + buildCheckboxes(); + } else { + performBatchUpdate(); + } + }); + }; + + var enableInlineFileCreation = function () { + $('#add-file').on('click', addFileForm); + $('#files').on('click', 'li .discard-file', discardFile); + $('form.edit_exercise, form.new_exercise').on('submit', function () { + $('#dummies').html(''); + }); + $('.delete-file').on('click', deleteFile); + }; + + var findFileTypeByFileExtension = function (file_extension) { + return _.find(file_types, function (file_type) { + return file_type.file_extension === file_extension; + }) || {}; + }; + + var getSelectedExecutionEnvironment = function () { + return _.find(execution_environments, function (execution_environment) { + return execution_environment.id === parseInt($('#exercise_execution_environment_id').val()); + }) || {}; + }; + + var highlightCode = function () { + $('pre code').each(function (index, element) { + hljs.highlightBlock(element); + }); + }; + + var inferFileAttributes = function () { + $(document).on('change', 'input[type="file"]', function () { + var filename = $(this).val().split(/\\|\//g).pop(); + var file_extension = '.' + filename.split('.')[1]; + var file_type = findFileTypeByFileExtension(file_extension); + var name = filename.split('.')[0]; + var parent = $(this).parents('li'); + parent.find('input[name*="name"]').val(name); + parent.find('select[name*="file_type_id"]').val(file_type.id).trigger('chosen:updated'); + }); + }; + + var insertTabAtCursor = function (textarea) { + var selection_start = textarea.get(0).selectionStart; + var selection_end = textarea.get(0).selectionEnd; + textarea.val(textarea.val().substring(0, selection_start) + "\t" + textarea.val().substring(selection_end)); + textarea.get(0).selectionStart = selection_start + 1; + textarea.get(0).selectionEnd = selection_start + 1; + }; + + var observeFileRoleChanges = function () { + $(document).on('change', 'select[name$="[role]"]', function () { + var is_test_file = $(this).val() === 'teacher_defined_test'; + var parent = $(this).parents('.card'); + var fields = parent.find('.test-related-fields'); + if (is_test_file) { + fields.slideDown(); + } else { + fields.slideUp(); + parent.find('[name$="[feedback_message]"]').val(''); + parent.find('[name$="[weight]"]').val(1); + } + }); + }; + + var old_execution_environment = $('#exercise_execution_environment_id').val(); + var observeExecutionEnvironment = function () { + $('#exercise_execution_environment_id').on('change', function () { + new_execution_environment = $('#exercise_execution_environment_id').val(); + + if (new_execution_environment == '' && !$('#exercise_unpublished').prop('checked')) { + if (confirm('<%= I18n.t('exercises.form.unpublish_warning') %>')) { + $('#exercise_unpublished').prop('checked', true); + } else { + return $('#exercise_execution_environment_id').val(old_execution_environment).trigger("chosen:updated"); + } + } + old_execution_environment = new_execution_environment; + }); + }; + + var observeUnpublishedState = function () { + $('#exercise_unpublished').on('change', function () { + if ($('#exercise_unpublished').prop('checked')) { + if (!confirm('<%= I18n.t('exercises.form.unpublish_warning') %>')) { + $('#exercise_unpublished').prop('checked', false); + } + } else if ($('#exercise_execution_environment_id').val() == '') { + alert('<%= I18n.t('exercises.form.no_execution_environment_selected') %>'); + $('#exercise_unpublished').prop('checked', true); + } }) - }, - dataType: 'json', - method: 'PUT' - }); - jqxhr.done(window.CodeOcean.refresh); - jqxhr.fail(ajaxError); - }; + }; - var toggleCodeHeight = function() { - $('code').on('click', function() { - $(this).css({ - 'max-height': 'initial' - }); - }); - }; - - var updateFileTemplates = function(fileType) { - var rel_url_root = '<%= (defined? Rails.application.config.relative_url_root) && Rails.application.config.relative_url_root != nil && Rails.application.config.relative_url_root != "" ? Rails.application.config.relative_url_root : "" %>'; - var jqxhr = $.ajax({ - url: rel_url_root + '/file_templates/by_file_type/' + fileType + '.json', - dataType: 'json' - }); - jqxhr.done(function(response) { - var noTemplateLabel = $('#noTemplateLabel').data('text'); - var options = ""; - for (var i = 0; i < response.length; i++) { - options += "" - } - $("#code_ocean_file_file_template_id").find('option').remove().end().append($(options)); - }); - jqxhr.fail(ajaxError); - } - - if ($.isController('exercises') || $.isController('submissions')) { - // ignore tags table since it is in the dom before other tables - if ($('table:not(#tags-table)').isPresent()) { - enableBatchUpdate(); - observeExportButtons(); - } else if ($('.edit_exercise, .new_exercise').isPresent()) { - execution_environments = $('form').data('execution-environments'); - file_types = $('form').data('file-types'); - - enableInlineFileCreation(); - inferFileAttributes(); - observeFileRoleChanges(); - observeExecutionEnvironment(); - observeUnpublishedState(); - overrideTextareaTabBehavior(); - } else if ($('#files.jstree').isPresent()) { - var fileTypeSelect = $('#code_ocean_file_file_type_id'); - fileTypeSelect.on("change", function() {updateFileTemplates(fileTypeSelect.val())}); - updateFileTemplates(fileTypeSelect.val()); + var observeExportButtons = function () { + $('.export-start').on('click', function (e) { + e.preventDefault(); + $('#export-modal').modal({ + height: 250 + }); + $('#export-modal').modal('show'); + exportExerciseStart($(this).data().exerciseId); + }); + $('body').on('click', '.export-retry-button', function () { + exportExerciseStart($(this).data().exerciseId); + }); + $('body').on('click', '.export-action', function () { + exportExerciseConfirm($(this).data().exerciseId); + }); } - toggleCodeHeight(); - if (window.hljs) { - highlightCode(); + + var exportExerciseStart = function (exerciseID) { + var $exerciseDiv = $('#export-exercise'); + var $messageDiv = $exerciseDiv.children('.export-message'); + var $actionsDiv = $exerciseDiv.children('.export-exercise-actions'); + + $messageDiv.removeClass('export-failure'); + + $messageDiv.html('<%= I18n.t('exercises.export_codeharbor.checking_codeharbor') %>'); + $actionsDiv.html('
'); + + return $.ajax({ + type: 'POST', + url: '/exercises/' + exerciseID + '/export_external_check', + dataType: 'json', + success: function (response) { + $messageDiv.html(response.message); + return $actionsDiv.html(response.actions); + }, + error: function (a, b, c) { + return alert('error:' + c); + } + }); + }; + + var exportExerciseConfirm = function (exerciseID) { + var $exerciseDiv = $('#export-exercise'); + var $messageDiv = $exerciseDiv.children('.export-message'); + var $actionsDiv = $exerciseDiv.children('.export-exercise-actions'); + + return $.ajax({ + type: 'POST', + url: '/exercises/' + exerciseID + '/export_external_confirm', + dataType: 'json', + success: function (response) { + $messageDiv.html(response.message) + $actionsDiv.html(response.actions); + + if (response.status == 'success') { + $messageDiv.addClass('export-success'); + setTimeout((function () { + $('#export-modal').modal('hide'); + $messageDiv.html('').removeClass('export-success'); + }), 3000); + } else { + $messageDiv.addClass('export-failure'); + } + }, + error: function (a, b, c) { + return alert('error:' + c); + } + }); + }; + + var overrideTextareaTabBehavior = function () { + $('.form-group textarea[name$="[content]"]').on('keydown', function (event) { + if (event.which === TAB_KEY_CODE) { + event.preventDefault(); + insertTabAtCursor($(this)); + } + }); + }; + + var performBatchUpdate = function () { + var jqxhr = $.ajax({ + data: { + exercises: _.map($('tbody tr'), function (element) { + return { + id: $(element).data('id'), + public: $('.public input', element).prop('checked') + }; + }) + }, + dataType: 'json', + method: 'PUT' + }); + jqxhr.done(window.CodeOcean.refresh); + jqxhr.fail(ajaxError); + }; + + var toggleCodeHeight = function () { + $('code').on('click', function () { + $(this).css({ + 'max-height': 'initial' + }); + }); + }; + + var updateFileTemplates = function (fileType) { + var rel_url_root = '<%= (defined? Rails.application.config.relative_url_root) && Rails.application.config.relative_url_root != nil && Rails.application.config.relative_url_root != "" ? Rails.application.config.relative_url_root : "" %>'; + var jqxhr = $.ajax({ + url: rel_url_root + '/file_templates/by_file_type/' + fileType + '.json', + dataType: 'json' + }); + jqxhr.done(function (response) { + var noTemplateLabel = $('#noTemplateLabel').data('text'); + var options = ""; + for (var i = 0; i < response.length; i++) { + options += "" + } + $("#code_ocean_file_file_template_id").find('option').remove().end().append($(options)); + }); + jqxhr.fail(ajaxError); + } + + if ($.isController('exercises') || $.isController('submissions')) { + // ignore tags table since it is in the dom before other tables + if ($('table:not(#tags-table)').isPresent()) { + enableBatchUpdate(); + observeExportButtons(); + } else if ($('.edit_exercise, .new_exercise').isPresent()) { + execution_environments = $('form').data('execution-environments'); + file_types = $('form').data('file-types'); + + enableInlineFileCreation(); + inferFileAttributes(); + observeFileRoleChanges(); + observeExecutionEnvironment(); + observeUnpublishedState(); + overrideTextareaTabBehavior(); + } else if ($('#files.jstree').isPresent()) { + var fileTypeSelect = $('#code_ocean_file_file_type_id'); + fileTypeSelect.on("change", function () { + updateFileTemplates(fileTypeSelect.val()) + }); + updateFileTemplates(fileTypeSelect.val()); + } else if ($('.export-start').isPresent()) { + observeExportButtons(); + } + toggleCodeHeight(); + if (window.hljs) { + highlightCode(); + } } - } - if ($('#editor-edit').isPresent()) { - configureEditors(); - initializeEditors(); - $('.frame').show(); - } + if ($('#editor-edit').isPresent()) { + configureEditors(); + initializeEditors(); + $('.frame').show(); + } }); diff --git a/app/views/exercises/show.html.slim b/app/views/exercises/show.html.slim index c046a2d5..90513d77 100644 --- a/app/views/exercises/show.html.slim +++ b/app/views/exercises/show.html.slim @@ -7,7 +7,17 @@ h1 = @exercise - = render('shared/edit_button', object: @exercise) + .btn-group.float-right + = render('shared/edit_button', object: @exercise) + button.btn.btn-secondary.float-right.dropdown-toggle data-toggle='dropdown' type='button' + ul.dropdown-menu.dropdown-menu-right role='menu' + li = link_to(t('exercises.index.implement'), implement_exercise_path(@exercise), 'data-turbolinks' => "false", class: 'dropdown-item text-dark') if policy(@exercise).implement? + li = link_to(t('shared.statistics'), statistics_exercise_path(@exercise), 'data-turbolinks' => "false", class: 'dropdown-item text-dark') if policy(@exercise).statistics? + li = link_to(t('activerecord.models.user_exercise_feedback.other'), feedback_exercise_path(@exercise), class: 'dropdown-item text-dark') if policy(@exercise).feedback? + li = link_to(t('activerecord.models.request_for_comment.other'), requests_for_comments_exercise_path(@exercise), class: 'dropdown-item text-dark') if policy(@exercise).requests_for_comments? + li = link_to(t('shared.destroy'), @exercise, data: {confirm: t('shared.confirm_destroy')}, method: :delete, class: 'dropdown-item text-dark') if policy(@exercise).destroy? + li = link_to(t('exercises.index.clone'), clone_exercise_path(@exercise), data: {confirm: t('shared.confirm_destroy')}, method: :post, class: 'dropdown-item text-dark') if policy(@exercise).clone? + li = link_to(t('exercises.export_codeharbor.label'), '', class: 'dropdown-item export-start text-dark', data: {'exercise-id' => @exercise.id}) if policy(@exercise).export_external_confirm? = row(label: 'exercise.title', value: @exercise.title) = row(label: 'exercise.user', value: link_to_if(policy(@exercise.author).show?, @exercise.author, @exercise.author)) @@ -42,3 +52,5 @@ ul.list-unstyled#files - if policy(file).destroy? .clearfix = link_to(t('shared.destroy'), file, class:'btn btn-warning btn-sm float-right', data: {confirm: t('shared.confirm_destroy')}, method: :delete) = render('shared/file', file: file) + += render('shared/modal', id: 'export-modal', title: t('exercises.export_codeharbor.dialogtitle'), template: 'exercises/_export_dialogcontent') \ No newline at end of file