Skip to content
Snippets Groups Projects
CoursewareStructuralElement.vue 79.8 KiB
Newer Older
Ron Lucke's avatar
Ron Lucke committed
    <focus-trap v-model="consumModeTrap">
        <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 && canAddElements" :isContentBar="true" @blockAdded="updateContainerList">
Ron Lucke's avatar
Ron Lucke committed
                        <template #buttons>
                            <router-link v-if="prevElement" :to="'/structural_element/' + prevElement.id">
                                <div class="cw-ribbon-button cw-ribbon-button-prev" :title="textRibbon.perv" />
                            </router-link>
                            <div v-else class="cw-ribbon-button cw-ribbon-button-prev-disabled" :title="$gettext('Keine vorherige Seite')"/>
Ron Lucke's avatar
Ron Lucke committed
                            <router-link v-if="nextElement" :to="'/structural_element/' + nextElement.id">
                                <div class="cw-ribbon-button cw-ribbon-button-next" :title="textRibbon.next" />
                            </router-link>
                            <div v-else class="cw-ribbon-button cw-ribbon-button-next-disabled" :title="$gettext('Keine nächste Seite')"/>
Ron Lucke's avatar
Ron Lucke committed
                        </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>
Ron Lucke's avatar
Ron Lucke committed
                                </span>
                            </li>
                            <li
                                class="cw-ribbon-breadcrumb-item cw-ribbon-breadcrumb-item-current"
                                :title="structuralElement.attributes.title"
                            >
                                <span>{{ structuralElement.attributes.title || "" }}</span>
Ron Lucke's avatar
Ron Lucke committed
                                <span v-if="isTask">[ {{ solverName }} ]</span>
Ron Lucke's avatar
Ron Lucke committed
                            </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"
Moritz Strohm's avatar
Moritz Strohm committed
                                :context="structuralElement.attributes.title"
Ron Lucke's avatar
Ron Lucke committed
                                @editCurrentElement="menuAction('editCurrentElement')"
                                @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')"
Ron Lucke's avatar
Ron Lucke committed
                                @removeLock="menuAction('removeLock')"
Ron Lucke's avatar
Ron Lucke committed
                            />
                        </template>
                    </courseware-ribbon>
Ron Lucke's avatar
Ron Lucke committed
                    
                    <div v-if="structuralElementLoaded && !isLink" class="cw-companion-box-wrapper">
                        <courseware-companion-box
                            v-if="!canVisit"
                            mood="sad"
                            :msgCompanion="$gettext('Diese Seite steht Ihnen leider nicht zur Verfügung.')"
                        />
                        <courseware-companion-box
                            v-if="blockedByAnotherUser"
                            :msgCompanion="$gettextInterpolate($gettext('Die Einstellungen dieser Seite werden im Moment von %{blockingUserName} bearbeitet'), {blockingUserName: blockingUserName})"
                            mood="pointing"
                        >
                            <template #companionActions>
                                <button v-if="userIsTeacher" class="button" @click="menuAction('removeLock')">
                                    {{ textRemoveLock.title }}
                                </button>
                            </template>
                        </courseware-companion-box>
                        <courseware-empty-element-box
                            v-if="showEmptyElementBox"
                            :canEdit="canEdit"
                            :noContainers="noContainers"
                        />
                        <courseware-welcome-screen v-if="noContainers && isRoot && canEdit" />
Ron Lucke's avatar
Ron Lucke committed
                    </div>
Ron Lucke's avatar
Ron Lucke committed

                    <div
                        v-if="canVisit && !editView && !isLink"
Ron Lucke's avatar
Ron Lucke committed
                        class="cw-container-wrapper"
                        :class="{
                            'cw-container-wrapper-consume': consumeMode,
                            'cw-container-wrapper-discuss': discussView,
                        }"
                    >
                        <courseware-structural-element-discussion
                            v-if="!noContainers && discussView"
                            :structuralElement="structuralElement"
                            :canEdit="canEdit"
Ron Lucke's avatar
Ron Lucke committed
                        <component
                            v-for="container in containers"
                            :key="container.id"
                            :is="containerComponent(container)"
                            :container="container"
Ron Lucke's avatar
Ron Lucke committed
                            :canAddElements="canAddElements"
                            :isTeacher="userIsTeacher"
                            class="cw-container-item"
                    <div
                        v-if="isLink"
                        class="container-wrapper"
                        :class="{
                            'cw-container-wrapper-consume': consumeMode,
                            'cw-container-wrapper-discuss': discussView,
                        }"
                    >
                        <courseware-structural-element-discussion
                            v-if="discussView"
                            :structuralElement="structuralElement"
                            :canEdit="canEdit"
                        />
                        <courseware-companion-box
                            v-if="editView"
                            :msgCompanion="$gettextInterpolate($gettext('Dieser Inhalt ist aus den persönlichen Lerninhalten von %{ ownerName } verlinkt und kann nur dort bearbeitet werden.'), { ownerName: ownerName })"
                            mood="pointing"
                        />
                        <component
                            v-for="container in linkedContainers"
                            :key="container.id"
                            :is="containerComponent(container)"
                            :container="container"
                            :canEdit="false"
                            :canAddElements="false"
                            :isTeacher="userIsTeacher"
                            class="cw-container-item"
                        />
                    </div>
                    <div v-if="canVisit && canEdit && editView" class="cw-container-wrapper cw-container-wrapper-edit">
                        <template v-if="!processing">
                            <span aria-live="assertive" class="assistive-text">{{ assistiveLive }}</span>
                            <span id="operation" class="assistive-text">
                                {{$gettext('Drücken Sie die Leertaste, um neu anzuordnen.')}}
                            </span>
                            <draggable
                                class="cw-structural-element-list"
                                tag="ol"
                                role="listbox"
                                v-model="containerList"
                                v-bind="dragOptions"
                                handle=".cw-sortable-handle"
                                @start="isDragging = true"
                                @end="dropContainer"
                            >
Ron Lucke's avatar
Ron Lucke committed
                                <li
                                    v-for="container in containerList"
                                    :key="container.id"
                                    class="cw-container-item-sortable"
                                >
                                    <span
                                        :class="{ 'cw-sortable-handle-dragging': isDragging }"
                                        class="cw-sortable-handle"
                                        tabindex="0"
                                        role="option"
                                        aria-describedby="operation"
                                        :ref="'sortableHandle' + container.id"
                                        @keydown="keyHandler($event, container.id)"
                                    ></span>
                                    <component
                                        :is="containerComponent(container)"
                                        :container="container"
                                        :canEdit="canEdit"
                                        :canAddElements="canAddElements"
                                        :isTeacher="userIsTeacher"
                                        class="cw-container-item"
                                        ref="containers"
                                        :class="{ 'cw-container-item-selected': keyboardSelected === container.id}"
                                    />
