From 4ead9d5bad822214ff2e767b9266cc47552d96bf Mon Sep 17 00:00:00 2001 From: Splines <37160523+Splines@users.noreply.github.com> Date: Thu, 30 May 2024 21:47:51 +0200 Subject: [PATCH] Redesign lecture edit page for lecturers (#628) * Upgrade Rails to v7.1 and run `bundle update` See the upgrade guide here: https://edgeguides.rubyonrails.org/upgrading_ruby_on_rails.html * Use older version of `html-parser` for `thredded` See https://github.com/thredded/thredded/issues/979 * Use new `config.autoload_lib` in Rails 7.1 See https://edgeguides.rubyonrails.org/upgrading_ruby_on_rails.html#config-autoload-lib-and-config-autoload-lib-once Eager loading is on by default for production. * Remove unused app environment variables usage The file `config/app_environment_variables.rb` does not exist in our codebase anymore. * Run `bin/rails app:update` to update configurations * Add new framework defaults for Rails 7.1 file * Update `listen` gem version This was done because `bin/rails app:update` failed with: ** Execute app:update:active_storage rails active_storage:update bin/rails aborted! Gem::LoadError: can't activate listen (~> 3.5), already activated listen-3.0.8. Make sure all dependencies are added to Gemfile. * Add TODO note for upcoming serialize change * Reduce new framework defaults list * Add migrations introduced by rails update task * Remove unneeded ActiveStorage migrations * Remove defaults for sha-256 as we are unaffected * Use new Rails 7.1 defaults * Fix TODO rubocop warning * Update bundler version to 2.5.9 You can do so locally via `bundle update --bundler` * Remove unnecessary entries in `Gemfile.lock` Performed automatically via `bundle install`. * Address `Passing the coder as positional arg` deprecation This is a followup to https://github.com/rails/rails/pull/47463 * add yaml coder explicitly for serializing arrays * Migrate from globalize to mobility due to serialization warnings * Update gem lockfile to include `mobility` `bundle install` also removed globalize automatically for us. * Add `I18nLocaleAccessors` as replacement for `globalize_attribute_names` * Remove obsolete comment regarding `globalize` * Fix Rails `secrets` deprecation warning (Devise) This is due to https://github.com/heartcombo/devise/issues/5644. * Use `install_folder` in cypress on rails `cypress_folder` is deprecated as config option * Init dummy Bootstrap nav pills * Group accordion items into nav pane * Style pillars & improve accessibility * Remove accordion wrappers & design lecture content pane * Center lectures header & improve vertical alignment * Add margin to bottom of lecture pane * Internationalize lectures navbar headers * Decaffeinate `lectures.coffee` via local CLI of decaffeinate see https://decaffeinate-project.org/ * Format `lectures.js` according to ESLint * Remove unnecessary use of Array.from * Use shorter variations of null checks * Remove unnecessary Coffeescript comment * Fix ESLint errors * Make better use of JS function syntax * Configure url hashes for bootstrap tabs Might also be known as "deep linking". * Simplify url hash update logic * Remove unused variable `s` * Use focus listener (not click listener) for accessibility * Implement many small UI improvements in lectures * Re-initialize masonry grid system for lecture content * Remove unnecessary spacing * Add confirmation dialog to delete forum * Add scrollbar to announcements list if too long * Redirect to correct page after creating a new announcement * Redirect to correct page after "Forum" actions * Redirect to correct page after "Comments" actions * Increase bottom margin of lecture pane * Check if errors are present to avoid nil error * Fix valid_annotations_status include check * Only load lectures_admin js related code when needed We also perform an early return if we no erdbeere examples are searched for, i.e. when the element is not yet visible on the page. * Use icons for save/cancel in assignments table * Fix structures cancel button (erdbeere) * Improve positioning of "structures" text * Get rid of unused debug message * Fix import of media for lectures not working * Remove TODO note * Delete unused tags/modal partial rendering * Stay on subpage upon save action * Fix broken browser navigation * Fix weird masonry grid system bug * Wait until tab content is shown before setting up grid system --------- Co-authored-by: fosterfarrell9 <28628554+fosterfarrell9@users.noreply.github.com> --- .config/eslint.mjs | 1 + app/assets/javascripts/application.js | 1 + app/assets/javascripts/lectures.coffee | 351 --------------- app/assets/javascripts/lectures.js | 414 ++++++++++++++++++ app/assets/javascripts/lectures_admin.js | 45 ++ app/assets/javascripts/masonry_grid.js | 12 + app/assets/stylesheets/lectures.scss | 51 ++- app/controllers/announcements_controller.rb | 2 +- app/controllers/lectures_controller.rb | 23 +- app/javascript/packs/application.js | 7 - app/models/lecture.rb | 2 +- app/views/assignments/_form.html.erb | 18 +- .../lectures/edit/_announcements.html.erb | 63 +-- app/views/lectures/edit/_assignments.html.erb | 209 ++++----- app/views/lectures/edit/_comments.html.erb | 165 +++---- app/views/lectures/edit/_erdbeere.html.erb | 39 +- app/views/lectures/edit/_form.html.erb | 215 +++++++-- app/views/lectures/edit/_forum.html.erb | 86 ++-- app/views/lectures/edit/_header.html.erb | 10 +- .../lectures/edit/_imported_media.html.erb | 87 ++-- .../edit/_organizational_concept.html.erb | 140 +++--- app/views/lectures/edit/_people.html.erb | 203 ++++----- app/views/lectures/edit/_preferences.html.erb | 320 +++++++------- app/views/lectures/edit/_structures.html.erb | 21 +- app/views/lectures/edit/_tags.html.erb | 107 ++--- app/views/lectures/edit/_tutorials.html.erb | 104 ++--- app/views/lectures/edit_structures.coffee | 1 + app/views/lectures/update.coffee | 4 + app/views/tutorials/_form.html.erb | 2 +- config/locales/de.yml | 4 + config/locales/en.yml | 4 + 31 files changed, 1385 insertions(+), 1326 deletions(-) delete mode 100644 app/assets/javascripts/lectures.coffee create mode 100644 app/assets/javascripts/lectures.js create mode 100644 app/assets/javascripts/lectures_admin.js create mode 100644 app/assets/javascripts/masonry_grid.js diff --git a/.config/eslint.mjs b/.config/eslint.mjs index f75205cf7..008e58374 100644 --- a/.config/eslint.mjs +++ b/.config/eslint.mjs @@ -33,6 +33,7 @@ const customGlobals = { // Common global methods initBootstrapPopovers: "readable", + initMasonryGridSystem: "readable", // Thyme & Annotation tool globals // TODO: This is a "hack" right now to get rid of "xy is not defined" error diff --git a/app/assets/javascripts/application.js b/app/assets/javascripts/application.js index 49b7e76e6..88e205048 100644 --- a/app/assets/javascripts/application.js +++ b/app/assets/javascripts/application.js @@ -35,6 +35,7 @@ //= require lectures //= require lessons //= require main +//= require masonry_grid //= require media //= require notifications //= require profile diff --git a/app/assets/javascripts/lectures.coffee b/app/assets/javascripts/lectures.coffee deleted file mode 100644 index 6e8d60759..000000000 --- a/app/assets/javascripts/lectures.coffee +++ /dev/null @@ -1,351 +0,0 @@ -# Place all the behaviors and hooks related to the matching controller here. -# All this logic will automatically be available in application.js. -# You can use CoffeeScript in this file: http://coffeescript.org/ - -disableExceptOrganizational = -> - $('#lecture-organizational-warning').show() - $('.fa-edit').hide() - $('.new-in-lecture').hide() - $('[data-bs-toggle="collapse"]').prop('disabled', true).removeClass('clickable') - return - -$(document).on 'turbolinks:load', -> - initBootstrapPopovers() - # if any input is given to the lecture form (for people in lecture), - # disable other input - $('#lecture-form :input').on 'change', -> - $('#lecture-basics-warning').show() - $('.fa-edit:not(#update-teacher-button,#update-editors-button)').hide() - $('.new-in-lecture').hide() - $('[data-bs-toggle="collapse"]').prop('disabled', true).removeClass('clickable') - return - - # if any input is given to the preferences form, disable other input - $('#lecture-preferences-form :input').on 'change', -> - $('#lecture-preferences-warning').show() - $('[data-bs-toggle="collapse"]').prop('disabled', true).removeClass('clickable') - $('.fa-edit').hide() - $('.new-in-lecture').hide() - return - - # if any input is given to the comments form, disable other input - $('#lecture-comments-form :input').on 'change', -> - $('#lecture-comments-warning').show() - $('[data-bs-toggle="collapse"]').prop('disabled', true).removeClass('clickable') - $('.fa-edit').hide() - $('.new-in-lecture').hide() - return - - # if any input is given to the assignments form, disable other input - $('#lecture-assignments-form :input').on 'change', -> - $('#lecture-assignments-warning').show() - $('[data-bs-toggle="collapse"]').prop('disabled', true).removeClass('clickable') - $('.new-in-lecture').hide() - return - - # if any input is given to the organizational form, disable other input - $('#lecture-organizational-form :input').on 'change', -> - disableExceptOrganizational() - return - - trixElement = document.querySelector('#lecture-concept-trix') - if trixElement? - content = trixElement.dataset.content - editor = trixElement.editor - editor.setSelectedRange([0,65535]) - editor.deleteInDirection("forward") - editor.insertHTML(content) - document.activeElement.blur() - trixElement.addEventListener 'trix-change', -> - disableExceptOrganizational() - return - - # if absolute numbering box is checked/unchecked, enable/disable selection of - # start section - $('#lecture_absolute_numbering').on 'change', -> - if $(this).prop('checked') - $('#lecture_start_section').prop('disabled', false) - else - $('#lecture_start_section').prop('disabled', true) - return - - # reload current page if lecture basics editing is cancelled - $('#lecture-basics-cancel').on 'click', -> - location.reload(true) - return - - # reload current page if lecture preferences editing is cancelled - $('#cancel-lecture-preferences').on 'click', -> - location.reload(true) - return - - # reload current page if lecture comments editing is cancelled - $('#cancel-lecture-comments').on 'click', -> - location.reload(true) - return - - # reload current page if lecture preferences editing is cancelled - $('#cancel-lecture-organizational').on 'click', -> - location.reload(true) - return - - # restore assignments form if lecture assignments editing is cancelled - $('#cancel-lecture-assignments').on 'click', -> - $('#lecture-assignments-warning').hide() - $('[data-bs-toggle="collapse"]').prop('disabled', false).addClass('clickable') - $('.new-in-lecture').show() - maxSize = $('#lecture_submission_max_team_size').data('value') - $('#lecture_submission_max_team_size').val(maxSize) - gracePeriod = $('#lecture_submission_grace_period').data('value') - $('#lecture_submission_grace_period').val(gracePeriod) - return - - # hide the media tab if hide media button is clicked - $('#hide-media-button').on 'click', -> - $('#lecture-media-card').hide() - $('#lecture-content-card').removeClass('col-xxl-9') - $('#show-media-button').show() - return - - # display the media tab if show media button is clicked - $('#show-media-button').on 'click', -> - $('#lecture-content-card').addClass('col-xxl-9') - $('#lecture-media-card').show() - $('#show-media-button').hide() - return - - # mousenter over a medium -> colorize lessons and tags - $('[id^="lecture-medium_"]').on 'mouseenter', -> - if this.dataset.type == 'Lesson' - lessonId = this.dataset.id - $('.lecture-lesson[data-id="'+lessonId+'"]') - .removeClass('bg-secondary') - .addClass('bg-info') - tags = $(this).data('tags') - for t in tags - $('.lecture-tag[data-id="'+t+'"]').removeClass('bg-light') - .addClass('bg-warning') - return - - # mouseleave over lesson -> restore original color of lessons and tags - $('[id^="lecture-medium_"]').on 'mouseleave', -> - if this.dataset.type == 'Lesson' - lessonId = this.dataset.id - $('.lecture-lesson[data-id="'+lessonId+'"]').removeClass('bg-info') - .addClass('bg-secondary') - tags = $(this).data('tags') - for t in tags - $('.lecture-tag[data-id="'+t+'"]').removeClass('bg-warning') - return - - # mouseenter over lesson -> colorize tags - $('[id^="lecture-lesson_"]').on 'mouseenter', -> - tags = $(this).data('tags') - for t in tags - $('.lecture-tag[data-id="'+t+'"]').addClass('bg-warning') - return - - # mouseleave over lesson -> restore original color of tags - $('[id^="lecture-lesson_"]').on 'mouseleave', -> - tags = $(this).data('tags') - for t in tags - $('.lecture-tag[data-id="'+t+'"]').removeClass('bg-warning') - return - - # mouseenter over tag -> colorize lessons - $('[id^="lecture-tag_"]').on 'mouseenter', -> - lessons = $(this).data('lessons') - for l in lessons - $('.lecture-lesson[data-id="'+l+'"]').removeClass('bg-secondary') - .addClass('bg-info') - return - - # mouseleave over tag -> restore original color of lessons - $('[id^="lecture-tag_"]').on 'mouseleave', -> - lessons = $(this).data('lessons') - for l in lessons - $('.lecture-lesson[data-id="'+l+'"]').removeClass('bg-info') - .addClass('bg-secondary') - return - - $('#edited-media-tab a[data-bs-toggle="tab"]').on 'shown.bs.tab', (e) -> - sort = e.target.dataset.sort # newly activated tab - path = $('#create-new-medium').prop('href') - if path - new_path = path.replace(/\?sort=.+?&/, '?sort=' + sort + '&') - $('#create-new-medium').prop('href', new_path) - return - - userModalContent = document.getElementById('lectureUserModalContent') - if userModalContent? and userModalContent.dataset.filled == 'false' - lectureId = userModalContent.dataset.lecture - $.ajax Routes.show_subscribers_path(lectureId), - type: 'GET' - dataType: 'json' - data: { - lecture: lectureId - } - success: (result) -> - $('#lectureUserModalButton').hide() if result.length == 0 - for u in result - row = document.createElement('div') - row.className = 'row mx-2 border-left border-right border-bottom' - colName = document.createElement('div') - colName.className = 'col-6' - colName.innerHTML = u[0] - row.appendChild(colName) - colMail = document.createElement('div') - colMail.className = 'col-6' - colMail.innerHTML = u[1] - row.appendChild(colMail) - userModalContent.appendChild(row) - userModalContent.dataset.filled = 'true' - return - - # on small mobile display, use shortened tag badges and - # shortened course titles - mobileDisplay = -> - $('.tagbadge').hide() - $('.courseMenuItem').hide() - $('.tagbadgeshort').show() - $('.courseMenuItemShort').show() - $('#secondnav').show() - $('#lecturesDropdown').appendTo($('#secondnav')) - $('#notificationDropdown').appendTo($('#secondnav')) - $('#feedback-btn').appendTo($('#secondnav')) - $('#searchField').appendTo($('#secondnav')) - $('#second-admin-nav').show() - $('#adminDetails').appendTo($('#second-admin-nav')) - $('#adminUsers').appendTo($('#second-admin-nav')) - $('#adminProfile').appendTo($('#second-admin-nav')) - $('#teachableDrop').prependTo($('#second-admin-nav')) - $('#adminMain').css('flex-direction', 'row') - $('#adminHome').css('padding-right', '0.5rem') - $('#adminCurrentLecture').css('padding-right', '0.5rem') - $('#adminSearch').css('padding-right', '0.5rem') - $('#mampfbrand').hide() - return - - # on large display, use normal tag badges and course titles - largeDisplay = -> - $('.tagbadge').show() - $('.courseMenuItem').show() - $('.tagbadgeshort').hide() - $('.courseMenuItemShort').hide() - $('#secondnav').hide() - $('#lecturesDropdown').appendTo($('#firstnav')) - $('#notificationDropdown').appendTo($('#firstnav')) - $('#feedback-btn').appendTo($('#firstnav')) - $('#searchField').appendTo($('#firstnav')) - $('#second-admin-nav').hide() - $('#teachableDrop').appendTo($('#first-admin-nav')) - $('#adminDetails').appendTo($('#first-admin-nav')) - $('#adminUsers').appendTo($('#first-admin-nav')) - $('#adminProfile').appendTo($('#first-admin-nav')) - $('#adminMain').removeAttr('style') - $('#adminHome').removeAttr('style') - $('#adminCurrentLecture').removeAttr('style') - $('#adminSearch').removeAttr('style') - $('#mampfbrand').show() - return - - # highlight tagbadges if screen is very small - if window.matchMedia("screen and (max-width: 767px)").matches - mobileDisplay() - - if window.matchMedia("screen and (max-device-width: 767px)").matches - mobileDisplay() - - # mediaQuery listener for very small screens - match_verysmall = window.matchMedia("screen and (max-width: 767px)") - match_verysmall.addListener (result) -> - if result.matches - mobileDisplay() - return - - match_verysmalldevice = window.matchMedia("screen and (max-device-width: 767px)") - match_verysmalldevice.addListener (result) -> - if result.matches - mobileDisplay() - return - - # mediaQuery listener for normal screens - match_normal = window.matchMedia("screen and (min-width: 768px)") - match_normal.addListener (result) -> - if result.matches - largeDisplay() - return - - match_normal = window.matchMedia("screen and (min-device-width: 768px)") - match_normal.addListener (result) -> - if result.matches - largeDisplay() - return - - $('#erdbeere_structures_heading').on 'click', -> - lectureId = $(this).data('lecture') - loading = $(this).data('loading') - $('#erdbeereStructuresBody').empty().append(loading) - $.ajax Routes.edit_structures_path(lectureId), - type: 'GET' - dataType: 'script' - return - - $lectureStructures = $('#lectureStructuresInfo') - if $lectureStructures.length > 0 - structures = $lectureStructures.data('structures') - for s in structures - $('#structure-item-' + s).show() - - $('#switchGlobalStructureSearch').on 'click', -> - if $(this).is(':checked') - $('[id^="structure-item-"]').show() - else - $('[id^="structure-item-"]').hide() - structures = $lectureStructures.data('structures') - for s in structures - $('#structure-item-' + s).show() - return - - $(document).on 'change', '#lecture_course_id', -> - $('#lecture_term_id').removeClass('is-invalid') - $('#new-lecture-term-error').empty() - courseId = parseInt($(this).val()) - termInfo = $(this).data('terminfo').filter (x) -> x[0] == courseId - console.log termInfo[0] - if (termInfo[0]?) - if termInfo[0][1] - $('#newLectureTerm').hide() - $('#lecture_term_id').prop('disabled', true) - $('#newLectureSort').hide() - else - $('#newLectureTerm').show() - $('#lecture_term_id').prop('disabled', false) - $('#newLectureSort').show() - return - - $(document).on 'change', '#medium_publish_media_0', -> - $('[id^="medium_released_"]').attr('disabled', true) - $('#access-text').css('color','grey') - return - - $(document).on 'change', '#medium_publish_media_1', -> - $('[id^="medium_released_"]').attr('disabled', false) - $('#access-text').css('color','') - return - - $('#import_sections').on 'change', -> - if $(this).prop('checked') - $('#import_tags').prop('disabled', false) - else - $('#import_tags').prop('disabled', true).prop('checked', false) - return - - return - -# clean up everything before turbolinks caches -$(document).on 'turbolinks:before-cache', -> - $('.lecture-tag').removeClass('bg-warning') - $('.lecture-lesson').removeClass('bg-info').addClass('bg-secondary') - $(document).off 'change', '#lecture_course_id' - return diff --git a/app/assets/javascripts/lectures.js b/app/assets/javascripts/lectures.js new file mode 100644 index 000000000..ab7ba4479 --- /dev/null +++ b/app/assets/javascripts/lectures.js @@ -0,0 +1,414 @@ +// Load example data (erdbeere) for structures +function loadExampleStructures() { + const structuresBody = $("#erdbeereStructuresBody"); + const lectureId = structuresBody.data("lecture"); + if (lectureId === undefined) { + return; + } + const loading = structuresBody.data("loading"); + structuresBody.empty().append(loading); + $.ajax(Routes.edit_structures_path(lectureId), { + type: "GET", + dataType: "script", + complete: () => { + $("#erdbeere-structures-cancel").on("click", () => { + loadExampleStructures(); + }); + }, + }); +}; + +// eslint-disable-next-line no-unused-vars +function registerErdbeereExampleChanges() { + // Erdbeere Examples unsaved changes warning + $("#lecture-structures-form").on("input", function () { + $("#lecture-erdbeere-examples-warning").show(); + }); + + $("#erdbeere-structures-cancel").on("click", function () { + $("#lecture-erdbeere-examples-warning").hide(); + }); +} + +function disableExceptOrganizational() { + $("#lecture-organizational-warning").show(); + $(".fa-edit").hide(); + $(".new-in-lecture").hide(); + $('[data-bs-toggle="collapse"]').prop("disabled", true).removeClass("clickable"); +}; + +$(document).on("turbolinks:load", function () { + $("#delete-forum").on("click", () => { + const sureToDeleteMsg = $("#delete-forum").data("sureToDelete"); + const reallyDelete = confirm(sureToDeleteMsg); + return reallyDelete; + }); + + // if any input is given to the lecture form (for people in lecture), + // disable other input + $("#lecture-form :input").on("change", function () { + $("#lecture-basics-warning").show(); + $(".fa-edit:not(#update-teacher-button,#update-editors-button)").hide(); + $(".new-in-lecture").hide(); + $('[data-bs-toggle="collapse"]').prop("disabled", true).removeClass("clickable"); + }); + + // if any input is given to the preferences form, disable other input + $("#lecture-preferences-form :input").on("change", function () { + $("#lecture-preferences-warning").show(); + $('[data-bs-toggle="collapse"]').prop("disabled", true).removeClass("clickable"); + $(".fa-edit").hide(); + $(".new-in-lecture").hide(); + }); + + // if any input is given to the comments form, disable other input + $("#lecture-comments-form :input").on("change", function () { + $("#lecture-comments-warning").show(); + $('[data-bs-toggle="collapse"]').prop("disabled", true).removeClass("clickable"); + $(".fa-edit").hide(); + $(".new-in-lecture").hide(); + }); + + // if any input is given to the assignments form, disable other input + $("#lecture-assignments-form :input").on("change", function () { + $("#lecture-assignments-warning").show(); + $('[data-bs-toggle="collapse"]').prop("disabled", true).removeClass("clickable"); + $(".new-in-lecture").hide(); + }); + + // if any input is given to the organizational form, disable other input + $("#lecture-organizational-form :input").on("change", function () { + disableExceptOrganizational(); + }); + + const trixElement = document.querySelector("#lecture-concept-trix"); + if (trixElement) { + const { content } = trixElement.dataset; + const { editor } = trixElement; + editor.setSelectedRange([0, 65535]); + editor.deleteInDirection("forward"); + editor.insertHTML(content); + document.activeElement.blur(); + trixElement.addEventListener("trix-change", function () { + disableExceptOrganizational(); + }); + } + + // if absolute numbering box is checked/unchecked, enable/disable selection of + // start section + $("#lecture_absolute_numbering").on("change", function () { + if ($(this).prop("checked")) { + $("#lecture_start_section").prop("disabled", false); + } + else { + $("#lecture_start_section").prop("disabled", true); + } + }); + + // reload current page if lecture basics editing is cancelled + $("#lecture-basics-cancel").on("click", function () { + location.reload(true); + }); + + // reload current page if lecture preferences editing is cancelled + $("#cancel-lecture-preferences").on("click", function () { + location.reload(true); + }); + + // reload current page if lecture comments editing is cancelled + $("#cancel-lecture-comments").on("click", function () { + location.reload(true); + }); + + // reload current page if lecture preferences editing is cancelled + $("#cancel-lecture-organizational").on("click", function () { + location.reload(true); + }); + + // restore assignments form if lecture assignments editing is cancelled + $("#cancel-lecture-assignments").on("click", function () { + $("#lecture-assignments-warning").hide(); + $('[data-bs-toggle="collapse"]').prop("disabled", false).addClass("clickable"); + $(".new-in-lecture").show(); + const maxSize = $("#lecture_submission_max_team_size").data("value"); + $("#lecture_submission_max_team_size").val(maxSize); + const gracePeriod = $("#lecture_submission_grace_period").data("value"); + $("#lecture_submission_grace_period").val(gracePeriod); + }); + + // hide the media tab if hide media button is clicked + $("#hide-media-button").on("click", function () { + $("#lecture-media-card").hide(); + $("#lecture-content-card").removeClass("col-xxl-9"); + $("#show-media-button").show(); + }); + + // display the media tab if show media button is clicked + $("#show-media-button").on("click", function () { + $("#lecture-content-card").addClass("col-xxl-9"); + $("#lecture-media-card").show(); + $("#show-media-button").hide(); + }); + + // mousenter over a medium -> colorize lessons and tags + $('[id^="lecture-medium_"]').on("mouseenter", function () { + if (this.dataset.type === "Lesson") { + const lessonId = this.dataset.id; + $('.lecture-lesson[data-id="' + lessonId + '"]') + .removeClass("bg-secondary") + .addClass("bg-info"); + } + const tags = $(this).data("tags"); + for (const t of tags) { + $('.lecture-tag[data-id="' + t + '"]').removeClass("bg-light") + .addClass("bg-warning"); + } + }); + + // mouseleave over lesson -> restore original color of lessons and tags + $('[id^="lecture-medium_"]').on("mouseleave", function () { + if (this.dataset.type === "Lesson") { + const lessonId = this.dataset.id; + $('.lecture-lesson[data-id="' + lessonId + '"]').removeClass("bg-info") + .addClass("bg-secondary"); + } + const tags = $(this).data("tags"); + for (const t of tags) { + $('.lecture-tag[data-id="' + t + '"]').removeClass("bg-warning"); + } + }); + + // mouseenter over lesson -> colorize tags + $('[id^="lecture-lesson_"]').on("mouseenter", function () { + const tags = $(this).data("tags"); + for (const t of tags) { + $('.lecture-tag[data-id="' + t + '"]').addClass("bg-warning"); + } + }); + + // mouseleave over lesson -> restore original color of tags + $('[id^="lecture-lesson_"]').on("mouseleave", function () { + const tags = $(this).data("tags"); + for (const t of tags) { + $('.lecture-tag[data-id="' + t + '"]').removeClass("bg-warning"); + } + }); + + // mouseenter over tag -> colorize lessons + $('[id^="lecture-tag_"]').on("mouseenter", function () { + const lessons = $(this).data("lessons"); + for (const l of lessons) { + $('.lecture-lesson[data-id="' + l + '"]').removeClass("bg-secondary") + .addClass("bg-info"); + } + }); + + // mouseleave over tag -> restore original color of lessons + $('[id^="lecture-tag_"]').on("mouseleave", function () { + const lessons = $(this).data("lessons"); + for (const l of lessons) { + $('.lecture-lesson[data-id="' + l + '"]').removeClass("bg-info") + .addClass("bg-secondary"); + } + }); + + $('#edited-media-tab a[data-bs-toggle="tab"]').on("shown.bs.tab", function (e) { + const { + sort, + } = e.target.dataset; // newly activated tab + const path = $("#create-new-medium").prop("href"); + if (path) { + const new_path = path.replace(/\?sort=.+?&/, "?sort=" + sort + "&"); + $("#create-new-medium").prop("href", new_path); + } + }); + + const userModalContent = document.getElementById("lectureUserModalContent"); + if (userModalContent && (userModalContent.dataset.filled === "false")) { + const lectureId = userModalContent.dataset.lecture; + $.ajax(Routes.show_subscribers_path(lectureId), { + type: "GET", + dataType: "json", + data: { + lecture: lectureId, + }, + success(result) { + if (result.length === 0) { + $("#lectureUserModalButton").hide(); + } + for (const res of result) { + var row = document.createElement("div"); + row.className = "row mx-2 border-left border-right border-bottom"; + var colName = document.createElement("div"); + colName.className = "col-6"; + colName.innerHTML = res[0]; + row.appendChild(colName); + var colMail = document.createElement("div"); + colMail.className = "col-6"; + colMail.innerHTML = res[1]; + row.appendChild(colMail); + userModalContent.appendChild(row); + userModalContent.dataset.filled = "true"; + } + }, + }, + ); + } + + // on small mobile display, use shortened tag badges and + // shortened course titles + const mobileDisplay = function () { + $(".tagbadge").hide(); + $(".courseMenuItem").hide(); + $(".tagbadgeshort").show(); + $(".courseMenuItemShort").show(); + $("#secondnav").show(); + $("#lecturesDropdown").appendTo($("#secondnav")); + $("#notificationDropdown").appendTo($("#secondnav")); + $("#feedback-btn").appendTo($("#secondnav")); + $("#searchField").appendTo($("#secondnav")); + $("#second-admin-nav").show(); + $("#adminDetails").appendTo($("#second-admin-nav")); + $("#adminUsers").appendTo($("#second-admin-nav")); + $("#adminProfile").appendTo($("#second-admin-nav")); + $("#teachableDrop").prependTo($("#second-admin-nav")); + $("#adminMain").css("flex-direction", "row"); + $("#adminHome").css("padding-right", "0.5rem"); + $("#adminCurrentLecture").css("padding-right", "0.5rem"); + $("#adminSearch").css("padding-right", "0.5rem"); + $("#mampfbrand").hide(); + }; + + // on large display, use normal tag badges and course titles + const largeDisplay = function () { + $(".tagbadge").show(); + $(".courseMenuItem").show(); + $(".tagbadgeshort").hide(); + $(".courseMenuItemShort").hide(); + $("#secondnav").hide(); + $("#lecturesDropdown").appendTo($("#firstnav")); + $("#notificationDropdown").appendTo($("#firstnav")); + $("#feedback-btn").appendTo($("#firstnav")); + $("#searchField").appendTo($("#firstnav")); + $("#second-admin-nav").hide(); + $("#teachableDrop").appendTo($("#first-admin-nav")); + $("#adminDetails").appendTo($("#first-admin-nav")); + $("#adminUsers").appendTo($("#first-admin-nav")); + $("#adminProfile").appendTo($("#first-admin-nav")); + $("#adminMain").removeAttr("style"); + $("#adminHome").removeAttr("style"); + $("#adminCurrentLecture").removeAttr("style"); + $("#adminSearch").removeAttr("style"); + $("#mampfbrand").show(); + }; + + // highlight tagbadges if screen is very small + if (window.matchMedia("screen and (max-width: 767px)").matches) { + mobileDisplay(); + } + + if (window.matchMedia("screen and (max-device-width: 767px)").matches) { + mobileDisplay(); + } + + // mediaQuery listener for very small screens + const match_verysmall = window.matchMedia("screen and (max-width: 767px)"); + match_verysmall.addListener(function (result) { + if (result.matches) { + mobileDisplay(); + } + }); + + const match_verysmalldevice = window.matchMedia("screen and (max-device-width: 767px)"); + match_verysmalldevice.addListener(function (result) { + if (result.matches) { + mobileDisplay(); + } + }); + + // mediaQuery listener for normal screens + let match_normal = window.matchMedia("screen and (min-width: 768px)"); + match_normal.addListener(function (result) { + if (result.matches) { + largeDisplay(); + } + }); + + match_normal = window.matchMedia("screen and (min-device-width: 768px)"); + match_normal.addListener(function (result) { + if (result.matches) { + largeDisplay(); + } + }); + + let structures; + loadExampleStructures(); + + const $lectureStructures = $("#lectureStructuresInfo"); + if ($lectureStructures.length > 0) { + structures = $lectureStructures.data("structures"); + for (const s of structures) { + $("#structure-item-" + s).show(); + } + } + + $("#switchGlobalStructureSearch").on("click", function () { + if ($(this).is(":checked")) { + $('[id^="structure-item-"]').show(); + } + else { + $('[id^="structure-item-"]').hide(); + structures = $lectureStructures.data("structures"); + for (const s of structures) { + $("#structure-item-" + s).show(); + } + } + }); + + $(document).on("change", "#lecture_course_id", function () { + $("#lecture_term_id").removeClass("is-invalid"); + $("#new-lecture-term-error").empty(); + const courseId = parseInt($(this).val()); + const termInfo = $(this).data("terminfo").filter(x => x[0] === courseId); + console.log(termInfo[0]); + if (termInfo[0]) { + if (termInfo[0][1]) { + $("#newLectureTerm").hide(); + $("#lecture_term_id").prop("disabled", true); + $("#newLectureSort").hide(); + } + else { + $("#newLectureTerm").show(); + $("#lecture_term_id").prop("disabled", false); + $("#newLectureSort").show(); + } + return; + } + }); + + $(document).on("change", "#medium_publish_media_0", function () { + $('[id^="medium_released_"]').attr("disabled", true); + $("#access-text").css("color", "grey"); + }); + + $(document).on("change", "#medium_publish_media_1", function () { + $('[id^="medium_released_"]').attr("disabled", false); + $("#access-text").css("color", ""); + }); + + $("#import_sections").on("change", function () { + if ($(this).prop("checked")) { + $("#import_tags").prop("disabled", false); + } + else { + $("#import_tags").prop("disabled", true).prop("checked", false); + } + }); +}); + +// clean up everything before turbolinks caches +$(document).on("turbolinks:before-cache", function () { + $(".lecture-tag").removeClass("bg-warning"); + $(".lecture-lesson").removeClass("bg-info").addClass("bg-secondary"); + $(document).off("change", "#lecture_course_id"); +}); diff --git a/app/assets/javascripts/lectures_admin.js b/app/assets/javascripts/lectures_admin.js new file mode 100644 index 000000000..6ec62b780 --- /dev/null +++ b/app/assets/javascripts/lectures_admin.js @@ -0,0 +1,45 @@ +/** + * Takes care of the URL hashes, such that each bootstrab tab gets assigned + * its own URL hash, e.g. "#assignments". + * + * This is necessary to be able to share the URL with a specific tab open. + * It also allows to stay on the same tab after a page reload + * (which is done when an edit action is saved/canceled, + * also see the lectures controller update action). + * + * Find out more details in this guide: + * https://webdesign.tutsplus.com/how-to-add-deep-linking-to-the-bootstrap-4-tabs-component--cms-31180t + */ +function configureUrlHashesForBootstrapTabs() { + $('#lecture-nav-pills button[role="tab"]').on("focus", function () { + const hash = $(this).attr("href"); + const urlWithoutHash = location.href.split("#")[0]; + const newUrl = `${urlWithoutHash}${hash}`; + history.pushState(null, "", newUrl); + }); +} + +function navigateToActiveNavTab() { + if (location.hash) { + const hrefXPathIdentifier = `button[href="${location.hash}"]`; + $(`#lecture-nav-pills ${hrefXPathIdentifier}`).tab("show"); + } + else { + $("#lecture-nav-content").focus(); + } +} + +$(document).on("ready turbolinks:load", function () { + initBootstrapPopovers(); + configureUrlHashesForBootstrapTabs(); + navigateToActiveNavTab(); + + // Reinitialize the masonry grid system when the lecture content is shown + $("#lecture-nav-content").on("shown.bs.tab", () => { + initMasonryGridSystem(); + }); +}); + +$(window).on("hashchange", function () { + navigateToActiveNavTab(); +}); diff --git a/app/assets/javascripts/masonry_grid.js b/app/assets/javascripts/masonry_grid.js new file mode 100644 index 000000000..9e0826029 --- /dev/null +++ b/app/assets/javascripts/masonry_grid.js @@ -0,0 +1,12 @@ +$(document).on("turbolinks:load", function () { + initMasonryGridSystem(); +}); + +/** + * Inits the masonry grid system for elements with the class "masonry-grid". + */ +function initMasonryGridSystem() { + $(".masonry-grid").masonry({ + percentPosition: true, + }); +} diff --git a/app/assets/stylesheets/lectures.scss b/app/assets/stylesheets/lectures.scss index 6e31dd03d..2ee04b58b 100644 --- a/app/assets/stylesheets/lectures.scss +++ b/app/assets/stylesheets/lectures.scss @@ -73,4 +73,53 @@ &:hover { color: white !important; } -} \ No newline at end of file +} + +#lecture-nav-pills { + background-color: white; + justify-content: center; + padding-top: 1em; + padding-bottom: 1em; + + box-shadow: 0px 2px 7px 0rem rgba(130,26,59,0.2); + border: #821A3B solid 1px; + border-radius: 0.4em; + margin-bottom: 1.5em; +} + +.lecture-nav-pill-button { + --bs-nav-link-color: #821A3B; + --bs-nav-link-hover-color: #8d1c40; + --bs-nav-pills-link-active-bg: #821A3B; + + &:focus-visible { + box-shadow: 0 0 0 0.25rem rgba(130, 26, 59, 0.25); + } +} + + +.lecture-pane { + background-color: white; + padding: 1.5em; + margin-bottom: 6em; + box-shadow: 0px 0px 5px rgba(0, 0, 0, 0.2); + border: gray 1px solid; + border-radius: 0.4em; +} + +.lecture-pane-separator { + border-top: 3px dotted #555555; + margin-top: 2em; + margin-bottom: 2em; +} + +h3.lecture-pane-header { + color: #838383; + font-size: 1.3em; +} + +#announcements-list { + max-height: 17em; + overflow-x: hidden; + overflow-y: auto; +} diff --git a/app/controllers/announcements_controller.rb b/app/controllers/announcements_controller.rb index 8425893a6..d225d27b2 100644 --- a/app/controllers/announcements_controller.rb +++ b/app/controllers/announcements_controller.rb @@ -37,7 +37,7 @@ def create redirect_to announcements_path return end - redirect_to edit_lecture_path(@announcement.lecture) + redirect_to "#{edit_lecture_path(@announcement.lecture)}#communication" return end @errors = @announcement.errors[:details].join(", ") diff --git a/app/controllers/lectures_controller.rb b/app/controllers/lectures_controller.rb index 10682539f..719f59540 100644 --- a/app/controllers/lectures_controller.rb +++ b/app/controllers/lectures_controller.rb @@ -120,7 +120,16 @@ def update end @lecture.touch @lecture.forum&.update(name: @lecture.forum_title) - redirect_to edit_lecture_path(@lecture) if @lecture.valid? + + # Redirect to the correct subpage + if @lecture.valid? + if params[:subpage].present? + redirect_to "#{edit_lecture_path(@lecture)}##{params[:subpage]}" + else + redirect_to edit_lecture_path(@lecture) + end + end + @errors = @lecture.errors end @@ -150,28 +159,28 @@ def add_forum forum.save @lecture.update(forum_id: forum.id) if forum.valid? end - redirect_to edit_lecture_path(@lecture) + redirect_to "#{edit_lecture_path(@lecture)}#communication" end # lock forum for this lecture def lock_forum @lecture.forum.update(locked: true) if @lecture.forum? @lecture.touch - redirect_to edit_lecture_path(@lecture) + redirect_to "#{edit_lecture_path(@lecture)}#communication" end # unlock forum for this lecture def unlock_forum @lecture.forum.update(locked: false) if @lecture.forum? @lecture.touch - redirect_to edit_lecture_path(@lecture) + redirect_to "#{edit_lecture_path(@lecture)}#communication" end # destroy forum for this lecture def destroy_forum @lecture.forum.destroy if @lecture.forum? @lecture.update(forum_id: nil) - redirect_to edit_lecture_path(@lecture) + redirect_to "#{edit_lecture_path(@lecture)}#communication" end # show all announcements for this lecture @@ -241,12 +250,12 @@ def close_comments @lecture.lessons.each do |lesson| lesson.media.update(annotations_status: -1) end - redirect_to edit_lecture_path(@lecture) + redirect_to "#{edit_lecture_path(@lecture)}#communication" end def open_comments @lecture.open_comments!(current_user) - redirect_to edit_lecture_path(@lecture) + redirect_to "#{edit_lecture_path(@lecture)}#communication" end def search diff --git a/app/javascript/packs/application.js b/app/javascript/packs/application.js index 0586df987..889b776c1 100644 --- a/app/javascript/packs/application.js +++ b/app/javascript/packs/application.js @@ -43,11 +43,4 @@ document.addEventListener("turbolinks:load", function () { // DO not uncomment, evil // widget.reset(); } - - // Init Masonry grid system - // see https://getbootstrap.com/docs/5.0/examples/masonry/ - // and official documentation: https://masonry.desandro.com/ - $(".masonry-grid").masonry({ - percentPosition: true, - }); }); diff --git a/app/models/lecture.rb b/app/models/lecture.rb index 4ad4a39a5..7fd197471 100644 --- a/app/models/lecture.rb +++ b/app/models/lecture.rb @@ -841,7 +841,7 @@ def stale? end def valid_annotations_status? - [-1, 1].include?(annotations_status) + [0, 1].include?(annotations_status) end private diff --git a/app/views/assignments/_form.html.erb b/app/views/assignments/_form.html.erb index 0eec8828a..c79387994 100644 --- a/app/views/assignments/_form.html.erb +++ b/app/views/assignments/_form.html.erb @@ -69,13 +69,17 @@
- <%= f.submit t('buttons.save'), - class: 'btn btn-sm btn-primary ms-2' %> - <%= link_to t('buttons.cancel'), - cancel_editing_assignment_path(assignment), - class: 'btn btn-sm btn-secondary', - role: 'button', - remote: true %> + + <%= f.button nil, class: "btn btn-primary" do %> + + <% end %> + + + <%= link_to cancel_editing_assignment_path(assignment), remote: true, + role: "button", type: "button", + class: "btn btn-secondary" do %> + + <% end %>
<%= f.hidden_field :lecture_id, diff --git a/app/views/lectures/edit/_announcements.html.erb b/app/views/lectures/edit/_announcements.html.erb index 5d14d926e..b49696cf7 100644 --- a/app/views/lectures/edit/_announcements.html.erb +++ b/app/views/lectures/edit/_announcements.html.erb @@ -1,44 +1,21 @@ -
-
- -
-
-
-
-
- <%= link_to t('buttons.create_announcement'), - new_announcement_path(params: { lecture: lecture.id }), - class: 'btn btn-sm btn-secondary', - id: 'new-announcement-button', - remote: true %> -
-
- <% announcements.each do |a| %> -
- <%= render partial: 'announcements/row_for_lecture_edit', - locals: { announcement: a }%> -
- <% end %> -
-
+

+ <%= t('categories.announcements') %> +

+ +
+ <%= link_to t('buttons.create_announcement'), + new_announcement_path(params: { lecture: lecture.id }), + class: 'btn btn-sm btn-secondary', + id: 'new-announcement-button', + remote: true %> +
+ +
+<% announcements.each_with_index do |a, index| %> +
+ <%= render partial: 'announcements/row_for_lecture_edit', + locals: { announcement: a }%>
+<% end %> +
diff --git a/app/views/lectures/edit/_assignments.html.erb b/app/views/lectures/edit/_assignments.html.erb index 2bdea32f3..c55877c15 100644 --- a/app/views/lectures/edit/_assignments.html.erb +++ b/app/views/lectures/edit/_assignments.html.erb @@ -1,120 +1,99 @@ -
-
- +

+ <%= t('basics.assignments') %> +

+ +
+
+ <%= link_to t('admin.assignment.new'), + new_assignment_path(params: { lecture_id: lecture.id }), + class: 'btn btn-sm btn-primary', + id: 'newAssignmentButton', + remote: true %>
-
-
-
-
- <%= link_to t('admin.assignment.new'), - new_assignment_path(params: { lecture_id: lecture.id }), - class: 'btn btn-sm btn-primary', - id: 'newAssignmentButton', - remote: true %> -
+
+
+
+
+
+
+ <%= t('basics.title') %> +
+
+
+
+ <%= t('basics.deadline') %> +
+
+
+
+ <%= t('basics.medium') %> +
+
+
+
+ <%= t('submission.file_format') %> +
+
+
+
+ <%= t('assignment.deletion_date') %> +
-
-
-
-
-
- <%= t('basics.title') %> -
-
-
-
- <%= t('basics.deadline') %> -
-
-
-
- <%= t('basics.medium') %> -
-
-
-
- <%= t('submission.file_format') %> -
-
-
-
- <%= t('assignment.deletion_date') %> -
-
-
-
- <%= t('basics.action') %> - <%= helpdesk(t('assignment.destruction_info'), true) %> -
-
-
-
- <% (lecture.scheduled_assignments.sort_by(&:deadline).reverse + - lecture.assignments.order('deadline DESC')).each do |a| %> - <%= render partial: 'assignments/row', - locals: { assignment: a } %> - <% end %> +
+
+ <%= t('basics.action') %> + <%= helpdesk(t('assignment.destruction_info'), true) %> +
- <%= form_with model: lecture, - html: { id: 'lecture-assignments-form', - class: 'mt-4 px-2' } do |f| %> -
-
- <%= f.label :submission_max_team_size, - t('admin.lecture.submission_max_team_size'), - class: "form-label" %> - <%= helpdesk t('admin.lecture.info.submission_max_team_size'), - false %> - <%= f.number_field :submission_max_team_size, - min: 1, - max: 10000, - class: 'form-control mx-2', - data: - { value: lecture.submission_max_team_size } %> -
-
- <%= f.label :submission_grace_period, - t('admin.lecture.submission_grace_period'), - class: "form-label" %> - <%= helpdesk t('admin.lecture.info.submission_grace_period'), - false %> - <%= f.number_field :submission_grace_period, - min: 0, - max: 262800, - class: 'form-control mx-2', - data: { value: lecture.submission_grace_period } %> -
-
-
- <%= t('warnings.unsaved_changes') %> - <%= f.submit t('buttons.save_and_exit'), - class: "btn btn-sm btn-primary" %> - -
- <% end %>
-
\ No newline at end of file + <% (lecture.scheduled_assignments.sort_by(&:deadline).reverse + + lecture.assignments.order('deadline DESC')).each do |a| %> + <%= render partial: 'assignments/row', + locals: { assignment: a } %> + <% end %> +
+ +<%= form_with model: lecture, + html: { id: 'lecture-assignments-form', + class: 'mt-4 px-2' } do |f| %> +
+
+ <%= f.label :submission_max_team_size, + t('admin.lecture.submission_max_team_size'), + class: "form-label" %> + <%= helpdesk t('admin.lecture.info.submission_max_team_size'), + false %> + <%= f.number_field :submission_max_team_size, + min: 1, + max: 10000, + class: 'form-control', + data: + { value: lecture.submission_max_team_size } %> +
+
+ <%= f.label :submission_grace_period, + t('admin.lecture.submission_grace_period'), + class: "form-label" %> + <%= helpdesk t('admin.lecture.info.submission_grace_period'), + false %> + <%= f.number_field :submission_grace_period, + min: 0, + max: 262800, + class: 'form-control', + data: { value: lecture.submission_grace_period } %> +
+
+
+ <%= t('warnings.unsaved_changes') %> + <%= hidden_field_tag :subpage, "assignments" %> + <%= f.submit t('buttons.save'), + class: "btn btn-sm btn-primary" %> + +
+<% end %> \ No newline at end of file diff --git a/app/views/lectures/edit/_comments.html.erb b/app/views/lectures/edit/_comments.html.erb index cdc6345c5..1018f65e1 100644 --- a/app/views/lectures/edit/_comments.html.erb +++ b/app/views/lectures/edit/_comments.html.erb @@ -1,103 +1,74 @@ -
-
- +
+

+ <%= t('categories.comments') %> +

+ +
+
+ <%= link_to t('buttons.close_comments'), + lecture_close_comments_path(lecture), + class: 'btn btn-sm btn-outline-secondary' %> + <%= link_to t('buttons.open_comments'), + lecture_open_comments_path(lecture), + class: 'btn btn-sm btn-outline-secondary' %> +
+
+ + +<%= form_with model: lecture, + html: { id: 'lecture-comments-form' } do |f| %> + +
+ <%= f.check_box :comments_disabled, + class: 'form-check-input' %> + <%= f.label :comments_disabled, + t('admin.lecture.comments_disabled'), + { class: 'form-check-label' } %> + <%= helpdesk(t('admin.lecture.info.comments_disabled'), + false) %>
-
-
-
-
- <%= link_to t('buttons.close_comments'), - lecture_close_comments_path(lecture), - class: 'btn btn-sm btn-primary' %> - <%= link_to t('buttons.open_comments'), - lecture_open_comments_path(lecture), - class: 'btn btn-sm btn-primary' %> -
+ + +
+ <%= t('admin.lecture.enable_annotation_button') %> + <%= helpdesk(t('admin.lecture.enable_annotation_button_helpdesk'), false) %> + +
+
+ <%= f.radio_button :annotations_status, + 1, + class: 'form-check-input' %> + <%= f.label :annotations_status, + t('basics.yes_lc'), + value: 1, + class: 'form-check-label' %>
- <%= form_with model: lecture, - html: { id: 'lecture-comments-form' } do |f| %> -
-
- <%= f.check_box :comments_disabled, +
+ <%= f.radio_button :annotations_status, + 0, class: 'form-check-input' %> - <%= f.label :comments_disabled, - t('admin.lecture.comments_disabled'), - { class: 'form-check-label' } %> - <%= helpdesk(t('admin.lecture.info.comments_disabled'), - false) %> -
-
-
-
-
- <%= t('warnings.unsaved_changes') %> - <%= f.submit t('buttons.save_and_exit'), - class: "btn btn-sm btn-primary" %> - -
-
- -
- <%= t('admin.lecture.enable_annotation_button') %> - <%= helpdesk(t('admin.lecture.enable_annotation_button_helpdesk'), - false) %> -
-
- <%= f.radio_button :annotations_status, - 1, - class: 'form-check-input' %> - <%= f.label :annotations_status, - t('basics.yes_lc'), - value: 1, - class: 'form-check-label' %> -
-
- <%= f.radio_button :annotations_status, - -1, - class: 'form-check-input' %> - <%= f.label :annotations_status, - t('basics.no_lc'), - value: -1, - class: 'form-check-label' %> -
-
-
-
-
-
- <%= t('warnings.unsaved_changes') %> - <%= f.submit t('buttons.save_and_exit'), - class: "btn btn-sm btn-primary" %> - -
-
-
- <% end %> + <%= f.label :annotations_status, + t('basics.no_lc'), + value: 0, + class: 'form-check-label' %>
-
+ + +
+
+
+ <%= t('warnings.unsaved_changes') %> + <%= hidden_field_tag :subpage, "communication" %> + <%= f.submit t('buttons.save'), + class: "btn btn-sm btn-primary" %> + +
+
+ +
+<% end %> diff --git a/app/views/lectures/edit/_erdbeere.html.erb b/app/views/lectures/edit/_erdbeere.html.erb index b7a1267e8..e4f47b5da 100644 --- a/app/views/lectures/edit/_erdbeere.html.erb +++ b/app/views/lectures/edit/_erdbeere.html.erb @@ -1,29 +1,10 @@ -
-
- -
-
-
-
-
-
\ No newline at end of file +
+

+ <%= t('categories.erdbeere.plural') %> +

+ + +
+
diff --git a/app/views/lectures/edit/_form.html.erb b/app/views/lectures/edit/_form.html.erb index e0f17c223..5060e5b82 100644 --- a/app/views/lectures/edit/_form.html.erb +++ b/app/views/lectures/edit/_form.html.erb @@ -1,63 +1,190 @@ +<%= javascript_include_tag :lectures_admin %> <% cache lecture do %>
- <%= render partial: 'lectures/edit/header', - locals: { lecture: lecture } %> -
- <% if !lecture.seminar? || lecture.legacy_seminar %> -
- <%= render partial: 'lectures/edit/content', - locals: { lecture: lecture } %> + +
+ <%= render partial: 'lectures/edit/header', + locals: { lecture: lecture } %> +
+ + + + +
+ +
+
+ <% if !lecture.seminar? || lecture.legacy_seminar %> +
+ <%= render partial: 'lectures/edit/content', + locals: { lecture: lecture } %> +
+ <% else %> +
+ <%= render partial: 'lectures/edit/seminar_content', + locals: { lecture: lecture } %> +
+ <% end %> +
+ <%= render partial: 'lectures/edit/media', + locals: { media: media, lecture: lecture } %> +
- <% else %> -
- <%= render partial: 'lectures/edit/seminar_content', - locals: { lecture: lecture } %> +
+ + +
+
+ <%= render partial: 'lectures/edit/preferences', + locals: { lecture: lecture } %>
- <% end %> -
- <%= render partial: 'lectures/edit/media', - locals: { media: media, - lecture: lecture } %>
-
-
-
- <%= render partial: 'lectures/edit/tags', - locals: { lecture: lecture } %> - <%= render partial: 'lectures/edit/imported_media', - locals: { lecture: lecture } %> - <%= render partial: 'lectures/edit/erdbeere', - locals: { lecture: lecture } %> - <%= render partial: 'lectures/edit/people', - locals: { lecture: lecture } %> - <%= render partial: 'lectures/edit/organizational_concept', - locals: { lecture: lecture } %> - <%= render partial: 'lectures/edit/tutorials', - locals: { lecture: lecture } %> - <%= render partial: 'lectures/edit/assignments', - locals: { lecture: lecture } %> - <%= render partial: 'lectures/edit/announcements', - locals: { lecture: lecture, - announcements: announcements } %> - <%= render partial: 'lectures/edit/forum', - locals: { lecture: lecture } %> - <%= render partial: 'lectures/edit/comments', - locals: { lecture: lecture } %> - <%= render partial: 'lectures/edit/preferences', - locals: { lecture: lecture } %> + + +
+
+ <%= render partial: 'lectures/edit/people', + locals: { lecture: lecture } %> + <%= render partial: 'lectures/edit/tutorials', + locals: { lecture: lecture } %> +
+
+ + +
+
+ <%= render partial: 'lectures/edit/organizational_concept', + locals: { lecture: lecture } %> +
+
+ + +
+
+ <%= render partial: 'lectures/edit/announcements', + locals: { lecture: lecture, announcements: announcements } %> + <%= render partial: 'lectures/edit/forum', + locals: { lecture: lecture } %> + <%= render partial: 'lectures/edit/comments', + locals: { lecture: lecture } %> +
+
+ + +
+
+ <%= render partial: 'lectures/edit/assignments', + locals: { lecture: lecture } %> +
+
+ + +
+
+

+ <%= t('basics.course_editors') %> + <%= helpdesk(t('admin.lecture.info.course_editors'), false) %> +

+ +
+ <% if lecture.course.editors.present? %> +
    + <% lecture.course.editors.each do |e| %> +
  • + <%= e.name %> +
  • + <% end %> +
+ <% else %> +
+ <%= t('admin.lecture.no_course_editors') %> + <% end %> +
+ + <%= render partial: 'lectures/edit/tags', + locals: { lecture: lecture } %> + <%= render partial: 'lectures/edit/imported_media', + locals: { lecture: lecture } %> + <%= render partial: 'lectures/edit/erdbeere', + locals: { lecture: lecture } %> +
+ + + <%= render partial: 'media/modal' %> <%= render partial: 'sections/content_modal' %> - <%= render partial: 'tags/modal' %> <%= render partial: 'announcements/modal' %> <%= render partial: 'lectures/publish/publish', locals: { lecture: lecture } %> + <% unless lecture.stale? %> <%= render partial: 'lectures/edit/user_modal', locals: { lecture: lecture } %> <% end %> + <% if !lecture.seminar? || lecture.legacy_seminar %> <%= render partial: 'chapters/modal', locals: { lecture: lecture } %> @@ -67,8 +194,10 @@ <%= render partial: 'talks/modal', locals: { lecture: lecture } %> <% end %> + <% if lecture.importable_toc? %> <%= render partial: 'lectures/edit/import_toc_modal', locals: { lecture: lecture } %> <% end %> + <% end %> diff --git a/app/views/lectures/edit/_forum.html.erb b/app/views/lectures/edit/_forum.html.erb index 837854fb9..59e9ddeef 100644 --- a/app/views/lectures/edit/_forum.html.erb +++ b/app/views/lectures/edit/_forum.html.erb @@ -1,49 +1,37 @@ -
-
- -
-
-
-
-
- <% if lecture.forum? && lecture.forum %> - <% if lecture.forum&.locked %> - <%= link_to t('buttons.unlock_board'), - unlock_forum_path(lecture), - class: 'btn btn-sm btn-primary' %> - <% else %> - <%= link_to t('buttons.lock_board'), - lock_forum_path(lecture), - class: 'btn btn-sm btn-primary' %> - <% end %> - <%= link_to t('buttons.delete_board'), - destroy_forum_path(lecture), - class: 'btn btn-sm btn-danger' %> - <% else %> - <%= link_to t('buttons.add_board'), - add_forum_path(lecture), - class: 'btn btn-sm btn-primary' %> - <% end %> -
-
-
-
-
\ No newline at end of file +
+

+ <%= t('categories.board') %> +

+ +
+ <% if lecture.forum? && lecture.forum %> + + <% if lecture.forum&.locked %> + <%= link_to unlock_forum_path(lecture), + class: "btn btn-sm btn-outline-primary", role: :button do %> + + <%= t('buttons.unlock_board') %> + <% end %> + <% else %> + <%= link_to lock_forum_path(lecture), + class: "btn btn-sm btn-outline-primary", role: :button do %> + + <%= t('buttons.lock_board') %> + <% end %> + <% end %> + + <%= link_to destroy_forum_path(lecture), + id: "delete-forum", class: "btn btn-sm btn-outline-danger", role: :button, + "data-sure-to-delete": t('buttons.delete_board_sure_to_delete') do %> + + <%= t('buttons.delete_board') %> + <% end %> + + <% else %> + <%= link_to add_forum_path(lecture), + class: "btn btn-sm btn-outline-primary", role: :button do %> + + <%= t('buttons.add_board') %> + <% end %> + <% end %> +
diff --git a/app/views/lectures/edit/_header.html.erb b/app/views/lectures/edit/_header.html.erb index c75e3ce1e..01be44f43 100644 --- a/app/views/lectures/edit/_header.html.erb +++ b/app/views/lectures/edit/_header.html.erb @@ -1,6 +1,6 @@ -
-
-

+
+
+

<%= lecture.sort_localized %> @@ -12,14 +12,14 @@ <%= lecture.title_with_teacher_no_type %> <%= link_to '', lecture_path(lecture), - class: 'fas fa-eye text-dark mt-3', + class: 'fas fa-eye text-dark d-contents', data: { toggle: 'tooltip', placement: 'bottom' }, title: t('buttons.show'), style: 'text-decoration: none;' %>

-
+
<% if current_user.admin || lecture.course.edited_by?(current_user) %> <%= link_to t('buttons.parent_course'), edit_course_path(lecture.course), diff --git a/app/views/lectures/edit/_imported_media.html.erb b/app/views/lectures/edit/_imported_media.html.erb index 03e1cccb3..80cca67b4 100644 --- a/app/views/lectures/edit/_imported_media.html.erb +++ b/app/views/lectures/edit/_imported_media.html.erb @@ -1,53 +1,34 @@ -
-
- -
-
-
-
- <%= render partial: 'lectures/import/imported_media', - locals: { media: lecture.imported_media, - lecture: lecture } %> -
-
-
- -
-
-
-
- <%= render partial: 'media/catalog/search_form', - locals: { purpose: 'import' } %> -
-
-
-
-
-
\ No newline at end of file +
+

+ <%= t('admin.lecture.imported_media') %> + + + (<%= lecture.imported_media.size %>) + + +

+ +
+ <%= render partial: 'lectures/import/imported_media', + locals: { media: lecture.imported_media, + lecture: lecture } %> +
+ +
+
+ +
+
+ +
+
+ <%= render partial: 'media/catalog/search_form', + locals: { purpose: 'import' } %> +
+
+
diff --git a/app/views/lectures/edit/_organizational_concept.html.erb b/app/views/lectures/edit/_organizational_concept.html.erb index ef5875a98..af6c2f422 100644 --- a/app/views/lectures/edit/_organizational_concept.html.erb +++ b/app/views/lectures/edit/_organizational_concept.html.erb @@ -1,85 +1,63 @@ -
-
- +
+ <% end %> +
+
+
+ <%= f.trix_editor :organizational_concept, + id: 'lecture-concept-trix', + data: { content: lecture.organizational_concept } %>
-
-
- <%= form_with model: lecture, - html: { id: 'lecture-organizational-form' } do |f| %> -
-
-
- <%= f.check_box :organizational, - class: 'form-check-input' %> - <%= f.label :organizational, - t('admin.lecture.organizational_visible'), - { class: 'form-check-label' } %> - <%= helpdesk(t('admin.lecture.info.organizational'), false) %> -
-
-
-
- <%= f.check_box :muesli, - class: 'form-check-input' %> - <%= f.label :muesli, - t('admin.lecture.uses_muesli'), - { class: 'form-label' } %> - <%= helpdesk(t('admin.lecture.info.muesli'), false) %> -
-
- <% if !lecture.term && lecture.organizational %> -
-
- <%= f.check_box :organizational_on_top, - class: 'form-check-input' %> - <%= f.label :organizational_on_top, - t('admin.lecture.organizational_on_top'), - { class: 'form-check-label' } %> - <%= helpdesk(t('admin.lecture.info.organizational_on_top'), - false) %> -
-
- <% end %> -
-
-
- <%= f.trix_editor :organizational_concept, - id: 'lecture-concept-trix', - data: { content: lecture.organizational_concept } %> -
-
-
-
-
- <%= t('warnings.unsaved_changes') %> - <%= f.submit t('buttons.save_and_exit'), - class: "btn btn-primary" %> - -
-
-
- <% end %> +
+
+
+
+ <%= t('warnings.unsaved_changes') %> + <%= hidden_field_tag :subpage, "orga" %> + <%= f.submit t('buttons.save'), + class: "btn btn-primary" %> +
-
\ No newline at end of file +
+<% end %> diff --git a/app/views/lectures/edit/_people.html.erb b/app/views/lectures/edit/_people.html.erb index c32de1a3b..e1d047f46 100644 --- a/app/views/lectures/edit/_people.html.erb +++ b/app/views/lectures/edit/_people.html.erb @@ -1,128 +1,87 @@ -
-
- +
+ +
+ <%= f.label :editor_ids, + t('basics.lecture_editors'), + class: "form-label" %> + <%= helpdesk(t('admin.lecture.info.lecture_editors'), false) %> +
+ <%= f.select :editor_ids, + options_for_select([[t('none'), '']] + + lecture.select_editors, + lecture.editors.map(&:id)), + {}, + { multiple: true, + class: 'selectize', + data: { ajax: true, + model: 'user', + filled: false, + placeholder: t('basics.enter_two_letters'), + no_results: t('basics.no_results') } } %> +
+
-
-
- <% if current_user.can_update_personell?(lecture) %> - <%= form_with model: lecture, html: { id: 'lecture-form' } do |f| %> -
-
- <%= f.label :teacher_id, - t('basics.teacher'), - class: "form-label" %> - <%= helpdesk(t('admin.lecture.info.teacher'), false) %> -
- <%= f.select :teacher_id, - options_for_select([[lecture.teacher.info, - lecture.teacher.id]], - lecture.teacher.id), - {}, - { class: 'selectize', - data: { ajax: true, - model: 'user', - filled: false, - placeholder: - t('basics.enter_two_letters'), - no_results: - t('basics.no_results') } } %> -
-
-
-
-
- <%= t('basics.course_editors') %> - <%= helpdesk(t('admin.lecture.info.course_editors'), false) %> - <% if lecture.course.editors.present? %> -
    - <% lecture.course.editors.each do |e| %> -
  • - <%= e.name %> -
  • - <% end %> -
- <% else %> -
- <%= t('admin.lecture.no_course_editors') %> - <% end %> -
-
- <%= f.label :editor_ids, - t('basics.lecture_editors'), - class: "form-label" %> - <%= helpdesk(t('admin.lecture.info.lecture_editors'), false) %> -
- <%= f.select :editor_ids, - options_for_select([[t('none'), '']] + - lecture.select_editors, - lecture.editors.map(&:id)), - {}, - { multiple: true, - class: 'selectize', - data: { ajax: true, - model: 'user', - filled: false, - placeholder: t('basics.enter_two_letters'), - no_results: t('basics.no_results') } } %> -
-
-
-
-
- <%= t('basics.subscribers_count_nc') %> - - <%= lecture.users.count %> - - <%= helpdesk(t('admin.lecture.info.subscribers_count'), false) %> - <% if lecture.users.any? && !lecture.stale? %> - - <% end %> -
-
-
-
-
- <%= t('warnings.unsaved_changes') %> - <%= f.submit t('buttons.save'), - class: "btn btn-sm btn-primary" %> - -
-
-
- <% end %> - <% else %> - <%= t('admin.lecture.no_access_to_users_html', - project_mail: mail_to(DefaultSetting::PROJECT_EMAIL, nil)) %> +
+
+ <%= t('basics.subscribers_count_nc') %> + + <%= lecture.users.count %> + + <%= helpdesk(t('admin.lecture.info.subscribers_count'), false) %> + <% if lecture.users.any? && !lecture.stale? %> + <% end %>
-
+ + +
+ <%= t('warnings.unsaved_changes') %> + <%= hidden_field_tag :subpage, "people" %> + <%= f.submit t('buttons.save'), + class: "btn btn-sm btn-primary" %> + +
+ <% end %> +<% else %> + <%= t('admin.lecture.no_access_to_users_html', + project_mail: mail_to(DefaultSetting::PROJECT_EMAIL, nil)) %> +<% end %> diff --git a/app/views/lectures/edit/_preferences.html.erb b/app/views/lectures/edit/_preferences.html.erb index 9fb9adfcd..da5399e39 100644 --- a/app/views/lectures/edit/_preferences.html.erb +++ b/app/views/lectures/edit/_preferences.html.erb @@ -1,177 +1,153 @@ -
-
- +
+ <% end %> + <% unless lecture.course.term_independent %> +
+ <%= f.label :sort, + t('basics.type'), + class: "form-label" %> + <%= f.select :sort, + options_for_select(Lecture.sort_localized.invert.to_a, + lecture.sort), + {}, + { class: 'form-select' } %> +
+
+
+ <% end %> + <% unless lecture.seminar? %> +
+ <%= f.label :start_chapter, t('admin.lecture.first_chapter'), + class: "form-label" %> + <%= helpdesk(t('admin.lecture.info.first_chapter'), false) %> + <%= f.number_field :start_chapter, + value: lecture.start_chapter || 1, + class: 'form-control' %> +
+
+
+ <%= f.label :start_section, + t('admin.lecture.first_section'), + class: "form-label" %> + <%= helpdesk(t('admin.lecture.info.first_section'), false) %> + <%= f.number_field :start_section, + value: lecture.start_section || 1, + class: 'form-control', + disabled: !lecture.absolute_numbering %> +
+
+
+
+ <%= f.check_box :absolute_numbering, + class: 'form-check-input' %> + <%= f.label :absolute_numbering, + t('admin.lecture.absolute_numbering'), + { class: 'form-check-label' } %> + <%= helpdesk(t('admin.lecture.info.absolute_numbering'), + false) %> +
+
+ <% end %> +
+
+
+ <%= f.label :passphrase, + t('admin.lecture.passphrase'), + class: "form-label" %> + <%= helpdesk(t('admin.lecture.info.passphrase'), false) %> + <%= f.text_field :passphrase, + { class: 'form-control', + size: 50 } %> +
+
+
+ <%= t('basics.language') %> +
+ <% I18n.available_locales.each do |locale| %> +
+ <%= f.radio_button :locale, + locale.to_s, + class: 'form-check-input' %> + <%= f.label :locale, + t('locales.' + locale.to_s), + value: locale.to_s, + class: 'form-check-label' %> +
+ <% end %> +
+
+ <% unless lecture.seminar? %> +
+ <%= t('admin.lecture.content_mode') %> + <%= helpdesk(t('admin.lecture.info.variants'), true) %> +
+
+ <%= f.radio_button :content_mode, + 'video', + class: 'form-check-input' %> + <%= f.label :content_mode, + t('admin.lecture.video_based'), + value: 'video', + class: 'form-check-label' %> +
+
+ <%= f.radio_button :content_mode, + 'manuscript', + class: 'form-check-input' %> + <%= f.label :content_mode, + t('admin.lecture.script_based'), + value: 'manuscript', + class: 'form-check-label' %> +
+
+
+ <% end %> + <% if !lecture.term %> +
+
+
+ <%= f.check_box :disable_teacher_display, + class: 'form-check-input' %> + <%= f.label :disable_teacher_display, + t('admin.lecture.disable_teacher_display'), + { class: 'form-check-label' } %> + <%= helpdesk(t('admin.lecture.info.disable_teacher_display'), + false) %> +
+
-
-
- <%= form_with model: lecture, - html: { id: 'lecture-preferences-form' } do |f| %> -
- <% unless lecture.course.term_independent %> -
- <%= f.label :term_id, - t('basics.term'), - class: "form-label" %> - <%= helpdesk(t('admin.lecture.info.term'), false) %> - <%= f.select :term_id, - options_for_select(@terms, - lecture.term&.id), - {}, - { class: 'form-select' } %> -
-
-
- <% end %> - <% unless lecture.course.term_independent %> -
- <%= f.label :sort, - t('basics.type'), - class: "form-label" %> - <%= f.select :sort, - options_for_select(Lecture.sort_localized.invert.to_a, - lecture.sort), - {}, - { class: 'form-select' } %> -
-
-
- <% end %> - <% unless lecture.seminar? %> -
- <%= f.label :start_chapter, t('admin.lecture.first_chapter'), - class: "form-label" %> - <%= helpdesk(t('admin.lecture.info.first_chapter'), false) %> - <%= f.number_field :start_chapter, - value: lecture.start_chapter || 1, - class: 'form-control' %> -
-
-
- <%= f.label :start_section, - t('admin.lecture.first_section'), - class: "form-label" %> - <%= helpdesk(t('admin.lecture.info.first_section'), false) %> - <%= f.number_field :start_section, - value: lecture.start_section || 1, - class: 'form-control', - disabled: !lecture.absolute_numbering %> -
-
-
-
- <%= f.check_box :absolute_numbering, - class: 'form-check-input' %> - <%= f.label :absolute_numbering, - t('admin.lecture.absolute_numbering'), - { class: 'form-check-label' } %> - <%= helpdesk(t('admin.lecture.info.absolute_numbering'), - false) %> -
-
- <% end %> -
-
-
- <%= f.label :passphrase, - t('admin.lecture.passphrase'), - class: "form-label" %> - <%= helpdesk(t('admin.lecture.info.passphrase'), false) %> - <%= f.text_field :passphrase, - { class: 'form-control', - size: 50 } %> -
-
-
- <%= t('basics.language') %> -
- <% I18n.available_locales.each do |locale| %> -
- <%= f.radio_button :locale, - locale.to_s, - class: 'form-check-input' %> - <%= f.label :locale, - t('locales.' + locale.to_s), - value: locale.to_s, - class: 'form-check-label' %> -
- <% end %> -
-
- <% unless lecture.seminar? %> -
- <%= t('admin.lecture.content_mode') %> - <%= helpdesk(t('admin.lecture.info.variants'), true) %> -
-
- <%= f.radio_button :content_mode, - 'video', - class: 'form-check-input' %> - <%= f.label :content_mode, - t('admin.lecture.video_based'), - value: 'video', - class: 'form-check-label' %> -
-
- <%= f.radio_button :content_mode, - 'manuscript', - class: 'form-check-input' %> - <%= f.label :content_mode, - t('admin.lecture.script_based'), - value: 'manuscript', - class: 'form-check-label' %> -
-
-
- <% end %> - <% if !lecture.term %> -
-
-
- <%= f.check_box :disable_teacher_display, - class: 'form-check-input' %> - <%= f.label :disable_teacher_display, - t('admin.lecture.disable_teacher_display'), - { class: 'form-check-label' } %> - <%= helpdesk(t('admin.lecture.info.disable_teacher_display'), - false) %> -
-
-
- <% end %> -
-
-
- <%= t('warnings.unsaved_changes') %> - <%= f.submit t('buttons.save_and_exit'), - class: "btn btn-primary" %> - -
-
-
- <% end %> + <% end %> +
+
+
+ <%= t('warnings.unsaved_changes') %> + <%= hidden_field_tag :subpage, "settings" %> + <%= f.submit t('buttons.save'), + class: "btn btn-primary" %> +
+<% end %> diff --git a/app/views/lectures/edit/_structures.html.erb b/app/views/lectures/edit/_structures.html.erb index 0931f3a70..318624003 100644 --- a/app/views/lectures/edit/_structures.html.erb +++ b/app/views/lectures/edit/_structures.html.erb @@ -1,11 +1,7 @@ -
-
-
- <%= t('erdbeere.select_structures') %> - <%= helpdesk(t('erdbeere.info.structures'), false) %> -
-
-
+

+ <%= t('erdbeere.select_structures') %> + <%= helpdesk(t('erdbeere.info.structures'), false) %> +

<%= form_with model: lecture, html: { id: 'lecture-structures-form' } do |f| %> <% all_structures.each do |s| %> @@ -19,13 +15,12 @@
<% end %>
-
+ diff --git a/app/views/lectures/edit/_tags.html.erb b/app/views/lectures/edit/_tags.html.erb index 391797b3c..047e42977 100644 --- a/app/views/lectures/edit/_tags.html.erb +++ b/app/views/lectures/edit/_tags.html.erb @@ -1,65 +1,42 @@ -
-
- -
-
-
-
    -
  • -
    - <%= t('admin.lecture.course_tags_html', - course: course_link_or_text(lecture.course, current_user), - count: @course_tags.size) %> - <%= helpdesk(t('admin.lecture.info.course_tags'), false) %> -
    - <% if @course_tags %> - <%= render partial: "tags/tag", - collection: @course_tags.natural_sort_by(&:title), - cached: true %> - <% else %> - <%= t('admin.lecture.no_course_tags') %> - <% end %> -
  • -
  • -
    - <%= t('admin.lecture.additional_tags', - count: @extra_tags.size) %> - <%= helpdesk(t('admin.lecture.info.additional_tags'), false) %> -
    - <%= render partial: "tags/tag", - collection: @extra_tags.natural_sort_by(&:title), - cached: true %> -
  • -
  • -
    - <%= t('admin.lecture.untreated_tags', - count: @deferred_tags.size) %> - <%= helpdesk(t('admin.lecture.info.untreated_tags'), false) %> -
    - <%= render partial: "tags/tag", - collection: @deferred_tags.natural_sort_by(&:title), - cached: true %> -
  • -
-
-
-
+
+

+ <%= t('admin.lecture.related_tags') %> +

+ +
    +
  • +
    + <%= t('admin.lecture.course_tags_html', + course: course_link_or_text(lecture.course, current_user), + count: @course_tags.size) %> + <%= helpdesk(t('admin.lecture.info.course_tags'), false) %> +
    + <% if @course_tags %> + <%= render partial: "tags/tag", + collection: @course_tags.natural_sort_by(&:title), + cached: true %> + <% else %> + <%= t('admin.lecture.no_course_tags') %> + <% end %> +
  • +
  • +
    + <%= t('admin.lecture.additional_tags', + count: @extra_tags.size) %> + <%= helpdesk(t('admin.lecture.info.additional_tags'), false) %> +
    + <%= render partial: "tags/tag", + collection: @extra_tags.natural_sort_by(&:title), + cached: true %> +
  • +
  • +
    + <%= t('admin.lecture.untreated_tags', + count: @deferred_tags.size) %> + <%= helpdesk(t('admin.lecture.info.untreated_tags'), false) %> +
    + <%= render partial: "tags/tag", + collection: @deferred_tags.natural_sort_by(&:title), + cached: true %> +
  • +
diff --git a/app/views/lectures/edit/_tutorials.html.erb b/app/views/lectures/edit/_tutorials.html.erb index 8152e34eb..1dded934e 100644 --- a/app/views/lectures/edit/_tutorials.html.erb +++ b/app/views/lectures/edit/_tutorials.html.erb @@ -1,69 +1,47 @@ -
-
- -
-
-
- <% if current_user.can_update_personell?(lecture) %> -
-
- <%= link_to t('admin.tutorial.new'), - new_tutorial_path(params: { lecture_id: lecture.id }), - class: 'btn btn-sm btn-primary', - id: 'newTutorialButton', - remote: true %> -
+
+
+ <%= t('basics.tutors') %> +
-
-
-
-
-
- <%= t('basics.title') %> -
-
-
-
- <%= t('basics.tutors') %> -
-
-
-
- <%= t('basics.action') %> - <%= helpdesk(t('tutorial.destruction_info'), true) %> -
-
-
-
- <% lecture.tutorials.each do |t| %> - <%= render partial: 'tutorials/row', - locals: { tutorial: t } %> - <% end %> +
+
+ <%= t('basics.action') %> + <%= helpdesk(t('tutorial.destruction_info'), true) %> +
- <% else %> - <%= t('admin.lecture.no_access_to_users_html', - project_mail: mail_to(DefaultSetting::PROJECT_EMAIL, nil)) %> - <% end %> +
+ <% lecture.tutorials.each do |t| %> + <%= render partial: 'tutorials/row', + locals: { tutorial: t } %> + <% end %>
-
\ No newline at end of file +<% else %> + <%= t('admin.lecture.no_access_to_users_html', + project_mail: mail_to(DefaultSetting::PROJECT_EMAIL, nil)) %> +<% end %> diff --git a/app/views/lectures/edit_structures.coffee b/app/views/lectures/edit_structures.coffee index 14b57658b..32aec6a0f 100644 --- a/app/views/lectures/edit_structures.coffee +++ b/app/views/lectures/edit_structures.coffee @@ -31,3 +31,4 @@ renderMathInElement structuresBody, throwOnError: false initBootstrapPopovers() +registerErdbeereExampleChanges() \ No newline at end of file diff --git a/app/views/lectures/update.coffee b/app/views/lectures/update.coffee index c04bd4abc..a9430383f 100644 --- a/app/views/lectures/update.coffee +++ b/app/views/lectures/update.coffee @@ -6,6 +6,8 @@ $('#lecture-term-error').empty() # display error messages +<% if @errors.present? %> + <% if @errors[:teacher].present? %> $('#lecture-teacher-error').append('<%= @errors[:teacher].join(" ") %>').show() <% end %> @@ -13,3 +15,5 @@ $('#lecture-teacher-error').append('<%= @errors[:teacher].join(" ") %>').show() <% if @errors[:course].present? %> $('#lecture-term-error').append('<%= @errors[:course].join(" ") %>').show() <% end %> + +<% end %> diff --git a/app/views/tutorials/_form.html.erb b/app/views/tutorials/_form.html.erb index 200940f0c..1fe039da7 100644 --- a/app/views/tutorials/_form.html.erb +++ b/app/views/tutorials/_form.html.erb @@ -27,7 +27,7 @@
<%= f.submit t('buttons.save'), - class: 'btn btn-sm btn-primary ms-2' %> + class: 'btn btn-sm btn-primary' %> <%= link_to t('buttons.cancel'), cancel_editing_tutorial_path(tutorial), class: 'btn btn-sm btn-secondary', diff --git a/config/locales/de.yml b/config/locales/de.yml index eb7c8b186..23ec38b1d 100644 --- a/config/locales/de.yml +++ b/config/locales/de.yml @@ -437,6 +437,9 @@ de: special: 'Special' special_short: '' special_content: 'Inhalt' + orga: 'Orga' + organization: 'Organisation' + communication: 'Kommunikation' new_lecture: 'Neue Veranstaltung anlegen' no_courses_yet: 'Es muss zunächst ein Modul angelegt werden.' no_terms_yet: 'Es muss zunächst ein Semester angelegt werden.' @@ -3464,6 +3467,7 @@ de: unlock_board: 'Forum entsperren' lock_board: 'Forum sperren' delete_board: 'Forum löschen' + delete_board_sure_to_delete: 'Forum wirklich löschen?' add_board: 'Forum anlegen' all_courses: 'alle Module' edited_courses: 'von mir editierte Module' diff --git a/config/locales/en.yml b/config/locales/en.yml index 43838a82e..6eb83a3c3 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -436,6 +436,9 @@ en: special: 'Special' special_short: '' special_content: 'Content' + orga: 'Orga' + organization: 'Organization' + communication: 'Communication' new_lecture: 'Create a new event series' no_courses_yet: 'You have to create a course first.' no_terms_yet: 'You have to create a term first.' @@ -3282,6 +3285,7 @@ en: unlock_board: 'Unlock Board' lock_board: 'Lock Board' delete_board: 'Delete Board' + delete_board_sure_to_delete: 'Really delete this board?' add_board: 'Create Board' all_courses: 'All Courses' edited_courses: 'Courses edited by myself'