diff --git a/app/views/contents/courseware/courseware_manager.php b/app/views/contents/courseware/courseware_manager.php
deleted file mode 100644
index 7d06bb78204934d77c4cecf654e797b8bed282a3..0000000000000000000000000000000000000000
--- a/app/views/contents/courseware/courseware_manager.php
+++ /dev/null
@@ -1 +0,0 @@
-<div id="courseware-manager-app" entry-type="users" entry-id="<?= $user_id ?>"></div>
diff --git a/app/views/course/courseware/manager.php b/app/views/course/courseware/manager.php
deleted file mode 100644
index 3107cf5a44f5cbc3ca307858a3f086da93b2d1aa..0000000000000000000000000000000000000000
--- a/app/views/course/courseware/manager.php
+++ /dev/null
@@ -1 +0,0 @@
-<div id="courseware-manager-app" entry-type="courses" entry-id="<?= Context::getId() ?>"></div>
diff --git a/resources/assets/javascripts/bootstrap/courseware.js b/resources/assets/javascripts/bootstrap/courseware.js
index 503ae89f873a53ad0a236a4e03f5aaf44fa221e7..ccb789217d8beb47e2ee983a5c77077a2b0c3bd5 100644
--- a/resources/assets/javascripts/bootstrap/courseware.js
+++ b/resources/assets/javascripts/bootstrap/courseware.js
@@ -43,17 +43,6 @@ STUDIP.domReady(() => {
});
}
- if (document.getElementById('courseware-manager-app')) {
- STUDIP.Vue.load().then(({ createApp }) => {
- import(
- /* webpackChunkName: "courseware-manager-app" */
- '@/vue/courseware-manager-app.js'
- ).then(({ default: mountApp }) => {
- return mountApp(STUDIP, createApp, '#courseware-manager-app');
- });
- });
- }
-
if (document.getElementById('courseware-content-bookmark-app')) {
STUDIP.Vue.load().then(({ createApp }) => {
import(
diff --git a/resources/assets/stylesheets/scss/courseware.scss b/resources/assets/stylesheets/scss/courseware.scss
index c04365d283a4b50cb83b9586804d79752349cadb..7de515e01409606892a98697e45d383f6daa40a1 100644
--- a/resources/assets/stylesheets/scss/courseware.scss
+++ b/resources/assets/stylesheets/scss/courseware.scss
@@ -2433,81 +2433,6 @@ d a s h b o a r d e n d
p r o g r e s s e n d
* * * * * * * * * * * */
-/* * * * * *
-o b l o n g
-* * * * * */
-
-.cw-oblong-large {
- border: solid thin $content-color-40;
- width: 520px;
-
- .cw-oblong-value,
- .cw-oblong-description {
- display: inline-block;
- height: 90px;
- vertical-align: top;
- line-height: 90px;
- text-align: center;
- }
-
- .cw-oblong-value {
- width: 89px;
- color: $black;
- background-color: $content-color-20;
- border-right: solid thin $content-color-40;
- font-size: xx-large;
- }
- .cw-oblong-description {
- width: 426px;
- color: $black;
- font-size: large;
- img {
- vertical-align: middle;
- margin-right: 10px;
- }
-
- }
-}
-
-.cw-oblong-small {
- border: solid thin $content-color-40;
- width: 340px;
- margin-right: 1em;
- margin-bottom: 5px;
-
- .cw-oblong-value,
- .cw-oblong-description {
- display: inline-block;
- height: 60px;
- vertical-align: top;
- line-height: 60px;
- text-align: center;
- }
-
- .cw-oblong-value {
- width: 59px;
- background-color: $content-color-20;
- border-right: solid thin $content-color-40;
- color: $black;
- font-size: x-large;
- }
- .cw-oblong-description {
- width: calc(100% - 64px);
- background-color: $white;
- color: $black;
- overflow: hidden;
- img {
- vertical-align: middle;
- margin-right: 8px;
- }
-
- }
-}
-
-/* * * * * * * * * *
-o b l o n g e n d
-* * * * * * * * * */
-
/* * * * * * * * * * * * * * *
p r o g r e s s c i r c l e
* * * * * * * * * * * * * * */
@@ -2595,249 +2520,7 @@ p r o g r e s s c i r c l e
p r o g r e s s c i r c l e e n d
* * * * * * * * * * * * * * * * * */
-/* * * * * * *
-m a n a g e r
-* * * * * * */
-
-.cw-course-manager {
- display: flex;
- flex-wrap: wrap;
- max-width: 1600px;
-
- .cw-course-manager-tabs {
- width: calc(50% - 10px);
- margin-right: 20px;
-
- &:last-child{
- margin: 0;
- }
-
- .cw-tabs-content {
- min-height: 400px;
- padding: 10px;
- resize: vertical;
- overflow-y: auto;
- scrollbar-width: thin;
- scrollbar-color: $base-color $white;
- }
- }
-}
-
-.cw-manager-element {
-
- .cw-sort-ease-move {
- transition: all 0.4s ease-in-out;
- }
-
- .cw-manager-element-title {
-
- img {
- cursor: pointer;
- vertical-align: text-bottom;
- }
-
- .cw-manager-element-breadcrumb {
- display: inline;
- .cw-manager-element-breadcrumb-home,
- .cw-manager-element-breadcrumb-item {
- cursor: pointer;
- color: $base-color;
-
- &::after {
- content: ' / ';
- }
- &:hover {
- color: $active-color;
- }
- }
- }
- .cw-manager-element-actions {
- position: relative;
- display: inline;
- float: right;
- cursor: pointer;
- z-index: 32;
- }
- header {
- padding: 0.25em 0 0.5em 0;
- font-size: 1.6em;
- font-weight: 700;
-
- button.cw-insert-element {
- background: transparent;
- border: none;
- cursor: pointer;
- padding: 0;
- }
- }
- }
- .cw-manager-element-containers {
- margin-bottom: 8px;
- }
- .cw-manager-container {
- margin-bottom: 10px;
- border: solid thin $content-color-40;
-
- &:last-child {
- margin-bottom: 0;
- }
-
- .cw-manager-container-title {
- font-weight: 700;
- display: flex;
- gap: 5px;
- justify-content: space-between;
- padding: 4px 4px 4px 8px;
- color: $base-color;
- background-color: $content-color-20;
-
- &.cw-manager-container-clickable-title {
- cursor: pointer;
- justify-content: start;
- }
-
- img {
- vertical-align: middle;
- }
- }
- .cw-manager-container-blocks {
- margin: 4px;
- }
- .cw-manager-block {
- border: solid thin $content-color-40;
- display: flex;
- gap: 5px;
- justify-content: space-between;
- padding: 1em;
- margin-bottom: 4px;
- background-color: $white;
-
- &.cw-manager-block-clickable {
- cursor: pointer;
- justify-content: start;
-
- &:hover {
- background-color: $base-color;
- color: $white;
- }
- }
-
- img {
- vertical-align: middle;
- }
-
- &:last-child {
- margin-bottom: 0;
- }
- }
-
- }
-
- .cw-manager-element-item-wrapper {
- margin-bottom: 4px;
-
- &:last-child {
- margin-bottom: 0;
- }
- }
-
- .cw-manager-element-item {
- display: flex;
- border: solid thin $content-color-40;
- padding: 1em;
- justify-content: space-between;
- vertical-align: middle;
- background-color: $white;
- color: $base-color;
-
- img {
- vertical-align: middle;
- }
- .cw-manager-element-item-solver-name {
- flex: fit-content;
- padding-left: 0.25em;
- }
-
- &:hover {
- color: $white;
- background-color: $base-color;
- }
-
- &.cw-manager-element-item-sorting:hover{
- background-color: $white;
- color: $base-color;
- }
-
- &.cw-manager-element-item-inserter {
- padding-left: 2em;
- justify-content: start;
- @include background-icon(arr_2left, clickable, 16);
- background-position: 6px center;
- background-repeat: no-repeat;
- &:hover {
- @include background-icon(arr_2left, info-alt, 16);
- }
-
- img {
- margin-right: 1em;
- }
- }
- }
-
- .cw-manager-filing {
- border: solid thin $content-color-40;
- margin-top: 4px;
- @include background-icon(arr_eol-down, clickable, 24);
- background-color: $white;
- background-position: calc(50% - 10em) calc(50% - 1px);
- background-repeat: no-repeat;
- padding: 1em 0;
- color: $base-color;
- text-align: center;
- width: 100%;
- font-weight: 600;
- cursor: pointer;
-
- &:hover {
- border-color: $base-color;
- }
-
- &.cw-manager-filing-active {
- @include background-icon(arr_eol-down, info-alt, 24);
- background-color: $base-color;
- border: solid thin $base-color;
- color: $white;
- }
- &.cw-manager-filing-disabled {
- @include background-icon(arr_eol-down, inactive, 24);
- background-color: $white;
- color: $dark-gray-color-80;
- }
- }
-
- .cw-manager-block-buttons,
- .cw-manager-container-buttons,
- .cw-manager-element-item-buttons {
- button {
- background: transparent;
- border: none;
- cursor: pointer;
- padding: 0;
-
- &[disabled] {
- visibility: hidden;
- }
- }
- }
-
- .cw-collapsible-content {
- display: none;
- &.cw-collapsible-content-open {
- display: block;
- }
- }
-}
.cw-import-zip {
margin-bottom: 1em;
@@ -2862,10 +2545,6 @@ m a n a g e r
}
}
-/* * * * * * * * * * *
-m a n a g e r e n d
-* * * * * * * * * * */
-
/* * * * * *
b l o c k s
* * * * * */
@@ -5188,31 +4867,6 @@ cw tiles
cw tiles end
*/
-/* cw manager copy */
-
-.cw-manager-copy-selector {
- ul {
- padding: 0;
- margin: 0;
- list-style: none;
- }
- .cw-manager-copy-selector-course {
- display: block;
- color: $base-color;
- line-height: 2em;
-
- &:hover {
- color: $active-color;
- }
-
- img {
- vertical-align: text-bottom;
- }
- }
-}
-
-/* cw manager copy end*/
-
/* courseware template preview */
.cw-template-preview {
display: flex;
diff --git a/resources/vue/components/courseware/CoursewareCourseManager.vue b/resources/vue/components/courseware/CoursewareCourseManager.vue
deleted file mode 100644
index ad9ae98119c37fe473a2428bdb396f485e50558e..0000000000000000000000000000000000000000
--- a/resources/vue/components/courseware/CoursewareCourseManager.vue
+++ /dev/null
@@ -1,236 +0,0 @@
-<template>
- <div class="cw-course-manager-wrapper">
- <div class="cw-course-manager">
- <courseware-tabs class="cw-course-manager-tabs">
- <courseware-tab :name="$gettext('Diese Courseware')" :selected="true" :index="0">
- <courseware-manager-element
- type="current"
- :currentElement="currentElement"
- @selectElement="setCurrentId"
- @reloadElement="reloadElements"
- />
- </courseware-tab>
- <courseware-tab :name="$gettext('Export')" :index="1">
- <button
- class="button"
- @click.prevent="doExportCourseware"
- :class="{
- disabled: exportRunning,
- }"
- >
- <translate>Alles exportieren</translate>
- </button>
- <courseware-companion-box v-show="exportRunning" :msgCompanion="$gettext('Export läuft, bitte haben sie einen Moment Geduld...')" mood="pointing"/>
- <div v-if="exportRunning" class="cw-import-zip">
- <header>{{exportState}}:</header>
- <div class="progress-bar-wrapper">
- <div class="progress-bar" role="progressbar" :style="{width: exportProgress + '%'}" :aria-valuenow="exportProgress" aria-valuemin="0" aria-valuemax="100">{{ exportProgress }}%</div>
- </div>
- </div>
- </courseware-tab>
- </courseware-tabs>
-
- <courseware-tabs class="cw-course-manager-tabs">
- <courseware-tab :name="$gettext('FAQ')" :index="0">
- <courseware-collapsible-box :open="true" :title="$gettext('Wie finde ich die gewünschte Stelle?')">
- <p><translate>
- Wählen Sie auf der linken Seite "Diese Courseware" aus.
- Beim Laden der Seite ist dies immer gewählt. Die Überschrift
- gibt an, welche Seite Sie gerade ausgewählt haben. Darunter befinden
- sich die Abschnitte der Seite und innerhalb dieser deren Blöcke.
- Möchten Sie eine Seite, die unterhalb der gewählten liegt, bearbeiten,
- können Sie diese über die Schaltflächen im Bereich "Unterseiten" wählen.
- Über der Überschrift wird eine Navigation eingeblendet, mit der Sie beliebig
- weit hoch in der Hierarchie springen können.
- </translate></p>
- </courseware-collapsible-box>
- <courseware-collapsible-box :title="$gettext('Wie sortiere ich Objekte?')">
- <p><translate>
- Seiten, Abschnitte und Blöcke lassen sich in ihrer Reihenfolge sortieren.
- Hierzu wählen Sie auf der linken Seite unter "Diese Courseware" die Schaltfläche "Unterseiten sortieren",
- "Abschnitte sortieren" oder "Blöcke sortieren".
- An den Objekten werden Pfeile angezeigt, mit diesen können die Objekte an die gewünschte
- Position gebracht werden. Um die neue Sortierung zu speichern, wählen Sie "Sortieren beenden".
- Sie können die Änderungen auch rückgängig machen, indem Sie "Sortieren abbrechen" wählen.
- </translate></p>
- </courseware-collapsible-box>
- <courseware-collapsible-box :title="$gettext('Wie verschiebe ich Objekte?')">
- <p><translate>
- Seiten, Abschnitte und Blöcke lassen sich verschieben.
- Hierzu wählen Sie auf der linken Seite unter "Diese Courseware" die Schaltfläche
- "Seite an dieser Stelle einfügen", "Abschnitt an dieser Stelle einfügen" oder
- "Block an dieser Stelle einfügen". Wählen Sie dann auf der rechten Seite unter
- "Verschieben" das Objekt aus, das Sie verschieben möchten. Verschiebbare Objekte
- erkennen Sie an den zwei nach links zeigenden gelben Pfeilen.
- </translate></p>
- </courseware-collapsible-box>
- <courseware-collapsible-box :title="$gettext('Wie kopiere ich Objekte?')">
- <p><translate>
- Seiten, Abschnitte und Blöcke lassen sich aus einer anderen Veranstaltung und Ihren
- eigenen Inhalten kopieren.
- Hierzu wählen Sie auf der linken Seite unter "Diese Courseware" die Schaltfläche
- "Seite an dieser Stelle einfügen", "Abschnitt an dieser Stelle einfügen" oder
- "Block an dieser Stelle einfügen". Wählen Sie dann auf der rechten Seite unter
- "Kopieren" erst die Veranstaltung aus der Sie kopieren möchten oder Ihre eigenen
- Inhalte. Wählen sie dann das Objekt aus, das Sie kopieren möchten. Kopierbare Objekte
- erkennen Sie an den zwei nach links zeigenden gelben Pfeilen.
- </translate></p>
- </courseware-collapsible-box>
- </courseware-tab>
- <courseware-tab :name="$gettext('Verschieben')" :selected="true" :index="1">
- <courseware-manager-element
- type="self"
- :currentElement="selfElement"
- :moveSelfPossible="moveSelfPossible"
- :moveSelfChildPossible="moveSelfChildPossible"
- @selectElement="setSelfId"
- @reloadElement="reloadElements"
- />
- </courseware-tab>
-
- <courseware-tab :name="$gettext('Kopieren')" :index="2">
- <courseware-manager-copy-selector @loadSelf="reloadElements" @reloadElement="reloadElements" />
- </courseware-tab>
-
- <courseware-tab :name="$gettext('Verknüpfen')" :index="3">
- <courseware-manager-link-selector @loadSelf="reloadElements" @reloadElement="reloadElements" />
- </courseware-tab>
-
- <courseware-tab :name="$gettext('Importieren')" :index="4">
- <courseware-manager-import />
- </courseware-tab>
- <courseware-tab v-if="context.type === 'courses'" :name="$gettext('Aufgabe verteilen')" :index="4">
- <courseware-manager-task-distributor />
- </courseware-tab>
- </courseware-tabs>
- </div>
- <courseware-companion-overlay />
- </div>
-</template>
-<script>
-import CoursewareTabs from './CoursewareTabs.vue';
-import CoursewareTab from './CoursewareTab.vue';
-import CoursewareCollapsibleBox from './CoursewareCollapsibleBox.vue';
-import CoursewareManagerElement from './CoursewareManagerElement.vue';
-import CoursewareManagerCopySelector from './CoursewareManagerCopySelector.vue';
-import CoursewareManagerLinkSelector from './CoursewareManagerLinkSelector.vue';
-import CoursewareManagerTaskDistributor from './CoursewareManagerTaskDistributor.vue';
-import CoursewareCompanionOverlay from './CoursewareCompanionOverlay.vue';
-import CoursewareCompanionBox from './CoursewareCompanionBox.vue';
-import CoursewareManagerImport from './CoursewareManagerImport.vue';
-import CoursewareExport from '@/vue/mixins/courseware/export.js';
-import { mapActions, mapGetters } from 'vuex';
-
-export default {
- name: 'courseware-course-manager',
- components: {
- CoursewareTabs,
- CoursewareTab,
- CoursewareCollapsibleBox,
- CoursewareManagerElement,
- CoursewareManagerCopySelector,
- CoursewareManagerLinkSelector,
- CoursewareCompanionOverlay,
- CoursewareCompanionBox,
- CoursewareManagerTaskDistributor,
- CoursewareManagerImport
- },
-
- mixins: [CoursewareExport],
-
- data() {
- return {
- exportRunning: false,
- currentElement: {},
- currentId: null,
- selfElement: {},
- selfId: null,
- };
- },
-
- computed: {
- ...mapGetters({
- courseware: 'courseware',
- context: 'context',
- structuralElementById: 'courseware-structural-elements/byId',
- exportState: 'exportState',
- exportProgress: 'exportProgress'
- }),
- moveSelfPossible() {
- if (this.selfElement.relationships === undefined) {
- return false
- } else if (this.selfElement.relationships.parent.data === null) {
- return false;
- } else if (this.currentElement.id === this.selfElement.relationships.parent.data.id) {
- return false;
- } else if (this.currentId === this.selfId) {
- return false;
- } else {
- return true;
- }
- },
- moveSelfChildPossible() {
- return this.currentId !== this.selfId;
- },
- },
-
- methods: {
- ...mapActions({
- loadCoursewareStructure: 'courseware-structure/load',
- createStructuralElement: 'createStructuralElement',
- updateStructuralElement: 'updateStructuralElement',
- deleteStructuralElement: 'deleteStructuralElement',
- loadStructuralElement: 'loadStructuralElement',
- lockObject: 'lockObject',
- unlockObject: 'unlockObject',
- addBookmark: 'addBookmark',
- companionInfo: 'companionInfo',
- }),
- async reloadElements() {
- await this.setCurrentId(this.currentId);
- await this.setSelfId(this.selfId);
- this.$emit("reload");
- },
- async setCurrentId(target) {
- this.currentId = target;
- await this.loadStructuralElement(this.currentId);
- this.initCurrent();
- },
- initCurrent() {
- this.currentElement = _.cloneDeep(this.structuralElementById({ id: this.currentId }));
- },
- async setSelfId(target) {
- this.selfId = target;
- await this.loadStructuralElement(this.selfId);
- this.initSelf();
- },
- initSelf() {
- this.selfElement = _.cloneDeep(this.structuralElementById({ id: this.selfId }));
- },
-
- async doExportCourseware() {
- if (this.exportRunning) {
- return false;
- }
-
- this.exportRunning = true;
-
- await this.loadCoursewareStructure();
- await this.sendExportZip(
- this.courseware.relationships.root.data.id,
- {withChildren: true}
- );
-
- this.exportRunning = false;
- },
- },
- watch: {
- courseware(newValue, oldValue) {
- let currentId = newValue.relationships.root.data.id;
- this.setCurrentId(currentId);
- this.setSelfId(currentId);
- },
- },
-
-};
-</script>
diff --git a/resources/vue/components/courseware/CoursewareManagerBlock.vue b/resources/vue/components/courseware/CoursewareManagerBlock.vue
deleted file mode 100644
index b28c085e8dafeb4eaae7cf2f374076ecabd8bbb7..0000000000000000000000000000000000000000
--- a/resources/vue/components/courseware/CoursewareManagerBlock.vue
+++ /dev/null
@@ -1,48 +0,0 @@
-<template>
- <div :class="{ 'cw-manager-block-clickable': inserter }" class="cw-manager-block" @click="clickItem">
- <span v-if="inserter">
- <studip-icon shape="arr_2left" role="sort" />
- </span>
- {{ block.attributes.title }}
- <div v-if="sortBlocks" class="cw-manager-block-buttons">
- <button :disabled="!canMoveUp" @click="moveUp" :title="$gettext('Element nach oben verschieben')">
- <studip-icon shape="arr_2up" role="sort" />
- </button>
- <button :disabled="!canMoveDown" @click="moveDown" :title="$gettext('Element nach unten verschieben')">
- <studip-icon shape="arr_2down" role="sort" />
- </button>
- </div>
- </div>
-</template>
-
-<script>
-export default {
- name: 'courseware-manager-block',
- props: {
- block: Object,
- inserter: Boolean,
- sortBlocks: Boolean,
- elementType: String,
- canMoveUp: Boolean,
- canMoveDown: Boolean,
- sectionId: Number
- },
- methods: {
- clickItem() {
- if (this.inserter) {
- this.$emit('insertBlock', {block: this.block, source: this.elementType});
- }
- },
- moveUp() {
- if (this.sortBlocks) {
- this.$emit('moveUp', this.block.id, this.sectionId);
- }
- },
- moveDown() {
- if (this.sortBlocks) {
- this.$emit('moveDown', this.block.id, this.sectionId);
- }
- },
- },
-};
-</script>
diff --git a/resources/vue/components/courseware/CoursewareManagerContainer.vue b/resources/vue/components/courseware/CoursewareManagerContainer.vue
deleted file mode 100644
index 9849c17ad0e894bee1029988d47ea600611d45f2..0000000000000000000000000000000000000000
--- a/resources/vue/components/courseware/CoursewareManagerContainer.vue
+++ /dev/null
@@ -1,282 +0,0 @@
-<template>
- <div class="cw-manager-container">
- <div
- :class="{ 'cw-manager-container-clickable-title': inserter }"
- class="cw-manager-container-title"
- @click="clickItem"
- >
- <span v-if="inserter">
- <studip-icon shape="arr_2left" role="sort" />
- </span>
- {{ container.attributes.title }} ({{container.attributes.width}})
- <div v-if="sortContainers" class="cw-manager-container-buttons">
- <button :disabled="!canMoveUp" @click="moveUp" :title="$gettext('Element nach oben verschieben')">
- <studip-icon shape="arr_2up" role="sort" />
- </button>
- <button :disabled="!canMoveDown" @click="moveDown" :title="$gettext('Element nach unten verschieben')">
- <studip-icon shape="arr_2down" role="sort" />
- </button>
- </div>
- </div>
- <courseware-collapsible-box :open="false" :title="$gettext('Blöcke')" class="cw-manager-container-blocks">
- <div v-if="canSortChildren">
- <button v-show="!sortBlocksActive && isCurrent" class="button sort" @click="sortBlocks">
- <translate>Blöcke sortieren</translate>
- </button>
- <button v-show="sortBlocksActive && isCurrent" class="button accept" @click="storeBlocksSort">
- <translate>Sortieren beenden</translate>
- </button>
- <button v-show="sortBlocksActive && isCurrent" class="button cancel" @click="resetBlocksSort">
- <translate>Sortieren abbrechen</translate>
- </button>
- </div>
- <p v-if="!hasChildren">
- <translate>Dieser Abschnitt enthält keine Blöcke.</translate>
- </p>
- <div v-else-if="sectionsWithBlocksCurrentState.length === 1">
- <transition-group name="cw-sort-ease" tag="div">
- <courseware-manager-block
- v-for="(block, blockIndex) in sectionsWithBlocksCurrentState[0].blocks"
- :key="block.id"
- :block="block"
- :inserter="blockInserter"
- :sortBlocks="sortBlocksActive"
- :canMoveUp="blockIndex !== 0"
- :canMoveDown="blockIndex + 1 !== sectionsWithBlocksCurrentState[0].blocks.length"
- :elementType="elementType"
- :sectionId="0"
- @insertBlock="insertBlock"
- @moveUp="moveBlockUp"
- @moveDown="moveBlockDown"
- />
- </transition-group>
- </div>
- <div v-else>
- <courseware-collapsible-box
- v-for="(section, index) in sectionsWithBlocksCurrentState"
- :key="section.id"
- :open="true"
- :title="section.name"
- class="cw-manager-container-blocks"
- >
- <transition-group name="cw-sort-ease" tag="div">
- <courseware-manager-block
- v-for="(block, blockIndex) in sectionsWithBlocksCurrentState[index].blocks"
- :key="block.id"
- :block="block"
- :inserter="blockInserter"
- :sortBlocks="sortBlocksActive"
- :canMoveUp="blockIndex !== 0 || index !== 0"
- :canMoveDown="index + 1 !== sectionsWithBlocksCurrentState.length || blockIndex + 1 !== sectionsWithBlocksCurrentState[index].blocks.length"
- :elementType="elementType"
- :sectionId="index"
- @insertBlock="insertBlock"
- @moveUp="moveBlockUp"
- @moveDown="moveBlockDown"
- />
- </transition-group>
- </courseware-collapsible-box>
- </div>
- <courseware-manager-filing
- v-if="isCurrent && !sortContainers && !sortBlocksActive"
- :parentId="container.id"
- :parentItem="container"
- itemType="block"
- @deactivated="reloadContainer"
- />
- </courseware-collapsible-box>
- </div>
-</template>
-
-<script>
-import CoursewareCollapsibleBox from './CoursewareCollapsibleBox.vue';
-import CoursewareManagerBlock from './CoursewareManagerBlock.vue';
-import CoursewareManagerFiling from './CoursewareManagerFiling.vue';
-import { mapGetters, mapActions } from 'vuex';
-
-export default {
- name: 'courseware-manager-container',
- components: {
- CoursewareCollapsibleBox,
- CoursewareManagerBlock,
- CoursewareManagerFiling,
- },
- props: {
- container: Object,
- isCurrent: Boolean,
- inserter: Boolean,
- blockInserter: Boolean,
- sortContainers: Boolean,
- elementType: String,
- canMoveUp: Boolean,
- canMoveDown: Boolean
- },
- data() {
- return {
- sortBlocksActive: false,
- sectionsWithBlocksCurrentState: [],
- };
- },
- computed: {
- ...mapGetters({
- blockById: 'courseware-blocks/byId',
- }),
- hasChildren() {
- return this.getBlocksCount >= 1;
- },
- canSortChildren() {
- return this.getBlocksCount > 1;
- },
- containerType() {
- return this.container.attributes['container-type'];
- },
- hasSections() {
- return this.containerType === 'tabs' || this.containerType === 'accordion';
- },
- getBlocksCount() {
- if (this.sectionsWithBlocksCurrentState === null) {
- return 0;
- } else {
- let blocks = 0;
-
- this.sectionsWithBlocksCurrentState.forEach((section) => {
- if (section.blocks !== undefined) {
- blocks += section.blocks.length;
- }
- });
-
- // If there are more that one section and only one block,
- // we make sotring of that block possible, by just assuming that there are 2 blocks.
- // By doing this, we only provide the sorting feature when there are more than one section (section).
- if (this.sectionsWithBlocksCurrentState.length > 1 && blocks == 1) {
- blocks++;
- }
-
- return blocks;
- }
- }
- },
- mounted() {
- this.initSections();
- },
- methods: {
- ...mapActions({
- sortBlocksInContainer: 'sortBlocksInContainer',
- updateContainer: 'updateContainer',
- loadContainer: 'loadContainer',
- lockObject: 'lockObject',
- unlockObject: 'unlockObject'
- }),
- reloadContainer() {
- this.loadContainer(this.container.id);
- },
- clickItem() {
- if (this.inserter) {
- this.$emit('insertContainer', {container: this.container, source: this.elementType});
- }
- },
- getSectionsWithBlocks() {
- if (!this.container) {
- return [];
- }
- if (!this.container.attributes.payload.sections) {
- return [];
- }
-
- const blockSections = _.cloneDeep(this.container.attributes.payload.sections);
-
- blockSections.forEach((section) => {
- if(section.blocks !== undefined) {
- section.blocks = section.blocks.flatMap(
- (id) => {
- return this.blockById({ id }) ?? [] //remove blocks which could not be loaded
- }
- );
- }
- });
-
- return blockSections;
- },
- initSections() {
- this.sectionsWithBlocksCurrentState = this.getSectionsWithBlocks();
- },
- insertBlock(data) {
- this.$emit('insertBlock', data);
- this.initSections();
- },
- sortBlocks() {
- this.sortBlocksActive = true;
- },
- async storeBlocksSort() {
- const container = JSON.parse(JSON.stringify(this.container));
-
- this.sectionsWithBlocksCurrentState.forEach((section, index)=> {
- if (section.blocks !== undefined) {
- container.attributes.payload.sections[index].blocks = section.blocks.map(({ id }) => ( id ));
- }
- });
- await this.lockObject({id: container.id, type: 'courseware-containers'});
- await this.updateContainer({ container: container, structuralElementId: this.container.relationships['structural-element'].data.id });
- await this.unlockObject({id: container.id, type: 'courseware-containers'});
-
- this.sortBlocksActive = false;
- },
- resetBlocksSort() {
- this.sectionsWithBlocksCurrentState = this.getSectionsWithBlocks();
- this.sortBlocksActive = false;
- },
- moveUp() {
- if (this.sortContainers) {
- this.$emit('moveUp', this.container.id);
- }
- },
- moveDown() {
- if (this.sortContainers) {
- this.$emit('moveDown', this.container.id);
- }
- },
- moveBlockUp(blockId, sectionId) {
- let view = this;
- this.sectionsWithBlocksCurrentState[sectionId].blocks.every((block, index) => {
- if (block.id === blockId) {
- if (index === 0) {
- if (sectionId !== 0) {
- view.sectionsWithBlocksCurrentState[sectionId-1].blocks.push(view.sectionsWithBlocksCurrentState[sectionId].blocks.splice(index, 1)[0]);
- }
- return false;
- }
- view.sectionsWithBlocksCurrentState[sectionId].blocks.splice(index - 1, 0, view.sectionsWithBlocksCurrentState[sectionId].blocks.splice(index, 1)[0]);
- return false;
- } else {
- return true;
- }
- });
- },
- moveBlockDown(blockId, sectionId) {
- let view = this;
- this.sectionsWithBlocksCurrentState[sectionId].blocks.every((block, index) => {
- if (block.id === blockId) {
- if (index === view.sectionsWithBlocksCurrentState[sectionId].blocks.length - 1) {
- if (sectionId !== view.sectionsWithBlocksCurrentState.length - 1) {
- view.sectionsWithBlocksCurrentState[sectionId + 1].blocks.unshift(view.sectionsWithBlocksCurrentState[sectionId].blocks.splice(index, 1)[0]);
- }
- return false;
- }
- view.sectionsWithBlocksCurrentState[sectionId].blocks.splice(index + 1, 0, view.sectionsWithBlocksCurrentState[sectionId].blocks.splice(index, 1)[0]);
- return false;
- } else {
- return true;
- }
- });
- },
- },
- watch: {
- container: {
- handler() {
- this.initSections();
- },
- deep: true
- }
- },
-};
-</script>
diff --git a/resources/vue/components/courseware/CoursewareManagerCopySelector.vue b/resources/vue/components/courseware/CoursewareManagerCopySelector.vue
deleted file mode 100644
index 415e79a81bc916a7357c17c8d82dd5c5e4b4441f..0000000000000000000000000000000000000000
--- a/resources/vue/components/courseware/CoursewareManagerCopySelector.vue
+++ /dev/null
@@ -1,238 +0,0 @@
-<template>
- <div class="cw-manager-copy-selector">
- <div v-if="sourceEmpty" class="cw-manager-copy-selector-source">
- <button class="button" @click="selectSource('own'); loadOwnCourseware()"><translate>Aus persönlichen Lernmaterialien kopieren</translate></button>
- <button class="button" @click="selectSource('remote')"><translate>Aus Veranstaltung kopieren</translate></button>
- </div>
- <div v-else>
- <courseware-companion-box v-if="copyAllInProgress" :msgCompanion="copyAllInProgressText" mood="pointing" />
- <button class="button" @click="reset"><translate>Quelle auswählen</translate></button>
- <button v-show="!sourceOwn && hasRemoteCid" class="button" @click="selectNewCourse"><translate>Veranstaltung auswählen</translate></button>
- <button v-show="!sourceOwn && hasRemoteCid" class="button" @click="mergeContent"><translate>Alle Inhalte kopieren</translate></button>
- <div v-if="sourceRemote">
- <h2 v-if="!hasRemoteCid"><translate>Veranstaltungen</translate></h2>
- <ul v-if="!hasRemoteCid && semesterMap.length > 0">
- <li v-for="semester in semesterMap" :key="semester.id">
- <h3>{{semester.attributes.title}}</h3>
- <ul>
- <li v-for="course in coursesBySemester(semester)" :key="course.id">
- <a
- href="#"
- class="cw-manager-copy-selector-course"
- @click="loadRemoteCourseware(course.id)"
- >
- <studip-icon :shape="getCourseIcon(course)" />
- {{course.attributes.title}}
- </a>
- </li>
- </ul>
- </li>
- </ul>
- <courseware-companion-box
- v-if="!hasRemoteCid && semesterMap.length === 0"
- :msgCompanion="$gettext('Es wurden keine Veranstaltung mit Courseware-Inhalten gefunden.')"
- mood="sad"
- />
- <courseware-manager-element
- v-if="remoteId !== '' && hasRemoteCid"
- type="remote"
- :currentElement="remoteElement"
- @selectElement="setRemoteId"
- @loadSelf="loadSelf"
- @reloadElement="reloadElement"
- />
- <courseware-companion-box
- v-if="remoteId === '' && hasRemoteCid"
- :msgCompanion="$gettext('In dieser Veranstaltung wurden noch keine Inhalte angelegt.')"
- mood="sad"
- />
- </div>
- <div v-if="sourceOwn">
- <courseware-manager-element
- v-if="ownId !== ''"
- type="own"
- :currentElement="ownElement"
- @selectElement="setOwnId"
- @loadSelf="loadSelf"
- />
- <courseware-companion-box
- v-else
- :msgCompanion="$gettext('Sie haben noch keine eigenen Inhalte angelegt.')"
- mood="sad"
- />
- </div>
- </div>
- </div>
-</template>
-
-<script>
-import CoursewareManagerElement from './CoursewareManagerElement.vue';
-import { mapActions, mapGetters } from 'vuex';
-import CoursewareCompanionBox from './CoursewareCompanionBox.vue';
-
-export default {
- name: 'courseware-manager-copy-selector',
- components:{
- CoursewareManagerElement,
- CoursewareCompanionBox,
- },
- props: {},
- data() {
- return {
- source: '',
- courses: [],
- remoteCid: '',
- remoteCoursewareInstance: {},
- remoteId: '',
- remoteElement: {},
- ownCoursewareInstance: {},
- ownId: '',
- ownElement: {},
- semesterMap: [],
- copyAllInProgress: false,
- copyAllInProgressText: ''
- }
- },
- computed: {
- ...mapGetters({
- courseware: 'courseware',
- semesterById: 'semesters/byId',
- structuralElementById: 'courseware-structural-elements/byId',
- userId: 'userId',
- }),
- sourceEmpty() {
- return this.source === '';
- },
- sourceOwn() {
- return this.source === 'own';
- },
- sourceRemote() {
- return this.source === 'remote';
- },
- hasRemoteCid() {
- return this.remoteCid !== '';
- },
- loadedCourses() {
- return [...this.courses].sort((a, b) => a.attributes.title > b.attributes.title);
- }
- },
- methods: {
- ...mapActions({
- loadAnotherCourseware: 'courseware-structure/loadAnotherCourseware',
- loadUsersCourses: 'loadUsersCourses',
- loadStructuralElement: 'loadStructuralElement',
- loadSemester: 'semesters/loadById',
- copyStructuralElement: 'copyStructuralElement',
- loadElement: 'courseware-structural-elements/loadById',
-
- }),
- selectSource(source) {
- this.source = source;
- this.copyAllInProgress = false;
- },
- async loadRemoteCourseware(cid) {
- this.remoteCid = cid;
- this.remoteCoursewareInstance = await this.loadAnotherCourseware({ id: this.remoteCid, type: 'courses'});
- if (this.remoteCoursewareInstance !== null) {
- this.setRemoteId(this.remoteCoursewareInstance.relationships.root.data.id);
- } else {
- this.remoteId = '';
- }
- },
- async loadOwnCourseware() {
- this.ownCoursewareInstance = await this.loadAnotherCourseware({ id: this.userId, type: 'users' });
- if (this.ownCoursewareInstance !== null) {
- this.setOwnId(this.ownCoursewareInstance.relationships.root.data.id);
- } else {
- this.ownId = '';
- }
- },
- reset() {
- this.selectSource('');
- this.remoteCid = '';
- this.copyAllInProgress = false;
- },
- selectNewCourse() {
- this.remoteCid = '';
- this.remoteId = '';
- this.copyAllInProgress = false;
- },
- async setRemoteId(target) {
- this.remoteId = target;
- await this.loadStructuralElement(this.remoteId);
- this.initRemote();
- },
- initRemote() {
- this.remoteElement = this.structuralElementById({ id: this.remoteId });
- },
- async setOwnId(target) {
- this.ownId = target;
- await this.loadStructuralElement(this.ownId);
- this.initOwn();
- },
- initOwn() {
- this.ownElement = this.structuralElementById({ id: this.ownId });
- },
- loadSelf(data) {
- this.$emit('loadSelf', data);
- },
- loadSemesterMap() {
- let view = this;
- let semesters = [];
- this.courses.every(course => {
- let semId = course.relationships['start-semester'].data.id;
- if(!semesters.includes(semId)) {
- semesters.push(semId);
- }
- return true;
- });
- semesters.every(semester => {
- view.loadSemester({id: semester}).then( () => {
- view.semesterMap.push(view.semesterById({id: semester}));
- view.semesterMap.sort((a, b) => a.attributes.start < b.attributes.start);
- });
- return true;
- });
- },
- coursesBySemester(semester) {
- return this.loadedCourses.filter(course => {
- return course.relationships['start-semester'].data.id === semester.id}
- );
- },
- getCourseIcon(course) {
- if (course.attributes['course-type'] === 99) {
- return 'studygroup';
- }
-
- return 'seminar';
- },
- reloadElement() {
- this.$emit("reloadElement");
- },
- async mergeContent() {
- this.copyAllInProgressText = this.$gettext('Inhalte werden kopiert…');
- this.copyAllInProgress = true;
- let parentId = this.courseware.relationships.root.data.id; //current root
- let elementId = this.remoteCoursewareInstance.relationships.root.data.id; // remote root
- try {
- await this.copyStructuralElement({
- parentId: parentId,
- elementId: elementId,
- migrate: true
- });
- } catch(error) {
- console.debug(error);
- this.copyAllInProgressText = this.$gettext('Beim Kopiervorgang sind Fehler aufgetreten.');
- }
- await this.loadElement({id: parentId, options: {include: 'children'}})
- this.copyAllInProgressText = this.$gettext('Kopiervorgang abgeschlossen.');
-
- }
- },
- async mounted() {
- this.courses = await this.loadUsersCourses({ userId: this.userId, withCourseware: true });
- this.loadSemesterMap();
- }
-
-}
-</script>
diff --git a/resources/vue/components/courseware/CoursewareManagerElement.vue b/resources/vue/components/courseware/CoursewareManagerElement.vue
deleted file mode 100644
index 232d21c053e52b341d07e6a89498acf433e1d73c..0000000000000000000000000000000000000000
--- a/resources/vue/components/courseware/CoursewareManagerElement.vue
+++ /dev/null
@@ -1,654 +0,0 @@
-<template>
- <div class="cw-manager-element">
- <div v-if="currentElement">
- <courseware-companion-box v-if="insertingInProgress" :msgCompanion="text.inProgress" mood="pointing" />
- <courseware-companion-box v-if="copyingFailed && !insertingInProgress" :msgCompanion="copyProcessFailedMessage" mood="sad" />
- <div class="cw-manager-element-title">
- <nav aria-label="Breadcrumb" class="cw-manager-element-breadcrumb">
- <a
- v-for="element in breadcrumb"
- :key="element.id"
- :title="element.attributes.title"
- href="#"
- class="cw-manager-element-breadcrumb-item"
- @click="selectChapter(element.id)"
- >
- {{ element.attributes.title }}
- </a>
- </nav>
- <header>
- <button class="cw-insert-element"
- v-if="elementInserterActive && moveSelfPossible && canEdit"
- :title="elementTitle"
- @click="insertElement({element: currentElement, source: type})"
- >
- <studip-icon shape="arr_2left" size="24" role="clickable" />
- </button>
- {{ elementName }}
- </header>
- </div>
- <courseware-collapsible-box
- v-if="!elementsOnly"
- :open="true"
- :title="$gettext('Abschnitt')"
- class="cw-manager-element-containers"
- >
- <div v-if="canSortContainers">
- <button v-show="!sortContainersActive && isCurrent" class="button sort" @click="sortContainers">
- <translate>Abschnitte sortieren</translate>
- </button>
- <button v-show="sortContainersActive && isCurrent" class="button accept" @click="storeContainersSort">
- <translate>Sortieren beenden</translate>
- </button>
- <button v-show="sortContainersActive && isCurrent" class="button cancel" @click="resetContainersSort">
- <translate>Sortieren abbrechen</translate>
- </button>
- </div>
- <p v-if="!hasContainers">
- <translate>Dieses Element enthält keine Abschnitte.</translate>
- </p>
- <transition-group name="cw-sort-ease" tag="div">
- <courseware-manager-container
- v-for="(container, index) in sortArrayContainers"
- :key="container.id"
- :container="container"
- :isCurrent="isCurrent"
- :sortContainers="sortContainersActive"
- :inserter="containerInserterActive && moveSelfChildPossible"
- :elementType="type"
- :blockInserter="blockInserterActive"
- :canMoveUp="index !== 0"
- :canMoveDown="index+1 !== sortArrayContainers.length"
- @insertContainer="insertContainer"
- @insertBlock="insertBlock"
- @moveUp="moveUpContainer"
- @moveDown="moveDownContainer"
- />
- </transition-group>
- <courseware-manager-filing
- v-if="isCurrent && !sortContainersActive && canEdit"
- :parentId="currentElement.id"
- :parentItem="currentElement"
- itemType="container"
- />
- </courseware-collapsible-box>
- <courseware-collapsible-box :open="true" :title="$gettext('Unterseiten')" class="cw-manager-element-subchapters">
- <div v-if="canSortChildren">
- <button v-show="!sortChildrenActive && isCurrent" class="button sort" @click="sortChildren">
- <translate>Unterseiten sortieren</translate>
- </button>
- <button v-show="sortChildrenActive && isCurrent" class="button accept" @click="storeChildrenSort">
- <translate>Sortieren beenden</translate>
- </button>
- <button v-show="sortChildrenActive && isCurrent" class="button cancel" @click="resetChildrenSort">
- <translate>Sortieren abbrechen</translate>
- </button>
- </div>
- <p v-if="!hasChildren">
- <translate>Dieses Element enthält keine Unterseiten.</translate>
- </p>
- <transition-group name="cw-sort-ease" tag="div">
- <courseware-manager-element-item
- v-for="(child, index) in sortArrayChildren"
- :key="child.id"
- :element="child"
- :sortChapters="sortChildrenActive"
- :inserter="elementInserterActive && moveSelfChildPossible && filingData.parentItem.id !== child.id"
- :type="type"
- :canMoveUp="index !== 0"
- :canMoveDown="index+1 !== sortArrayChildren.length"
- @selectChapter="selectChapter"
- @insertElement="insertElement"
- @moveUp="moveUpChild"
- @moveDown="moveDownChild"
- />
- </transition-group>
- <courseware-manager-filing
- v-if="isCurrent && !sortChildrenActive && canEdit"
- :parentId="currentElement.id"
- :parentItem="currentElement"
- itemType="element"
- />
- </courseware-collapsible-box>
- </div>
- </div>
-</template>
-
-<script>
-import StudipIcon from '../StudipIcon.vue';
-import CoursewareCollapsibleBox from './CoursewareCollapsibleBox.vue';
-import CoursewareManagerContainer from './CoursewareManagerContainer.vue';
-import CoursewareManagerElementItem from './CoursewareManagerElementItem.vue';
-import CoursewareManagerFiling from './CoursewareManagerFiling.vue';
-import CoursewareCompanionBox from './CoursewareCompanionBox.vue';
-import { mapActions, mapGetters } from 'vuex';
-import { forEach } from 'jszip';
-
-export default {
- name: 'courseware-manager-element',
- components: {
- CoursewareCollapsibleBox,
- CoursewareManagerContainer,
- CoursewareManagerElementItem,
- CoursewareManagerFiling,
- CoursewareCompanionBox,
- StudipIcon,
- },
- props: {
- type: {
- validator(value) {
- return ['current', 'self', 'remote', 'own', 'import', 'link'].includes(value);
- },
- },
- remoteCoursewareRangeId: String,
- currentElement: Object,
- moveSelfPossible: {
- default: true
- },
- moveSelfChildPossible: {
- default: true
- },
- elementsOnly: {
- default: false
- }
- },
- data() {
- return {
- elementInserterActive: false,
- containerInserterActive: false,
- blockInserterActive: false,
- sortChildrenActive: false,
- sortContainersActive: false,
- sortArrayChildren: [],
- discardStateArrayChildren: [],
- sortArrayContainers: [],
- discardStateArrayContainers: [],
- insertingInProgress: false,
- copyingFailed: false,
- linkingFailed: false,
- text: {
- inProgress: this.$gettext('Vorgang läuft. Bitte warten Sie einen Moment.'),
- copyProcessFailed: [],
- linkProcessFailed: [],
- },
- };
- },
- computed: {
- ...mapGetters({
- childrenById: 'courseware-structure/children',
- containerById: 'courseware-containers/byId',
- filingData: 'filingData',
- structuralElementById: 'courseware-structural-elements/byId',
- }),
- isCurrent() {
- return this.type === 'current';
- },
- isSelf() {
- return this.type === 'self';
- },
- isRemote() {
- return this.type === 'remote';
- },
- isImport() {
- return this.type === 'import';
- },
- isOwn() {
- return this.type === 'own';
- },
- isSorting() {
- return this.sortChildrenActive || this.sortContainersActive || this.sortBlocksActive;
- },
- canEdit() {
- if (this.currentElement.attributes) {
- return this.currentElement.attributes['can-edit'];
- } else {
- return false;
- }
- },
- breadcrumb() {
- if (!this.currentElement) {
- return [];
- }
-
- const finder = (parent) => {
- const parentId = parent.relationships?.parent?.data?.id;
- if (!parentId) {
- return null;
- }
- const element = this.structuralElementById({id: parentId});
- if (!element) {
- console.error("CoursewareManagerElement#breadcrumb: Could not find parent by ID.");
- }
-
- return element;
- };
-
- const visitAncestors = function* (node) {
- const parent = finder(node);
- if (parent) {
- yield parent;
- yield *visitAncestors(parent);
- }
- };
-
- return [...visitAncestors(this.currentElement)].reverse()
- },
- elementName() {
- if (this.currentElement.attributes) {
- return this.currentElement.attributes.title
- }
-
- return '';
- },
- elementTitle() {
- let title = this.elementName;
- if (this.elementInserterActive && this.moveSelfPossible && this.canEdit) {
- if (this.isRemote || this.isOwn) {
- title = this.$gettextInterpolate(
- this.$gettext('%{ elementTitle } kopieren'),
- {elementTitle: this.elementName}
- );
- } else {
- title = this.$gettextInterpolate(
- this.$gettext('%{ elementTitle } verschieben'),
- {elementTitle: this.elementName}
- );
- }
- }
-
- return title;
- },
- hasChildren() {
- if (this.children === null) {
- return false;
- } else {
- return this.children.length >= 1;
- }
- },
- canSortChildren() {
- if (this.children === null) {
- return false;
- } else {
- return this.children.length > 1 && this.canEdit;
- }
- },
- hasContainers() {
- if (this.containers === null) {
- return false;
- } else {
- return this.containers.length >= 1;
- }
- },
- canSortContainers() {
- if (this.containers === null) {
- return false;
- } else {
- return this.containers.length > 1 && this.canEdit;
- }
- },
- emptyContainers() {
- if (this.containers === null) {
- return true;
- } else {
- return this.containers.length === 0;
- }
- },
- containers() {
- if (!this.currentElement || !this.currentElement.relationships) {
- return [];
- }
-
- return this.currentElement.relationships.containers.data.map(({id}) => this.containerById({ id }));
- },
- children() {
- if (!this.currentElement) {
- return [];
- }
-
- return this.childrenById(this.currentElement.id)
- .map((id) => this.structuralElementById({ id }))
- .filter(Boolean);
- },
- copyProcessFailedMessage() {
- let message = this.$gettext('Der Kopiervorgang ist fehlgeschlagen.');
- if (this.text.copyProcessFailed.length) {
- message = this.text.copyProcessFailed.join('<br>');
- }
- return message;
- }
- },
- methods: {
- ...mapActions({
- createStructuralElement: 'createStructuralElement',
- updateStructuralElement: 'updateStructuralElement',
- deleteStructuralElement: 'deleteStructuralElement',
- copyStructuralElement: 'copyStructuralElement',
- linkStructuralElement: 'linkStructuralElement',
- loadStructuralElement: 'loadStructuralElement',
- loadContainer: 'loadContainer',
- updateContainer: 'updateContainer',
- deleteContainer: 'deleteContainer',
- copyContainer: 'copyContainer',
- updateBlock: 'updateBlock',
- deleteBlock: 'deleteBlock',
- copyBlock: 'copyBlock',
- lockObject: 'lockObject',
- unlockObject: 'unlockObject',
- sortContainersInStructualElements: 'sortContainersInStructualElements',
- sortChildrenInStructualElements: 'sortChildrenInStructualElements',
- setFilingData: 'cwManagerFilingData',
- }),
-
- selectChapter(target) {
- this.resetFilingData();
- this.$emit('selectElement', target);
- },
-
- validateSource(source) {
- return ['self', 'remote', 'own', 'link'].includes(source);
- },
-
- afterInsertCompletion() {
- this.$nextTick(() => {
- // will run after $emit is done
- this.resetFilingData();
- setTimeout(() => {
- this.insertingInProgress = false;
- }, 250);
- });
- },
-
- resetFilingData() {
- this.setFilingData({});
- },
-
- showFailedCopyProcessCompanion() {
- this.copyingFailed = true;
- this.insertingInProgress = false;
- },
-
- showFailedLinkProcessCompanion() {
- this.linkingFailed = true;
- this.insertingInProgress = false;
- },
-
- async insertElement(data) {
- let source = data.source;
- let element = data.element;
-
- if (!this.validateSource(source)) {
- console.log('unreliable source:');
- console.log(source);
- console.log(element);
- return;
- }
- if(!this.insertingInProgress) {
- this.insertingInProgress = true;
- if (source === 'self') {
- element.relationships.parent.data.id = this.filingData.parentItem.id;
- element.attributes.position = this.childrenById(this.filingData.parentItem.id).length + 1;
- await this.lockObject({ id: element.id, type: 'courseware-structural-elements' });
- await this.updateStructuralElement({
- element: element,
- id: element.id,
- });
- await this.unlockObject({ id: element.id, type: 'courseware-structural-elements' });
- this.loadStructuralElement(this.currentElement.id);
- this.$emit('reloadElement');
- }
- if (source === 'remote' || source === 'own') {
- //create Element
- let parentId = this.filingData.parentItem.id;
- await this.copyStructuralElement({
- parentId: parentId,
- elementId: element.id,
- migrate: false
- }).catch((error) => {
- let message = this.$gettextInterpolate(
- this.$gettext('%{ pageTitle } konnte nicht kopiert werden.'),
- {pageTitle: element.attributes.title}
- );
- this.text.copyProcessFailed.push(message);
- this.showFailedCopyProcessCompanion();
- });
- this.$emit('loadSelf', parentId);
- }
- if (source === 'link') {
- let parentId = this.filingData.parentItem.id;
- await this.linkStructuralElement({
- parentId: parentId,
- elementId: element.id,
- }).catch((error) => {
- let message = this.$gettextInterpolate(
- this.$gettext('%{ pageTitle } konnte nicht verknüpft werden.'),
- {pageTitle: element.attributes.title}
- );
- this.text.linkProcessFailed.push(message);
- this.showFailedLinkProcessCompanion();
- });
- this.$emit('loadSelf', parentId);
- }
- this.afterInsertCompletion();
- }
- },
- async insertContainer(data) {
- let source = data.source;
- let container = data.container;
-
- if (!this.validateSource(source)) {
- console.log('unreliable source:');
- console.log(source);
- console.log(container);
- return;
- }
- if(!this.insertingInProgress) {
- this.insertingInProgress = true;
- if (source === 'self') {
- container.relationships['structural-element'].data.id = this.filingData.parentItem.id;
- container.attributes.position = this.filingData.parentItem.relationships.containers.data.length + 1;
- await this.lockObject({id: container.id, type: 'courseware-containers'});
- await this.updateContainer({
- container: container,
- structuralElementId: this.currentElement.id
- });
- await this.unlockObject({id: container.id, type: 'courseware-containers'});
- this.$emit('reloadElement');
- } else if (source === 'remote' || source === 'own') {
- let parentId = this.filingData.parentItem.id;
- await this.copyContainer({
- parentId: parentId,
- container: container,
- }).catch((error) => {
- let message = this.$gettextInterpolate(
- this.$gettext('Abschnitt "%{ containerTitle }" konnte nicht kopiert werden'),
- {containerTitle: container.attributes.title}
- );
- this.text.copyProcessFailed.push(message);
- this.showFailedCopyProcessCompanion();
- });
- this.$emit('loadSelf', parentId);
- }
- this.afterInsertCompletion();
- }
-
- },
- async insertBlock(data) {
- let source = data.source;
- let block = data.block;
-
- if (!this.validateSource(source)) {
- console.debug('unreliable source:', source, block);
- return;
- }
-
- if(!this.insertingInProgress) {
- this.insertingInProgress = true;
- if (source === 'self') {
- block.relationships.container.data.id = this.filingData.parentItem.id;
- block.attributes.position = this.filingData.parentItem.relationships.blocks.data.length + 1;
-
- let sourceContainer = await this.containerById({id: block.relationships.container.data.id});
- sourceContainer.attributes.payload.sections.forEach(section => {
- let index = section.blocks.indexOf(block.id);
- if(index !== -1) {
- section.blocks.splice(index, 1);
- }
- });
- await this.lockObject({id: sourceContainer.id, type: 'courseware-containers'});
- await this.updateContainer({
- container: sourceContainer,
- structuralElementId: sourceContainer.relationships['structural-element'].data.id
- });
- await this.unlockObject({id: sourceContainer.id, type: 'courseware-containers'});
-
- let destinationContainer = await this.containerById({id: this.filingData.parentItem.id});
- destinationContainer.attributes.payload.sections[destinationContainer.attributes.payload.sections.length-1].blocks.push(block.id);
- await this.lockObject({id: destinationContainer.id, type: 'courseware-containers'});
- await this.updateContainer({
- container: destinationContainer,
- structuralElementId: destinationContainer.relationships['structural-element'].data.id
- });
- await this.unlockObject({id: destinationContainer.id, type: 'courseware-containers'});
-
- await this.lockObject({id: block.id, type: 'courseware-blocks'});
- await this.updateBlock({
- block: block,
- containerId: this.filingData.parentItem.id
- });
- await this.unlockObject({id: block.id, type: 'courseware-blocks'});
- await this.loadContainer(sourceContainer.id);
- await this.loadContainer(destinationContainer.id);
- this.$emit('reloadElement');
- } else if (source === 'remote' || source === 'own') {
- let parentId = this.filingData.parentItem.id;
- await this.copyBlock({
- parentId: parentId,
- block: block,
- }).catch((error) => {
- let message = this.$gettextInterpolate(
- this.$gettext('Block "%{ blockTitle }" konnte nicht kopiert werden'),
- {blockTitle: block.attributes.title}
- );
- this.text.copyProcessFailed.push(message);
- this.showFailedCopyProcessCompanion();
- });
- await this.loadContainer(parentId);
- this.$emit('loadSelf',this.filingData.parentItem.relationships['structural-element'].data.id);
- }
- this.afterInsertCompletion();
- }
- },
-
- sortChildren() {
- this.discardStateArrayChildren = [...this.children]; //copy array because of watcher?
- this.sortChildrenActive = true;
- },
- sortContainers() {
- this.discardStateArrayContainers = [...this.containers];
- this.sortContainersActive = true;
- },
-
- storeChildrenSort() {
- this.sortChildrenInStructualElements({parent: this.currentElement, children: this.sortArrayChildren});
-
- this.discardStateArrayChildren = [];
- this.sortChildrenActive = false;
- },
- resetChildrenSort() {
- this.sortArrayChildren = this.discardStateArrayChildren;
- this.sortChildrenActive = false;
- },
-
- storeContainersSort() {
- this.sortContainersInStructualElements({structuralElement: this.currentElement, containers: this.sortArrayContainers});
-
- this.discardStateArrayContainers = [];
- this.sortContainersActive = false;
- },
- resetContainersSort() {
- this.sortArrayContainers = this.discardStateArrayContainers;
- this.sortContainersActive = false;
- },
-
- moveUpChild(childId) {
- this.moveUp(childId, this.sortArrayChildren);
- },
- moveDownChild(childId) {
- this.moveDown(childId, this.sortArrayChildren);
- },
- moveUpContainer(containerId) {
- this.moveUp(containerId, this.sortArrayContainers);
- },
- moveDownContainer(containerId) {
- this.moveDown(containerId, this.sortArrayContainers);
- },
-
- moveUp(itemId, sortArray) {
- sortArray.every((item, index) => {
- if (item.id === itemId) {
- if (index === 0) {
- return false;
- }
- sortArray.splice(index - 1, 0, sortArray.splice(index, 1)[0]);
- return false;
- } else {
- return true;
- }
- });
- },
- moveDown(itemId, sortArray) {
- sortArray.every((item, index) => {
- if (item.id === itemId) {
- if (index === sortArray.length - 1) {
- return false;
- }
- sortArray.splice(index + 1, 0, sortArray.splice(index, 1)[0]);
- return false;
- } else {
- return true;
- }
- });
- },
- updateFilingData(data) {
- if (Object.keys(data).length !== 0) {
- switch (data.itemType) {
- case 'element':
- this.elementInserterActive = true;
- break;
- case 'container':
- this.containerInserterActive = true;
- break;
- case 'block':
- this.blockInserterActive = true;
- break;
- }
- this.copyingFailed = false;
- this.text.copyProcessFailed = [];
- this.linkingFailed = false;
- this.text.linkProcessFailed = [];
- } else {
- this.elementInserterActive = false;
- this.containerInserterActive = false;
- this.blockInserterActive = false;
- }
- }
- },
- mounted() {
- this.updateFilingData(this.filingData);
- },
- watch: {
- filingData(newValue) {
- if (!['self', 'remote', 'own', 'import', 'link'].includes(this.type)) {
- return false;
- }
- this.updateFilingData(newValue);
- },
- containers(newContainers) {
- this.sortArrayContainers = newContainers;
- },
- children(newChildren) {
- this.sortArrayChildren = newChildren;
- }
- },
-};
-</script>
diff --git a/resources/vue/components/courseware/CoursewareManagerElementItem.vue b/resources/vue/components/courseware/CoursewareManagerElementItem.vue
deleted file mode 100644
index 7b827d175cc90dda360c405a4dc23f1df29f1675..0000000000000000000000000000000000000000
--- a/resources/vue/components/courseware/CoursewareManagerElementItem.vue
+++ /dev/null
@@ -1,141 +0,0 @@
-<template>
- <div class="cw-manager-element-item-wrapper">
- <a
- v-if="!sortChapters"
- href="#"
- class="cw-manager-element-item"
- :class="[inserter ? 'cw-manager-element-item-inserter' : '']"
- :title="elementTitle"
- @click="clickItem">
- {{ element.attributes.title }}
- <span v-if="task" class="cw-manager-element-item-solver-name">| {{ solverName }}</span>
- </a>
- <div
- v-else
- class="cw-manager-element-item cw-manager-element-item-sorting"
- >
- {{ element.attributes.title }}
- <div v-if="sortChapters" class="cw-manager-element-item-buttons">
- <button :disabled="!canMoveUp" @click="moveUp" :title="$gettext('Element nach oben verschieben')">
- <studip-icon shape="arr_2up" role="sort" />
- </button>
- <button :disabled="!canMoveDown" @click="moveDown" :title="$gettext('Element nach unten verschieben')">
- <studip-icon shape="arr_2down" role="sort" />
- </button>
- </div>
- </div>
- </div>
-</template>
-
-<script>
-import { mapGetters, mapActions } from 'vuex';
-
-export default {
- name: 'courseware-manager-element-item',
- props: {
- element: Object,
- inserter: Boolean,
- sortChapters: Boolean,
- type: String,
- canMoveUp: Boolean,
- canMoveDown: Boolean
- },
- computed: {
- ...mapGetters({
- taskById: 'courseware-tasks/byId',
- userById: 'users/byId',
- groupById: 'status-groups/byId',
- }),
- isTask() {
- return this.element.attributes.purpose === 'task';
- },
- task() {
- if (this.element.relationships.task.data) {
- return this.taskById({
- id: this.element.relationships.task.data.id,
- });
- }
-
- return null;
- },
- solver() {
- if (this.task) {
- const solver = this.task.relationships.solver.data;
- if (solver.type === 'users') {
- return this.userById({ id: solver.id });
- }
- if (solver.type === 'status-groups') {
- return this.groupById({ id: solver.id });
- }
- }
-
- return null;
- },
- solverName() {
- if (this.solver) {
- if (this.solver.type === 'users') {
- return this.solver.attributes['formatted-name'];
- }
- if (this.solver.type === 'status-groups') {
- return this.solver.attributes.name;
- }
- }
-
- return '';
- },
- elementTitle() {
- let title = this.element.attributes.title;
- if (this.inserter) {
- if (this.type === 'remote' || this.type === 'own') {
- title = this.$gettextInterpolate(
- this.$gettext('%{ elementTitle } kopieren'),
- {elementTitle: this.element.attributes.title}
- );
- } else {
- title = this.$gettextInterpolate(
- this.$gettext('%{ elementTitle } verschieben'),
- {elementTitle: this.element.attributes.title}
- );
- }
- }
-
- return title;
- }
- },
- methods: {
- ...mapActions({
- loadTask: 'loadTask',
- }),
- clickItem() {
- if (this.sortChapters) {
- return false;
- }
- if (this.inserter) {
- this.$emit('insertElement', {element: this.element, source: this.type});
- } else {
- this.$emit('selectChapter', this.element.id);
- }
- },
- moveUp() {
- if (this.sortChapters) {
- this.$emit('moveUp', this.element.id);
- }
- },
- moveDown() {
- if (this.sortChapters) {
- this.$emit('moveDown', this.element.id);
- }
- },
- loadElementTask() {
- if (this.element.relationships.task.data && this.task === undefined) {
- this.loadTask({
- taskId: this.element.relationships.task.data.id,
- });
- }
- }
- },
- mounted() {
- this.loadElementTask();
- },
-};
-</script>
diff --git a/resources/vue/components/courseware/CoursewareManagerFiling.vue b/resources/vue/components/courseware/CoursewareManagerFiling.vue
deleted file mode 100644
index 71aeb612e1cfccf4f49c30b4ba54b18f9db919c6..0000000000000000000000000000000000000000
--- a/resources/vue/components/courseware/CoursewareManagerFiling.vue
+++ /dev/null
@@ -1,71 +0,0 @@
-<template>
- <button
- class="cw-manager-filing"
- :class="{ 'cw-manager-filing-active': active, 'cw-manager-filing-disabled': disabled }"
- :aria-pressed="active"
- @click="toggleFiling"
- >
- <span v-if="itemType === 'element'"><translate>Seite an dieser Stelle einfügen</translate> </span>
- <span v-if="itemType === 'container'"><translate>Abschnitt an dieser Stelle einfügen</translate> </span>
- <span v-if="itemType === 'block'"><translate>Block an dieser Stelle einfügen</translate> </span>
- </button>
-</template>
-
-<script>
-import { mapActions, mapGetters } from 'vuex';
-
-export default {
- name: 'courseware-manager-filing',
- props: {
- parentId: String,
- parentItem: Object,
- itemType: String, // element || container || block
- },
- data() {
- return {
- active: false,
- disabled: false,
- data: {},
- };
- },
- computed: {
- ...mapGetters({
- filingData: 'filingData',
- }),
- },
- methods: {
- ...mapActions({
- cwManagerFilingData: 'cwManagerFilingData'
- }),
- toggleFiling() {
- if (this.disabled) {
- return false;
- }
- if (this.active) {
- this.cwManagerFilingData({});
- } else {
- this.cwManagerFilingData({ parentId: this.parentId, itemType: this.itemType, parentItem: this.parentItem });
- }
- },
- },
- watch: {
- filingData(newValue, oldValue) {
- if (Object.keys(newValue).length !== 0) {
- if (newValue.parentId === this.parentId && newValue.itemType === this.itemType) {
- this.active = true;
- } else {
- this.disabled = true;
- }
- } else {
- this.active = false;
- this.disabled = false;
- if (Object.keys(oldValue).length !== 0) {
- if (oldValue.parentId === this.parentId && oldValue.itemType === this.itemType) {
- this.$emit('deactivated');
- }
- }
- }
- },
- },
-};
-</script>
diff --git a/resources/vue/components/courseware/CoursewareManagerImport.vue b/resources/vue/components/courseware/CoursewareManagerImport.vue
deleted file mode 100644
index 6fd3b476be23930c9f6431642e56b90d58d90e24..0000000000000000000000000000000000000000
--- a/resources/vue/components/courseware/CoursewareManagerImport.vue
+++ /dev/null
@@ -1,194 +0,0 @@
-<template>
- <div>
- <courseware-companion-box v-show="!importRunning && importDone && importErrors.length === 0" :msgCompanion="$gettext('Import erfolgreich!')" mood="special"/>
- <courseware-companion-box v-show="!importRunning && importDone && importErrors.length > 0" :msgCompanion="$gettext('Import abgeschlossen. Es sind Fehler aufgetreten!')" mood="unsure"/>
- <courseware-companion-box v-show="!importRunning && !importDone && importErrors.length > 0" :msgCompanion="$gettext('Import fehlgeschlagen. Es sind Fehler aufgetreten!')" mood="sad"/>
- <courseware-companion-box v-show="importRunning" :msgCompanion="$gettext('Import läuft. Bitte verlassen Sie die Seite nicht bis der Import abgeschlossen wurde.')" mood="pointing"/>
- <form class="default" @submit.prevent="">
-
- <fieldset v-show="importRunning">
- <legend><translate>Import läuft...</translate></legend>
- <div v-if="importRunning" class="cw-import-zip">
- <header><translate>Importiere Dateien</translate>:</header>
- <div class="progress-bar-wrapper">
- <div class="progress-bar" role="progressbar" :style="{width: importFilesProgress + '%'}" :aria-valuenow="importFilesProgress" aria-valuemin="0" aria-valuemax="100">{{ importFilesProgress }}%</div>
- </div>
- {{ importFilesState }}
- </div>
- <div v-if="fileImportDone && importRunning" class="cw-import-zip">
- <header><translate>Importiere Elemente</translate>:</header>
- <div class="progress-bar-wrapper">
- <div class="progress-bar" role="progressbar" :style="{width: importStructuresProgress + '%'}" :aria-valuenow="importStructuresProgress" aria-valuemin="0" aria-valuemax="100">{{ importStructuresProgress }}%</div>
- </div>
- {{ importStructuresState }}
- </div>
- </fieldset>
- <fieldset v-show="importErrors.length > 0">
- <legend><translate>Fehlermeldungen</translate></legend>
- <ul>
- <li v-for="(error, index) in importErrors" :key="index"> {{error}} </li>
- </ul>
- </fieldset>
- <fieldset v-show="!importRunning">
- <legend><translate>Import</translate></legend>
- <label>
- <translate>Importdatei</translate>
- <input class="cw-file-input" ref="importFile" type="file" accept=".zip" @change="setImport" />
- </label>
- <label>
- <translate>Importverhalten</translate>
- <select v-model="importBehavior">
- <option value="default"><translate>Inhalte anhängen</translate></option>
- <option value="migrate"><translate>Inhalte zusammenführen</translate></option>
- </select>
- </label>
- </fieldset>
- <footer v-show="!importRunning">
- <button
- class="button"
- @click.prevent="doImportCourseware"
- :disabled="!importZip"
- >
- <translate>Importieren</translate>
- </button>
- </footer>
- </form>
- </div>
-</template>
-
-<script>
-import CoursewareCompanionBox from './CoursewareCompanionBox.vue';
-
-import CoursewareImport from '@/vue/mixins/courseware/import.js';
-import { mapActions, mapGetters } from 'vuex';
-import JSZip from 'jszip';
-
-export default {
- name: 'courseware-manager-import',
- components: {
- CoursewareCompanionBox,
- },
- mixins: [CoursewareImport],
- data() {
- return {
- importBehavior: 'default',
- importRunning: false,
- importZip: null,
- zip: null
- }
- },
- computed: {
- ...mapGetters({
- courseware: 'courseware',
- importFilesState: 'importFilesState',
- importFilesProgress: 'importFilesProgress',
- importStructuresState: 'importStructuresState',
- importStructuresProgress: 'importStructuresProgress',
- importErrors: 'importErrors',
- }),
- fileImportDone() {
- return this.importFilesProgress === 100;
- },
- importDone() {
- return this.importFilesProgress === 100 && this.importStructuresProgress === 100;
- }
- },
- methods: {
- ...mapActions({
- loadCoursewareStructure: 'courseware-structure/load',
- setImportFilesProgress: 'setImportFilesProgress',
- setImportStructuresProgress: 'setImportStructuresProgress',
- setImportErrors: 'setImportErrors',
- }),
-
- setImport(event) {
- this.importZip = event.target.files[0];
- this.setImportFilesProgress(0);
- this.setImportStructuresProgress(0);
- this.setImportErrors([]);
- },
-
- async doImportCourseware() {
- if (this.importZip === null) {
- return false;
- }
-
- this.importRunning = true;
-
- let view = this;
-
- view.zip = new JSZip();
-
- await view.zip.loadAsync(this.importZip).then(async function () {
- let errors = [];
- let missingFiles = false;
- if (view.zip.file('courseware.json') === null) {
- errors.push(view.$gettext('Das Archiv enthält keine courseware.json Datei.'));
- missingFiles = true;
- }
- if (view.zip.file('files.json') === null) {
- errors.push(view.$gettext('Das Archiv enthält keine files.json Datei.'));
- missingFiles = true;
- }
- if (view.zip.file('data.xml') !== null) {
- errors.push(view.$gettext(
- 'Das Archiv enthält eine data.xml Datei. Möglicherweise handelt es sich um einen Export aus dem Courseware-Plugin. Diese Archive sind nicht kompatibel mit dieser Courseware.'
- ));
- }
- if (missingFiles) {
- view.setImportErrors(errors);
- return;
- }
-
- let data = await view.zip.file('courseware.json').async('string');
- let courseware = null;
- let data_files = await view.zip.file('files.json').async('string');
- let files = null;
- let jsonErrors = false;
- try {
- courseware = JSON.parse(data);
- } catch (error) {
- jsonErrors = true;
- errors.push(view.$gettext('Die Beschreibung der Courseware-Inhalte ist nicht valide.'));
- errors.push(error);
- }
- try {
- files = JSON.parse(data_files);
- } catch (error) {
- jsonErrors = true;
- errors.push(view.$gettext('Die Beschreibung der Dateien ist nicht valide.'));
- errors.push(error);
- }
- if (jsonErrors) {
- view.setImportErrors(errors);
- return;
- }
-
- await view.loadCoursewareStructure();
- const rootId = view.courseware.relationships.root.data.id;
-
- await view.importCourseware(courseware, rootId, files, view.importBehavior);
- });
-
- this.importZip = null;
- this.importRunning = false;
- this.$refs.importFile.value = '';
- },
-
- getFileSizeText(size) {
- if (size / 1024 < 1000) {
- return (size / 1024).toFixed(2) + ' kB';
- } else {
- return (size / 1048576).toFixed(2) + ' MB';
- }
- },
- },
- mounted() {
- let view = this;
-
- window.onbeforeunload = function() {
- return view.importRunning ? true : null
- }
- }
-}
-</script>
diff --git a/resources/vue/components/courseware/CoursewareManagerLinkSelector.vue b/resources/vue/components/courseware/CoursewareManagerLinkSelector.vue
deleted file mode 100644
index a735024a2adeeec32b4db1bebc1249580cef7585..0000000000000000000000000000000000000000
--- a/resources/vue/components/courseware/CoursewareManagerLinkSelector.vue
+++ /dev/null
@@ -1,74 +0,0 @@
-<template>
- <div class="cw-manager-link-selector">
- <courseware-manager-element
- v-if="ownId !== null"
- type="link"
- :currentElement="ownElement"
- :elementsOnly="true"
- @selectElement="setOwnId"
- @loadSelf="loadSelf"
- />
- </div>
-</template>
-
-<script>
-import CoursewareManagerElement from './CoursewareManagerElement.vue';
-import { mapActions, mapGetters } from 'vuex';
-
-export default {
- name: 'courseware-manager-link-selector',
- components: { CoursewareManagerElement },
-
- data() {
- return {
- ownCoursewareInstance: {},
- ownId: null,
- ownElement: {},
- }
- },
-
- computed: {
- ...mapGetters({
- courseware: 'courseware',
- structuralElementById: 'courseware-structural-elements/byId',
- userId: 'userId',
- }),
- },
-
- methods: {
- ...mapActions({
- loadAnotherCourseware: 'courseware-structure/loadAnotherCourseware',
- loadStructuralElementById: 'courseware-structural-elements/loadById',
- }),
- async loadOwnCourseware() {
- this.ownCoursewareInstance = await this.loadAnotherCourseware({ id: this.userId, type: 'users' });
- if (this.ownCoursewareInstance !== null) {
- await this.setOwnId(this.ownCoursewareInstance.relationships.root.data.id);
- } else {
- this.ownId = '';
- }
- },
- async setOwnId(target) {
- this.ownId = target;
- const options = {
- include: 'children'
- };
- await this.loadStructuralElementById({ id: this.ownId, options });
- this.initOwn();
- },
- initOwn() {
- this.ownElement = this.structuralElementById({ id: this.ownId });
- },
- reloadElement() {
- this.$emit('reloadElement');
- },
- loadSelf(data) {
- this.$emit('loadSelf', data);
- },
- },
-
- async mounted() {
- await this.loadOwnCourseware();
- },
-}
-</script>
diff --git a/resources/vue/components/courseware/CoursewareManagerTaskDistributor.vue b/resources/vue/components/courseware/CoursewareManagerTaskDistributor.vue
deleted file mode 100644
index 7189fa3c797d94677752c2aaf78656ba05ccef9c..0000000000000000000000000000000000000000
--- a/resources/vue/components/courseware/CoursewareManagerTaskDistributor.vue
+++ /dev/null
@@ -1,355 +0,0 @@
-<template>
- <div class="cw-manager-task-distributor">
- <form class="default" @submit.prevent="">
- <fieldset>
- <legend><translate>Aufgabe</translate></legend>
- <label>
- <translate>Aufgabentitel</translate>
- <input type="text" v-model="taskTitle" />
- </label>
- <label>
- <translate>Aufgabenvorlage</translate>
- <select v-model="selectedElementId">
- <option value="" disabled>
- <translate>wählen Sie eine Vorlage aus</translate>
- </option>
- <option v-for="template in taskTemplates" :key="template.id" :value="template.id">
- {{ template.attributes.title }}
- </option>
- </select>
- </label>
- <label>
- <translate>Abgabefrist</translate>
- <input type="date" v-model="submissionDate" />
- </label>
- <label>
- <translate>Inhalte ergänzen</translate>
- <select class="size-s" v-model="solverMayAddBlocks">
- <option value="true"><translate>ja</translate></option>
- <option value="false"><translate>nein</translate></option>
- </select>
- </label>
- <label>
- <translate>Typ</translate>
- <select v-model="taskSolverType">
- <option value="autor"><translate>für Studierende</translate></option>
- <option value="group"><translate>für Gruppen</translate></option>
- </select>
- </label>
- </fieldset>
- <fieldset v-show="taskSolverType === 'autor'" class="cw-manager-task-distributor-task-solvers">
- <legend><translate>Aufgabe Studierenden zuweisen</translate></legend>
- <courseware-companion-box
- v-show="autor_members.length === 0"
- :msgCompanion="$gettext('Es wurden keine Studierenden in dieser Veranstaltung gefunden.')"
- mood="pointing"
- />
- <table v-show="autor_members.length > 0" class="default">
- <thead>
- <tr>
- <th><input type="checkbox" v-model="bulkSelectAutors"/></th>
- <th><translate>Name</translate></th>
- </tr>
- </thead>
- <tbody>
- <tr v-for="user in autor_members" :key="user.user_id">
- <td><input type="checkbox" v-model="selectedAutors" :value="user.user_id" /></td>
- <td>{{ user.formattedname }}</td>
- </tr>
- </tbody>
- </table>
- </fieldset>
- <fieldset v-show="taskSolverType === 'group'" class="cw-manager-task-distributor-task-solvers">
- <legend><translate>Aufgabe Gruppen zuweisen</translate></legend>
- <courseware-companion-box
- v-show="groups.length === 0"
- :msgCompanion="$gettext('Es wurden keine Gruppen in dieser Veranstaltung gefunden.')"
- mood="pointing"
- />
- <table v-show="groups.length > 0" class="default">
- <thead>
- <tr>
- <th><input type="checkbox" v-model="bulkSelectGroups"/></th>
- <th><translate>Gruppenname</translate></th>
- </tr>
- </thead>
- <tbody>
- <tr v-for="group in groups" :key="group.id">
- <td><input type="checkbox" v-model="selectedGroups" :value="group.id" /></td>
- <td>{{ group.name }}</td>
- </tr>
- </tbody>
- </table>
- </fieldset>
- <footer>
- <button class="button" name="create_task" :disabled="!targetSelected" @click="createTask">
- <translate>Aufgabe verteilen</translate>
- </button>
- <span
- v-if="!targetSelected"
- class="tooltip tooltip-icon "
- :data-tooltip="$gettext('Bitte wählen aus, an welcher Stelle die Aufgabe eingefügt werden soll.')"
- tabindex="0"
- title=""
- >
- </span>
- </footer>
- </form>
- </div>
-</template>
-
-<script>
-import { mapActions, mapGetters } from 'vuex';
-import CoursewareCompanionBox from './CoursewareCompanionBox.vue';
-
-export default {
- name: 'courseware-manager-task-distributor',
- components: {
- CoursewareCompanionBox,
- },
- data() {
- return {
- ownCoursewareInstance: null,
- ownCoursewareElements: [],
- taskSolverType: 'autor',
- selectedElementId: '',
- selectedAutors: [],
- bulkSelectAutors: false,
- selectedGroups: [],
- bulkSelectGroups: false,
- taskTitle: '',
- submissionDate: '',
- solverMayAddBlocks: true,
- };
- },
- computed: {
- ...mapGetters({
- context: 'context',
- userId: 'userId',
- structuralElementById: 'courseware-structural-elements/byId',
- relatedCourseMemberships: 'course-memberships/related',
- relatedCourseStatusGroups: 'status-groups/related',
- relatedUser: 'users/related',
- filingData: 'filingData',
- }),
- users() {
- const parent = { type: 'courses', id: this.context.id };
- const relationship = 'memberships';
- const memberships = this.relatedCourseMemberships({ parent, relationship });
-
- return (
- memberships?.map((membership) => {
- const parent = { type: membership.type, id: membership.id };
- const member = this.relatedUser({ parent, relationship: 'user' });
-
- return {
- user_id: member.id,
- formattedname: member.attributes['formatted-name'],
- username: member.attributes['username'],
- perm: membership.attributes['permission'],
- };
- }) ?? []
- );
- },
- groups() {
- const parent = { type: 'courses', id: this.context.id };
- const relationship = 'status-groups';
- const statusGroups = this.relatedCourseStatusGroups({ parent, relationship });
-
- return (
- statusGroups?.map((statusGroup) => {
- return {
- id: statusGroup.id,
- name: statusGroup.attributes['name'],
- };
- }) ?? []
- );
- },
- autor_members() {
- if (Object.keys(this.users).length === 0 && this.users.constructor === Object) {
- return [];
- }
-
- let members = this.users
- .filter(function (user) {
- return user.perm === 'autor';
- })
- .map((obj) => ({ ...obj, active: false }));
-
- return members;
- },
- ownCoursewareRootId() {
- if (this.ownCoursewareInstance !== null) {
- return this.ownCoursewareInstance.relationships.root.data.id;
- } else {
- return '';
- }
- },
- ownCoursewareRoot() {
- if (this.ownCoursewareRootId !== '') {
- return this.structuralElementById({ id: this.ownCoursewareRootId });
- } else {
- return null;
- }
- },
- taskTemplates() {
- let templates = this.ownCoursewareElements.filter((elem) => {
- return elem.attributes.purpose === 'template';
- });
-
- return templates;
- },
- targetSelected() {
- return this.filingData.itemType === 'element';
- },
- },
- methods: {
- ...mapActions({
- loadCourseMemberships: 'course-memberships/loadRelated',
- loadCourseStatusGroups: 'status-groups/loadRelated',
- loadRemoteCoursewareStructure: 'loadRemoteCoursewareStructure',
- loadStructuralElementById: 'courseware-structural-elements/loadById',
- loadStructuralElement: 'loadStructuralElement',
- createTaskGroup: 'createTaskGroup',
- companionWarning: 'companionWarning',
- companionSuccess: 'companionSuccess',
- }),
- async loadOwnCourseware() {
- this.ownCoursewareInstance = await this.loadRemoteCoursewareStructure({
- rangeId: this.userId,
- rangeType: 'users',
- });
- await this.loadStructuralElementById({ id: this.ownCoursewareRootId, options: { include: 'children' } });
- let children = this.ownCoursewareRoot.relationships.children.data;
- for (let i = 0; i < children.length; i++) {
- this.ownCoursewareElements.push(this.structuralElementById({ id: children[i].id }));
- }
- },
- async createTask() {
- if (!this.targetSelected) {
- return;
- }
-
- if (this.taskTitle === '') {
- this.companionWarning({
- info: this.$gettext('Bitte wählen Sie einen Aufgabentitel aus.'),
- });
-
- return false;
- }
- if (this.selectedElementId.trim() === '') {
- this.companionWarning({
- info: this.$gettext('Bitte wählen Sie eine Aufgabenvorlage aus.'),
- });
-
- return false;
- }
- if (this.submissionDate === '') {
- this.companionWarning({
- info: this.$gettext('Bitte wählen Sie eine Abgabefrist aus.'),
- });
-
- return false;
- }
- if (!['autor', 'group'].includes(this.taskSolverType)) {
- this.companionWarning({
- info: this.$gettext('Bitte wählen Sie aus, an wen die Aufgabe verteilt werden sollen.'),
- });
-
- return false;
- }
- if (this.taskSolverType === 'autor') {
- if (this.selectedAutors.length === 0) {
- this.companionWarning({
- info: this.$gettext('Bitte wählen Sie mindestens einen Studierenden aus.'),
- });
- return false;
- }
- }
- if (this.taskSolverType === 'group') {
- if (this.selectedGroups.length === 0) {
- this.companionWarning({
- info: this.$gettext('Bitte wählen Sie mindestens eine Gruppe aus.'),
- });
- return false;
- }
- }
-
- const taskGroup = {
- attributes: {
- title: this.taskTitle,
- 'submission-date': new Date(this.submissionDate).toISOString(),
- 'solver-may-add-blocks': this.solverMayAddBlocks,
- },
- relationships: {
- solvers: {
- data: [],
- },
- target: {
- data: {
- id: this.filingData.parentItem.id,
- type: 'courseware-structural-elements',
- },
- },
- 'task-template': {
- data: {
- id: this.selectedElementId,
- type: 'courseware-structural-elements',
- },
- },
- },
- };
-
- let solvers;
- if (this.taskSolverType === 'autor') {
- solvers = this.selectedAutors.map((id) => ({ type: 'users', id }));
- }
- if (this.taskSolverType === 'group') {
- solvers = this.selectedGroups.map((id) => ({ type: 'status-groups', id }));
- }
- taskGroup.relationships.solvers.data = solvers;
-
- await this.createTaskGroup({ taskGroup });
-
- this.resetTask();
-
- this.companionSuccess({
- info: this.$gettext('Aufgabe wurde verteilt.'),
- });
- },
- resetTask() {
- this.taskTitle = '';
- this.taskSolverType = 'autor';
- this.selectedElementId = '';
- this.submissionDate = '';
- this.solverMayAddBlocks = true;
- this.bulkSelectAutors = false;
- this.selectedAutors = [];
- this.bulkSelectGroups = false;
- this.selectedGroups = [];
- }
- },
- mounted() {
- const parent = { type: 'courses', id: this.context.id };
- this.loadCourseMemberships({ parent, relationship: 'memberships', options: { include: 'user', 'page[offset]': 0, 'page[limit]': 10000, 'filter[permission]': 'autor' } });
- this.loadCourseStatusGroups({ parent, relationship: 'status-groups' });
- this.loadOwnCourseware();
- },
- watch: {
- bulkSelectAutors(newState) {
- if (newState) {
- this.selectedAutors = this.autor_members.map( autor => autor.user_id);
- } else {
- this.selectedAutors = [];
- }
- },
- bulkSelectGroups(newState) {
- if (newState) {
- this.selectedGroups = this.groups.map( group => group.id);
- } else {
- this.selectedGroups = [];
- }
- }
- }
-};
-</script>
diff --git a/resources/vue/components/courseware/CoursewareOblong.vue b/resources/vue/components/courseware/CoursewareOblong.vue
deleted file mode 100644
index b1e601dea8aa99fca5cc19259051bc0b772d4f7f..0000000000000000000000000000000000000000
--- a/resources/vue/components/courseware/CoursewareOblong.vue
+++ /dev/null
@@ -1,44 +0,0 @@
-<template>
- <div class="cw-oblong" :class="['cw-oblong-' + oblongSize]">
- <div class="cw-oblong-value">
- <slot name="oblongValue"></slot>
- </div>
- <div class="cw-oblong-description">
- <studip-icon v-if="icon" :shape="icon" role="info" :size="iconSize"></studip-icon>{{ name }}
- </div>
- </div>
-</template>
-
-<script>
-export default {
- name: 'courseware-oblong',
- props: {
- icon: String,
- name: String,
- color: String,
- size: String,
- },
- computed: {
- oblongSize() {
- switch (this.size) {
- case 'large':
- return 'large';
- case 'small':
- return 'small';
- default:
- return 'small';
- }
- },
- iconSize() {
- switch (this.size) {
- case 'large':
- return 48;
- case 'small':
- return 24;
- default:
- return 24;
- }
- },
- },
-};
-</script>
diff --git a/resources/vue/components/courseware/ManagerApp.vue b/resources/vue/components/courseware/ManagerApp.vue
deleted file mode 100644
index 777c5af052f0e5a5bc78ca747e11f889c653ea9c..0000000000000000000000000000000000000000
--- a/resources/vue/components/courseware/ManagerApp.vue
+++ /dev/null
@@ -1,40 +0,0 @@
-<template>
- <courseware-course-manager @reload="rebuildStructure"></courseware-course-manager>
-</template>
-
-<script>
-import CoursewareCourseManager from './CoursewareCourseManager.vue';
-import { mapActions, mapGetters } from 'vuex';
-
-export default {
- components: { CoursewareCourseManager },
- computed: {
- ...mapGetters({
- courseware: 'courseware',
- structuralElements: 'courseware-structural-elements/all',
- }),
- },
- methods: {
- ...mapActions({
- buildStructure: 'courseware-structure/build',
- invalidateStructureCache: 'courseware-structure/invalidateCache',
- loadCoursewareStructure: 'courseware-structure/load',
- }),
- async rebuildStructure() {
- // compute order of structural elements once more
- await this.buildStructure();
-
- // throw away stale cache
- this.invalidateStructureCache();
- },
- },
- async mounted() {
- await this.loadCoursewareStructure();
- },
- watch: {
- async structuralElements(newElements, oldElements) {
- this.rebuildStructure();
- },
- },
-};
-</script>
diff --git a/resources/vue/courseware-manager-app.js b/resources/vue/courseware-manager-app.js
deleted file mode 100644
index 8d6454a3a379aa3d5ab887cc2611cc8e5fea2727..0000000000000000000000000000000000000000
--- a/resources/vue/courseware-manager-app.js
+++ /dev/null
@@ -1,90 +0,0 @@
-import CoursewareModule from './store/courseware/courseware.module';
-import CoursewareStructureModule from './store/courseware/structure.module';
-import ManagerApp from './components/courseware/ManagerApp.vue';
-import Vuex from 'vuex';
-import axios from 'axios';
-import { mapResourceModules } from '@elan-ev/reststate-vuex';
-
-
-const mountApp = async (STUDIP, createApp, element) => {
- const getHttpClient = () =>
- axios.create({
- baseURL: STUDIP.URLHelper.getURL(`jsonapi.php/v1`, {}, true),
- headers: {
- 'Content-Type': 'application/vnd.api+json',
- },
- });
-
- const httpClient = getHttpClient();
-
- const store = new Vuex.Store({
- modules: {
- courseware: CoursewareModule,
- 'courseware-structure': CoursewareStructureModule,
- ...mapResourceModules({
- names: [
- 'courses',
- 'course-memberships',
- 'courseware-blocks',
- 'courseware-block-comments',
- 'courseware-block-feedback',
- 'courseware-containers',
- 'courseware-instances',
- 'courseware-structural-elements',
- 'courseware-task-groups',
- 'courseware-tasks',
- 'courseware-user-data-fields',
- 'courseware-user-progresses',
- 'files',
- 'file-refs',
- 'folders',
- 'status-groups',
- 'users',
- 'institutes',
- 'institute-memberships',
- 'semesters',
- 'sem-classes',
- 'sem-types',
- ],
- httpClient: httpClient,
- }),
- },
- });
-
- // get id of parent structural element
- let entry_id = null;
- let entry_type = null;
- let elem;
-
- if ((elem = document.getElementById(element.substring(1))) !== undefined) {
- if (elem.attributes !== undefined) {
- if (elem.attributes['entry-type'] !== undefined) {
- entry_type = elem.attributes['entry-type'].value;
- }
-
- if (elem.attributes['entry-id'] !== undefined) {
- entry_id = elem.attributes['entry-id'].value;
- }
- }
- }
-
- store.dispatch('setUrlHelper', STUDIP.URLHelper);
- store.dispatch('setUserId', STUDIP.USER_ID);
- await store.dispatch('users/loadById', {id: STUDIP.USER_ID});
- store.dispatch('setHttpClient', httpClient);
- store.dispatch('coursewareContext', {
- id: entry_id,
- type: entry_type,
- });
-
- const app = createApp({
- render: (h) => h(ManagerApp),
- store,
- });
-
- app.$mount(element);
-
- return app;
-};
-
-export default mountApp;
diff --git a/resources/vue/store/courseware/courseware.module.js b/resources/vue/store/courseware/courseware.module.js
index 7c1f2fec6dd6d240fc688e94a43f4b76ce63f45a..5eb40a00855bb9764194d44e443ecb1fd04b3954 100644
--- a/resources/vue/store/courseware/courseware.module.js
+++ b/resources/vue/store/courseware/courseware.module.js
@@ -13,8 +13,7 @@ const getDefaultState = () => {
httpClient: null,
lastElement: null,
msg: 'Dehydrated',
- msgCompanionOverlay:
- 'Hallo! Ich bin Ihr persönlicher Companion. Wussten Sie schon, dass Courseware jetzt noch einfacher zu bedienen ist?',
+ msgCompanionOverlay: '',
styleCompanionOverlay: 'default',
pluginManager: null,
showCompanionOverlay: false,
@@ -24,7 +23,6 @@ const getDefaultState = () => {
userId: null,
viewMode: 'read',
dashboardViewMode: 'default',
- filingData: {},
userIsTeacher: false,
teacherStatusLoaded: false,
@@ -170,9 +168,6 @@ const getters = {
pluginManager(state) {
return state.pluginManager;
},
- filingData(state) {
- return state.filingData;
- },
showStructuralElementEditDialog(state) {
return state.showStructuralElementEditDialog;
},
@@ -1008,10 +1003,6 @@ export const actions = {
return dispatch('loadStructuralElement', structuralElement.id);
},
- cwManagerFilingData(context, msg) {
- context.commit('cwManagerFilingDataSet', msg);
- },
-
async loadRelatedPaginated({ dispatch, rootGetters }, { type, parent, relationship, options }) {
const limit = 100;
let offset = 0;
@@ -1431,10 +1422,6 @@ export const mutations = {
state.pluginManager = pluginManager;
},
- cwManagerFilingDataSet(state, data) {
- state.filingData = data;
- },
-
setShowStructuralElementEditDialog(state, showEdit) {
state.showStructuralElementEditDialog = showEdit;
},
diff --git a/resources/vue/store/courseware/structure.module.js b/resources/vue/store/courseware/structure.module.js
index 0eba83936a10253ead770e1208a372cc6f144739..230ea17350e7e9469d885251b2c619a0500aebcc 100644
--- a/resources/vue/store/courseware/structure.module.js
+++ b/resources/vue/store/courseware/structure.module.js
@@ -107,23 +107,6 @@ const actions = {
return instance;
},
- // load the structure of a specified courseware
- async loadAnotherCourseware({ commit, dispatch, rootGetters }, context) {
- const instance = await dispatch('loadInstance', context);
-
- const root = rootGetters['courseware-structural-elements/related']({
- parent: { id: instance.id, type: instance.type },
- relationship: 'root',
- });
- if (!root) {
- throw new Error(`Could not find root of courseware { id: ${instance.id}, type: ${instance.type}`);
- }
-
- await dispatch('loadDescendants', { root });
-
- return instance;
- },
-
loadInstance({ commit, dispatch, rootGetters }, context) {
let parent = context;
parent = {