From fca7f5cf475db33a970a52417421337752b54bc4 Mon Sep 17 00:00:00 2001
From: Viktoria Wiebe <vwiebe@uni-osnabrueck.de>
Date: Wed, 3 Aug 2022 09:29:03 +0000
Subject: [PATCH] fixes #1171 and #1169

Closes #1171 and #1169

Merge request studip/studip!844
---
 app/controllers/contents/courseware.php       |  19 +--
 app/controllers/course/courseware.php         |  20 +---
 app/views/course/courseware/action_widget.php |   1 -
 app/views/course/courseware/export_widget.php |   1 -
 app/views/course/courseware/view_widget.php   |   1 -
 lib/classes/sidebar/VueWidget.php             |  20 ++++
 resources/vue/base-components.js              |   2 +
 resources/vue/components/SidebarWidget.vue    |  19 +++
 .../courseware/CoursewareActionWidget.vue     | 110 +++++++++---------
 .../courseware/CoursewareExportWidget.vue     |  48 ++++----
 .../courseware/CoursewareRibbon.vue           |   2 +
 .../courseware/CoursewareViewWidget.vue       |  54 +++++----
 .../vue/components/courseware/IndexApp.vue    |   6 +-
 templates/widgets/vue-widget.php              |   1 +
 14 files changed, 170 insertions(+), 134 deletions(-)
 delete mode 100644 app/views/course/courseware/action_widget.php
 delete mode 100644 app/views/course/courseware/export_widget.php
 delete mode 100644 app/views/course/courseware/view_widget.php
 create mode 100644 lib/classes/sidebar/VueWidget.php
 create mode 100644 resources/vue/components/SidebarWidget.vue
 create mode 100644 templates/widgets/vue-widget.php

diff --git a/app/controllers/contents/courseware.php b/app/controllers/contents/courseware.php
index c1ca32137bd..c7edcaced56 100644
--- a/app/controllers/contents/courseware.php
+++ b/app/controllers/contents/courseware.php
@@ -117,11 +117,7 @@ class Contents_CoursewareController extends AuthenticatedController
     private function setCoursewareSidebar()
     {
         $sidebar = \Sidebar::Get();
-        $actions = new TemplateWidget(
-            _('Aktionen'),
-            $this->get_template_factory()->open('course/courseware/action_widget')
-        );
-        $sidebar->addWidget($actions)->addLayoutCSSClass('courseware-action-widget');
+        $sidebar->addWidget(new VueWidget('courseware-action-widget'));
 
         $views = new TemplateWidget(
             _('Suche'),
@@ -129,17 +125,8 @@ class Contents_CoursewareController extends AuthenticatedController
         );
         $sidebar->addWidget($views)->addLayoutCSSClass('courseware-search-widget');
 
-        $views = new \TemplateWidget(
-            _('Ansichten'),
-            $this->get_template_factory()->open('course/courseware/view_widget')
-        );
-        $sidebar->addWidget($views)->addLayoutCSSClass('courseware-view-widget');
-
-        $exports = new TemplateWidget(
-            _('Export '),
-            $this->get_template_factory()->open('course/courseware/export_widget')
-        );
-        $sidebar->addWidget($exports)->addLayoutCSSClass('courseware-export-widget');
+        $sidebar->addWidget(new VueWidget('courseware-view-widget'));
+        $sidebar->addWidget(new VueWidget('courseware-export-widget'));
     }
 
     private function getLicences()
diff --git a/app/controllers/course/courseware.php b/app/controllers/course/courseware.php
index 7c41756a92f..9174192226a 100644
--- a/app/controllers/course/courseware.php
+++ b/app/controllers/course/courseware.php
@@ -110,11 +110,7 @@ class Course_CoursewareController extends AuthenticatedController
     private function setIndexSidebar(): void
     {
         $sidebar = Sidebar::Get();
-        $actions = new TemplateWidget(
-            _('Aktionen'),
-            $this->get_template_factory()->open('course/courseware/action_widget')
-        );
-        $sidebar->addWidget($actions)->addLayoutCSSClass('courseware-action-widget');
+        $sidebar->addWidget(new VueWidget('courseware-action-widget'));
 
         $views = new TemplateWidget(
             _('Suche'),
@@ -122,20 +118,10 @@ class Course_CoursewareController extends AuthenticatedController
         );
         $sidebar->addWidget($views)->addLayoutCSSClass('courseware-search-widget');
 
-        $views = new TemplateWidget(
-            _('Ansichten'),
-            $this->get_template_factory()->open('course/courseware/view_widget')
-        );
-        $sidebar->addWidget($views)->addLayoutCSSClass('courseware-view-widget');
-
-        $exports = new TemplateWidget(
-            _('Export '),
-            $this->get_template_factory()->open('course/courseware/export_widget')
-        );
-        $sidebar->addWidget($exports)->addLayoutCSSClass('courseware-export-widget');
+        $sidebar->addWidget(new VueWidget('courseware-view-widget'));
+        $sidebar->addWidget(new VueWidget('courseware-export-widget'));
     }
 
