diff --git a/resources/vue/components/courseware/CoursewareActionWidget.vue b/resources/vue/components/courseware/CoursewareActionWidget.vue index 6238690bac433dbbfffb22028971d7edba5b5f4e..f2a443631c9ce38fede623b3cd801b7dccea09c2 100644 --- a/resources/vue/components/courseware/CoursewareActionWidget.vue +++ b/resources/vue/components/courseware/CoursewareActionWidget.vue @@ -1,12 +1,22 @@ <template> - <ul class="widget-list widget-links cw-action-widget"> - <li v-show="canEdit" class="cw-action-widget-edit" @click="editElement"><translate>Seite bearbeiten</translate></li> - <li v-show="canEdit" class="cw-action-widget-add" @click="addElement"><translate>Seite hinzufügen</translate></li> + <ul class="widget-list widget-links cw-action-widget" v-if="structuralElement"> + <li v-show="canEdit" class="cw-action-widget-edit" @click="editElement"> + <translate>Seite bearbeiten</translate> + </li> + <li v-show="canEdit" class="cw-action-widget-add" @click="addElement"> + <translate>Seite hinzufügen</translate> + </li> <li class="cw-action-widget-info" @click="showElementInfo"><translate>Informationen anzeigen</translate></li> <li class="cw-action-widget-star" @click="createBookmark"><translate>Lesezeichen setzen</translate></li> - <li v-show="canEdit" @click="exportElement" class="cw-action-widget-export"><translate>Seite exportieren</translate></li> - <li v-show="canEdit && oerEnabled" @click="oerElement" class="cw-action-widget-oer"><translate>Seite auf %{oerTitle} veröffentlichen</translate></li> - <li v-show="!isRoot && canEdit" class="cw-action-widget-trash" @click="deleteElement"><translate>Seite löschen</translate></li> + <li v-show="canEdit" @click="exportElement" class="cw-action-widget-export"> + <translate>Seite exportieren</translate> + </li> + <li v-show="canEdit && oerEnabled" @click="oerElement" class="cw-action-widget-oer"> + <translate>Seite auf %{oerTitle} veröffentlichen</translate> + </li> + <li v-show="!isRoot && canEdit" class="cw-action-widget-trash" @click="deleteElement"> + <translate>Seite löschen</translate> + </li> </ul> </template> @@ -17,29 +27,16 @@ import { mapActions, mapGetters } from 'vuex'; export default { name: 'courseware-action-widget', + props: ['structuralElement'], components: { - StudipIcon + StudipIcon, }, mixins: [CoursewareExport], - data() { - return { - currentId: null, - currentElement: {}, - } - }, computed: { - ...mapGetters({ - structuralElementById: 'courseware-structural-elements/byId', + ...mapGetters({ oerEnabled: 'oerEnabled', oerTitle: 'oerTitle', }), - structuralElement() { - if (!this.currentId) { - return null; - } - - return this.structuralElementById({ id: this.currentId }); - }, isRoot() { if (!this.structuralElement) { return true; @@ -53,15 +50,12 @@ export default { } return this.structuralElement.attributes['can-edit']; }, - }, - async mounted() { - if (!this.currentId) { - this.setCurrentId(this.$route.params.id); - } + currentId() { + return this.structuralElement?.id; + }, }, methods: { ...mapActions({ - loadStructuralElement: 'loadStructuralElement', showElementEditDialog: 'showElementEditDialog', showElementAddDialog: 'showElementAddDialog', showElementDeleteDialog: 'showElementDeleteDialog', @@ -70,19 +64,8 @@ export default { showElementOerDialog: 'showElementOerDialog', companionInfo: 'companionInfo', addBookmark: 'addBookmark', - lockObject: 'lockObject' + lockObject: 'lockObject', }), - async setCurrentId(id) { - this.currentId = id; - await this.loadStructuralElement(this.currentId); - this.initCurrent(); - }, - initCurrent() { - this.currentElement = JSON.parse(JSON.stringify(this.structuralElement)); - if (!this.currentElement.attributes.payload.meta) { - this.currentElement.attributes.payload.meta = {}; - } - }, async editElement() { await this.lockObject({ id: this.currentId, type: 'courseware-structural-elements' }); this.showElementEditDialog(true); @@ -106,12 +89,7 @@ export default { }, oerElement() { this.showElementOerDialog(true); - } - }, - watch: { - $route(to) { - this.setCurrentId(to.params.id); }, }, -} -</script> \ No newline at end of file +}; +</script> diff --git a/resources/vue/components/courseware/CoursewareStructuralElement.vue b/resources/vue/components/courseware/CoursewareStructuralElement.vue index 493c312e44d55da0a2dc5f355240e40b5606fadd..ba2b72d7ea5d58dda02eef747ffa8350a4083d3f 100755 --- a/resources/vue/components/courseware/CoursewareStructuralElement.vue +++ b/resources/vue/components/courseware/CoursewareStructuralElement.vue @@ -1,200 +1,240 @@ <template> <div> - <div :class="{ 'cw-structural-element-consumemode': consumeMode }" class="cw-structural-element" v-if="validContext"> - <div class="cw-structural-element-content" v-if="structuralElement"> - <courseware-ribbon :canEdit="canEdit"> - <template #buttons> - <router-link v-if="prevElement" :to="'/structural_element/' + prevElement.id"> - <button class="cw-ribbon-button cw-ribbon-button-prev" :title="textRibbon.perv" /> - </router-link> - <button v-else class="cw-ribbon-button cw-ribbon-button-prev-disabled" /> - <router-link v-if="nextElement" :to="'/structural_element/' + nextElement.id"> - <button class="cw-ribbon-button cw-ribbon-button-next" :title="textRibbon.next" /> - </router-link> - <button v-else class="cw-ribbon-button cw-ribbon-button-next-disabled" /> - </template> - <template #breadcrumbList> - <li - v-for="ancestor in ancestors" - :key="ancestor.id" - :title="ancestor.attributes.title" - class="cw-ribbon-breadcrumb-item" - > - <span> - <router-link :to="'/structural_element/' + ancestor.id"> - {{ ancestor.attributes.title }} + <div + :class="{ 'cw-structural-element-consumemode': consumeMode }" + class="cw-structural-element" + v-if="validContext" + > + <div class="cw-structural-element-content" v-if="structuralElement"> + <courseware-ribbon :canEdit="canEdit"> + <template #buttons> + <router-link v-if="prevElement" :to="'/structural_element/' + prevElement.id"> + <button class="cw-ribbon-button cw-ribbon-button-prev" :title="textRibbon.perv" /> </router-link> - </span> - </li><li class="cw-ribbon-breadcrumb-item cw-ribbon-breadcrumb-item-current" :title="structuralElement.attributes.title"> - <span>{{ structuralElement.attributes.title }}</span> - </li> - </template> - <template #breadcrumbFallback> - <li class="cw-ribbon-breadcrumb-item cw-ribbon-breadcrumb-item-current" :title="structuralElement.attributes.title"> - <span>{{ structuralElement.attributes.title }}</span> - </li> - </template> - <template #menu> - <studip-action-menu - v-if="!consumeMode" - :items="menuItems" - class="cw-ribbon-action-menu" - @editCurrentElement="menuAction('editCurrentElement')" - @addElement="menuAction('addElement')" - @deleteCurrentElement="menuAction('deleteCurrentElement')" - @showInfo="menuAction('showInfo')" - @showExportOptions="menuAction('showExportOptions')" - @oerCurrentElement="menuAction('oerCurrentElement')" - @setBookmark="menuAction('setBookmark')" - /> - </template> - </courseware-ribbon> + <button v-else class="cw-ribbon-button cw-ribbon-button-prev-disabled" /> + <router-link v-if="nextElement" :to="'/structural_element/' + nextElement.id"> + <button class="cw-ribbon-button cw-ribbon-button-next" :title="textRibbon.next" /> + </router-link> + <button v-else class="cw-ribbon-button cw-ribbon-button-next-disabled" /> + </template> + <template #breadcrumbList> + <li + v-for="ancestor in ancestors" + :key="ancestor.id" + :title="ancestor.attributes.title" + class="cw-ribbon-breadcrumb-item" + > + <span> + <router-link :to="'/structural_element/' + ancestor.id"> + {{ ancestor.attributes.title }} + </router-link> + </span> + </li> + <li + class="cw-ribbon-breadcrumb-item cw-ribbon-breadcrumb-item-current" + :title="structuralElement.attributes.title" + > + <span>{{ structuralElement.attributes.title }}</span> + </li> + </template> + <template #breadcrumbFallback> + <li + class="cw-ribbon-breadcrumb-item cw-ribbon-breadcrumb-item-current" + :title="structuralElement.attributes.title" + > + <span>{{ structuralElement.attributes.title }}</span> + </li> + </template> + <template #menu> + <studip-action-menu + v-if="!consumeMode" + :items="menuItems" + class="cw-ribbon-action-menu" + @editCurrentElement="menuAction('editCurrentElement')" + @addElement="menuAction('addElement')" + @deleteCurrentElement="menuAction('deleteCurrentElement')" + @showInfo="menuAction('showInfo')" + @showExportOptions="menuAction('showExportOptions')" + @oerCurrentElement="menuAction('oerCurrentElement')" + @setBookmark="menuAction('setBookmark')" + /> + </template> + </courseware-ribbon> - <div v-if="canRead" class="cw-container-wrapper" :class="{ 'cw-container-wrapper-consume': consumeMode }"> - <div v-if="structuralElementLoaded" class="cw-companion-box-wrapper"> - <courseware-empty-element-box - v-if="(empty && !isRoot && canEdit) || (empty && !canEdit) || (!noContainers && empty && isRoot && canEdit)" + <div + v-if="canRead" + class="cw-container-wrapper" + :class="{ 'cw-container-wrapper-consume': consumeMode }" + > + <div v-if="structuralElementLoaded" class="cw-companion-box-wrapper"> + <courseware-empty-element-box + v-if=" + (empty && !isRoot && canEdit) || + (empty && !canEdit) || + (!noContainers && empty && isRoot && canEdit) + " + :canEdit="canEdit" + :noContainers="noContainers" + /> + <courseware-wellcome-screen v-if="noContainers && isRoot && canEdit" /> + </div> + <component + v-for="container in containers" + :key="container.id" + :is="containerComponent(container)" + :container="container" :canEdit="canEdit" - :noContainers="noContainers" + :isTeacher="isTeacher" + class="cw-container-item" /> - <courseware-wellcome-screen v-if="noContainers && isRoot && canEdit"/> </div> - <component - v-for="container in containers" - :key="container.id" - :is="containerComponent(container)" - :container="container" - :canEdit="canEdit" - :isTeacher="isTeacher" - class="cw-container-item" - /> - </div> - <div v-else class="cw-container-wrapper" :class="{ 'cw-container-wrapper-consume': consumeMode }"> - <div v-if="structuralElementLoaded" class="cw-companion-box-wrapper"> - <courseware-companion-box mood="sad" :msgCompanion="$gettext('Diese Seite steht Ihnen leider nicht zur Verfügung')" /> + <div v-else class="cw-container-wrapper" :class="{ 'cw-container-wrapper-consume': consumeMode }"> + <div v-if="structuralElementLoaded" class="cw-companion-box-wrapper"> + <courseware-companion-box + mood="sad" + :msgCompanion="$gettext('Diese Seite steht Ihnen leider nicht zur Verfügung')" + /> + </div> </div> </div> - </div> - <courseware-companion-overlay /> - - <studip-dialog - v-if="showEditDialog" - :title="textEdit.title" - :confirmText="textEdit.confirm" - :confirmClass="'accept'" - :closeText="textEdit.close" - :closeClass="'cancel'" - height="500" - width="500" - class="studip-dialog-with-tab" - @close="closeEditDialog" - @confirm="storeCurrentElement" - > - <template v-slot:dialogContent> - <courseware-tabs class="cw-tab-in-dialog"> - <courseware-tab :name="textEdit.basic" :selected="true"> - <form class="default" @submit.prevent=""> - <label> - <translate>Titel</translate> - <input type="text" v-model="currentElement.attributes.title" /> - </label> - <label> - <translate>Beschreibung</translate> - <textarea - v-model="currentElement.attributes.payload.description" - class="cw-structural-element-description" + <courseware-companion-overlay /> + + <studip-dialog + v-if="showEditDialog" + :title="textEdit.title" + :confirmText="textEdit.confirm" + :confirmClass="'accept'" + :closeText="textEdit.close" + :closeClass="'cancel'" + height="500" + width="500" + class="studip-dialog-with-tab" + @close="closeEditDialog" + @confirm="storeCurrentElement" + > + <template v-slot:dialogContent> + <courseware-tabs class="cw-tab-in-dialog"> + <courseware-tab :name="textEdit.basic" :selected="true"> + <form class="default" @submit.prevent=""> + <label> + <translate>Titel</translate> + <input type="text" v-model="currentElement.attributes.title" /> + </label> + <label> + <translate>Beschreibung</translate> + <textarea + v-model="currentElement.attributes.payload.description" + class="cw-structural-element-description" + /> + </label> + </form> + </courseware-tab> + <courseware-tab :name="textEdit.meta"> + <form class="default" @submit.prevent=""> + <label> + <translate>Farbe</translate> + <v-select + v-model="currentElement.attributes.payload.color" + :options="colors" + :reduce="(color) => color.class" + label="class" + class="cw-vs-select" + > + <template #open-indicator="selectAttributes"> + <span v-bind="selectAttributes" + ><studip-icon shape="arr_1down" size="10" + /></span> + </template> + <template #no-options="{ search, searching, loading }"> + <translate>Es steht keine Auswahl zur Verfügung</translate>. + </template> + <template #selected-option="{ name, hex }"> + <span class="vs__option-color" :style="{ 'background-color': hex }"></span + ><span>{{ name }}</span> + </template> + <template #option="{ name, hex }"> + <span class="vs__option-color" :style="{ 'background-color': hex }"></span + ><span>{{ name }}</span> + </template> + </v-select> + </label> + <label> + <translate>Zweck</translate> + <select v-model="currentElement.attributes.purpose"> + <option value="content"><translate>Inhalt</translate></option> + <option value="template"><translate>Vorlage</translate></option> + <option value="oer"><translate>OER-Material</translate></option> + <option value="portfolio"><translate>ePortfolio</translate></option> + <option value="draft"><translate>Entwurf</translate></option> + <option value="other"><translate>Sonstiges</translate></option> + </select> + </label> + <label> + <translate>Lizenztyp</translate> + <select v-model="currentElement.attributes.payload.license_type"> + <option v-for="license in licenses" :key="license.id" :value="license.id"> + {{ license.name }} + </option> + </select> + </label> + <label> + <translate>Geschätzter zeitlicher Aufwand</translate> + <input type="text" v-model="currentElement.attributes.payload.required_time" /> + </label> + <label> + <translate>Niveau</translate><br /> + <translate>von</translate> + <select v-model="currentElement.attributes.payload.difficulty_start"> + <option + v-for="difficulty_start in 12" + :key="difficulty_start" + :value="difficulty_start" + > + {{ difficulty_start }} + </option> + </select> + <translate>bis</translate> + <select v-model="currentElement.attributes.payload.difficulty_end"> + <option + v-for="difficulty_end in 12" + :key="difficulty_end" + :value="difficulty_end" + > + {{ difficulty_end }} + </option> + </select> + </label> + </form> + </courseware-tab> + <courseware-tab :name="textEdit.image"> + <form class="default" @submit.prevent=""> + <img + v-if="image" + :src="image" + class="cw-structural-element-image-preview" + :alt="$gettext('Vorschaubild')" /> - </label> - </form> - </courseware-tab> - <courseware-tab :name="textEdit.meta"> - <form class="default" @submit.prevent=""> - <label> - <translate>Farbe</translate> - <v-select - v-model="currentElement.attributes.payload.color" - :options="colors" - :reduce="color => color.class" - label="class" - class="cw-vs-select" - > - <template #open-indicator="selectAttributes"> - <span v-bind="selectAttributes"><studip-icon shape="arr_1down" size="10"/></span> - </template> - <template #no-options="{ search, searching, loading }"> - <translate>Es steht keine Auswahl zur Verfügung</translate>. - </template> - <template #selected-option="{name, hex}"> - <span class="vs__option-color" :style="{'background-color': hex}"></span><span>{{name}}</span> - </template> - <template #option="{name, hex}"> - <span class="vs__option-color" :style="{'background-color': hex}"></span><span>{{name}}</span> - </template> - </v-select> - </label> - <label> - <translate>Zweck</translate> - <select v-model="currentElement.attributes.purpose"> - <option value="content"><translate>Inhalt</translate></option> - <option value="template"><translate>Vorlage</translate></option> - <option value="oer"><translate>OER-Material</translate></option> - <option value="portfolio"><translate>ePortfolio</translate></option> - <option value="draft"><translate>Entwurf</translate></option> - <option value="other"><translate>Sonstiges</translate></option> - </select> - </label> - <label> - <translate>Lizenztyp</translate> - <select v-model="currentElement.attributes.payload.license_type"> - <option v-for="license in licenses" :key="license.id" :value="license.id">{{license.name}}</option> - </select> - </label> - <label> - <translate>Geschätzter zeitlicher Aufwand</translate> - <input type="text" v-model="currentElement.attributes.payload.required_time" /> - </label> - <label> - <translate>Niveau</translate><br> - <translate>von</translate> - <select v-model="currentElement.attributes.payload.difficulty_start"> - <option v-for="difficulty_start in 12" :key="difficulty_start" :value="difficulty_start">{{difficulty_start}}</option> - </select> - <translate>bis</translate> - <select v-model="currentElement.attributes.payload.difficulty_end"> - <option v-for="difficulty_end in 12" :key="difficulty_end" :value="difficulty_end">{{difficulty_end}}</option> - </select> - </label> - </form> - </courseware-tab> - <courseware-tab :name="textEdit.image"> - <form class="default" @submit.prevent=""> - <img - v-if="image" - :src="image" - class="cw-structural-element-image-preview" - :alt="$gettext('Vorschaubild')" + <label v-if="image"> + <button class="button" @click="deleteImage" v-translate>Bild löschen</button> + </label> + <div v-if="uploadFileError" class="messagebox messagebox_error"> + {{ uploadFileError }} + </div> + <label v-if="!image"> + <translate>Bild hochladen</translate> + <input ref="upload_image" type="file" accept="image/*" @change="checkUploadFile" /> + </label> + </form> + </courseware-tab> + <courseware-tab :name="textEdit.approval"> + <courseware-structural-element-permissions + v-if="inCourse" + :element="currentElement" + @updateReadApproval="updateReadApproval" + @updateWriteApproval="updateWriteApproval" /> - <label v-if="image"> - <button class="button" @click="deleteImage" v-translate>Bild löschen</button> - </label> - <div v-if="uploadFileError" class="messagebox messagebox_error"> - {{ uploadFileError }} - </div> - <label v-if="!image"> - <translate>Bild hochladen</translate> - <input ref="upload_image" type="file" accept="image/*" @change="checkUploadFile" /> - </label> - </form> - </courseware-tab> - <courseware-tab :name="textEdit.approval"> - <courseware-structural-element-permissions - v-if="inCourse" - :element="currentElement" - @updateReadApproval="updateReadApproval" - @updateWriteApproval="updateWriteApproval" - /> - <!-- <h1> + <!-- <h1> <translate>Lehrende in Stud.IP</translate> </h1> <label> @@ -206,191 +246,209 @@ /> <translate>Seite zum kopieren für Lehrende freigeben</translate> </label> --> - </courseware-tab> - <courseware-tab v-if="inCourse" :name="textEdit.visible"> - <form class="default" @submit.prevent=""> - <label> - <translate>Sichtbar ab</translate> - <input type="date" v-model="currentElement.attributes['release-date']" /> - </label> - <label> - <translate>Unsichtbar ab</translate> - <input type="date" v-model="currentElement.attributes['withdraw-date']" /> - </label> - </form> - </courseware-tab> - </courseware-tabs> - </template> - </studip-dialog> - - <studip-dialog - v-if="showAddDialog" - :title="$gettext('Seite hinzufügen')" - :confirmText="'Erstellen'" - :confirmClass="'accept'" - :closeText="$gettext('Schließen')" - :closeClass="'cancel'" - class="cw-structural-element-dialog" - @close="closeAddDialog" - @confirm="createElement" - > - <template v-slot:dialogContent> - <form class="default" @submit.prevent=""> - <label> - <translate>Position der neuen Seite</translate> - <select v-model="newChapterParent"> - <option v-if="!isRoot" value="sibling"> - <translate>Neben der aktuellen Seite</translate> - </option> - <option value="descendant"><translate>Unterhalb der aktuellen Seite</translate></option> - </select> - </label> - <label> - <translate>Name der neuen Seite</translate><br /> - <input v-model="newChapterName" type="text" /> - </label> - </form> - </template> - </studip-dialog> - - <studip-dialog - v-if="showInfoDialog" - :title="textInfo.title" - :closeText="textInfo.close" - :closeClass="'cancel'" - @close="showElementInfoDialog(false)" - > - <template v-slot:dialogContent> - <table class="cw-structural-element-info"> - <tr> - <td><translate>Titel</translate>:</td> - <td>{{ structuralElement.attributes.title }}</td> - </tr> - <tr> - <td><translate>Beschreibung</translate>:</td> - <td>{{ structuralElement.attributes.payload.description }}</td> - </tr> - <tr> - <td><translate>Seite wurde erstellt von</translate>:</td> - <td>{{ owner }}</td> - </tr> - <tr> - <td><translate>Seite wurde erstellt am</translate>:</td> - <td><iso-date :date="structuralElement.attributes.mkdate" /></td> - </tr> - <tr> - <td><translate>Zuletzt bearbeitet von</translate>:</td> - <td>{{ editor }}</td> - </tr> - <tr> - <td><translate>Zuletzt bearbeitet am</translate>:</td> - <td><iso-date :date="structuralElement.attributes.chdate" /></td> - </tr> - </table> - </template> - </studip-dialog> - - <studip-dialog - v-if="showExportDialog" - :title="textExport.title" - :confirmText="textExport.confirm" - :confirmClass="'accept'" - :closeText="textExport.close" - :closeClass="'cancel'" - height="350" - @close="showElementExportDialog(false)" - @confirm="exportCurrentElement" - > - <template v-slot:dialogContent> - <div v-show="!exportRunning"> - <translate>Hiermit exportieren Sie die Seite "{{ currentElement.attributes.title }}" als ZIP-Datei.</translate> + </courseware-tab> + <courseware-tab v-if="inCourse" :name="textEdit.visible"> + <form class="default" @submit.prevent=""> + <label> + <translate>Sichtbar ab</translate> + <input type="date" v-model="currentElement.attributes['release-date']" /> + </label> + <label> + <translate>Unsichtbar ab</translate> + <input type="date" v-model="currentElement.attributes['withdraw-date']" /> + </label> + </form> + </courseware-tab> + </courseware-tabs> + </template> + </studip-dialog> - <div class="cw-element-export"> + <studip-dialog + v-if="showAddDialog" + :title="$gettext('Seite hinzufügen')" + :confirmText="'Erstellen'" + :confirmClass="'accept'" + :closeText="$gettext('Schließen')" + :closeClass="'cancel'" + class="cw-structural-element-dialog" + @close="closeAddDialog" + @confirm="createElement" + > + <template v-slot:dialogContent> + <form class="default" @submit.prevent=""> <label> - <input type="checkbox" v-model="exportChildren"> - <translate>Unterseiten exportieren</translate> + <translate>Position der neuen Seite</translate> + <select v-model="newChapterParent"> + <option v-if="!isRoot" value="sibling"> + <translate>Neben der aktuellen Seite</translate> + </option> + <option value="descendant"><translate>Unterhalb der aktuellen Seite</translate></option> + </select> </label> + <label> + <translate>Name der neuen Seite</translate><br /> + <input v-model="newChapterName" type="text" /> + </label> + </form> + </template> + </studip-dialog> + + <studip-dialog + v-if="showInfoDialog" + :title="textInfo.title" + :closeText="textInfo.close" + :closeClass="'cancel'" + @close="showElementInfoDialog(false)" + > + <template v-slot:dialogContent> + <table class="cw-structural-element-info"> + <tr> + <td><translate>Titel</translate>:</td> + <td>{{ structuralElement.attributes.title }}</td> + </tr> + <tr> + <td><translate>Beschreibung</translate>:</td> + <td>{{ structuralElement.attributes.payload.description }}</td> + </tr> + <tr> + <td><translate>Seite wurde erstellt von</translate>:</td> + <td>{{ owner }}</td> + </tr> + <tr> + <td><translate>Seite wurde erstellt am</translate>:</td> + <td><iso-date :date="structuralElement.attributes.mkdate" /></td> + </tr> + <tr> + <td><translate>Zuletzt bearbeitet von</translate>:</td> + <td>{{ editor }}</td> + </tr> + <tr> + <td><translate>Zuletzt bearbeitet am</translate>:</td> + <td><iso-date :date="structuralElement.attributes.chdate" /></td> + </tr> + </table> + </template> + </studip-dialog> + + <studip-dialog + v-if="showExportDialog" + :title="textExport.title" + :confirmText="textExport.confirm" + :confirmClass="'accept'" + :closeText="textExport.close" + :closeClass="'cancel'" + height="350" + @close="showElementExportDialog(false)" + @confirm="exportCurrentElement" + > + <template v-slot:dialogContent> + <div v-show="!exportRunning"> + <translate + >Hiermit exportieren Sie die Seite "{{ currentElement.attributes.title }}" als + ZIP-Datei.</translate + > + + <div class="cw-element-export"> + <label> + <input type="checkbox" v-model="exportChildren" /> + <translate>Unterseiten exportieren</translate> + </label> + </div> </div> - </div> - <courseware-companion-box v-show="exportRunning" :msgCompanion="$gettext('Export läuft, bitte haben sie einen Moment Geduld...')" mood="pointing"/> - <div v-show="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> + <courseware-companion-box + v-show="exportRunning" + :msgCompanion="$gettext('Export läuft, bitte haben sie einen Moment Geduld...')" + mood="pointing" + /> + <div v-show="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> - </div> - </template> - - </studip-dialog> - - <studip-dialog - v-if="showOerDialog" - height="600" - width="600" - :title="textOer.title" - :confirmText="textOer.confirm" - :confirmClass="'accept'" - :closeText="textOer.close" - :closeClass="'cancel'" - @close="showElementOerDialog(false)" - @confirm="publishCurrentElement" - > - - <template v-slot:dialogContent> - <form class="default" @submit.prevent=""> - <fieldset> - <legend><translate>Grunddaten</translate></legend> - <label> - <p><translate>Vorschaubild</translate>:</p> - <img - v-if="currentElement.relationships.image.data" - :src="currentElement.relationships.image.meta['download-url']" - width="400" - /> - </label> - <label> - <p><translate>Beschreibung</translate>:</p> - <p> {{ currentElement.attributes.payload.description }}</p> - </label> - <label> - <translate>Niveau</translate>: - <p> {{ currentElement.attributes.payload.difficulty_start }} - {{ currentElement.attributes.payload.difficulty_end }}</p> - </label> - <label> - <translate>Lizenztyp</translate>: - <p>{{currentLicenseName}}</p> - </label> - <label> - <translate>Sie können diese Daten unter "Seite bearbeiten" verändern</translate>. - </label> + </template> + </studip-dialog> - </fieldset> - <fieldset> - <legend><translate>Einstellungen</translate></legend> - <label> - <translate>Unterseiten veröffentlichen</translate> - <input type="checkbox" v-model="oerChildren"> - </label> - </fieldset> - </form> - </template> - - </studip-dialog> - - <studip-dialog - v-if="showDeleteDialog" - :title="textDelete.title" - :question="textDelete.alert" - height="180" - @confirm="deleteCurrentElement" - @close="closeDeleteDialog" - ></studip-dialog> - </div> - <div v-else> - <courseware-companion-box v-if="currentElement !== ''" :msgCompanion="textCompanionWrongContext" mood="sad"/> - </div> + <studip-dialog + v-if="showOerDialog" + height="600" + width="600" + :title="textOer.title" + :confirmText="textOer.confirm" + :confirmClass="'accept'" + :closeText="textOer.close" + :closeClass="'cancel'" + @close="showElementOerDialog(false)" + @confirm="publishCurrentElement" + > + <template v-slot:dialogContent> + <form class="default" @submit.prevent=""> + <fieldset> + <legend><translate>Grunddaten</translate></legend> + <label> + <p><translate>Vorschaubild</translate>:</p> + <img + v-if="currentElement.relationships.image.data" + :src="currentElement.relationships.image.meta['download-url']" + width="400" + /> + </label> + <label> + <p><translate>Beschreibung</translate>:</p> + <p>{{ currentElement.attributes.payload.description }}</p> + </label> + <label> + <translate>Niveau</translate>: + <p> + {{ currentElement.attributes.payload.difficulty_start }} - + {{ currentElement.attributes.payload.difficulty_end }} + </p> + </label> + <label> + <translate>Lizenztyp</translate>: + <p>{{ currentLicenseName }}</p> + </label> + <label> + <translate>Sie können diese Daten unter "Seite bearbeiten" verändern</translate>. + </label> + </fieldset> + <fieldset> + <legend><translate>Einstellungen</translate></legend> + <label> + <translate>Unterseiten veröffentlichen</translate> + <input type="checkbox" v-model="oerChildren" /> + </label> + </fieldset> + </form> + </template> + </studip-dialog> + <studip-dialog + v-if="showDeleteDialog" + :title="textDelete.title" + :question="textDelete.alert" + height="180" + @confirm="deleteCurrentElement" + @close="closeDeleteDialog" + ></studip-dialog> + </div> + <div v-else> + <courseware-companion-box + v-if="currentElement !== ''" + :msgCompanion="textCompanionWrongContext" + mood="sad" + /> + </div> </div> </template> @@ -430,13 +488,12 @@ export default { IsoDate, StudipDialog, }, - props: ['orderedStructuralElements'], + props: ['orderedStructuralElements', 'structuralElement'], mixins: [CoursewareExport], data() { return { - currentId: null, newChapterName: '', newChapterParent: 'descendant', currentElement: '', @@ -500,6 +557,10 @@ export default { exportProgress: 'exportProgress', }), + currentId() { + return this.structuralElement?.id; + }, + textOer() { return { title: this.$gettext('Seite auf') + ' ' + this.oerTitle + ' ' + this.$gettext('veröffentlichen'), @@ -517,7 +578,12 @@ export default { textDelete.title = this.$gettext('Seite unwiderruflich löschen'); textDelete.alert = this.$gettext('Möchten Sie die Seite wirklich löschen?'); if (this.structuralElementLoaded) { - textDelete.alert = this.$gettext('Möchten Sie die Seite') +' "'+ this.structuralElement.attributes.title + '" '+ this.$gettext('wirklich löschen?'); + textDelete.alert = + this.$gettext('Möchten Sie die Seite') + + ' "' + + this.structuralElement.attributes.title + + '" ' + + this.$gettext('wirklich löschen?'); } return textDelete; @@ -527,13 +593,19 @@ export default { let valid = false; let context = this.$store.getters.context; if (context.type === 'courses' && this.currentElement.relationships) { - if (this.currentElement.relationships.course && context.id === this.currentElement.relationships.course.data.id) { + if ( + this.currentElement.relationships.course && + context.id === this.currentElement.relationships.course.data.id + ) { valid = true; } } if (context.type === 'users' && this.currentElement.relationships) { - if (this.currentElement.relationships.user && context.id === this.currentElement.relationships.user.data.id) { + if ( + this.currentElement.relationships.user && + context.id === this.currentElement.relationships.user.data.id + ) { valid = true; } } @@ -545,14 +617,6 @@ export default { return this.structuralElement.relationships?.image?.meta?.['download-url'] ?? null; }, - structuralElement() { - if (!this.currentId) { - return null; - } - - return this.structuralElementById({ id: this.currentId }); - }, - structuralElementLoaded() { return this.structuralElement !== null && this.structuralElement !== {}; }, @@ -655,16 +719,30 @@ export default { { id: 4, label: this.$gettext('Lesezeichen setzen'), icon: 'star', emit: 'setBookmark' }, ]; if (this.canEdit) { - menu.push({ id: 1, label: this.$gettext('Seite bearbeiten'), icon: 'edit', emit: 'editCurrentElement' }); + menu.push({ + id: 1, + label: this.$gettext('Seite bearbeiten'), + icon: 'edit', + emit: 'editCurrentElement', + }); menu.push({ id: 2, label: this.$gettext('Seite hinzufügen'), icon: 'add', emit: 'addElement' }); - menu.push({ id: 5, label: this.$gettext('Seite exportieren'), icon: 'export', emit: 'showExportOptions' }); - + menu.push({ + id: 5, + label: this.$gettext('Seite exportieren'), + icon: 'export', + emit: 'showExportOptions', + }); } if (this.canEdit && this.oerEnabled) { menu.push({ id: 6, label: this.textOer.title, icon: 'oer-campus', emit: 'oerCurrentElement' }); } - if(!this.isRoot && this.canEdit) { - menu.push({ id: 7, label: this.$gettext('Seite löschen'), icon: 'trash', emit: 'deleteCurrentElement' }); + if (!this.isRoot && this.canEdit) { + menu.push({ + id: 7, + label: this.$gettext('Seite löschen'), + icon: 'trash', + emit: 'deleteCurrentElement', + }); } menu.sort((a, b) => a.id - b.id); @@ -672,29 +750,148 @@ export default { }, colors() { const colors = [ - {name: this.$gettext('Schwarz'), class: 'black', hex: '#000000', level: 100, icon: 'black', darkmode: true}, - {name: this.$gettext('Weiß'), class: 'white', hex: '#ffffff', level: 100, icon: 'white', darkmode: false}, - - {name: this.$gettext('Blau'), class: 'studip-blue', hex: '#28497c', level: 100, icon: 'blue', darkmode: true}, - {name: this.$gettext('Hellblau'), class: 'studip-lightblue', hex: '#e7ebf1', level: 40, icon: 'lightblue', darkmode: false}, - {name: this.$gettext('Rot'), class: 'studip-red', hex: '#d60000', level: 100, icon: 'red', darkmode: false}, - {name: this.$gettext('Grün'), class: 'studip-green', hex: '#008512', level: 100, icon: 'green', darkmode: true}, - {name: this.$gettext('Gelb'), class: 'studip-yellow', hex: '#ffbd33', level: 100, icon: 'yellow', darkmode: false}, - {name: this.$gettext('Grau'), class: 'studip-gray', hex: '#636a71', level: 100, icon: 'grey', darkmode: true}, - - {name: this.$gettext('Holzkohle'), class: 'charcoal', hex: '#3c454e', level: 100, icon: false, darkmode: true}, - {name: this.$gettext('Königliches Purpur'), class: 'royal-purple', hex: '#8656a2', level: 80, icon: false, darkmode: true}, - {name: this.$gettext('Leguangrün'), class: 'iguana-green', hex: '#66b570', level: 60, icon: false, darkmode: true}, - {name: this.$gettext('Königin blau'), class: 'queen-blue', hex: '#536d96', level: 80, icon: false, darkmode: true}, - {name: this.$gettext('Helles Seegrün'), class: 'verdigris', hex: '#41afaa', level: 80, icon: false, darkmode: true}, - {name: this.$gettext('Maulbeere'), class: 'mulberry', hex: '#bf5796', level: 80, icon: false, darkmode: true}, - {name: this.$gettext('Kürbis'), class: 'pumpkin', hex: '#f26e00', level: 100, icon: false, darkmode: true}, - {name: this.$gettext('Sonnenschein'), class: 'sunglow', hex: '#ffca5c', level: 80, icon: false, darkmode: false}, - {name: this.$gettext('Apfelgrün'), class: 'apple-green', hex: '#8bbd40', level: 80, icon: false, darkmode: true}, + { + name: this.$gettext('Schwarz'), + class: 'black', + hex: '#000000', + level: 100, + icon: 'black', + darkmode: true, + }, + { + name: this.$gettext('Weiß'), + class: 'white', + hex: '#ffffff', + level: 100, + icon: 'white', + darkmode: false, + }, + + { + name: this.$gettext('Blau'), + class: 'studip-blue', + hex: '#28497c', + level: 100, + icon: 'blue', + darkmode: true, + }, + { + name: this.$gettext('Hellblau'), + class: 'studip-lightblue', + hex: '#e7ebf1', + level: 40, + icon: 'lightblue', + darkmode: false, + }, + { + name: this.$gettext('Rot'), + class: 'studip-red', + hex: '#d60000', + level: 100, + icon: 'red', + darkmode: false, + }, + { + name: this.$gettext('Grün'), + class: 'studip-green', + hex: '#008512', + level: 100, + icon: 'green', + darkmode: true, + }, + { + name: this.$gettext('Gelb'), + class: 'studip-yellow', + hex: '#ffbd33', + level: 100, + icon: 'yellow', + darkmode: false, + }, + { + name: this.$gettext('Grau'), + class: 'studip-gray', + hex: '#636a71', + level: 100, + icon: 'grey', + darkmode: true, + }, + + { + name: this.$gettext('Holzkohle'), + class: 'charcoal', + hex: '#3c454e', + level: 100, + icon: false, + darkmode: true, + }, + { + name: this.$gettext('Königliches Purpur'), + class: 'royal-purple', + hex: '#8656a2', + level: 80, + icon: false, + darkmode: true, + }, + { + name: this.$gettext('Leguangrün'), + class: 'iguana-green', + hex: '#66b570', + level: 60, + icon: false, + darkmode: true, + }, + { + name: this.$gettext('Königin blau'), + class: 'queen-blue', + hex: '#536d96', + level: 80, + icon: false, + darkmode: true, + }, + { + name: this.$gettext('Helles Seegrün'), + class: 'verdigris', + hex: '#41afaa', + level: 80, + icon: false, + darkmode: true, + }, + { + name: this.$gettext('Maulbeere'), + class: 'mulberry', + hex: '#bf5796', + level: 80, + icon: false, + darkmode: true, + }, + { + name: this.$gettext('Kürbis'), + class: 'pumpkin', + hex: '#f26e00', + level: 100, + icon: false, + darkmode: true, + }, + { + name: this.$gettext('Sonnenschein'), + class: 'sunglow', + hex: '#ffca5c', + level: 80, + icon: false, + darkmode: false, + }, + { + name: this.$gettext('Apfelgrün'), + class: 'apple-green', + hex: '#8bbd40', + level: 80, + icon: false, + darkmode: true, + }, ]; let elementColors = []; - colors.forEach( color => { - if(color.darkmode) { + colors.forEach((color) => { + if (color.darkmode) { elementColors.push(color); } }); @@ -702,34 +899,21 @@ export default { return elementColors; }, currentLicenseName() { - for(let i = 0; i < this.licenses.length; i++) { + for (let i = 0; i < this.licenses.length; i++) { if (this.licenses[i]['id'] == this.currentElement.attributes.payload.license_type) { return this.licenses[i]['name']; } } return ''; - } - }, - - watch: { - $route(to) { - this.setCurrentId(to.params.id); }, }, - mounted() { - if (!this.currentId) { - this.setCurrentId(this.$route.params.id); - } - }, - methods: { ...mapActions({ createStructuralElement: 'createStructuralElement', updateStructuralElement: 'updateStructuralElement', deleteStructuralElement: 'deleteStructuralElement', - loadStructuralElement: 'loadStructuralElement', lockObject: 'lockObject', unlockObject: 'unlockObject', addBookmark: 'addBookmark', @@ -745,11 +929,6 @@ export default { showElementOerDialog: 'showElementOerDialog', }), - async setCurrentId(id) { - this.currentId = id; - await this.loadStructuralElement(this.currentId); - this.initCurrent(); - }, initCurrent() { this.currentElement = _.cloneDeep(this.structuralElement); this.uploadFileError = ''; @@ -785,7 +964,7 @@ export default { }, async closeEditDialog() { await this.unlockObject({ id: this.currentId, type: 'courseware-structural-elements' }); - this.showElementEditDialog(false) + this.showElementEditDialog(false); this.initCurrent(); }, closeAddDialog() { @@ -837,7 +1016,7 @@ export default { id: this.currentId, }); await this.unlockObject({ id: this.currentId, type: 'courseware-structural-elements' }); - this.setCurrentId(this.$route.params.id); + this.$emit('select', this.currentId); this.showElementEditDialog(false); }, @@ -849,7 +1028,7 @@ export default { this.exportRunning = true; await this.sendExportZip(this.currentElement.id, { - withChildren: this.exportChildren + withChildren: this.exportChildren, }); this.exportRunning = false; @@ -857,7 +1036,7 @@ export default { }, async publishCurrentElement() { - this.exportToOER(this.currentElement, {withChildren: this.oerChildren}); + this.exportToOER(this.currentElement, { withChildren: this.oerChildren }); }, async closeDeleteDialog() { @@ -889,7 +1068,12 @@ export default { }); let newElement = this.$store.getters['courseware-structural-elements/lastCreated']; this.companionSuccess({ - info: this.$gettext('Seite') +' "' + newElement.attributes.title + '" ' + this.$gettext('wurde erfolgreich angelegt.'), + info: + this.$gettext('Seite') + + ' "' + + newElement.attributes.title + + '" ' + + this.$gettext('wurde erfolgreich angelegt.'), }); }, containerComponent(container) { @@ -909,7 +1093,17 @@ export default { created() { this.pluginManager.registerComponentsLocally(this); }, + + watch: { + structuralElement() { + this.initCurrent(); + }, + }, + // this line provides all the components to courseware plugins - provide: () => ({ containerComponents: ContainerComponents, coursewarePluginComponents: CoursewarePluginComponents }), + provide: () => ({ + containerComponents: ContainerComponents, + coursewarePluginComponents: CoursewarePluginComponents, + }), }; </script> diff --git a/resources/vue/components/courseware/IndexApp.vue b/resources/vue/components/courseware/IndexApp.vue index dde33980a5ee2af7bc0d4de07489eb50721ba362..e4b16cdb35c35637a869a8108391eaaf41276dcb 100755 --- a/resources/vue/components/courseware/IndexApp.vue +++ b/resources/vue/components/courseware/IndexApp.vue @@ -1,18 +1,18 @@ <template> <div v-if="courseware"> <courseware-structural-element + :structural-element="selected" :ordered-structural-elements="orderedStructuralElements" + @select="selectStructuralElement" ></courseware-structural-element> <MountingPortal mountTo="#courseware-action-widget" name="sidebar-actions"> - <courseware-action-widget></courseware-action-widget> + <courseware-action-widget :structural-element="selected"></courseware-action-widget> </MountingPortal> <MountingPortal mountTo="#courseware-view-widget" name="sidebar-views"> <courseware-view-widget></courseware-view-widget> </MountingPortal> </div> - <div v-else> - <translate>Inhalte werden geladen</translate>... - </div> + <div v-else><translate>Inhalte werden geladen</translate>...</div> </template> <script> @@ -27,25 +27,48 @@ export default { CoursewareViewWidget, CoursewareActionWidget, }, - data: () => ({ orderedStructuralElements: [] }), + data: () => ({ + selected: null, + orderedStructuralElements: [], + }), computed: { ...mapGetters({ courseware: 'courseware', relatedStructuralElement: 'courseware-structural-elements/related', structuralElements: 'courseware-structural-elements/all', + structuralElementById: 'courseware-structural-elements/byId', userId: 'userId', }), }, methods: { - ...mapActions(['loadCoursewareStructure', 'loadTeacherStatus', 'coursewareBlockAdder']), + ...mapActions([ + 'coursewareBlockAdder', + 'loadCoursewareStructure', + 'loadStructuralElement', + 'loadTeacherStatus', + ]), + async selectStructuralElement(id) { + if (!id) { + return; + } + + await this.loadStructuralElement(id); + this.selected = this.structuralElementById({ id }); + }, }, async mounted() { await this.loadCoursewareStructure(); await this.loadTeacherStatus(this.userId); + const selectedId = this.$route.params?.id; + await this.selectStructuralElement(selectedId); }, watch: { - $route() { - this.coursewareBlockAdder({}); //reset block adder on navigate + $route(to) { + // reset block adder on navigate + this.coursewareBlockAdder({}); + + const selectedId = to.params?.id; + this.selectStructuralElement(selectedId); }, structuralElements(newElements, oldElements) { const nodes = buildNodes(this.structuralElements, this.relatedStructuralElement.bind(this));