diff --git a/app/controllers/courseware_controller.php b/app/controllers/courseware_controller.php index 138a98627267fd0a2f52f8bc6373c3cbc1c7fcef..ddee584320519145816a89e626118647a142a1ca 100644 --- a/app/controllers/courseware_controller.php +++ b/app/controllers/courseware_controller.php @@ -83,6 +83,5 @@ abstract class CoursewareController extends AuthenticatedController $sidebar->addWidget(new VueWidget('courseware-search-widget')); $sidebar->addWidget(new VueWidget('courseware-view-widget')); $sidebar->addWidget(new VueWidget('courseware-import-widget')); - $sidebar->addWidget(new VueWidget('courseware-export-widget')); } } diff --git a/resources/assets/stylesheets/scss/courseware/widgets.scss b/resources/assets/stylesheets/scss/courseware/widgets.scss index d5db20ca4b57030b57f5498a8906a03dc3b35614..44b069bd72a57e738c7b4fed4f864d69a2100461 100644 --- a/resources/assets/stylesheets/scss/courseware/widgets.scss +++ b/resources/assets/stylesheets/scss/courseware/widgets.scss @@ -11,6 +11,9 @@ .cw-action-widget-add { @include background-icon(add, clickable); } + .cw-action-widget-export { + @include background-icon(export, clickable); + } .cw-action-widget-info { @include background-icon(info, clickable); } diff --git a/resources/vue/components/StudipSquareButton.vue b/resources/vue/components/StudipSquareButton.vue new file mode 100644 index 0000000000000000000000000000000000000000..b7ae400aa5f7eb7d55fc59902b7b189f09e199ad --- /dev/null +++ b/resources/vue/components/StudipSquareButton.vue @@ -0,0 +1,54 @@ +<template> + <button class="square-button" @click="$emit('click')"> + <studip-icon :shape="icon" :size="50" /> + <span>{{ title }}</span> + </button> +</template> + +<script> +export default { + name: 'studip-square-button', + props: { + icon: { + type: String, + default: 'seminar', + }, + title: { + type: String, + required: true, + validator: (value) => { + return value !== ''; + }, + }, + }, +}; +</script> +<style scoped lang="scss"> +$size: 130px; +.square-button { + display: flex; + flex-direction: column; + justify-content: flex-start; + max-height: $size; + max-width: $size; + min-width: $size; + min-height: $size; + margin: 10px; + padding: 10px; + background-color: transparent; + border: solid thin var(--content-color-40); + cursor: pointer; + + img { + width: 100%; + height: 50px; + margin-bottom: 8px; + } + span { + color: var(--base-color); + } + &:hover span { + color: var(--red); + } +} +</style> diff --git a/resources/vue/components/courseware/IndexApp.vue b/resources/vue/components/courseware/IndexApp.vue index cbeeb4cc7fb218777830056e939d839f91ce12e5..8b6f59d2396954c978c36a8072596bb2b73250a5 100644 --- a/resources/vue/components/courseware/IndexApp.vue +++ b/resources/vue/components/courseware/IndexApp.vue @@ -21,9 +21,6 @@ <MountingPortal mountTo="#courseware-import-widget" name="sidebar-import"> <courseware-import-widget v-if="!showSearchResults && canEditSelected" :structural-element="selected"></courseware-import-widget> </MountingPortal> - <MountingPortal mountTo="#courseware-export-widget" name="sidebar-export"> - <courseware-export-widget v-if="!showSearchResults" :structural-element="selected" :canVisit="canVisit"></courseware-export-widget> - </MountingPortal> </div> <studip-progress-indicator v-if="structureLoadingState === 'loading'" @@ -46,7 +43,6 @@ import CoursewareCompanionBox from './layouts/CoursewareCompanionBox.vue'; import CoursewareCompanionOverlay from './layouts/CoursewareCompanionOverlay.vue'; import CoursewareViewWidget from './widgets/CoursewareViewWidget.vue'; import CoursewareActionWidget from './widgets/CoursewareActionWidget.vue'; -import CoursewareExportWidget from './widgets/CoursewareExportWidget.vue'; import CoursewareImportWidget from './widgets/CoursewareImportWidget.vue'; import CoursewareSearchWidget from './widgets/CoursewareSearchWidget.vue'; @@ -62,7 +58,6 @@ export default { CoursewareActionWidget, CoursewareCompanionBox, StudipProgressIndicator, - CoursewareExportWidget, CoursewareImportWidget, CoursewareSearchWidget, CoursewareCompanionOverlay, diff --git a/resources/vue/components/courseware/structural-element/CoursewareStructuralElement.vue b/resources/vue/components/courseware/structural-element/CoursewareStructuralElement.vue index 57bd762e612f4a4afd7d0fa387ed200514b6cb50..5edf736fdbad9467ebe24dc37420c3c2aa8c3a13 100644 --- a/resources/vue/components/courseware/structural-element/CoursewareStructuralElement.vue +++ b/resources/vue/components/courseware/structural-element/CoursewareStructuralElement.vue @@ -72,11 +72,7 @@ @addElement="menuAction('addElement')" @deleteCurrentElement="menuAction('deleteCurrentElement')" @showInfo="menuAction('showInfo')" - @showExportOptions="menuAction('showExportOptions')" - @oerCurrentElement="menuAction('oerCurrentElement')" @setBookmark="menuAction('setBookmark')" - @sortContainers="menuAction('sortContainers')" - @pdfExport="menuAction('pdfExport')" @showSuggest="menuAction('showSuggest')" @linkElement="menuAction('linkElement')" @removeLock="menuAction('removeLock')" @@ -425,73 +421,6 @@ </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"> - <span v-translate>Hiermit exportieren Sie die Seite "%{ currentElement.attributes.title }" als ZIP-Datei.</span> - <div class="cw-element-export"> - <label> - <input type="checkbox" v-model="exportChildren" /> - <translate>Unterseiten exportieren</translate> - </label> - </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> - </div> - </div> - </template> - </studip-dialog> - - <studip-dialog - v-if="showPdfExportDialog" - :title="textExport.title" - :confirmText="textExport.confirm" - confirmClass="accept" - :closeText="textExport.close" - closeClass="cancel" - height="350" - @close="showElementPdfExportDialog(false)" - @confirm="pdfExportCurrentElement" - > - <template v-slot:dialogContent> - <span v-translate>Hiermit exportieren Sie die Seite "%{ currentElement.attributes.title }" als PDF-Datei.</span> - <div class="cw-element-export"> - <label> - <input type="checkbox" v-model="pdfExportChildren" /> - <translate>Unterseiten exportieren</translate> - </label> - </div> - </template> - </studip-dialog> - <studip-dialog v-if="showOerDialog" height="600" @@ -632,6 +561,9 @@ <courseware-structural-element-dialog-import v-if="showImportDialog"/> <courseware-structural-element-dialog-copy v-if="showCopyDialog" /> <courseware-structural-element-dialog-link v-if="showLinkDialog"/> + <courseware-structural-element-dialog-export-chooser v-if="showExportChooserDialog" :canEdit="canEdit" :canVisit="canVisit" /> + <courseware-structural-element-dialog-export v-if="showExportDialog" :structuralElement="currentElement" /> + <courseware-structural-element-dialog-export-pdf v-if="showPdfExportDialog" :structuralElement="currentElement" /> </div> <div v-else> <courseware-companion-box @@ -657,6 +589,9 @@ import CoursewareStructuralElementDialogAdd from './CoursewareStructuralElementD import CoursewareStructuralElementDialogCopy from './CoursewareStructuralElementDialogCopy.vue'; import CoursewareStructuralElementDialogImport from './CoursewareStructuralElementDialogImport.vue'; import CoursewareStructuralElementDialogLink from './CoursewareStructuralElementDialogLink.vue'; +import CoursewareStructuralElementDialogExportChooser from './CoursewareStructuralElementDialogExportChooser.vue'; +import CoursewareStructuralElementDialogExport from './CoursewareStructuralElementDialogExport.vue'; +import CoursewareStructuralElementDialogExportPdf from './CoursewareStructuralElementDialogExportPdf.vue'; import CoursewareStructuralElementDiscussion from './CoursewareStructuralElementDiscussion.vue'; import CoursewareStructuralElementPermissions from './CoursewareStructuralElementPermissions.vue'; import CoursewareContentPermissions from '../CoursewareContentPermissions.vue'; @@ -678,6 +613,9 @@ export default { CoursewareStructuralElementDialogCopy, CoursewareStructuralElementDialogImport, CoursewareStructuralElementDialogLink, + CoursewareStructuralElementDialogExport, + CoursewareStructuralElementDialogExportChooser, + CoursewareStructuralElementDialogExportPdf, CoursewareStructuralElementDiscussion, CoursewareStructuralElementPermissions, CoursewareContentPermissions, @@ -710,11 +648,6 @@ export default { title: this.$gettext('Informationen zur Seite'), close: this.$gettext('Schließen'), }, - textExport: { - title: this.$gettext('Seite exportieren'), - confirm: this.$gettext('Exportieren'), - close: this.$gettext('Schließen'), - }, textAdd: { title: this.$gettext('Seite hinzufügen'), confirm: this.$gettext('Erstellen'), @@ -728,8 +661,6 @@ export default { title: this.$gettext('Sperre aufheben'), alert: this.$gettext('Möchten Sie die Sperre der Seite wirklich aufheben?'), }, - exportRunning: false, - exportChildren: false, oerExportRunning: false, oerChildren: true, pdfExportChildren: false, @@ -778,6 +709,7 @@ export default { showCopyDialog: 'showStructuralElementCopyDialog', showLinkDialog: 'showStructuralElementLinkDialog', showExportDialog: 'showStructuralElementExportDialog', + showExportChooserDialog: 'showStructuralElementExportChooserDialog', showPdfExportDialog: 'showStructuralElementPdfExportDialog', showInfoDialog: 'showStructuralElementInfoDialog', showDeleteDialog: 'showStructuralElementDeleteDialog', @@ -788,8 +720,6 @@ export default { oerCampusEnabled: 'oerCampusEnabled', oerEnableSuggestions: 'oerEnableSuggestions', licenses: 'licenses', - exportState: 'exportState', - exportProgress: 'exportProgress', userId: 'userId', viewMode: 'viewMode', taskById: 'courseware-tasks/byId', @@ -1321,12 +1251,6 @@ export default { case 'showInfo': this.showElementInfoDialog(true); break; - case 'showExportOptions': - this.showElementExportDialog(true); - break; - case 'oerCurrentElement': - this.showElementOerDialog(true); - break; case 'showSuggest': this.updateShowSuggestOerDialog(true); break; @@ -1467,36 +1391,7 @@ export default { this.processing = false; }, - async exportCurrentElement(data) { - if (this.exportRunning) { - return; - } - - this.exportRunning = true; - await this.sendExportZip(this.currentElement.id, { - withChildren: this.exportChildren, - }); - - this.exportRunning = false; - this.showElementExportDialog(false); - }, - - pdfExportCurrentElement() { - this.showElementPdfExportDialog(false); - let url = ''; - let withChildren = this.pdfExportChildren ? '/1' : '/0'; - if (this.context.type === 'users') { - url = STUDIP.URLHelper.getURL('dispatch.php/contents/courseware/pdf_export/' + this.structuralElement.id + withChildren); - } - if (this.context.type === 'courses') { - url = STUDIP.URLHelper.getURL('dispatch.php/course/courseware/pdf_export/' + this.structuralElement.id + withChildren); - } - - if (url) { - window.open(url , '_blank').focus(); - } - }, async publishCurrentElement() { if (this.oerExportRunning) { diff --git a/resources/vue/components/courseware/structural-element/CoursewareStructuralElementDialogExport.vue b/resources/vue/components/courseware/structural-element/CoursewareStructuralElementDialogExport.vue new file mode 100644 index 0000000000000000000000000000000000000000..914ebf5e8bb1dba2d68863a7c382ad33db3be48c --- /dev/null +++ b/resources/vue/components/courseware/structural-element/CoursewareStructuralElementDialogExport.vue @@ -0,0 +1,94 @@ +<template> + <studip-dialog + :title="$gettext('Seite exportieren')" + :confirmText="this.$gettext('Erstellen')" + confirmClass="accept" + :closeText="this.$gettext('Schließen')" + closeClass="cancel" + height="350" + @close="showElementExportDialog(false)" + @confirm="exportStructuralElement" + > + <template v-slot:dialogContent> + <div v-show="!exportRunning"> + {{ + $gettextInterpolate($gettext('Hiermit exportieren Sie die Seite "%{ pageTitle }" als ZIP-Datei.'), { + pageTitle: structuralElement.attributes.title, + }) + }} + <div class="cw-element-export"> + <label> + <input type="checkbox" v-model="exportChildren"> + {{ $gettext('Unterseiten exportieren') }} + </label> + </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> + </div> + </div> + </template> + </studip-dialog> +</template> +<script> +import CoursewareCompanionBox from '../layouts/CoursewareCompanionBox.vue'; +import CoursewareExport from '@/vue/mixins/courseware/export.js'; +import { mapActions, mapGetters } from 'vuex'; + +export default { + name: 'courseware-structural-element-dialog-export', + mixins: [CoursewareExport], + components: { CoursewareCompanionBox }, + props: { + structuralElement: Object, + }, + data() { + return { + exportRunning: false, + exportChildren: false, + }; + }, + computed: { + ...mapGetters({ + exportState: 'exportState', + exportProgress: 'exportProgress', + }), + }, + methods: { + ...mapActions({ + showElementExportDialog: 'showElementExportDialog', + }), + async exportStructuralElement(data) { + if (this.exportRunning) { + return; + } + + this.exportRunning = true; + + await this.sendExportZip(this.structuralElement.id, { + withChildren: this.exportChildren, + }); + + this.exportRunning = false; + this.showElementExportDialog(false); + }, + }, +}; +</script> diff --git a/resources/vue/components/courseware/structural-element/CoursewareStructuralElementDialogExportChooser.vue b/resources/vue/components/courseware/structural-element/CoursewareStructuralElementDialogExportChooser.vue new file mode 100644 index 0000000000000000000000000000000000000000..be5dd8d52ddbc62b80889f9e3022f45e24921a63 --- /dev/null +++ b/resources/vue/components/courseware/structural-element/CoursewareStructuralElementDialogExportChooser.vue @@ -0,0 +1,119 @@ +<template> + <studip-dialog + :title="$gettext('Seite exportieren')" + :closeText="$gettext('Schließen')" + closeClass="cancel" + height="320" + width="610" + @close="showElementExportChooserDialog(false)" + > + <template v-slot:dialogContent> + <div class="square-button-panel"> + <studip-square-button + v-if="showExportArchiv" + icon="file-archive" + :title="$gettext('ZIP Datei herunterladen')" + @click="selectType('archiv')" + /> + <studip-square-button + v-if="showExportPdf" + icon="file-pdf" + :title="$gettext('PDF Datei herunterladen')" + @click="selectType('pdf')" + /> + <studip-square-button + v-if="showOer" + icon="oer-campus" + :title="$gettext('Auf OER Campus veröffentlichen')" + @click="selectType('oer')" + /> + </div> + <courseware-companion-box + v-if="!showExportArchiv && !showExportPdf && !showOer" + mood="pointing" + :msgCompanion="$gettext('Keine Exportoptionen verfügbar.')" + /> + </template> + </studip-dialog> +</template> + +<script> +import CoursewareCompanionBox from '../layouts/CoursewareCompanionBox.vue'; +import StudipSquareButton from './../../StudipSquareButton.vue'; +import { mapActions, mapGetters } from 'vuex'; + +export default { + name: 'courseware-structural-element-dialog-export-chooser', + props: ['canEdit', 'canVisit'], + components: { + CoursewareCompanionBox, + StudipSquareButton, + }, + computed: { + ...mapGetters({ + context: 'context', + oerCampusEnabled: 'oerCampusEnabled', + userIsTeacher: 'userIsTeacher', + }), + inCourseContext() { + return this.context.type === 'courses'; + }, + showExportArchiv() { + if (this.context.type === 'users') { + return true; + } + + return this.canEdit; + }, + showExportPdf() { + if (this.context.type === 'users') { + return true; + } + + return this.canVisit; + }, + showOer() { + if (!this.oerCampusEnabled) { + return false; + } + + if (this.context.type === 'users') { + return true; + } + + return this.userIsTeacher && this.canVisit; + }, + }, + methods: { + ...mapActions({ + showElementExportDialog: 'showElementExportDialog', + showElementExportChooserDialog: 'showElementExportChooserDialog', + showElementPdfExportDialog: 'showElementPdfExportDialog', + showElementOerDialog: 'showElementOerDialog', + }), + selectType(type) { + switch (type) { + case 'archiv': + this.showElementExportDialog(true); + break; + case 'pdf': + this.showElementPdfExportDialog(true); + break; + case 'oer': + this.showElementOerDialog(true); + break; + } + this.showElementExportChooserDialog(false); + }, + }, +}; +</script> +<style scoped lang="scss"> +.square-button-panel { + display: flex; + flex-direction: row; + flex-wrap: wrap; + width: 100%; + justify-content: center; +} +</style> diff --git a/resources/vue/components/courseware/structural-element/CoursewareStructuralElementDialogExportPdf.vue b/resources/vue/components/courseware/structural-element/CoursewareStructuralElementDialogExportPdf.vue new file mode 100644 index 0000000000000000000000000000000000000000..a2aca9d42f249bdf0ebf1639858e1a749ad115b3 --- /dev/null +++ b/resources/vue/components/courseware/structural-element/CoursewareStructuralElementDialogExportPdf.vue @@ -0,0 +1,71 @@ +<template> + <studip-dialog + :title="$gettext('Seite exportieren')" + :confirmText="this.$gettext('Erstellen')" + confirmClass="accept" + :closeText="this.$gettext('Schließen')" + closeClass="cancel" + height="350" + @close="showElementPdfExportDialog(false)" + @confirm="pdfExportCurrentElement" + > + <template v-slot:dialogContent> + {{ + $gettextInterpolate($gettext('Hiermit exportieren Sie die Seite "%{ pageTitle }" als PDF-Datei.'), { + pageTitle: structuralElement.attributes.title, + }) + }} + <div class="cw-element-export"> + <label> + <input type="checkbox" v-model="pdfExportChildren"> + {{ $gettext('Unterseiten exportieren') }} + </label> + </div> + </template> + </studip-dialog> +</template> + +<script> +import { mapActions, mapGetters } from 'vuex'; + +export default { + name: 'courseware-structural-element-dialog-export', + props: { + structuralElement: Object, + }, + data() { + return { + pdfExportChildren: false, + }; + }, + computed: { + ...mapGetters({ + context: 'context', + }), + }, + methods: { + ...mapActions({ + showElementPdfExportDialog: 'showElementPdfExportDialog', + }), + pdfExportCurrentElement() { + this.showElementPdfExportDialog(false); + let url = ''; + let withChildren = this.pdfExportChildren ? '/1' : '/0'; + if (this.context.type === 'users') { + url = STUDIP.URLHelper.getURL( + 'dispatch.php/contents/courseware/pdf_export/' + this.structuralElement.id + withChildren + ); + } + if (this.context.type === 'courses') { + url = STUDIP.URLHelper.getURL( + 'dispatch.php/course/courseware/pdf_export/' + this.structuralElement.id + withChildren + ); + } + + if (url) { + window.open(url, '_blank').focus(); + } + }, + }, +}; +</script> diff --git a/resources/vue/components/courseware/widgets/CoursewareActionWidget.vue b/resources/vue/components/courseware/widgets/CoursewareActionWidget.vue index 6543594a02ecfb906bac6c1529defce847645507..800d47f7775aff8727c5fcace5a5d5af6d071453 100644 --- a/resources/vue/components/courseware/widgets/CoursewareActionWidget.vue +++ b/resources/vue/components/courseware/widgets/CoursewareActionWidget.vue @@ -7,6 +7,11 @@ {{ $gettext('Seite hinzufügen') }} </button> </li> + <li class="cw-action-widget-export"> + <button @click="exportElement"> + {{ $gettext('Seite exportieren') }} + </button> + </li> </ul> </template> </sidebar-widget> @@ -33,10 +38,14 @@ export default { methods: { ...mapActions({ showElementAddDialog: 'showElementAddDialog', + showElementExportChooserDialog: 'showElementExportChooserDialog', }), addElement() { this.showElementAddDialog(true); }, + exportElement() { + this.showElementExportChooserDialog(true); + } }, }; </script> diff --git a/resources/vue/mixins/courseware/export.js b/resources/vue/mixins/courseware/export.js index bdd0a3b0cddbc699d2d6093fa08b94afc325153c..e5c92ad276aca73b7b0f97c3184a653a804e93ef 100644 --- a/resources/vue/mixins/courseware/export.js +++ b/resources/vue/mixins/courseware/export.js @@ -130,7 +130,7 @@ export default { if (withChildren && elements !== []) { let children = await this.exportStructuralElement(root_id, elements); - if (children.length) { + if (children?.length) { root_element.children = children; } } diff --git a/resources/vue/store/courseware/courseware.module.js b/resources/vue/store/courseware/courseware.module.js index f5bb68b6e78c346e50033b46b861ccb8273dc27e..75ce62580ff525331f774df9cfc35f5ece763b73 100644 --- a/resources/vue/store/courseware/courseware.module.js +++ b/resources/vue/store/courseware/courseware.module.js @@ -31,6 +31,7 @@ const getDefaultState = () => { showStructuralElementCopyDialog: false, showStructuralElementLinkDialog: false, showStructuralElementExportDialog: false, + showStructuralElementExportChooserDialog: false, showStructuralElementPdfExportDialog: false, showStructuralElementInfoDialog: false, showStructuralElementDeleteDialog: false, @@ -186,6 +187,9 @@ const getters = { showStructuralElementExportDialog(state) { return state.showStructuralElementExportDialog; }, + showStructuralElementExportChooserDialog(state) { + return state.showStructuralElementExportChooserDialog; + }, showStructuralElementPdfExportDialog(state) { return state.showStructuralElementPdfExportDialog; }, @@ -907,6 +911,10 @@ export const actions = { context.commit('setShowStructuralElementExportDialog', bool); }, + showElementExportChooserDialog(context, bool) { + context.commit('setShowStructuralElementExportChooserDialog', bool); + }, + showElementPdfExportDialog(context, bool) { context.commit('setShowStructuralElementPdfExportDialog', bool); }, @@ -1525,6 +1533,10 @@ export const mutations = { state.showStructuralElementExportDialog = showExport; }, + setShowStructuralElementExportChooserDialog(state, showExportChooser) { + state.showStructuralElementExportChooserDialog = showExportChooser; + }, + setShowStructuralElementPdfExportDialog(state, showPdfExport) { state.showStructuralElementPdfExportDialog = showPdfExport; },