-
     private function setDashboardSidebar(): void
     {
         $sidebar = Sidebar::Get();
diff --git a/app/views/course/courseware/action_widget.php b/app/views/course/courseware/action_widget.php
deleted file mode 100644
index b13792e2cc0..00000000000
--- a/app/views/course/courseware/action_widget.php
+++ /dev/null
@@ -1 +0,0 @@
-<aside id="courseware-action-widget" class="widget-sidebar"></aside>
diff --git a/app/views/course/courseware/export_widget.php b/app/views/course/courseware/export_widget.php
deleted file mode 100644
index 1d7feb2c6e4..00000000000
--- a/app/views/course/courseware/export_widget.php
+++ /dev/null
@@ -1 +0,0 @@
-<aside id="courseware-export-widget" class="widget-sidebar"></aside>
diff --git a/app/views/course/courseware/view_widget.php b/app/views/course/courseware/view_widget.php
deleted file mode 100644
index 826913411ac..00000000000
--- a/app/views/course/courseware/view_widget.php
+++ /dev/null
@@ -1 +0,0 @@
-<aside id="courseware-view-widget" class="widget-sidebar"></aside>
diff --git a/lib/classes/sidebar/VueWidget.php b/lib/classes/sidebar/VueWidget.php
new file mode 100644
index 00000000000..f4f0583acc2
--- /dev/null
+++ b/lib/classes/sidebar/VueWidget.php
@@ -0,0 +1,20 @@
+<?php
+/**
+ * This widget type delegates all rendering of the widget to vuejs.
+ *
+ * @author  Elmar Ludwig
+ * @license GPL2 or any later version
+ * @since   Stud.IP 5.0
+ */
+class VueWidget extends Widget
+{
+    /**
+     * Constructs the widget with the given id on the element.
+     */
+    public function __construct($id)
+    {
+        $this->id = $id;
+        $this->layout = 'widgets/vue-widget';
+        $this->forced_rendering = true;
+    }
+}
diff --git a/resources/vue/base-components.js b/resources/vue/base-components.js
index 526687b3976..97231d2827d 100644
--- a/resources/vue/base-components.js
+++ b/resources/vue/base-components.js
@@ -1,6 +1,7 @@
 import Multiselect from './components/Multiselect.vue';
 import EditableList from "./components/EditableList.vue";
 import Quicksearch from './components/Quicksearch.vue';
+import SidebarWidget from './components/SidebarWidget.vue';
 import StudipActionMenu from './components/StudipActionMenu.vue';
 import StudipAssetImg from './components/StudipAssetImg.vue';
 import StudipDateTime from './components/StudipDateTime.vue';
@@ -23,6 +24,7 @@ const BaseComponents = {
     EditableList,
     Quicksearch,
     RangeInput,
+    SidebarWidget,
     StudipActionMenu,
     StudipAssetImg,
     StudipDateTime,
diff --git a/resources/vue/components/SidebarWidget.vue b/resources/vue/components/SidebarWidget.vue
new file mode 100644
index 00000000000..34d935d84fa
--- /dev/null
+++ b/resources/vue/components/SidebarWidget.vue
@@ -0,0 +1,19 @@
+<template>
+    <div class="sidebar-widget">
+        <div class="sidebar-widget-header" v-if="title">
+            {{ title }}
+        </div>
+        <div class="sidebar-widget-content">
+            <slot name="content" />
+        </div>
+    </div>
+</template>
+
+<script>
+export default {
+    name: 'sidebar-widget',
+    props: {
+        title: String,
+    },
+}
+</script>
diff --git a/resources/vue/components/courseware/CoursewareActionWidget.vue b/resources/vue/components/courseware/CoursewareActionWidget.vue
index 572a94e6bc7..cc1738d55a1 100644
--- a/resources/vue/components/courseware/CoursewareActionWidget.vue
+++ b/resources/vue/components/courseware/CoursewareActionWidget.vue
@@ -1,60 +1,65 @@
 <template>
-    <ul class="widget-list widget-links cw-action-widget" v-if="structuralElement">
-        <li class="cw-action-widget-show-toc">
-            <button @click="toggleTOC">
-                {{ tocText }}
-            </button>
-        </li>
-        <li class="cw-action-widget-show-consume-mode">
-            <button @click="showConsumeMode">
-                <translate>Vollbild einschalten</translate>
-            </button>
-        </li>
-        <li v-if="canEdit" class="cw-action-widget-edit">
-            <button @click="editElement">
-                <translate>Seite bearbeiten</translate>
-            </button>
-        </li>
-        <li v-if="canEdit" class="cw-action-widget-sort">
-            <button @click="sortContainers">
-                <translate>Abschnitte sortieren</translate>
-            </button>
-        </li>
-        <li v-if="canEdit" class="cw-action-widget-add">
-            <button @click="addElement">
-                <translate>Seite hinzufügen</translate>
-            </button>
-        </li>
-        <li class="cw-action-widget-info">
-            <button @click="showElementInfo">
-                <translate>Informationen anzeigen</translate>
-            </button>
-        </li>
-        <li class="cw-action-widget-star">
-            <button @click="createBookmark">
-                <translate>Lesezeichen setzen</translate>
-            </button>
-        </li>
-        <li v-if="context.type === 'users'" class="cw-action-widget-link">
-            <button @click="linkElement">
-                <translate>Öffentlichen Link erzeugen</translate>
-            </button>
-        </li>
-        <li v-if="!isOwner" class="cw-action-widget-oer">
-            <button @click="suggestOER">
-                <translate>Material für %{oerTitle} vorschlagen</translate>
-            </button>
-        </li>
-        <li v-if="!isRoot && canEdit" class="cw-action-widget-trash">
-            <button @click="deleteElement">
-                <translate>Seite löschen</translate>
-            </button>
-        </li>
-    </ul>
+    <sidebar-widget :title="$gettext('Aktionen')" v-if="structuralElement">
+        <template #content>
+            <ul class="widget-list widget-links cw-action-widget">
+                <li class="cw-action-widget-show-toc">
+                    <button @click="toggleTOC">
+                        {{ tocText }}
+                    </button>
+                </li>
+                <li class="cw-action-widget-show-consume-mode">
+                    <button @click="showConsumeMode">
+                        <translate>Vollbild einschalten</translate>
+                    </button>
+                </li>
+                <li v-if="canEdit" class="cw-action-widget-edit">
+                    <button @click="editElement">
+                        <translate>Seite bearbeiten</translate>
+                    </button>
+                </li>
+                <li v-if="canEdit" class="cw-action-widget-sort">
+                    <button @click="sortContainers">
+                        <translate>Abschnitte sortieren</translate>
+                    </button>
+                </li>
+                <li v-if="canEdit" class="cw-action-widget-add">
+                    <button @click="addElement">
+                        <translate>Seite hinzufügen</translate>
+                    </button>
+                </li>
+                <li class="cw-action-widget-info">
+                    <button @click="showElementInfo">
+                        <translate>Informationen anzeigen</translate>
+                    </button>
+                </li>
+                <li class="cw-action-widget-star">
+                    <button @click="createBookmark">
+                        <translate>Lesezeichen setzen</translate>
+                    </button>
+                </li>
+                <li v-if="context.type === 'users'" class="cw-action-widget-link">
+                    <button @click="linkElement">
+                        <translate>Öffentlichen Link erzeugen</translate>
+                    </button>
+                </li>
+                <li v-if="!isOwner" class="cw-action-widget-oer">
+                    <button @click="suggestOER">
+                        <translate>Material für %{oerTitle} vorschlagen</translate>
+                    </button>
+                </li>
+                <li v-if="!isRoot && canEdit" class="cw-action-widget-trash">
+                    <button @click="deleteElement">
+                        <translate>Seite löschen</translate>
+                    </button>
+                </li>
+            </ul>
+        </template>
+    </sidebar-widget>
 </template>
 
 <script>
 import StudipIcon from './../StudipIcon.vue';
+import SidebarWidget from '../SidebarWidget.vue';
 import CoursewareExport from '@/vue/mixins/courseware/export.js';
 import { mapActions, mapGetters } from 'vuex';
 
@@ -63,6 +68,7 @@ export default {
     props: ['structuralElement', 'canVisit'],
     components: {
         StudipIcon,
+        SidebarWidget,
     },
     mixins: [CoursewareExport],
     computed: {
diff --git a/resources/vue/components/courseware/CoursewareExportWidget.vue b/resources/vue/components/courseware/CoursewareExportWidget.vue
index 7b55a7898cb..2c821b735cd 100644
--- a/resources/vue/components/courseware/CoursewareExportWidget.vue
+++ b/resources/vue/components/courseware/CoursewareExportWidget.vue
@@ -1,33 +1,41 @@
 <template>
-    <ul class="widget-list widget-links cw-export-widget" v-if="structuralElement">
-        <li v-if="showExportArchiv" class="cw-export-widget-export">
-            <button @click="exportElement">
-                <translate>Seite exportieren</translate>
-            </button>
-        </li>
-        <li v-if="showExportPdf" class="cw-export-widget-export-pdf">
-            <button @click="pdfElement">
-                <translate>Seite als pdf-Dokument exportieren</translate>
-            </button>
-        </li>
-        <li v-if="showOer" class="cw-export-widget-oer">
-            <button @click="oerElement">
-                <translate>Seite auf %{oerTitle} veröffentlichen</translate>
-            </button>
-        </li>
-        <li v-if="!showExportArchiv && !showExportPdf && !showOer">
-            <translate>Keine Exportoptionen verfügbar</translate>
-        </li>
-    </ul>
+    <sidebar-widget :title="$gettext('Export')" v-if="structuralElement">
+        <template #content>
+            <ul class="widget-list widget-links cw-export-widget" v-if="structuralElement">
+                <li v-if="showExportArchiv" class="cw-export-widget-export">
+                    <button @click="exportElement">
+                        <translate>Seite exportieren</translate>
+                    </button>
+                </li>
+                <li v-if="showExportPdf" class="cw-export-widget-export-pdf">
+                    <button @click="pdfElement">
+                        <translate>Seite als pdf-Dokument exportieren</translate>
+                    </button>
+                </li>
+                <li v-if="showOer" class="cw-export-widget-oer">
+                    <button @click="oerElement">
+                        <translate>Seite auf %{oerTitle} veröffentlichen</translate>
+                    </button>
+                </li>
+                <li v-if="!showExportArchiv && !showExportPdf && !showOer">
+                    <translate>Keine Exportoptionen verfügbar</translate>
+                </li>
+            </ul>
+        </template>
+    </sidebar-widget>
 </template>
 
 <script>
+import SidebarWidget from '../SidebarWidget.vue';
 import CoursewareExport from '@/vue/mixins/courseware/export.js';
 import { mapActions, mapGetters } from 'vuex';
 
 export default {
     name: 'courseware-export-widget',
     props: ['structuralElement', 'canVisit'],
+    components: {
+        SidebarWidget
+    },
     mixins: [CoursewareExport],
     computed: {
         ...mapGetters({
diff --git a/resources/vue/components/courseware/CoursewareRibbon.vue b/resources/vue/components/courseware/CoursewareRibbon.vue
index 72a3a509836..f6797fcd9a3 100644
--- a/resources/vue/components/courseware/CoursewareRibbon.vue
+++ b/resources/vue/components/courseware/CoursewareRibbon.vue
@@ -15,12 +15,14 @@
             </div>
             <div class="cw-ribbon-wrapper-right">
                 <button
+                    v-if="showToolbarButton"
                     class="cw-ribbon-button cw-ribbon-button-menu"
                     :title="textRibbon.toolbar"
                     @click.prevent="activeToolbar"
                 >
                 </button>
                 <button
+                    v-if="showModeSwitchButton"
                     ref="consumeModeSwitch"
                     class="cw-ribbon-button"
                     :class="[consumeMode ? 'cw-ribbon-button-zoom-out' : 'cw-ribbon-button-zoom']"
diff --git a/resources/vue/components/courseware/CoursewareViewWidget.vue b/resources/vue/components/courseware/CoursewareViewWidget.vue
index 86c157a1364..fb3de7441a2 100644
--- a/resources/vue/components/courseware/CoursewareViewWidget.vue
+++ b/resources/vue/components/courseware/CoursewareViewWidget.vue
@@ -1,35 +1,43 @@
 <template>
-    <ul class="widget-list widget-links sidebar-views cw-view-widget">
-        <li :class="{ active: readView }">
-            <button @click="setReadView">
-                <translate>Lesen</translate>
-            </button>
-        </li>
-        <li
-            v-if="canEdit"
-            :class="{ active: editView }"
-        >
-            <button @click="setEditView">
-                <translate>Bearbeiten</translate>
-            </button>
-        </li>
-        <li 
-            v-if="context.type === 'courses' && canVisit"
-            :class="{ active: discussView }"
-        >
-            <button @click="setDiscussView">
-                <translate>Kommentieren</translate>
-            </button>
-        </li>
-    </ul>
+    <sidebar-widget :title="$gettext('Ansichten')" v-if="structuralElement">
+        <template #content>
+            <ul class="widget-list widget-links sidebar-views cw-view-widget">
+                <li :class="{ active: readView }">
+                    <button @click="setReadView">
+                        <translate>Lesen</translate>
+                    </button>
+                </li>
+                <li
+                    v-if="canEdit"
+                    :class="{ active: editView }"
+                >
+                    <button @click="setEditView">
+                        <translate>Bearbeiten</translate>
+                    </button>
+                </li>
+                <li
+                    v-if="context.type === 'courses' && canVisit"
+                    :class="{ active: discussView }"
+                >
+                    <button @click="setDiscussView">
+                        <translate>Kommentieren</translate>
+                    </button>
+                </li>
+            </ul>
+        </template>
+    </sidebar-widget>
 </template>
 
 <script>
+import SidebarWidget from '../SidebarWidget.vue';
 import { mapActions, mapGetters } from 'vuex';
 
 export default {
     name: 'courseware-view-widget',
     props: ['structuralElement', 'canVisit'],
+    components: {
+        SidebarWidget
+    },
     computed: {
         ...mapGetters({
             viewMode: 'viewMode',
diff --git a/resources/vue/components/courseware/IndexApp.vue b/resources/vue/components/courseware/IndexApp.vue
index 5d9f1d202c1..7c9c15304b1 100644
--- a/resources/vue/components/courseware/IndexApp.vue
+++ b/resources/vue/components/courseware/IndexApp.vue
@@ -10,16 +10,16 @@
                 @select="selectStructuralElement"
             ></courseware-structural-element>
             <MountingPortal mountTo="#courseware-action-widget" name="sidebar-actions">
-                <courseware-action-widget :structural-element="selected" :canVisit="canVisit"></courseware-action-widget>
+                <courseware-action-widget :structural-element="selected" :canVisit="canVisit" v-if="!showSearchResults"></courseware-action-widget>
             </MountingPortal>
             <MountingPortal mountTo="#courseware-search-widget" name="sidebar-search">
                 <courseware-search-widget></courseware-search-widget>
             </MountingPortal>
             <MountingPortal mountTo="#courseware-view-widget" name="sidebar-views">
-                <courseware-view-widget :structural-element="selected" :canVisit="canVisit"></courseware-view-widget>
+                <courseware-view-widget :structural-element="selected" :canVisit="canVisit" v-if="!showSearchResults"></courseware-view-widget>
             </MountingPortal>
             <MountingPortal mountTo="#courseware-export-widget" name="sidebar-export">
-                <courseware-export-widget :structural-element="selected" :canVisit="canVisit"></courseware-export-widget>
+                <courseware-export-widget :structural-element="selected" :canVisit="canVisit" v-if="!showSearchResults"></courseware-export-widget>
             </MountingPortal>
         </div>
         <studip-progress-indicator
diff --git a/templates/widgets/vue-widget.php b/templates/widgets/vue-widget.php
new file mode 100644
index 00000000000..6c376613e8b
--- /dev/null
+++ b/templates/widgets/vue-widget.php
@@ -0,0 +1 @@
+<div id="<?= htmlReady($id) ?>"></div>
-- 
GitLab