Ron Lucke's avatar
Ron Lucke committed
                                </li>
                            </draggable>
                        </template>
                        <studip-progress-indicator v-if="processing" :description="$gettext('Vorgang wird bearbeitet...')" />
Ron Lucke's avatar
Ron Lucke committed
                    </div>
Ron Lucke's avatar
Ron Lucke committed
                <studip-dialog
                    v-if="showEditDialog"
                    :title="textEdit.title"
                    :confirmText="textEdit.confirm"
                    confirmClass="accept"
                    :closeText="textEdit.close"
Ron Lucke's avatar
Ron Lucke committed
                    closeClass="cancel"
                    height="500"
Ron Lucke's avatar
Ron Lucke committed
                    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" :index="0">
                                <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" :index="1">
                                <form class="default" @submit.prevent="">
                                    <label>
                                        <translate>Farbe</translate>
Ron Lucke's avatar
Ron Lucke committed
                                        <studip-select
Ron Lucke's avatar
Ron Lucke committed
                                            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>
Ron Lucke's avatar
Ron Lucke committed
                                                <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>
Ron Lucke's avatar
Ron Lucke committed
                                        </studip-select>
Ron Lucke's avatar
Ron Lucke committed
                                    </label>
                                    <label>
Ron Lucke's avatar
Ron Lucke committed
                                        <translate>Art des Lernmaterials</translate>
Ron Lucke's avatar
Ron Lucke committed
                                        <select v-model="currentElement.attributes.purpose">
                                            <option value="content"><translate>Inhalt</translate></option>
Ron Lucke's avatar
Ron Lucke committed
                                            <option v-if="!inCourse"  value="template"><translate>Aufgabenvorlage</translate></option>
Ron Lucke's avatar
Ron Lucke committed
                                            <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" :index="2">
                                <form class="default" @submit.prevent="">
                                    <img
                                        v-if="showPreviewImage"
Ron Lucke's avatar
Ron Lucke committed
                                        :src="image"
                                        class="cw-structural-element-image-preview"
                                        :alt="$gettext('Vorschaubild')"
                                    <label v-if="showPreviewImage">
Ron Lucke's avatar
Ron Lucke committed
                                        <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="!showPreviewImage">
Ron Lucke's avatar
Ron Lucke committed
                                        <translate>Bild hochladen</translate>
                                        <input ref="upload_image" type="file" accept="image/*" @change="checkUploadFile" />
                                    </label>
                                </form>
                            </courseware-tab>
                            <courseware-tab :name="textEdit.approval" :index="3">
                                <courseware-structural-element-permissions
                                    v-if="inCourse"
                                    :element="currentElement"
                                    @updateReadApproval="updateReadApproval"
                                    @updateWriteApproval="updateWriteApproval"
                                />
                                <courseware-content-permissions
                                    v-if="inContent"
                                    :element="currentElement"
                                    @updateReadApproval="updateReadApproval"
                                    @updateWriteApproval="updateWriteApproval"
                                />
Ron Lucke's avatar
Ron Lucke committed
                            </courseware-tab>
                            <courseware-tab v-if="inCourse" :name="textEdit.visible" :index="4">
                                <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="$gettext('Erstellen')"
                    confirmClass="accept"
                    :closeText="$gettext('Schließen')"
                    closeClass="cancel"
                    class="cw-structural-element-dialog"
                    :height="inCourse ? '300' : '430'"
Ron Lucke's avatar
Ron Lucke committed
                    @close="closeAddDialog"
                    @confirm="createElement"
                >
                    <template v-slot:dialogContent>
                        <form class="default" @submit.prevent="">
                            <label>
                                <translate>Position der neuen Seite</translate>
                                <select v-model="newChapterParent">
Ron Lucke's avatar
Ron Lucke committed
                                    <option v-if="!isRoot && canEditParent" value="sibling">
Ron Lucke's avatar
Ron Lucke committed
                                        <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>
Ron Lucke's avatar
Ron Lucke committed
                        </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>
Ron Lucke's avatar
Ron Lucke committed
                            </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>{{ editorName }}</td>
Ron Lucke's avatar
Ron Lucke committed
                            </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">
Ron Lucke's avatar
Ron Lucke committed
                            <span v-translate>Hiermit exportieren Sie die Seite "%{ currentElement.attributes.title }" als ZIP-Datei.</span>
Ron Lucke's avatar
Ron Lucke committed
                            <div class="cw-element-export">
Ron Lucke's avatar
Ron Lucke committed
                                    <input type="checkbox" v-model="exportChildren" />
                                    <translate>Unterseiten exportieren</translate>
Ron Lucke's avatar
Ron Lucke committed
                            </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>

Ron Lucke's avatar
Ron Lucke committed
                <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>
Ron Lucke's avatar
Ron Lucke committed
                        <span v-translate>Hiermit exportieren Sie die Seite "%{ currentElement.attributes.title }" als PDF-Datei.</span>
Ron Lucke's avatar
Ron Lucke committed
                            <div class="cw-element-export">
                                <label>
                                    <input type="checkbox" v-model="pdfExportChildren" />
                                    <translate>Unterseiten exportieren</translate>
                                </label>
                            </div>
                    </template>
                </studip-dialog>

Ron Lucke's avatar
Ron Lucke committed
                <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>
Ron Lucke's avatar
Ron Lucke committed
                        <form v-show="!oerExportRunning" class="default" @submit.prevent="">
Ron Lucke's avatar
Ron Lucke committed
                            <fieldset>
                                <legend><translate>Grunddaten</translate></legend>
Ron Lucke's avatar
Ron Lucke committed
                                    <p><translate>Vorschaubild</translate>:</p>
                                    <img
                                        v-if="currentElement.relationships.image.data"
                                        :src="currentElement.relationships.image.meta['download-url']"
                                        width="400"
                                    />
Ron Lucke's avatar
Ron Lucke committed
                                    <p><translate>Beschreibung</translate>:</p>
                                    <p>{{ currentElement.attributes.payload.description }}</p>
Ron Lucke's avatar
Ron Lucke committed
                                    <translate>Niveau</translate>:
                                    <p>
                                        {{ currentElement.attributes.payload.difficulty_start }} -
                                        {{ currentElement.attributes.payload.difficulty_end }}
                                    </p>
Ron Lucke's avatar
Ron Lucke committed
                                    <translate>Lizenztyp</translate>:
                                    <p>{{ currentLicenseName }}</p>
Ron Lucke's avatar
Ron Lucke committed
                                    <translate>Sie können diese Daten unter "Seite bearbeiten" verändern.</translate>
Ron Lucke's avatar
Ron Lucke committed
                            </fieldset>
                            <fieldset>
                                <legend><translate>Einstellungen</translate></legend>
