diff --git a/assets/css/courseware.scss b/assets/css/courseware.scss index ee322ab3..c67b1215 100644 --- a/assets/css/courseware.scss +++ b/assets/css/courseware.scss @@ -28,6 +28,57 @@ white-space: nowrap; } +.oc-cw-searchbar .oc-cw-tokenselector { + position: absolute; + z-index: 10; + border: thin solid $content-color-40; + background-color: $white; + margin-top: 12px; + min-width: 130px; + + @include arrow-top-border(9px, white, 1px, $content-color-40, 12px); + + &::before, &::after { + right: 90%; + } + + ul, li { + list-style-type: none; + padding: 0; + margin: 0; + } + + li { + display: block; + padding: 0.5em; + cursor: pointer; + + &:hover { + background-color: $light-gray-color-20; + } + } +} + +.oc-cw-searchbar .oc-cw-searchbar-token { + margin: 3px; + padding: 5px; + border: solid thin $black; + background-color: $content-color-20; + + display: inline; + + span { + margin: 0 5px; + display: inline-block; + vertical-align: middle; + } + + .oc-cw-remove-filter { + vertical-align: middle; + cursor: pointer; + } +} + .oc-cw-searchbar .oc-cw-searchbar-container .oc-cw-searchbar-input { padding: 8px; width: 100%; diff --git a/courseware/vueapp/components/CoursewareSearchBar.vue b/courseware/vueapp/components/CoursewareSearchBar.vue index efc1180d..e81d0c10 100644 --- a/courseware/vueapp/components/CoursewareSearchBar.vue +++ b/courseware/vueapp/components/CoursewareSearchBar.vue @@ -1,12 +1,24 @@ @@ -26,19 +48,64 @@ export default { name: "CoursewareSearchBar", + props: { + currentCourseSelectable: { + type: Boolean, + default: true, + }, + showCurrentCourse: { + type: Boolean, + default: true, + }, + }, + + emits: ['doSearch'], + data() { return { inputSearch: '', + showCourseSelector: false, + selectorPos: { + top: 0, + left: 0 + }, timer: null, delay: 800 //ms } }, methods: { + openCourseSelector() { + // filter needs to be selectable and not active + if (this.currentCourseSelectable && !this.showCurrentCourse) { + this.showCourseSelector = true; + + this.selectorPos.top = this.$refs.searchbar.offsetTop + 30; + this.selectorPos.left = this.$refs.searchbar.offsetLeft; + } + }, + + // this is done in order to avoid hiding (and therefore deactivating the token selector) + // before the click-events of the token selector had a chance to fire + delayedHideCourseSelector() { + let view = this; + + window.setTimeout(() => { + view.hideCourseSelector(); + }, 200); + }, + + hideCourseSelector() { + this.showCourseSelector = false; + }, + doSearch() { clearTimeout(this.timer); - this.$emit('doSearch', this.inputSearch); + this.$emit('doSearch', { + searchText: this.inputSearch, + showCurrentCourse: this.showCurrentCourse, + }); }, doLiveSearch() { @@ -47,6 +114,23 @@ export default { this.timer = setTimeout(() => { this.doSearch(); }, this.delay); + }, + + selectCourse() { + this.showCurrentCourse = true; + this.hideCourseSelector(); + this.doSearch(); + }, + + removeCourse() { + this.showCurrentCourse = false; + this.doSearch(); + } + }, + + updated() { + if (this.showCourseSelector) { + this.openCourseSelector(); } }, } diff --git a/courseware/vueapp/courseware-plugin-opencast-video.vue b/courseware/vueapp/courseware-plugin-opencast-video.vue index 6ee17bdd..bded2317 100644 --- a/courseware/vueapp/courseware-plugin-opencast-video.vue +++ b/courseware/vueapp/courseware-plugin-opencast-video.vue @@ -36,6 +36,8 @@ Videos video.token === this.currentVideoId); }, @@ -168,8 +175,9 @@ export default { }; }, - performSearch(searchText) { + performSearch({searchText, showCurrentCourse}) { this.searchText = searchText; + this.showCurrentCourse = showCurrentCourse; // this.resetPaging(); this.loadVideos(); }, @@ -217,6 +225,7 @@ export default { }, initCurrentData() { + this.showCurrentCourse = this.isCourse; this.currentVideoId = get(this.block, "attributes.payload.token", ""); this.currentEpisodeURL =STUDIP.ABSOLUTE_URI_STUDIP + 'plugins.php/opencastv3/redirect/perform/video/' + this.currentVideoId; this.currentVisible = get(this.block, "attributes.payload.visible", ""); @@ -240,11 +249,24 @@ export default { if (this.sortObj) { params.append('order', this.sortObj.field + "_" + this.sortObj.order) } + + let filters = []; if (this.searchText) { - let filters = [{ + filters.push({ type: 'text', value: this.searchText - }]; + }); + } + + if (this.showCurrentCourse) { + filters.push({ + type: 'course', + compare: '=', + value: this.context.id + }); + } + + if (filters.length > 0) { params.append('filters', JSON.stringify(filters)); } axios diff --git a/lib/Models/Videos.php b/lib/Models/Videos.php index 2d09b385..bc52a577 100644 --- a/lib/Models/Videos.php +++ b/lib/Models/Videos.php @@ -60,14 +60,14 @@ public static function getFilteredVideoIDs($user_id) { global $perm; - if ($perm->have_perm('admin', $user_id)) { + if ($perm->have_perm('tutor', $user_id)) { // get all courses and their playlists this user has access to. Only courses with activated OC plugin are included $courses = Helpers::getMyCourses($user_id); $stmt = \DBManager::get()->prepare($sql = 'SELECT oc_video.id FROM oc_video JOIN oc_playlist_seminar ON (oc_playlist_seminar.seminar_id IN (:courses)) JOIN oc_playlist ON (oc_playlist_seminar.playlist_id = oc_playlist.id) - JOIN oc_playlist_video ON (oc_playlist.id = oc_playlist_video.playlist_id) + JOIN oc_playlist_video ON (oc_playlist.id = oc_playlist_video.playlist_id AND oc_video.id = oc_playlist_video.video_id) WHERE 1 '); @@ -190,8 +190,8 @@ public static function getUserVideos($filters, $user_id = null) ':user_id'=> $user_id ]; - if ($perm->have_perm('admin', $user_id)) { - $where = ' WHERE oc_video.id IN (:video_ids) '; + if ($perm->have_perm('tutor', $user_id)) { + $where = ' WHERE oc_video.id IN (:video_ids) OR p.user_id = :user_id'; $params[':video_ids'] = self::getFilteredVideoIDs($user_id); } else { $sql = ' INNER JOIN oc_video_user_perms AS p ON (p.user_id = :user_id AND p.video_id = oc_video.id) ';