Skip to content
Snippets Groups Projects
Commit 55cb643f authored by Ron Lucke's avatar Ron Lucke
Browse files

Courseware - Vorlagen beim hinzufügen eine Seite anbieten

Closes #2021

Merge request studip/studip!1467
parent cdebb80f
No related branches found
No related tags found
No related merge requests found
......@@ -367,62 +367,12 @@
</courseware-tabs>
</template>
</studip-dialog>
<studip-dialog
<courseware-structural-element-dialog-add
v-if="showAddDialog"
:title="$gettext('Seite hinzufügen')"
:confirmText="$gettext('Erstellen')"
confirmClass="accept"
:closeText="$gettext('Schließen')"
closeClass="cancel"
class="cw-structural-element-dialog"
:height="inCourse ? '300' : '430'"
@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 && canEditParent" 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>
<label v-if="!inCourse">
<translate>Art des Lernmaterials</translate>
<select v-model="newChapterPurpose">
<option value="content"><translate>Inhalt</translate></option>
<option v-if="!inCourse" value="template"><translate>Aufgabenvorlage</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 v-if="!inCourse">
<translate>Lernmaterialvorlage</translate>
<select v-model="newChapterTemplate">
<option :value="null"><translate>ohne Vorlage</translate></option>
<option
v-for="template in selectableTemplates"
:key="template.id"
:value="template"
>
{{ template.attributes.name }}
</option>
</select>
</label>
</form>
</template>
</studip-dialog>
:structuralElement="structuralElement"
:isRoot="isRoot"
:canEditParent="canEditParent"
/>
<studip-dialog
v-if="showInfoDialog"
:title="textInfo.title"
......@@ -682,6 +632,7 @@
<script>
import ContainerComponents from './container-components.js';
import CoursewarePluginComponents from './plugin-components.js';
import CoursewareStructuralElementDialogAdd from './CoursewareStructuralElementDialogAdd.vue';
import CoursewareStructuralElementDialogCopy from './CoursewareStructuralElementDialogCopy.vue';
import CoursewareStructuralElementDialogImport from './CoursewareStructuralElementDialogImport.vue';
import CoursewareStructuralElementDialogLink from './CoursewareStructuralElementDialogLink.vue';
......@@ -700,6 +651,7 @@ import CoursewareTab from './CoursewareTab.vue';
import CoursewareExport from '@/vue/mixins/courseware/export.js';
import CoursewareOerMessage from '@/vue/mixins/courseware/oermessage.js';
import colorMixin from '@/vue/mixins/courseware/colors.js';
import wizardMixin from '@/vue/mixins/courseware/wizard.js';
import CoursewareDateInput from './CoursewareDateInput.vue';
import { FocusTrap } from 'focus-trap-vue';
import IsoDate from './IsoDate.vue';
......@@ -710,6 +662,7 @@ import { mapActions, mapGetters } from 'vuex';
export default {
name: 'courseware-structural-element',
components: {
CoursewareStructuralElementDialogAdd,
CoursewareStructuralElementDialogCopy,
CoursewareStructuralElementDialogImport,
CoursewareStructuralElementDialogLink,
......@@ -733,14 +686,10 @@ export default {
},
props: ['canVisit', 'orderedStructuralElements', 'structuralElement'],
mixins: [CoursewareExport, CoursewareOerMessage, colorMixin],
mixins: [CoursewareExport, CoursewareOerMessage, colorMixin, wizardMixin],
data() {
return {
newChapterName: '',
newChapterParent: 'descendant',
newChapterPurpose: 'content',
newChapterTemplate: null,
currentElement: '',
uploadFileError: '',
textCompanionWrongContext: this.$gettext('Die angeforderte Seite ist nicht Teil dieser Courseware.'),
......@@ -1231,11 +1180,6 @@ export default {
ownerName() {
return this.owner?.attributes['formatted-name'] ?? '?';
},
selectableTemplates() {
return this.templates.filter(template => {
return template.attributes.purpose === this.newChapterPurpose
});
},
complete() {
return this.elementProgress === 100;
},
......@@ -1253,7 +1197,6 @@ export default {
methods: {
...mapActions({
createStructuralElementWithTemplate: 'createStructuralElementWithTemplate',
updateStructuralElement: 'updateStructuralElement',
deleteStructuralElement: 'deleteStructuralElement',
lockObject: 'lockObject',
......@@ -1316,8 +1259,6 @@ export default {
this.showElementEditDialog(true);
break;
case 'addElement':
this.newChapterName = '';
this.newChapterParent = 'descendant';
this.errorEmptyChapterName = false;
this.showElementAddDialog(true);
break;
......@@ -1372,14 +1313,7 @@ export default {
this.showElementAddDialog(false);
},
checkUploadFile() {
const file = this.$refs?.upload_image?.files[0];
if (file.size > 2097152) {
this.uploadFileError = this.$gettext('Diese Datei ist zu groß. Bitte wählen Sie eine kleinere Datei.');
} else if (!file.type.includes('image')) {
this.uploadFileError = this.$gettext('Diese Datei ist kein Bild. Bitte wählen Sie ein Bild aus.');
} else {
this.uploadFileError = '';
}
this.uploadFileError = this.checkUploadImageFile(this.$refs?.upload_image?.files[0]);
},
deleteImage() {
if (!this.deletingPreviewImage) {
......@@ -1552,57 +1486,6 @@ export default {
this.companionError({ info: this.$gettext('Die Seite konnte nicht gelöscht werden.') });
});
},
async createElement() {
const title = this.newChapterName; // this is the title of the new element
const purpose = this.newChapterPurpose;
let parent_id = this.currentId; // new page is descandant as default
this.errorEmptyChapterName = title.trim();
if (this.errorEmptyChapterName === '') {
return;
}
if (this.newChapterParent === 'sibling') {
parent_id = this.structuralElement.relationships.parent.data.id;
}
this.showElementAddDialog(false);
this.createStructuralElementWithTemplate({
attributes: {
title: title,
purpose: purpose,
},
templateId: this.newChapterTemplate ? this.newChapterTemplate.id : null,
parentId: parent_id,
currentId: this.currentId,
})
.then(() => {
let newElement = this.$store.getters['courseware-structural-elements/lastCreated'];
this.companionSuccess({
info:
this.$gettextInterpolate(
this.$gettext('Die Seite %{ pageTitle } wurde erfolgreich angelegt.'),
{ pageTitle: newElement.attributes.title }
)
});
this.$router.push(newElement.id);
})
.catch(e => {
let errorMessage = this.$gettext('Es ist ein Fehler aufgetreten. Die Seite konnte nicht erstellt werden.');
if (e.status === 403) {
errorMessage = this.$gettext('Die Seite konnte nicht erstellt werden. Sie haben nicht die notwendigen Schreibrechte.');
}
this.companionError({ info: errorMessage });
});
let newElement = this.lastCreatedElement;
this.companionSuccess({
info: this.$gettextInterpolate(
this.$gettext('Die Seite %{ pageTitle } wurde erfolgreich angelegt.'),
{pageTitle: newElement.attributes.title}
)
});
this.newChapterName = '';
},
containerComponent(container) {
return 'courseware-' + container.attributes['container-type'] + '-container';
},
......
<template>
<studip-wizard-dialog
:title="$gettext('Seite hinzufügen')"
:confirmText="$gettext('Erstellen')"
:closeText="$gettext('Abbrechen')"
:slots="wizardSlots"
:lastRequiredSlotId="1"
:requirements="requirements"
@close="closeAddDialog"
@confirm="createElement"
>
<template v-slot:basic>
<form class="default" @submit.prevent="">
<label>
{{ $gettext('Position der neuen Seite') }}
<select v-model="pageParent">
<option v-if="!isRoot && canEditParent" value="sibling">
{{ $gettext('Neben der aktuellen Seite') }}
</option>
<option value="descendant">{{ $gettext('Unterhalb der aktuellen Seite') }}</option>
</select>
</label>
<label>
<span>{{ text.title }}</span>
<span aria-hidden="true" class="wizard-required">*</span>
<input type="text" v-model="title" required />
</label>
<label>
<span>{{ $gettext('Beschreibung') }}</span>
<textarea v-model="description" required />
</label>
</form>
</template>
<template v-slot:template>
<form v-if="hasTemplates" class="default" @submit.prevent="">
<label>
{{ $gettext('Art der Vorlage') }}
<select v-model="templatePurpose">
<option value="content">{{ $gettext('Inhalt') }}</option>
<option value="oer">{{ $gettext('OER-Material') }}</option>
<option value="portfolio">{{ $gettext('ePortfolio') }}</option>
<option value="draft">{{ $gettext('Entwurf') }}</option>
<option v-if="!inCourseContext" value="template">{{ $gettext('Aufgabenvorlage') }}</option>
<option value="other">{{ $gettext('Sonstiges') }}</option>
</select>
</label>
<label>
<span>{{ $gettext('Vorlage') }}</span>
<select v-model="selectedTemplate">
<option :value="null">{{ $gettext('ohne Vorlage') }}</option>
<option v-for="template in selectableTemplates" :key="template.id" :value="template">
{{ template.attributes.name }}
</option>
</select>
</label>
</form>
<courseware-companion-box
v-else
:msgCompanion="$gettext('Es wurden keine Vorlagen gefunden.')"
mood="pointing"
class="cw-companion-box-in-form"
/>
</template>
<template v-slot:layout>
<form class="default" @submit.prevent="">
<label>
{{ $gettext('Bild') }}
<input
class="cw-file-input"
ref="upload_image"
type="file"
accept="image/*"
@change="checkUploadFile"
/>
<courseware-companion-box
v-if="uploadFileError"
:msgCompanion="uploadFileError"
mood="sad"
class="cw-companion-box-in-form"
/>
</label>
<label>
{{ $gettext('Farbe') }}
<studip-select v-model="color" :options="colors" :reduce="(color) => color.class" label="class">
<template #open-indicator="selectAttributes">
<span v-bind="selectAttributes"><studip-icon shape="arr_1down" size="10" /></span>
</template>
<template #no-options>
{{ $gettext('Es steht keine Auswahl zur Verfügung.') }}
</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>
</studip-select>
</label>
</form>
</template>
<template v-slot:advanced>
<form class="default" @submit.prevent="">
<label>
{{ $gettext('Art des Lernmaterials') }}
<select v-model="purpose">
<option value="content">{{ $gettext('Inhalt') }}</option>
<option value="oer">{{ $gettext('OER-Material') }}</option>
<option value="portfolio">{{ $gettext('ePortfolio') }}</option>
<option value="draft">{{ $gettext('Entwurf') }}</option>
<option v-if="!inCourseContext" value="template">{{ $gettext('Aufgabenvorlage') }}</option>
<option value="other">{{ $gettext('Sonstiges') }}</option>
</select>
</label>
<label>
{{ $gettext('Lizenztyp') }}
<select v-model="license_type">
<option v-for="license in licenses" :key="license.id" :value="license.id">
{{ license.name }}
</option>
</select>
</label>
<label>
{{ $gettext('Geschätzter zeitlicher Aufwand') }}
<input type="text" v-model="required_time" />
</label>
<label>
{{ $gettext('Niveau') }}<br />
{{ $gettext('von') }}
<select v-model="difficulty_start">
<option v-for="difficulty_start in 12" :key="difficulty_start" :value="difficulty_start">
{{ difficulty_start }}
</option>
</select>
{{ $gettext('bis') }}
<select v-model="difficulty_end">
<option v-for="difficulty_end in 12" :key="difficulty_end" :value="difficulty_end">
{{ difficulty_end }}
</option>
</select>
</label>
</form>
</template>
</studip-wizard-dialog>
</template>
<script>
import CoursewareCompanionBox from './CoursewareCompanionBox.vue';
import StudipSelect from './../StudipSelect.vue';
import StudipWizardDialog from './../StudipWizardDialog.vue';
import colorMixin from '@/vue/mixins/courseware/colors.js';
import wizardMixin from '@/vue/mixins/courseware/wizard.js';
import { mapActions, mapGetters } from 'vuex';
export default {
name: 'courseware-structural-element-dialog-add',
mixins: [colorMixin, wizardMixin],
components: {
CoursewareCompanionBox,
StudipWizardDialog,
StudipSelect,
},
props: {
structuralElement: Object,
isRoot: Boolean,
canEditParent: Boolean,
},
data() {
return {
wizardSlots: [
{
id: 1,
valid: false,
name: 'basic',
title: this.$gettext('Grundeinstellungen'),
icon: 'courseware',
description: this.$gettext(
'Wählen Sie einen kurzen, prägnanten Titel und beschreiben Sie in einigen Worten den Inhalt der Seite.'
),
},
{
id: 2,
valid: true,
name: 'template',
title: this.$gettext('Vorlage'),
icon: 'content2',
description: this.$gettext('Vorlagen enthalten Abschnitte und Blöcke, die bereits für bestimmte Zwecke angeordent sind. Beim anlegen der Seite, wird diese mit Abschnitten und Blöcken befüllt.'),
},
{
id: 3,
valid: true,
name: 'layout',
title: this.$gettext('Erscheinung'),
icon: 'picture',
description: this.$gettext(
'Ein Vorschaubild motiviert Lernende die Seite zu erkunden. Die Kombination aus Bild und Farbe erleichtert das wiederfinden der Seite in einem Inhaltsverzeichnisblock.'
),
},
{
id: 4,
valid: true,
name: 'advanced',
title: this.$gettext('Zusatzangaben'),
icon: 'info-list',
description: this.$gettext(
'Hier können Sie detaillierte Angaben zur Seite eintragen. Diese sind besonders interessant wenn die Seite als OER geteilt wird.'
),
},
],
text: {
title: this.$gettext('Titel der neuen Seite'),
},
uploadFileError: '',
requirements: [],
pageParent: '',
title: '',
description: '',
purpose: '',
color: '',
license_type: '',
required_time: '',
difficulty_start: '',
difficulty_end: '',
templatePurpose: '',
selectedTemplate: null,
};
},
computed: {
...mapGetters({
licenses: 'licenses',
context: 'context',
lastCreatedStructuralElement: 'courseware-structural-elements/lastCreated',
structuralElementById: 'courseware-structural-elements/byId',
templates: 'courseware-templates/all',
}),
inCourseContext() {
return this.context.type === 'courses';
},
colors() {
return this.mixinColors.filter((color) => color.darkmode);
},
selectableTemplates() {
return this.templates.filter((template) => {
return template.attributes.purpose === this.templatePurpose;
});
},
hasTemplates() {
return this.templates.length > 0;
},
},
methods: {
...mapActions({
createStructuralElementWithTemplate: 'createStructuralElementWithTemplate',
updateStructuralElement: 'updateStructuralElement',
companionError: 'companionError',
companionInfo: 'companionInfo',
companionSuccess: 'companionSuccess',
showElementAddDialog: 'showElementAddDialog',
loadStructuralElementById: 'courseware-structural-elements/loadById',
uploadImageForStructuralElement: 'uploadImageForStructuralElement',
}),
initWizardData() {
this.pageParent = 'descendant';
this.title = '';
this.description = '';
this.purpose = 'content';
this.color = 'studip-blue';
this.license_type = '';
this.required_time = '';
this.difficulty_start = '';
this.difficulty_end = '';
this.templatePurpose = 'content';
this.selectedTemplate = null;
this.requirements.push({ slot: this.wizardSlots[0], text: this.text.title });
},
closeAddDialog() {
this.showElementAddDialog(false);
this.initWizardData();
},
checkUploadFile() {
this.uploadFileError = this.checkUploadImageFile(this.$refs?.upload_image?.files[0]);
},
async createElement() {
let parent_id = this.structuralElement.id; // new page is descandant as default
this.errorEmptyChapterName = this.title.trim();
if (this.errorEmptyChapterName === '') {
return;
}
if (this.pageParent === 'sibling') {
parent_id = this.structuralElement.relationships.parent.data.id;
}
this.showElementAddDialog(false);
const file = this.$refs?.upload_image?.files[0];
const element = {
attributes: {
title: this.title,
purpose: this.purpose,
payload: {
description: this.description,
color: this.color,
license_type: this.license_type,
required_time: this.required_time,
difficulty_start: this.difficulty_start,
difficulty_end: this.difficulty_end,
},
},
templateId: this.selectedTemplate ? this.selectedTemplate.id : null,
parentId: parent_id,
currentId: this.structuralElement.id,
};
try {
await this.createStructuralElementWithTemplate(element);
} catch (e) {
let errorMessage = this.$gettext(
'Es ist ein Fehler aufgetreten. Die Seite konnte nicht erstellt werden.'
);
if (e.status === 403) {
errorMessage = this.$gettext(
'Die Seite konnte nicht erstellt werden. Sie haben nicht die notwendigen Schreibrechte.'
);
}
this.companionError({ info: errorMessage });
return;
}
const newCreated = this.lastCreatedStructuralElement;
await this.loadStructuralElementById({ id: newCreated.id });
const newElement = this.structuralElementById({ id: newCreated.id });
this.companionSuccess({
info: this.$gettextInterpolate(this.$gettext('Die Seite %{ pageTitle } wurde erfolgreich angelegt.'), {
pageTitle: newElement.attributes.title,
}),
});
if (file && this.uploadFileError === '') {
try {
await this.uploadImageForStructuralElement({ structuralElement: newElement, file });
} catch (error) {
console.error(error);
this.companionError({
info: this.$gettext('Das Bild für das neue Lernmaterial konnte nicht gespeichert werden.'),
});
}
this.loadStructuralElementById({ id: newElement.id, options: { include: 'children' } });
}
this.initWizardData();
this.$router.push(newElement.id);
},
},
mounted() {
this.initWizardData();
if (!this.hasTemplates) {
this.wizardSlots.splice(1,1);
this.wizardSlots[1]['id'] = 2;
this.wizardSlots[2]['id'] = 3;
}
},
watch: {
title(newTitle) {
this.requirements = [];
const slot = this.wizardSlots[0];
if (newTitle === '') {
slot.valid = false;
this.requirements.push({ slot: slot, text: this.text.title });
} else {
slot.valid = true;
}
},
templatePurpose(newPurpose) {
this.selectedTemplate = null;
},
},
};
</script>
const wizardMixin = {
methods: {
checkUploadImageFile(file) {
let uploadFileError = '';
if (file.size > 2097152) {
uploadFileError = this.$gettext('Diese Datei ist zu groß. Bitte wählen Sie eine Datei aus, die kleiner als 2MB ist.');
}
if (!file.type.includes('image')) {
uploadFileError = this.$gettext('Diese Datei ist kein Bild. Bitte wählen Sie ein Bild aus.');
}
return uploadFileError;
},
}
};
export default wizardMixin;
\ No newline at end of file
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment