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'