Ron Lucke's avatar
Ron Lucke committed
                                    <translate>Unterseiten veröffentlichen</translate>
                                    <input type="checkbox" v-model="oerChildren" />
Ron Lucke's avatar
Ron Lucke committed
                            </fieldset>
                        </form>
Ron Lucke's avatar
Ron Lucke committed
                        <courseware-companion-box
                            v-show="oerExportRunning"
                            :msgCompanion="$gettext('Export läuft, bitte haben sie einen Moment Geduld...')"
                            mood="pointing"
                        />
Ron Lucke's avatar
Ron Lucke committed
                    </template>
                </studip-dialog>
                <studip-dialog
                    v-if="showSuggestOerDialog"
                    height="600"
                    width="600"
                    :title="textSuggestOer.title"
                    :confirmText="textSuggestOer.confirm"
                    confirmClass="accept"
                    :closeText="textSuggestOer.close"
                    closeClass="cancel"
                    @close="updateShowSuggestOerDialog(false)"
                    @confirm="sendOerSuggestion"
                >
                    <template v-slot:dialogContent>
Michaela Brückner's avatar
Michaela Brückner committed
                        <p><translate>Das folgende Courseware-Material wird %{ ownerName }
                            zur Veröffentlichung im OER Campus vorgeschlagen:</translate></p>
                        <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>
                        </table>
                        <form class="default" @submit.prevent="">
                            <label>
                                <translate>Ihr Vorschlag wird anonym versendet. Falls gewünscht, können Sie
                                    zusätzlich eine Nachricht verfassen:</translate>
                                <textarea
                                    v-model="additionalText"
                                    class="cw-structural-element-description"
                                />
                            </label>
                        </form>
                    </template>
                </studip-dialog>
Ron Lucke's avatar
Ron Lucke committed
                <studip-dialog
                    v-if="showDeleteDialog"
                    :title="textDelete.title"
                    :question="textDelete.alert"
Ron Lucke's avatar
Ron Lucke committed
                    @confirm="deleteCurrentElement"
                    @close="closeDeleteDialog"
                ></studip-dialog>
                    v-if="showPublicLinkDialog && inContent"
                    :title="$gettext('Öffentlichen Link für Seite erzeugen')"
                    :confirmText="$gettext('Erstellen')"
                    confirmClass="accept"
                    :closeText="$gettext('Abbrechen')"
                    closeClass="cancel"
                    class="cw-structural-element-dialog"
                    @close="closePublicLinkDialog"
                    @confirm="createElementPublicLink"
                >
                    <template v-slot:dialogContent>
                        <form class="default" @submit.prevent="">
                            <label>
                                <translate>Passwort</translate>
                                <input type="password" v-model="publicLink.password" />
                            </label>
                            <label>
                                <translate>Ablaufdatum</translate>
                                <input v-model="publicLink['expire-date']" type="date" class="size-l" />
                            </label>
                        </form>
                    </template>
                </studip-dialog>
Ron Lucke's avatar
Ron Lucke committed
                <studip-dialog
                    v-if="showRemoveLockDialog"
                    :title="textRemoveLock.title"
                    :question="textRemoveLock.alert"
                    height="200"
                    width="450"
                    @confirm="executeRemoveLock"
                    @close="showElementRemoveLockDialog(false)"
                ></studip-dialog>

                <courseware-structural-element-dialog-import v-if="showImportDialog"/>
                <courseware-structural-element-dialog-copy v-if="showCopyDialog" />
                <courseware-structural-element-dialog-link v-if="showLinkDialog"/>
Ron Lucke's avatar
Ron Lucke committed
            </div>
            <div v-else>
                <courseware-companion-box
                    v-if="currentElement !== ''"
                    :msgCompanion="textCompanionWrongContext"
                    mood="sad"
                />
            </div>
Ron Lucke's avatar
Ron Lucke committed
    </focus-trap>
</template>

<script>
import ContainerComponents from './container-components.js';
import CoursewarePluginComponents from './plugin-components.js';
import CoursewareStructuralElementDialogCopy from './CoursewareStructuralElementDialogCopy.vue';
import CoursewareStructuralElementDialogImport from './CoursewareStructuralElementDialogImport.vue';
import CoursewareStructuralElementDialogLink from './CoursewareStructuralElementDialogLink.vue';
import CoursewareStructuralElementDiscussion from './CoursewareStructuralElementDiscussion.vue';
import CoursewareStructuralElementPermissions from './CoursewareStructuralElementPermissions.vue';
import CoursewareContentPermissions from './CoursewareContentPermissions.vue';
import CoursewareAccordionContainer from './CoursewareAccordionContainer.vue';
import CoursewareCompanionBox from './CoursewareCompanionBox.vue';
import CoursewareWelcomeScreen from './CoursewareWelcomeScreen.vue';
import CoursewareEmptyElementBox from './CoursewareEmptyElementBox.vue';
import CoursewareListContainer from './CoursewareListContainer.vue';
import CoursewareTabsContainer from './CoursewareTabsContainer.vue';
import CoursewareRibbon from './CoursewareRibbon.vue';
import CoursewareTabs from './CoursewareTabs.vue';
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 CoursewareDateInput from './CoursewareDateInput.vue';
Ron Lucke's avatar
Ron Lucke committed
import { FocusTrap } from 'focus-trap-vue';
import IsoDate from './IsoDate.vue';
import StudipDialog from '../StudipDialog.vue';
Ron Lucke's avatar
Ron Lucke committed
import draggable from 'vuedraggable';
import { mapActions, mapGetters } from 'vuex';

