diff --git a/app/controllers/CourseController.scala b/app/controllers/CourseController.scala index 53db158b4..decb364dc 100644 --- a/app/controllers/CourseController.scala +++ b/app/controllers/CourseController.scala @@ -71,12 +71,11 @@ class CourseController @Inject() ( DB.find(classOf[Course]).where.isNotNull("name").orderBy("code").findList }.map(_.asScala.toResult(OK)) - private def getUserCourses( - user: User, - examIds: Option[List[Long]], - sectionIds: Option[List[Long]], - tagIds: Option[List[Long]] - ): Result = + private def getUserCourses(user: User, + examIds: Option[List[Long]], + sectionIds: Option[List[Long]], + tagIds: Option[List[Long]], + ownerIds: Option[List[Long]]): Result = var query = DB.find(classOf[Course]).where.isNotNull("name") if !user.hasRole(Role.Name.ADMIN) then query = query @@ -87,6 +86,10 @@ class CourseController @Inject() ( if tagIds.getOrElse(Nil).nonEmpty then query = query.in("exams.examSections.sectionQuestions.question.parent.tags.id", tagIds.get.asJava) + + if (ownerIds.getOrElse(Nil).nonEmpty) + query = + query.in("exams.examOwners.id", ownerIds.get.asJava) query.orderBy("name desc").findList.asScala.toResult(OK) // Actions -> @@ -101,12 +104,11 @@ class CourseController @Inject() ( DB.find(classOf[Course], id).toResult(OK) } - def listUsersCourses( - examIds: Option[List[Long]], - sectionIds: Option[List[Long]], - tagIds: Option[List[Long]] - ): Action[AnyContent] = + def listUsersCourses(examIds: Option[List[Long]], + sectionIds: Option[List[Long]], + tagIds: Option[List[Long]], + ownerIds: Option[List[Long]]): Action[AnyContent] = authenticated.andThen(authorized(Seq(Role.Name.TEACHER, Role.Name.ADMIN))) { request => val user = request.attrs(Auth.ATTR_USER) - getUserCourses(user, examIds, sectionIds, tagIds) + getUserCourses(user, examIds, sectionIds, tagIds, ownerIds) } diff --git a/app/controllers/ExamController.java b/app/controllers/ExamController.java index 9f3ad3a9e..d712209ea 100644 --- a/app/controllers/ExamController.java +++ b/app/controllers/ExamController.java @@ -154,12 +154,14 @@ public Result listExams( Optional> courseIds, Optional> sectionIds, Optional> tagIds, + Optional> ownerIds, Http.Request request ) { User user = request.attrs().get(Attrs.AUTHENTICATED_USER); List courses = courseIds.orElse(Collections.emptyList()); List sections = sectionIds.orElse(Collections.emptyList()); List tags = tagIds.orElse(Collections.emptyList()); + List owners = ownerIds.orElse(Collections.emptyList()); PathProperties pp = PathProperties.parse( "(id, name, examActiveStartDate, examActiveEndDate, course(id, code), examSections(id, name))" ); @@ -178,6 +180,9 @@ public Result listExams( if (!tags.isEmpty()) { el = el.in("examSections.sectionQuestions.question.parent.tags.id", tags); } + if (!owners.isEmpty()) { + el = el.in("questionOwners.id", user); + } return ok(el.findList(), pp); } diff --git a/app/controllers/ExamSectionController.java b/app/controllers/ExamSectionController.java index f5eca8fd6..772365c86 100644 --- a/app/controllers/ExamSectionController.java +++ b/app/controllers/ExamSectionController.java @@ -624,6 +624,7 @@ public Result listSections( Optional> courseIds, Optional> examIds, Optional> tagIds, + Optional> ownerIds, Http.Request request ) { User user = request.attrs().get(Attrs.AUTHENTICATED_USER); @@ -644,6 +645,9 @@ public Result listSections( if (tagIds.isPresent() && !tagIds.get().isEmpty()) { query = query.in("examSectionQuestions.question.tags.id", tagIds.get()); } + if (ownerIds.isPresent() && !ownerIds.get().isEmpty()) { + query = query.in("questionOwners.id", ownerIds.get()); + } Set sections = query.findSet(); return ok(sections, PathProperties.parse("(*, creator(id))")); } diff --git a/app/controllers/QuestionController.java b/app/controllers/QuestionController.java index 71591436c..d5dfd4809 100644 --- a/app/controllers/QuestionController.java +++ b/app/controllers/QuestionController.java @@ -84,11 +84,12 @@ public Result getQuestions( List courseIds, List tagIds, List sectionIds, + List ownerIds, Http.Request request ) { User user = request.attrs().get(Attrs.AUTHENTICATED_USER); if ( - user.hasRole(Role.Name.ADMIN) && Stream.of(examIds, courseIds, tagIds, sectionIds).allMatch(List::isEmpty) + user.hasRole(Role.Name.ADMIN) && Stream.of(examIds, courseIds, tagIds, sectionIds, ownerIds).allMatch(List::isEmpty) ) { return ok(Collections.emptySet()); } @@ -104,21 +105,23 @@ public Result getQuestions( .endJunction() .ne("state", QuestionState.DELETED.toString()); if (user.hasRole(Role.Name.TEACHER)) { - el = el.disjunction().eq("shared", true).eq("questionOwners", user).endJunction(); - } - if (!examIds.isEmpty()) { - el = el.in("examSectionQuestions.examSection.exam.id", examIds); - } - if (!courseIds.isEmpty()) { - el = el.in("examSectionQuestions.examSection.exam.course.id", courseIds); - } - if (!tagIds.isEmpty()) { - el = el.in("tags.id", tagIds); - } - if (!sectionIds.isEmpty()) { - el = el.in("examSectionQuestions.examSection.id", sectionIds); + if (ownerIds.isEmpty()) { + el = el.eq("questionOwners", user); + } else { + el = el.in("questionOwners.id", ownerIds); + } + } else { + el = el.inOrEmpty("questionOwners.id", ownerIds); } + el = el.inOrEmpty("examSectionQuestions.examSection.exam.id", examIds); + el = el.inOrEmpty("examSectionQuestions.examSection.exam.course.id", courseIds); + el = el.inOrEmpty("tags.id", tagIds); + el = el.inOrEmpty("examSectionQuestions.examSection.id", sectionIds); + Set questions = el.orderBy("created desc").findSet(); + if (user.hasRole(Role.Name.TEACHER) && !ownerIds.isEmpty()) { + questions = questions.stream().filter(question -> question.getQuestionOwners().contains(user)).collect(Collectors.toSet()); + } return ok(questions, pp); } diff --git a/app/controllers/TagController.java b/app/controllers/TagController.java index 5a46989cd..351357b78 100644 --- a/app/controllers/TagController.java +++ b/app/controllers/TagController.java @@ -44,6 +44,7 @@ public Result listTags( Optional> courseIds, Optional> examIds, Optional> sectionIds, + Optional> ownerIds, Http.Request request ) { User user = request.attrs().get(Attrs.AUTHENTICATED_USER); @@ -64,6 +65,9 @@ public Result listTags( if (sectionIds.isPresent() && !sectionIds.get().isEmpty()) { query = query.in("questions.examSectionQuestions.examSection.id", sectionIds.get()); } + if (ownerIds.isPresent() && !ownerIds.get().isEmpty()) { + query = query.in("questions.questionOwners.id", ownerIds.get()); + } Set tags = query.findSet(); return ok(tags, PathProperties.parse("(*, creator(id), questions(id))")); } diff --git a/conf/routes b/conf/routes index 39e0a9277..af86c3cc3 100644 --- a/conf/routes +++ b/conf/routes @@ -3,7 +3,9 @@ GET / controlle ############### Session interface ############ GET /app/session controllers.SessionController.checkSession(request: Request) PUT /app/session controllers.SessionController.extendSession(request: Request) ++nocsrf POST /app/session controllers.SessionController.login(request: Request) ++nocsrf DELETE /app/session controllers.SessionController.logout(request: Request) ############### User interface ############### @@ -33,7 +35,7 @@ PUT /app/exams/:eid/course/:cid controlle GET /app/exams/:id/preview controllers.ExamController.getExamPreview(id: Long, request: Request) GET /app/reviewerexams controllers.ExamController.getTeachersExams(request: Request) -GET /app/examsearch controllers.ExamController.listExams(courseIds: java.util.Optional[LongList], sectionIds: java.util.Optional[LongList], tagIds: java.util.Optional[LongList], request: Request) +GET /app/examsearch controllers.ExamController.listExams(courseIds: java.util.Optional[LongList], sectionIds: java.util.Optional[LongList], tagIds: java.util.Optional[LongList], owner: java.util.Optional[LongList], request: Request) PUT /app/exam/:eid/software/:sid controllers.ExamController.updateExamSoftware(eid: Long, sid: Long, request: Request) @@ -121,7 +123,7 @@ DELETE /app/exams/:eid/sections/:sid/questions/:qid controlle PUT /app/exams/:eid/sections/:sid/reorder controllers.ExamSectionController.reorderSectionQuestions(eid: Long, sid: Long, request: Request) PUT /app/exams/:eid/reorder controllers.ExamSectionController.reorderSections(eid: Long, request: Request) GET /app/exams/question/:id/distribution controllers.ExamSectionController.getQuestionDistribution(id: Long) -GET /app/sections controllers.ExamSectionController.listSections(filter: java.util.Optional[String], courseIds: java.util.Optional[LongList], examIds: java.util.Optional[LongList], tagIds: java.util.Optional[LongList], request: Request) +GET /app/sections controllers.ExamSectionController.listSections(filter: java.util.Optional[String], courseIds: java.util.Optional[LongList], examIds: java.util.Optional[LongList], tagIds: java.util.Optional[LongList], ownerIds: java.util.Optional[LongList], request: Request) ############### Section material interface ############### GET /app/materials controllers.ExamMaterialController.listMaterials(request: Request) @@ -181,12 +183,12 @@ DELETE /app/enrolments/configs/:eecid controlle ############### Courses interface ############### GET /app/courses controllers.CourseController.getCourses(filter: Option[String], q: Option[String]) -GET /app/courses/user controllers.CourseController.listUsersCourses(examIds: Option[List[Long]], sectionIds: Option[List[Long]], tagIds: Option[List[Long]]) +GET /app/courses/user controllers.CourseController.listUsersCourses(examIds: Option[List[Long]], sectionIds: Option[List[Long]], tagIds: Option[List[Long]], ownerIds: Option[List[Long]]) GET /app/courses/:id controllers.CourseController.getCourse(id: Long) ############### Question interface ############### -GET /app/questions controllers.QuestionController.getQuestions(exam: java.util.List[java.lang.Long], course: java.util.List[java.lang.Long], tag: java.util.List[java.lang.Long], section: java.util.List[java.lang.Long], request: Request) +GET /app/questions controllers.QuestionController.getQuestions(exam: java.util.List[java.lang.Long], course: java.util.List[java.lang.Long], tag: java.util.List[java.lang.Long], section: java.util.List[java.lang.Long], owner: java.util.List[java.lang.Long], request: Request) GET /app/questions/:id controllers.QuestionController.getQuestion(id: Long, request: Request) PUT /app/questions/:id controllers.QuestionController.updateQuestion(id: Long, request: Request) @@ -409,7 +411,7 @@ GET /app/availability/:roomId/:date controlle GET /app/languages controllers.LanguageController.getSupportedLanguages ################# Tag interface ################## -GET /app/tags controllers.TagController.listTags(filter: java.util.Optional[String], courseIds: java.util.Optional[LongList], examIds: java.util.Optional[LongList], sectionIds: java.util.Optional[LongList], request: Request) +GET /app/tags controllers.TagController.listTags(filter: java.util.Optional[String], courseIds: java.util.Optional[LongList], examIds: java.util.Optional[LongList], sectionIds: java.util.Optional[LongList], owner: java.util.Optional[LongList], request: Request) POST /app/tags/questions controllers.TagController.addTagToQuestions(request: Request) ################# General Settings interface ################## diff --git a/ui/src/app/question/library/library.service.ts b/ui/src/app/question/library/library.service.ts index 66877a1a0..612fca14d 100644 --- a/ui/src/app/question/library/library.service.ts +++ b/ui/src/app/question/library/library.service.ts @@ -19,6 +19,7 @@ import type { Observable } from 'rxjs'; import { map } from 'rxjs/operators'; import type { Course, Exam, ExamSection, ReverseQuestion, Tag } from '../../exam/exam.model'; import { QuestionService } from '../question.service'; +import {User} from "../../session/session.service"; export interface LibraryQuestion extends ReverseQuestion { icon: string; @@ -36,20 +37,23 @@ export class LibraryService { private Question: QuestionService, ) {} - listExams$ = (courseIds: number[], sectionIds: number[], tagIds: number[]): Observable => - this.http.get('/app/examsearch', { params: this.getQueryParams(courseIds, [], sectionIds, tagIds) }); + listExams$ = (courseIds: number[], sectionIds: number[], tagIds: number[], ownerIds: number[]): Observable => + this.http.get('/app/examsearch', { params: this.getQueryParams(courseIds, [], sectionIds, tagIds, ownerIds) }); - listCourses$ = (examIds: number[], sectionIds: number[], tagIds: number[]): Observable => - this.http.get('/app/courses/user', { params: this.getQueryParams([], examIds, sectionIds, tagIds) }); + listCourses$ = (examIds: number[], sectionIds: number[], tagIds: number[], ownerIds: number[]): Observable => + this.http.get('/app/courses/user', { params: this.getQueryParams([], examIds, sectionIds, tagIds, ownerIds) }); - listSections$ = (courseIds: number[], examIds: number[], tagIds: number[]): Observable => - this.http.get('/app/sections', { params: this.getQueryParams(courseIds, examIds, [], tagIds) }); + listSections$ = (courseIds: number[], examIds: number[], tagIds: number[], ownerIds: number[]): Observable => + this.http.get('/app/sections', { params: this.getQueryParams(courseIds, examIds, [], tagIds, ownerIds) }); - listTags$ = (courseIds: number[], examIds: number[], sectionIds: number[]): Observable => - this.http.get('/app/tags', { params: this.getQueryParams(courseIds, sectionIds, examIds, []) }); + listTags$ = (courseIds: number[], examIds: number[], sectionIds: number[], ownerIds: number[]): Observable => + this.http.get('/app/tags', { params: this.getQueryParams(courseIds, sectionIds, examIds, [], ownerIds) }); listAllTags$ = (): Observable => this.http.get('/app/tags'); + listAllOwners$ = (): Observable => + this.http.get('/app/users'); + addTagForQuestions$ = (tagId: number, questionIds: number[]) => this.http.post('/app/tags/questions', { questionIds: questionIds, tagId: tagId }); @@ -93,7 +97,7 @@ export class LibraryService { if (text) { return questions.filter((question) => { const re = new RegExp(text, 'i'); - const owner = question.creator ? question.creator.firstName + ' ' + question.creator.lastName : ''; + const owner = question.questionOwners.map((o)=> o.firstName + ' ' + o.lastName).toString(); return owner.match(re); }); } else { @@ -106,10 +110,11 @@ export class LibraryService { examIds: number[], sectionIds: number[], tagIds: number[], + ownerIds: number[] ): Observable => this.http .get('/app/questions', { - params: this.getQueryParams(courseIds, examIds, sectionIds, tagIds), + params: this.getQueryParams(courseIds, examIds, sectionIds, tagIds, ownerIds), }) .pipe( map((questions) => { @@ -137,7 +142,7 @@ export class LibraryService { }), ); - private getQueryParams = (courseIds: number[], examIds: number[], sectionIds: number[], tagIds: number[]) => { + private getQueryParams = (courseIds: number[], examIds: number[], sectionIds: number[], tagIds: number[], ownerIds: number[]) => { let params = new HttpParams(); const append = (key: string, idArray: number[], paramsObj: HttpParams) => { @@ -156,6 +161,9 @@ export class LibraryService { if (examIds.length > 0) { params = append('exam', examIds, params); } + if (ownerIds.length > 0) { + params = append('owner', ownerIds, params); + } return params; }; diff --git a/ui/src/app/question/library/results/library-results.component.html b/ui/src/app/question/library/results/library-results.component.html index cc3020586..c85d8d77d 100644 --- a/ui/src/app/question/library/results/library-results.component.html +++ b/ui/src/app/question/library/results/library-results.component.html @@ -24,6 +24,16 @@ > + + + + + + {{ question.creator?.firstName }} {{ question.creator?.lastName }} + + + @if (question.modified) { {{ question.modified | date: 'dd.MM.yyyy HH:mm' }} diff --git a/ui/src/app/question/library/search/library-search.component.html b/ui/src/app/question/library/search/library-search.component.html index c2fe34ec7..d80b58ad9 100644 --- a/ui/src/app/question/library/search/library-search.component.html +++ b/ui/src/app/question/library/search/library-search.component.html @@ -1,5 +1,5 @@
-
+
-
-
-
-
-
- -
- + + +
- @if (user.isAdmin) { -
+ +
  • + +
  • + + +
    +
    +
    @@ -183,10 +199,8 @@
    - }
    -
      diff --git a/ui/src/app/question/library/search/library-search.component.ts b/ui/src/app/question/library/search/library-search.component.ts index c46630ae4..e84521a7d 100644 --- a/ui/src/app/question/library/search/library-search.component.ts +++ b/ui/src/app/question/library/search/library-search.component.ts @@ -12,7 +12,7 @@ * on an "AS IS" basis, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the Licence for the specific language governing permissions and limitations under the Licence. */ -import { NgClass } from '@angular/common'; +import {NgClass, NgForOf} from '@angular/common'; import type { OnInit } from '@angular/core'; import { Component, EventEmitter, Output } from '@angular/core'; import { FormsModule } from '@angular/forms'; @@ -42,13 +42,13 @@ interface Filterable { selector: 'xm-library-search', templateUrl: './library-search.component.html', standalone: true, - imports: [NgClass, NgbDropdown, NgbDropdownToggle, NgbDropdownMenu, NgbDropdownItem, FormsModule, TranslateModule], + imports: [NgClass, NgbDropdown, NgbDropdownToggle, NgbDropdownMenu, NgbDropdownItem, FormsModule, TranslateModule, NgForOf], }) export class LibrarySearchComponent implements OnInit { @Output() updated: EventEmitter = new EventEmitter(); - filter = { owner: '', text: '' }; - limitations = { course: '', exam: '', section: '', tag: '' }; + filter = {owner: '', text: '' }; + limitations = { course: '', exam: '', section: '', tag: '', owner: '' }; user: User; courses: Filterable[] = []; filteredCourses = this.courses; @@ -58,6 +58,8 @@ export class LibrarySearchComponent implements OnInit { filteredSections = this.sections; tags: Filterable[] = []; filteredTags = this.tags; + owners: Filterable[] = []; + filteredOwners = this.owners; questions: LibraryQuestion[] = []; constructor( @@ -75,6 +77,7 @@ export class LibrarySearchComponent implements OnInit { this.exams = this.filteredExams = storedData.filters.exams || []; this.sections = this.filteredSections = storedData.filters.sections || []; this.tags = this.filteredTags = storedData.filters.tags || []; + this.owners = this.filteredOwners = storedData.filters.owners || []; this.filter.text = storedData.filters.text; this.filter.owner = storedData.filters.owner; this.query$().subscribe((questions) => { @@ -95,10 +98,19 @@ export class LibrarySearchComponent implements OnInit { this.updated.emit(results); this.saveFilters(); }; + applyOwnerSearchFilter = (user: Filterable) => { + user.filtered = !user.filtered + if (user.name){ + this.filter.owner = user.name + } else { + console.error('i18n_user_no_name') + } + this.applySearchFilter() + } listCourses = () => { const courses = this.courses.filter((c) => c.filtered); - return this.Library.listCourses$(this.getExamIds(), this.getSectionIds(), this.getTagIds()) + return this.Library.listCourses$(this.getExamIds(), this.getSectionIds(), this.getTagIds(), this.getOwnerIds()) .pipe( tap((resp) => { this.courses = this.filteredCourses = this.union( @@ -110,7 +122,7 @@ export class LibrarySearchComponent implements OnInit { object: r, filtered: false, })), - ); + ).sort((a, b) => a.name.localeCompare(b.name)); }), ) .subscribe(); @@ -118,7 +130,8 @@ export class LibrarySearchComponent implements OnInit { listExams$ = (): Observable => { const exams = this.exams.filter((e) => e.filtered); - return this.Library.listExams$(this.getCourseIds(), this.getSectionIds(), this.getTagIds()).pipe( + return this.Library.listExams$(this.getCourseIds(), this.getSectionIds(), this.getTagIds(), this.getOwnerIds()) + .pipe( tap((resp) => { this.exams = this.filteredExams = this.union( exams, @@ -130,14 +143,14 @@ export class LibrarySearchComponent implements OnInit { code: r.course?.code, period: this.formatPeriod(r.periodStart, r.periodEnd), })), - ); + ).sort((a, b) => a.name.localeCompare(b.name)); }), ); }; listSections$ = (): Observable => { const sections = this.sections.filter((s) => s.filtered); - return this.Library.listSections$(this.getCourseIds(), this.getExamIds(), this.getTagIds()).pipe( + return this.Library.listSections$(this.getCourseIds(), this.getExamIds(), this.getTagIds(), this.getOwnerIds()).pipe( tap((resp) => { this.sections = this.filteredSections = this.union( sections, @@ -147,14 +160,14 @@ export class LibrarySearchComponent implements OnInit { object: r, filtered: false, })), - ); + ).sort((a, b) => a.name.localeCompare(b.name)); }), ); }; listTags$ = () => { const tags = this.tags.filter((t) => t.filtered); - return this.Library.listTags$(this.getCourseIds(), this.getExamIds(), this.getSectionIds()).pipe( + return this.Library.listTags$(this.getCourseIds(), this.getExamIds(), this.getSectionIds(), this.getOwnerIds()).pipe( tap((resp) => { this.tags = this.filteredTags = this.union( tags, @@ -165,18 +178,62 @@ export class LibrarySearchComponent implements OnInit { filtered: false, usage: r.questions.length, })), - ); + ).sort((a, b) => a.name.localeCompare(b.name)); }), ); }; + listAllOwners$ = () => { + if (this.user.isAdmin) { + const owners = this.owners.filter((o) => o.filtered); + return this.Library.listAllOwners$() + .pipe( + tap((resp) => { + this.owners = this.filteredOwners = this.union( + owners, + resp.map((r) => ({ + id: r.id, + name: r.firstName + ' ' + r.lastName || '', + object: r, + filtered: false, + isSectionTag: false, + })), + ).filter((o)=> !o.object.isTeacher || !o.object.isAdmin).sort((a, b) => a.name.localeCompare(b.name)); + }), + ) + .subscribe(); + } else { + const questionOwners = this.questions.flatMap((q) => q.questionOwners); + const uniqueMap: Record = {}; + // Filter out duplicates based on the 'id' property + const uniqueArray = questionOwners.filter(obj => { + if (!uniqueMap[obj.id]) { + uniqueMap[obj.id] = obj; + return true; + } + return false; + }); + const commonOwners = uniqueArray.map((o) => { + return { + id: o.id, + name: o.firstName + ' ' + o.lastName || '', + object: o, + filtered: false, + isSectionTag: false, + } as Filterable; + }); + return this.owners = this.filteredOwners = this.filteredOwners.length == 0 ? commonOwners : this.filteredOwners + } +}; + getFilters = () => { const courses = this.courses.filter((c) => c.filtered); const exams = this.exams.filter((e) => e.filtered); const sections = this.sections.filter((s) => s.filtered); const tags = this.tags.filter((t) => t.filtered); - const res: (Filterable | Filterable | Filterable | Filterable)[] = []; - return res.concat(courses).concat(exams).concat(sections).concat(tags); + const owners = this.owners.filter((o) => o.filtered); + const res: (Filterable | Filterable | Filterable | Filterable | Filterable)[] = []; + return res.concat(courses).concat(exams).concat(sections).concat(tags).concat(owners); }; applyFilter = (f: Filterable) => { @@ -215,6 +272,12 @@ export class LibrarySearchComponent implements OnInit { formatPeriod = (s: string | null, e: string | null) => s && e ? `${DateTime.fromISO(s).toFormat('dd.LL.yyyy')}-${DateTime.fromISO(e).toFormat('dd.LL.yyyy')}` : ''; + filterOwners = () => { + this.filteredOwners = this.owners.filter( + (t) => t.name.toLowerCase().indexOf(this.limitations.owner.toLowerCase()) > -1, + ); + }; + private saveFilters = () => { const filters = { courses: this.courses, @@ -231,6 +294,7 @@ export class LibrarySearchComponent implements OnInit { private getExamIds = () => this.exams.filter((exam) => exam.filtered).map((exam) => exam.id); private getTagIds = () => this.tags.filter((tag) => tag.filtered).map((tag) => tag.id); private getSectionIds = () => this.sections.filter((section) => section.filtered).map((section) => section.id); + private getOwnerIds = () => this.owners.filter((owner) => owner.filtered).map((owner)=> owner.id); private union(filtered: Filterable[], tags: Filterable[]): Filterable[] { const filteredIds = filtered.map((f) => f.id); @@ -238,7 +302,7 @@ export class LibrarySearchComponent implements OnInit { } private query$ = (): Observable => - this.Library.search(this.getCourseIds(), this.getExamIds(), this.getSectionIds(), this.getTagIds()).pipe( + this.Library.search(this.getCourseIds(), this.getExamIds(), this.getSectionIds(), this.getTagIds(), this.getOwnerIds()).pipe( tap((questions) => { this.questions = questions; this.saveFilters(); diff --git a/ui/src/assets/i18n/en.json b/ui/src/assets/i18n/en.json index cdb6f44cc..968a5bcac 100644 --- a/ui/src/assets/i18n/en.json +++ b/ui/src/assets/i18n/en.json @@ -1159,5 +1159,6 @@ "i18n_button_preview": "Preview question", "i18n_no_preview_available": "No preview available", "i18n_used_in_exams": "Used in exams", - "i18n_quit_password": "SEB-poistumissalasana EN" + "i18n_quit_password": "SEB-poistumissalasana EN", + "i18n_creator": "Creator" } diff --git a/ui/src/assets/i18n/fi.json b/ui/src/assets/i18n/fi.json index f711b9ba1..0bc62aab4 100644 --- a/ui/src/assets/i18n/fi.json +++ b/ui/src/assets/i18n/fi.json @@ -1159,5 +1159,6 @@ "i18n_button_preview": "Esikatsele kysymys", "i18n_no_preview_available": "Kysymyksen esikatselu ei ole saatavilla", "i18n_used_in_exams": "Käytössä tenteissä", - "i18n_quit_password": "SEB-poistumissalasana" + "i18n_quit_password": "SEB-poistumissalasana", + "i18n_creator": "Luonut" } diff --git a/ui/src/assets/i18n/sv.json b/ui/src/assets/i18n/sv.json index fb763b1f8..1f02cdc86 100644 --- a/ui/src/assets/i18n/sv.json +++ b/ui/src/assets/i18n/sv.json @@ -1159,5 +1159,6 @@ "i18n_button_preview": "Preview question SV", "i18n_no_preview_available": "No preview available SV", "i18n_used_in_exams": "Käytössä tenteissä SV", - "i18n_quit_password": "SEB-poistumissalasana SV" + "i18n_quit_password": "SEB-poistumissalasana SV", + "i18n_creator": "Luoja SV" }