export default {
    name: 'courseware-structural-element',
    components: {
        CoursewareStructuralElementDialogCopy,
        CoursewareStructuralElementDialogImport,
        CoursewareStructuralElementDialogLink,
Ron Lucke's avatar
Ron Lucke committed
        CoursewareStructuralElementDiscussion,
        CoursewareStructuralElementPermissions,
        CoursewareRibbon,
        CoursewareListContainer,
        CoursewareAccordionContainer,
        CoursewareTabsContainer,
        CoursewareCompanionBox,
        CoursewareEmptyElementBox,
        CoursewareTabs,
        CoursewareTab,
Ron Lucke's avatar
Ron Lucke committed
        FocusTrap,
        IsoDate,
        StudipDialog,
Ron Lucke's avatar
Ron Lucke committed
        draggable,
    props: ['canVisit', 'orderedStructuralElements', 'structuralElement'],
    mixins: [CoursewareExport, CoursewareOerMessage, colorMixin],

    data() {
        return {
            newChapterName: '',
            newChapterParent: 'descendant',
            newChapterPurpose: 'content',
            newChapterTemplate: null,
            currentElement: '',
            uploadFileError: '',
            textCompanionWrongContext: this.$gettext('Die angeforderte Seite ist nicht Teil dieser Courseware.'),
            textEdit: {
                title: this.$gettext('Seite bearbeiten'),
                confirm: this.$gettext('Speichern'),
                close: this.$gettext('Schließen'),
                basic: this.$gettext('Grunddaten'),
                image: this.$gettext('Bild'),
                meta: this.$gettext('Metadaten'),
                approval: this.$gettext('Rechte'),
                visible: this.$gettext('Sichtbarkeit'),
            },
            textInfo: {
                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'),
                close: this.$gettext('Schließen'),
            },
            textRibbon: {
                perv: this.$gettext('zurück'),
                next: this.$gettext('weiter'),
            },
Ron Lucke's avatar
Ron Lucke committed
            textRemoveLock: {
                title: this.$gettext('Sperre aufheben'),
                alert: this.$gettext('Möchten Sie die Sperre der Seite wirklich aufheben?'),
            },
            exportRunning: false,
            exportChildren: false,
Ron Lucke's avatar
Ron Lucke committed
            oerExportRunning: false,
            oerChildren: true,
Ron Lucke's avatar
Ron Lucke committed
            pdfExportChildren: false,
Ron Lucke's avatar
Ron Lucke committed
            containerList: [],
            isDragging: false,
            dragOptions: {
                animation: 0,
                group: 'description',
                disabled: false,
                ghostClass: 'container-ghost',
            },
            errorEmptyChapterName: false,
Ron Lucke's avatar
Ron Lucke committed
            consumModeTrap: false,
            additionalText: '',

            publicLink: {
                passsword: '',
                'expire-date': ''
Ron Lucke's avatar
Ron Lucke committed
            },
            deletingPreviewImage: false,
            processing: false,
            keyboardSelected: null,
            assistiveLive: ''
        };
    },

    computed: {
        ...mapGetters({
            courseware: 'courseware',
Ron Lucke's avatar
Ron Lucke committed
            context: 'context',
            consumeMode: 'consumeMode',
            containerById: 'courseware-containers/byId',
            relatedContainers: 'courseware-containers/related',
            relatedStructuralElements: 'courseware-structural-elements/related',
Ron Lucke's avatar
Ron Lucke committed
            relatedTaskGroups: 'courseware-task-groups/related',
            relatedUsers: 'users/related',
            structuralElementById: 'courseware-structural-elements/byId',
            userIsTeacher: 'userIsTeacher',
            pluginManager: 'pluginManager',
            showEditDialog: 'showStructuralElementEditDialog',
            showAddDialog: 'showStructuralElementAddDialog',
            showImportDialog: 'showStructuralElementImportDialog',
            showCopyDialog: 'showStructuralElementCopyDialog',
            showLinkDialog: 'showStructuralElementLinkDialog',
            showExportDialog: 'showStructuralElementExportDialog',
Ron Lucke's avatar
Ron Lucke committed
            showPdfExportDialog: 'showStructuralElementPdfExportDialog',
            showInfoDialog: 'showStructuralElementInfoDialog',
            showDeleteDialog: 'showStructuralElementDeleteDialog',
            showOerDialog: 'showStructuralElementOerDialog',
            showSuggestOerDialog: 'showSuggestOerDialog',
            showPublicLinkDialog: 'showStructuralElementPublicLinkDialog',
Ron Lucke's avatar
Ron Lucke committed
            showRemoveLockDialog: 'showStructuralElementRemoveLockDialog',
Ron Lucke's avatar
Ron Lucke committed
            oerEnabled: 'oerEnabled',
            licenses: 'licenses',
            exportState: 'exportState',
            exportProgress: 'exportProgress',
Ron Lucke's avatar
Ron Lucke committed
            userId: 'userId',
Ron Lucke's avatar
Ron Lucke committed
            viewMode: 'viewMode',
            taskById: 'courseware-tasks/byId',
Ron Lucke's avatar
Ron Lucke committed
            userById: 'users/byId',
            lastCreatedElement: 'courseware-structural-elements/lastCreated',
Ron Lucke's avatar
Ron Lucke committed

            blocked: 'currentElementBlocked',
            blockerId: 'currentElementBlockerId',
            blockedByThisUser: 'currentElementBlockedByThisUser',
            blockedByAnotherUser: 'currentElementBlockedByAnotherUser',

            templates: 'courseware-templates/all',
        currentId() {
            return this.structuralElement?.id;
        },

        textOer() {
            return {
                title: this.$gettext('Lerninhalte auf dem OER Campus veröffentlichen'),
                confirm: this.$gettext('Veröffentlichen'),
                close: this.$gettext('Abbrechen'),
        textSuggestOer() {
            return {
                title: this.$gettext('Lerninhalt für den OER Campus vorschlagen'),
                confirm: this.$gettext('Lerninhalt vorschlagen'),
                close: this.$gettext('Abbrechen'),
            return this.context.type === 'courses';
        inContent() {
            // The rights tab in contents will be only visible to the owner.
            return this.context.type === 'users' && this.userId === this.currentElement.relationships.user.data.id;
        textDelete() {
            let textDelete = {};
            textDelete.title = this.$gettext('Seite unwiderruflich löschen');
            textDelete.alert = this.$gettext('Möchten Sie die Seite wirklich löschen?');
            if (this.structuralElementLoaded) {
                    this.$gettextInterpolate(
                        this.$gettext('Möchten Sie die Seite %{ pageTitle } und alle ihre Unterseiten wirklich löschen?'),
                        {pageTitle: this.structuralElement.attributes.title}
                    );
            }

            return textDelete;
        },

        validContext() {
            let valid = false;
            if (this.context.type === 'courses' && this.currentElement.relationships) {
                if (
                    this.currentElement.relationships.course &&
                    this.context.id === this.currentElement.relationships.course.data.id
            if (this.context.type === 'users' && this.currentElement.relationships) {
                if (
                    this.currentElement.relationships.user &&
                    this.context.id === this.currentElement.relationships.user.data.id
            if (this.context.type === 'sharedusers') {
                if (this.context.id === this.courseware.relationships.root.data.id) {
            if (this.context.type === 'public') {
            return valid;
        },

        image() {
            return this.structuralElement.relationships?.image?.meta?.['download-url'] ?? null;
        },

        showPreviewImage() {
            return this.image !== null && this.deletingPreviewImage === false;
        },

        structuralElementLoaded() {
            return this.structuralElement !== null && this.structuralElement !== {};
        },

        ancestors() {
            if (!this.structuralElement) {
                return [];
            }
            const finder = (parent) => {
                const parentId = parent.relationships?.parent?.data?.id;
                if (!parentId) {
                    return null;
                }
Ron Lucke's avatar
Ron Lucke committed
                const element = this.structuralElementById({ id: parentId });
                if (!element) {
                    console.error(`CoursewareStructuralElement#ancestors: Could not find parent by ID: "${parentId}".`);
                }

                return element;
            };

            const visitAncestors = function* (node) {
                const parent = finder(node);
                if (parent) {
                    yield parent;
Ron Lucke's avatar
Ron Lucke committed
                    yield* visitAncestors(parent);
Ron Lucke's avatar
Ron Lucke committed
            return [...visitAncestors(this.structuralElement)].reverse();
            const currentIndex = this.orderedStructuralElements.indexOf(this.structuralElement.id);
            if (currentIndex <= 0) {
            const previousId = this.orderedStructuralElements[currentIndex - 1];
            const previous = this.structuralElementById({ id: previousId });

            return previous;
            const currentIndex = this.orderedStructuralElements.indexOf(this.structuralElement.id);
            const lastIndex = this.orderedStructuralElements.length - 1;
            if (currentIndex === -1 || currentIndex === lastIndex) {
                return null;
            const nextId = this.orderedStructuralElements[currentIndex + 1];
            const next = this.structuralElementById({ id: nextId });

            return next;
        },
        empty() {
            if (this.containers === null) {
                return true;
            } else {
                return !this.containers.some((container) => container.relationships.blocks.data.length > 0);
Elmar Ludwig's avatar
Elmar Ludwig committed
            if (!this.structuralElement) {
                return [];
Elmar Ludwig's avatar
Elmar Ludwig committed
            return (
                this.relatedContainers({
                    parent: this.structuralElement,
                    relationship: 'containers',
                }) ?? []
            );
        },
        noContainers() {
            if (this.containers === null) {
                return true;
            } else {
                return this.containers.length === 0;
            }
        },

        canEdit() {
            if (!this.structuralElement) {
                return false;
            }
            return this.structuralElement.attributes['can-edit'];
        },
Ron Lucke's avatar
Ron Lucke committed
        canEditParent() {
            if (this.isRoot) {
                return false;
            }
            const parentId = this.structuralElement.relationships.parent.data.id;
            const parent = this.structuralElementById({ id: parentId });

            return parent.attributes['can-edit'];
        },

        isRoot() {
            return this.structuralElement.relationships.parent.data === null;
        },

        editor() {
            const editor = this.relatedUsers({
                parent: this.structuralElement,
                relationship: 'editor',
            });

            return editor ?? null;
        },

        editorName() {
            return this.editor?.attributes['formatted-name'] ?? '?';
        menuItems() {
            let menu = [
Ron Lucke's avatar
Ron Lucke committed
                { id: 4, label: this.$gettext('Informationen anzeigen'), icon: 'info', emit: 'showInfo' },
                { id: 5, label: this.$gettext('Lesezeichen setzen'), icon: 'star', emit: 'setBookmark' },
                { id: 6, label: this.$gettext('Lerninhalt für OER Campus vorschlagen'), icon: 'oer-campus', emit: 'showSuggest' },
Michaela Brückner's avatar
Michaela Brückner committed

            ];
            if (this.canEdit) {
Ron Lucke's avatar
Ron Lucke committed
                if (!this.blockedByAnotherUser) {
Ron Lucke's avatar
Ron Lucke committed
                    menu.push({
                        id: 1,
                        label: this.$gettext('Seite bearbeiten'),
                        icon: 'edit',
                        emit: 'editCurrentElement',
                    });
Ron Lucke's avatar
Ron Lucke committed
                if (this.blockedByAnotherUser && this.userIsTeacher) {
Ron Lucke's avatar
Ron Lucke committed
                    menu.push({
                        id: 1,
                        label: this.textRemoveLock.title,
                        icon: 'lock-unlocked',
                        emit: 'removeLock',
                    });
                }
Ron Lucke's avatar
Ron Lucke committed
                menu.push({ id: 3, label: this.$gettext('Seite hinzufügen'), icon: 'add', emit: 'addElement' });
            }
            if (this.context.type === 'users') {
                menu.push({ id: 7, label: this.$gettext('Öffentlichen Link erzeugen'), icon: 'group', emit: 'linkElement' });
Ron Lucke's avatar
Ron Lucke committed
            if (!this.isRoot && this.canEdit && !this.isTask && !this.blocked) {
Ron Lucke's avatar
Ron Lucke committed
                menu.push({
                    label: this.$gettext('Seite löschen'),
                    icon: 'trash',
                    emit: 'deleteCurrentElement',
                });
            }
            menu.sort((a, b) => a.id - b.id);

            return menu;
        },
        colors() {
            return this.mixinColors.filter(color => color.darkmode);
        },
        currentLicenseName() {
            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 '';
        },
Ron Lucke's avatar
Ron Lucke committed
        blockingUser() {
            if (this.blockedByAnotherUser) {
                return this.userById({id: this.blockerId});
            }

            return null;
Ron Lucke's avatar
Ron Lucke committed
        },
Ron Lucke's avatar
Ron Lucke committed
        blockingUserName() {
            return this.blockingUser ? this.blockingUser.attributes['formatted-name'] : '';
Ron Lucke's avatar
Ron Lucke committed
        },
Ron Lucke's avatar
Ron Lucke committed
        discussView() {
            return this.viewMode === 'discuss';
        },
        editView() {
            return this.viewMode === 'edit';
        },
Ron Lucke's avatar
Ron Lucke committed
        pdfExportURL() {
            if (this.context.type === 'users') {
                return STUDIP.URLHelper.getURL(
                    'dispatch.php/contents/courseware/pdf_export/' + this.structuralElement.id
                );
            }
            if (this.context.type === 'courses') {
                return STUDIP.URLHelper.getURL(
                    'dispatch.php/course/courseware/pdf_export/' + this.structuralElement.id
                );
            }

            return '';
        },
        isTask() {
            return this.structuralElement?.relationships.task.data !== null;
        },
        task() {
            if (!this.isTask) {
                return null;
            }

            return this.taskById({ id: this.structuralElement.relationships.task.data.id });
        },
Ron Lucke's avatar
Ron Lucke committed
        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 '';
        },
Ron Lucke's avatar
Ron Lucke committed
        canAddElements() {
            if (!this.isTask) {
                return true;
            }

            // still loading
            if (!this.task) {
                return false;
            }

            const taskGroup = this.relatedTaskGroups({ parent: this.task, relationship: 'task-group' });

            return taskGroup?.attributes['solver-may-add-blocks'];
        },
        showEmptyElementBox() {
            if (!this.empty) {
                return false;
            }

            return (
                (!this.isRoot && this.canEdit) || !this.canEdit || (!this.noContainers && this.isRoot && this.canEdit)
            );
        },

        isLink() {
            if (this.structuralElement) {
                return this.structuralElement.attributes['is-link'] === 1;
            }

            return false;
        },

        linkedElement() {
            if (this.isLink) {
                return this.structuralElementById({ id: this.structuralElement.attributes['target-id']});
            }

            return null;
        },

        linkedContainers() {
            let containers = [];
            let relatedContainers = this.linkedElement?.relationships?.containers?.data;

            if (relatedContainers) {
                for (const container of relatedContainers) {
                    containers.push(this.containerById({ id: container.id}));
                }
            }

            return containers;
        },
            const owner = this.relatedUsers({
                parent: this.structuralElement,
                relationship: 'owner',
            return owner ?? null;
            return this.owner?.attributes['formatted-name'] ?? '?';
        selectableTemplates() {
            return this.templates.filter(template => {
                return template.attributes.purpose === this.newElementPurpose
            });
        },
    },

    methods: {
        ...mapActions({
            createStructuralElement: 'createStructuralElement',
            updateStructuralElement: 'updateStructuralElement',
            deleteStructuralElement: 'deleteStructuralElement',
            lockObject: 'lockObject',
            unlockObject: 'unlockObject',
            addBookmark: 'addBookmark',
            companionInfo: 'companionInfo',
Ron Lucke's avatar
Ron Lucke committed
            companionWarning: 'companionWarning',
Ron Lucke's avatar
Ron Lucke committed
            companionError: 'companionError',
            uploadImageForStructuralElement: 'uploadImageForStructuralElement',
            deleteImageForStructuralElement: 'deleteImageForStructuralElement',
            companionSuccess: 'companionSuccess',
            showElementEditDialog: 'showElementEditDialog',
            showElementAddDialog: 'showElementAddDialog',
            showElementExportDialog: 'showElementExportDialog',
Ron Lucke's avatar
Ron Lucke committed
            showElementPdfExportDialog: 'showElementPdfExportDialog',
            showElementInfoDialog: 'showElementInfoDialog',
            showElementDeleteDialog: 'showElementDeleteDialog',
            showElementOerDialog: 'showElementOerDialog',
            showElementPublicLinkDialog: 'showElementPublicLinkDialog',
Ron Lucke's avatar
Ron Lucke committed
            showElementRemoveLockDialog: 'showElementRemoveLockDialog',
            updateShowSuggestOerDialog: 'updateShowSuggestOerDialog',
Ron Lucke's avatar
Ron Lucke committed
            updateContainer: 'updateContainer',
            sortContainersInStructualElements: 'sortContainersInStructualElements',
            loadTask: 'loadTask',
            loadStructuralElement: 'loadStructuralElement',
            createLink: 'createLink',
Ron Lucke's avatar
Ron Lucke committed
            setCurrentElementId: 'coursewareCurrentElement',
            this.currentElement = _.cloneDeep(this.structuralElement);
            this.uploadFileError = '';
            this.deletingPreviewImage = false;
        async menuAction(action) {
            switch (action) {
Ron Lucke's avatar
Ron Lucke committed
                case 'removeLock':
                    this.displayRemoveLockDialog();
                    break;
                case 'editCurrentElement':
Ron Lucke's avatar
Ron Lucke committed
                    await this.loadStructuralElement(this.currentId);
Ron Lucke's avatar
Ron Lucke committed
                    if (this.blockedByAnotherUser) {
                        this.companionInfo({ info: this.$gettext('Diese Seite wird bereits bearbeitet.') });

                        return false;
                    }
                    try {
                        await this.lockObject({ id: this.currentId, type: 'courseware-structural-elements' });
Ron Lucke's avatar
Ron Lucke committed
                    } catch(error) {
Ron Lucke's avatar
Ron Lucke committed
                        if (error.status === 409) {
                            this.companionInfo({ info: this.$gettext('Diese Seite wird bereits bearbeitet.') });
                        } else {
                            console.log(error);
                        }

                        return false;
                    }
Ron Lucke's avatar
Ron Lucke committed
                    this.initCurrent();
                    this.showElementEditDialog(true);
                    break;
                case 'addElement':
                    this.newChapterName = '';
                    this.newChapterParent = 'descendant';
Ron Lucke's avatar
Ron Lucke committed
                    this.errorEmptyChapterName = false;
                    this.showElementAddDialog(true);
                    break;
                case 'deleteCurrentElement':
Ron Lucke's avatar
Ron Lucke committed
                    await this.loadStructuralElement(this.currentId);
                    if (this.blockedByAnotherUser) {
                        this.companionInfo({
                            info: this.$gettextInterpolate(
                                this.$gettext('Löschen nicht möglich, da %{blockingUserName} die Seite bearbeitet.'),
                                {blockingUserName: this.blockingUserName}
                            )
                        });
Ron Lucke's avatar
Ron Lucke committed

                        return false;
                    }
                    await this.lockObject({ id: this.currentId, type: 'courseware-structural-elements' });
                    this.showElementDeleteDialog(true);
                    break;
                case 'showInfo':
                    this.showElementInfoDialog(true);
                    break;
                case 'showExportOptions':
                    this.showElementExportDialog(true);
                    break;
                case 'oerCurrentElement':
                    this.showElementOerDialog(true);
                    break;
                case 'showSuggest':
                    this.updateShowSuggestOerDialog(true);
                    break;
                case 'setBookmark':
                    this.setBookmark();
                    break;
                    this.showElementPublicLinkDialog(true);
            }
        },
        async closeEditDialog() {
Ron Lucke's avatar
Ron Lucke committed
            await this.loadStructuralElement(this.currentElement.id);
            if (this.blockedByThisUser) {
                await this.unlockObject({ id: this.currentId, type: 'courseware-structural-elements' });
                await this.loadStructuralElement(this.currentElement.id);
            }
            this.showElementEditDialog(false);
            this.initCurrent();
        },
        closeAddDialog() {
            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 = '';
            }
        },
        deleteImage() {
            if (!this.deletingPreviewImage) {
                this.deletingPreviewImage = true;
            }
        },
        async storeCurrentElement() {
Ron Lucke's avatar
Ron Lucke committed
            await this.loadStructuralElement(this.currentElement.id);
            if (this.blockedByAnotherUser) {
                this.companionWarning({
                    info: this.$gettextInterpolate(
                        this.$gettext('Ihre Änderungen konnten nicht gespeichert werden, da %{blockingUserName} die Bearbeitung übernommen hat.'),
                        {blockingUserName: this.blockingUserName}
                    )
                });
Ron Lucke's avatar
Ron Lucke committed
                this.showElementEditDialog(false);
                return false;
            }
            if (!this.blocked) {
                await this.lockObject({ id: this.currentId, type: 'courseware-structural-elements' });
            }
            const file = this.$refs?.upload_image?.files[0];
            if (file) {
                if (file.size > 2097152) {
                    return false;
                }

                this.uploadFileError = '';
                this.uploadImageForStructuralElement({
                    structuralElement: this.currentElement,
                    file,
                }).catch((error) => {
                    console.error(error);
                    this.uploadFileError = this.$gettext('Fehler beim Hochladen der Datei.');
                });
                await this.loadStructuralElement(this.currentElement.id);
            } else if (this.deletingPreviewImage) {
                await this.deleteImageForStructuralElement(this.currentElement);
Ron Lucke's avatar
Ron Lucke committed
            this.showElementEditDialog(false);

            if (this.currentElement.attributes['release-date'] !== '') {
                this.currentElement.attributes['release-date'] =
                    new Date(this.currentElement.attributes['release-date']).getTime() / 1000;
            }

            if (this.currentElement.attributes['withdraw-date'] !== '') {
                this.currentElement.attributes['withdraw-date'] =
                    new Date(this.currentElement.attributes['withdraw-date']).getTime() / 1000;
            }

            await this.updateStructuralElement({
                element: this.currentElement,
                id: this.currentId,
            });
            await this.unlockObject({ id: this.currentId, type: 'courseware-structural-elements' });
            this.$emit('select', this.currentId);
Ron Lucke's avatar
Ron Lucke committed
            this.initCurrent();
        dropContainer() {
            this.isDragging = false;
            this.storeSort();
Ron Lucke's avatar
Ron Lucke committed
        },

        async storeSort() {
            const timeout = setTimeout(() => this.processing = true, 800);
            if (this.blockedByAnotherUser) {
                this.companionInfo({ info: this.$gettext('Diese Seite wird bereits bearbeitet.') });
                clearTimeout(timeout);
                this.processing = false;
                return false;
            }
            try {
                await this.lockObject({ id: this.currentId, type: 'courseware-structural-elements' });
            } catch (error) {
                if (error.status === 409) {
                    this.companionInfo({ info: this.$gettext('Diese Seite wird bereits bearbeitet.') });
                } else {
                    console.log(error);
                }
Ron Lucke's avatar
Ron Lucke committed

                clearTimeout(timeout);
                this.processing = false;
                return false;
            }

            await this.sortContainersInStructualElements({
Ron Lucke's avatar
Ron Lucke committed
                structuralElement: this.structuralElement,
                containers: this.containerList,
            });
            this.$emit('select', this.currentId);

            clearTimeout(timeout);
            this.processing = false;
Ron Lucke's avatar
Ron Lucke committed
        },

        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);
        },

Ron Lucke's avatar
Ron Lucke committed
        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() {
Ron Lucke's avatar
Ron Lucke committed
            if (this.oerExportRunning) {
                return;
            }
            this.oerExportRunning = true;
            await this.exportToOER(this.currentElement, { withChildren: this.oerChildren });
            this.oerExportRunning = false;
            this.showElementOerDialog(false);
        },

        async closeDeleteDialog() {
Ron Lucke's avatar
Ron Lucke committed
            await this.loadStructuralElement(this.currentElement.id);
            if (this.blockedByThisUser) {
                await this.unlockObject({ id: this.currentId, type: 'courseware-structural-elements' });
            }
            this.showElementDeleteDialog(false);
        },
Ron Lucke's avatar
Ron Lucke committed
        async deleteCurrentElement() {
            await this.loadStructuralElement(this.currentElement.id);
            if (this.blockedByAnotherUser) {
                this.companionWarning({
                    info: this.$gettextInterpolate(
                        this.$gettext('Löschen nicht möglich, da %{blockingUserName} die Bearbeitung übernommen hat.'),
                        {blockingUserName: this.blockingUserName}
                    )
                });
Ron Lucke's avatar
Ron Lucke committed
                this.showElementDeleteDialog(false);
                return false;
            }
            let parent_id = this.structuralElement.relationships.parent.data.id;
Ron Lucke's avatar
Ron Lucke committed
            this.companionInfo({ info: this.$gettext('Lösche Seite und alle darunter liegenden Elemente.') });
            this.deleteStructuralElement({
                id: this.currentId,
                parentId: this.structuralElement.relationships.parent.data.id,
Ron Lucke's avatar
Ron Lucke committed
            })
            .then(response => {
                this.$router.push(parent_id);
                this.companionInfo({ info: this.$gettext('Die Seite wurde gelöscht.') });
            })
            .catch(error => {
                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
Ron Lucke's avatar
Ron Lucke committed
            let writeApproval = this.currentElement.attributes['write-approval'];
            let readApproval = this.currentElement.attributes['read-approval'];

            this.errorEmptyChapterName = title.trim();
            if (this.errorEmptyChapterName === '') {
Ron Lucke's avatar
Ron Lucke committed
                return;
            }
            if (this.newChapterParent === 'sibling') {
                parent_id = this.structuralElement.relationships.parent.data.id;
Ron Lucke's avatar
Ron Lucke committed
                writeApproval = [];
                readApproval = [];
            }
            this.showElementAddDialog(false);
Ron Lucke's avatar
Ron Lucke committed
            this.createStructuralElement({
Ron Lucke's avatar
Ron Lucke committed
                    title: title,
Ron Lucke's avatar
Ron Lucke committed
                    'write-approval':  writeApproval,
                    'read-approval': readApproval
                templateId: this.newChapterTemplate ? this.newChapterTemplate.id : null,
                parentId: parent_id,
                currentId: this.currentId,
Ron Lucke's avatar
Ron Lucke committed
            })
            .then(() => {
                let newElement = this.$store.getters['courseware-structural-elements/lastCreated'];
                this.companionSuccess({
                    info:
                        this.$gettextInterpolate(
                            this.$gettext('Die Seite %{ pageTitle } wurde erfolgreich angelegt.'), 
Ron Lucke's avatar
Ron Lucke committed
                            { pageTitle: newElement.attributes.title }
                        )
                });
            })
            .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 });
Ron Lucke's avatar
Ron Lucke committed

            let newElement = this.lastCreatedElement;
            this.companionSuccess({
                info: this.$gettextInterpolate(
                    this.$gettext('Die Seite %{ pageTitle } wurde erfolgreich angelegt.'),
                    {pageTitle: newElement.attributes.title}
                )
            });
Ron Lucke's avatar
Ron Lucke committed
            this.newChapterName = '';
        },
        containerComponent(container) {
            return 'courseware-' + container.attributes['container-type'] + '-container';
        },
        setBookmark() {
            this.addBookmark(this.structuralElement);
Ron Lucke's avatar
Ron Lucke committed
            this.companionInfo({ info: this.$gettext('Das Lesezeichen wurde gesetzt.') });
        },
        updateReadApproval(approval) {
            this.currentElement.attributes['read-approval'] = approval;
        },
        updateWriteApproval(approval) {
            this.currentElement.attributes['write-approval'] = approval;
        },
        sendOerSuggestion() {
            this.suggestViaAction(this.currentElement, this.additionalText);
            this.updateShowSuggestOerDialog(false);
            const date = this.publicLink['expire-date'];
            const publicLink = {
                attributes: {
                    password: this.publicLink.password,
                    'expire-date': date === '' ? new Date(0).toISOString() : new Date(date).toISOString()
                },
                relationships: {
                    'structural-element': {
                        data: {
                            id: this.currentElement.id,
                            type: 'courseware-structural-elements'
                        }
                    }
                }
            }

            await this.createLink({ publicLink });
            this.companionSuccess({
                info: this.$gettext('Öffentlicher Link wurde angelegt. Unter Freigaben finden Sie alle Ihre öffentlichen Links.'),
            });
            this.closeLinkDialog();
        },
Michaela Brückner's avatar
Michaela Brückner committed
            this.publicLink = {
                passsword: '',
                'expire-date': ''
            };
            this.showElementPublicLinkDialog(false);
Ron Lucke's avatar
Ron Lucke committed
        },
        displayRemoveLockDialog() {
            this.showElementRemoveLockDialog(true);
        },
        async executeRemoveLock() {
            await this.unlockObject({ id: this.currentId, type: 'courseware-structural-elements' });
            await this.loadStructuralElement(this.currentElement.id);
            this.showElementRemoveLockDialog(false);
        },
        updateContainerList() {
            this.containerList = this.containers;
            const containerRefs = this.$refs.containers;
            for (let ref of containerRefs) {
                ref.initCurrentData();
            }
        },
        keyHandler(e, containerId) {
            switch (e.keyCode) {
                case 27: // esc
                    this.abortKeyboardSorting(containerId);
                    break;
                case 32: // space
                    e.preventDefault();
                    if (this.keyboardSelected) {
                        this.storeKeyboardSorting(containerId);
                    } else {
                        this.keyboardSelected = containerId;
                        const container = this.containerById({id: containerId});
                        const index = this.containerList.findIndex(c => c.id === container.id);
                        this.assistiveLive = 
                            this.$gettextInterpolate(
                                this.$gettext('%{containerTitle} Abschnitt ausgewählt. Aktuelle Position in der Liste: %{pos} von %{listLength}. Drücken Sie die Aufwärts- und Abwärtspfeiltasten, um die Position zu ändern, die Leertaste zum Ablegen, die Escape-Taste zum Abbrechen.')
                                , {containerTitle: container.attributes.title, pos: index + 1, listLength: this.containerList.length}
                            );
                    }
                    break;
            }
            if (this.keyboardSelected) {
                switch (e.keyCode) {
                    case 9: //tab
                        this.abortKeyboardSorting(containerId);
                        break;
                    case 38: // up
                        e.preventDefault();
                        this.moveItemUp(containerId);
                        break;
                    case 40: // down
                        e.preventDefault();
                        this.moveItemDown(containerId);
                        break;
                }
            }
        },
        moveItemUp(containerId) {
            const currentIndex = this.containerList.findIndex(container => container.id === containerId);
            if (currentIndex !== 0) {
                const container = this.containerById({id: containerId});
                const newPos = currentIndex - 1;
                this.containerList.splice(newPos, 0, this.containerList.splice(currentIndex, 1)[0]);
                this.assistiveLive = 
                    this.$gettextInterpolate(
                        this.$gettext('%{containerTitle} Abschnitt. Aktuelle Position in der Liste: %{pos} von %{listLength}.')
                        , {containerTitle: container.attributes.title, pos: newPos + 1, listLength: this.containerList.length}
                    );
            }
        },
        moveItemDown(containerId) {
            const currentIndex = this.containerList.findIndex(container => container.id === containerId);
            if (this.containerList.length - 1 > currentIndex) {
                const container = this.containerById({id: containerId});
                const newPos = currentIndex + 1;
                this.containerList.splice(newPos, 0, this.containerList.splice(currentIndex, 1)[0]);
                this.assistiveLive = 
                    this.$gettextInterpolate(
                        this.$gettext('%{containerTitle} Abschnitt. Aktuelle Position in der Liste: %{pos} von %{listLength}.')
                        , {containerTitle: container.attributes.title, pos: newPos + 1, listLength: this.containerList.length}
                    );
            }
        },
        abortKeyboardSorting(containerId) {
            const container = this.containerById({id: containerId});
            this.keyboardSelected = null;
            this.assistiveLive = 
                this.$gettextInterpolate(
                    this.$gettext('%{containerTitle} Abschnitt, Neuordnung abgebrochen.')
                    , {containerTitle: container.attributes.title}
                );
            this.$emit('select', this.currentId);
        },
        storeKeyboardSorting(containerId) {
            const container = this.containerById({id: containerId});
            const currentIndex = this.containerList.findIndex(container => container.id === containerId);
            this.keyboardSelected = null;
            this.assistiveLive = 
                this.$gettextInterpolate(
                    this.$gettext('%{containerTitle} Abschnitt, abgelegt. Entgültige Position in der Liste: %{pos} von %{listLength}.')
                    , {containerTitle: container.attributes.title, pos: currentIndex + 1, listLength: this.containerList.length}
                );
            this.storeSort();
    },
    created() {
        this.pluginManager.registerComponentsLocally(this);
    },
Ron Lucke's avatar
Ron Lucke committed
        async structuralElement() {
            this.setCurrentElementId(this.structuralElement.id);
Ron Lucke's avatar
Ron Lucke committed
            if (this.isTask) {
                this.loadTask({
                    taskId: this.structuralElement.relationships.task.data.id,
                });
            }

            if (this.isLink) {
                this.loadStructuralElement(this.structuralElement.attributes['target-id']);
            }
Ron Lucke's avatar
Ron Lucke committed
        },
        containers() {
Elmar Ludwig's avatar
Elmar Ludwig committed
            this.containerList = this.containers;
        containerList() {
            if (this.keyboardSelected) {
                this.$nextTick(() => {
                    const selected = this.$refs['sortableHandle' + this.keyboardSelected][0];
                    selected.focus();
                    selected.scrollIntoView({behavior: "smooth", block: "center"});
                });
            }
        },
Ron Lucke's avatar
Ron Lucke committed
        consumeMode(newState) {
            this.consumModeTrap = newState;
        },
    // this line provides all the components to courseware plugins
    provide: () => ({
        containerComponents: ContainerComponents,
        coursewarePluginComponents: CoursewarePluginComponents,
    }),