diff --git a/src/main/resources/at/ac/uibk/gitsearch/service/vocabulary/vocabularyServiceConfig.json b/src/main/resources/at/ac/uibk/gitsearch/service/vocabulary/vocabularyServiceConfig.json index a9834eeff133694b9537c39bcab7cca6f07ffa80..338bec456f4783bc1e2aa6f55f3cd5576c09fffb 100644 --- a/src/main/resources/at/ac/uibk/gitsearch/service/vocabulary/vocabularyServiceConfig.json +++ b/src/main/resources/at/ac/uibk/gitsearch/service/vocabulary/vocabularyServiceConfig.json @@ -148,7 +148,7 @@ "OEResourceType": "Audience", "type": "java.lang.String", "required": "OPTIONAL", - "extraEntries": ["Anfänger", "Fortgeschrittene"] + "extraEntries": ["Anfänger"] }, { "property": "educationalLevel", diff --git a/src/main/webapp/app/exercise/exercise-details/exercise-details.component.ts b/src/main/webapp/app/exercise/exercise-details/exercise-details.component.ts index 7777f97641f34fac3d534a8864b325a7918f0ad8..c86c711692cc68584117008795282e3e5edd56dd 100644 --- a/src/main/webapp/app/exercise/exercise-details/exercise-details.component.ts +++ b/src/main/webapp/app/exercise/exercise-details/exercise-details.component.ts @@ -1,7 +1,7 @@ import { HttpResponse } from '@angular/common/http'; import { AfterViewInit, Component, EventEmitter, Input, OnDestroy, OnInit, Output } from '@angular/core'; import { Router } from '@angular/router'; -import { faArrowRight, faFolderOpen } from '@fortawesome/free-solid-svg-icons'; +import { faArrowRight } from '@fortawesome/free-solid-svg-icons'; import { ReviewManagementService } from 'app/admin/review-management/review-management.service'; import { ApplicationInfoService } from 'app/core/application/applicationInfo.service'; import { Account } from 'app/core/auth/account.model'; @@ -84,7 +84,11 @@ export class ExerciseBodyComponent implements OnInit, OnDestroy, AfterViewInit { return this.exercise; } set referencedExercise(exercise: SearchResultDTO | undefined) { - this.exercise = exercise as ExtendedSearchResultDTO; + const newExercise = exercise as ExtendedSearchResultDTO; + if (newExercise && newExercise.exerciseId !== this.exercise?.exerciseId) { + this.exercise = exercise as ExtendedSearchResultDTO; + this.updateParent(this.exercise); + } } exercise: ExtendedSearchResultDTO | undefined; @@ -100,7 +104,6 @@ export class ExerciseBodyComponent implements OnInit, OnDestroy, AfterViewInit { likeSubscription?: Subscription; authenticated = false; - treeIcon = faFolderOpen; faArrowRight = faArrowRight; oerLink?: string; @@ -206,8 +209,10 @@ export class ExerciseBodyComponent implements OnInit, OnDestroy, AfterViewInit { this.router.navigate(['/search'], { queryParams: { p: parentId } }); } - toParent(parentId: string): void { - this.toExercise(parentId); + toParent(): void { + if (this.referencedExercise) { + this.toExercise(this.referencedExercise.file.parentId); + } } toExercise(exerciseId: string): void { @@ -313,6 +318,8 @@ export class ExerciseBodyComponent implements OnInit, OnDestroy, AfterViewInit { alert(`exercise ${exercise.file.parentId} cannot be loaded`); }, }); + } else { + this.parent = undefined; } } diff --git a/src/main/webapp/app/exercise/exercise-details/exercise-tree/exercise-tree.component.css b/src/main/webapp/app/exercise/exercise-details/exercise-tree/exercise-tree.component.css new file mode 100644 index 0000000000000000000000000000000000000000..f67e336d2ea15429df0104c027d436d48e3bed76 --- /dev/null +++ b/src/main/webapp/app/exercise/exercise-details/exercise-tree/exercise-tree.component.css @@ -0,0 +1,13 @@ +:host { + font-size: small; +} +.exerciseTitle { + font-weight: bold; + cursor: hand; +} +.exerciseParent { + font-style: italic; +} +.childTitle { + cursor: hand; +} diff --git a/src/main/webapp/app/exercise/exercise-details/exercise-tree/exercise-tree.component.html b/src/main/webapp/app/exercise/exercise-details/exercise-tree/exercise-tree.component.html new file mode 100644 index 0000000000000000000000000000000000000000..4d30b5e82106b0639e4a4ad3335dd0171773411f --- /dev/null +++ b/src/main/webapp/app/exercise/exercise-details/exercise-tree/exercise-tree.component.html @@ -0,0 +1,21 @@ +<div><span class="info" jhiTranslate="exercise.details.collectionInfo">Collection info</span>:</div> +<div *ngIf="this.exerciseBodyComponent?.parent" class="exerciseParent" [ngbTooltip]="helpToParent"> + <fa-icon style="padding: 5px 0px 0px 5px" container="body" [icon]="upIcon"></fa-icon + ><span (click)="exerciseBodyComponent!.toParent()">..({{ this.exerciseBodyComponent!.parent!.metadata.title }})</span> +</div> +<ng-template #helpToParent data-container="body"> {{ 'exercise.help.toParent' | translate }} </ng-template> + +<ul> + <li> + <span class="exerciseTitle">{{ this.exerciseBodyComponent?.exercise?.metadata?.title }}</span> + <ul> + <li + *ngFor="let childInfo of this.exerciseBodyComponent?.getChildrenInfos()" + class="childTitle" + (click)="this.exerciseBodyComponent!.toExercise(childInfo.childId)" + > + {{ childInfo.title }} + </li> + </ul> + </li> +</ul> diff --git a/src/main/webapp/app/exercise/exercise-details/exercise-tree/exercise-tree.component.ts b/src/main/webapp/app/exercise/exercise-details/exercise-tree/exercise-tree.component.ts new file mode 100644 index 0000000000000000000000000000000000000000..8a8f14a3ea2a2fd77c4d8c9e15463fd0d36e6fa4 --- /dev/null +++ b/src/main/webapp/app/exercise/exercise-details/exercise-tree/exercise-tree.component.ts @@ -0,0 +1,17 @@ +import { Component, Input } from '@angular/core'; +import { faArrowUp } from '@fortawesome/free-solid-svg-icons'; +import { ExerciseBodyComponent } from '../exercise-details.component'; + +@Component({ + selector: 'exercise-tree', + templateUrl: './exercise-tree.component.html', + styleUrls: ['./exercise-tree.component.css'], +}) +export class ExerciseTreeComponent { + /** + * Provide data for tree. + */ + @Input() exerciseBodyComponent: ExerciseBodyComponent | undefined; + + upIcon = faArrowUp; +} diff --git a/src/main/webapp/app/exercise/exercise-details/exerciseComponents/exercise-body.component.html b/src/main/webapp/app/exercise/exercise-details/exerciseComponents/exercise-body.component.html index 52769fb60217ac177d30ad73a1489d33477951ad..364d70fc493bf15a9d7cae1bc8723274d1bb7aae 100644 --- a/src/main/webapp/app/exercise/exercise-details/exerciseComponents/exercise-body.component.html +++ b/src/main/webapp/app/exercise/exercise-details/exerciseComponents/exercise-body.component.html @@ -126,7 +126,7 @@ </div> <div class="star-container"> <span> - {{ exercise.searchStatistics?.downloads }} + {{ exercise.searchStatistics.downloads }} </span> </div> <ng-template #helpForDownloads> {{ 'exercise.details.downloads' | translate }}</ng-template> @@ -147,29 +147,8 @@ <jhi-bookmark-toggle [exercise]="exercise"></jhi-bookmark-toggle> - <div - *ngIf="exercise.file.parentId" - style="float: left; width: 100%; padding-top: 15px; margin-bottom: 25px" - [ngbTooltip]="helpToParent" - (click)="toParent(exercise.file.parentId)" - > - <span jhiTranslate="exercise.containedIn">This exercise is contained in</span>: - <span - >{{ parent!.metadata.title }} - <fa-icon style="padding: 5px 0px 0px 5px" container="body" [icon]="treeIcon"></fa-icon> - </span> - </div> - <ng-template #helpToParent data-container="body"> {{ 'exercise.help.toParent' | translate }} </ng-template> - - <div *ngIf="hasChildren()"> - <span jhiTranslate="exercise.collectionContains">Collection contains</span>: - <ul style="text-align: left"> - <li *ngFor="let childInfo of getChildrenInfos()" (click)="toExercise(childInfo.childId)" style="cursor: zoom-in"> - <span> - {{ childInfo.title }} - </span> - </li> - </ul> + <div> + <exercise-tree *ngIf="hasChildren() || exercise.file.parentId" [exerciseBodyComponent]="this"></exercise-tree> </div> <button diff --git a/src/main/webapp/app/exercise/exercise.module.ts b/src/main/webapp/app/exercise/exercise.module.ts index 60d79a6d88c41f412a4aab6d198eb0fea4a50f0a..527dde66c0860709b7049461505eaf2bc877a5a5 100644 --- a/src/main/webapp/app/exercise/exercise.module.ts +++ b/src/main/webapp/app/exercise/exercise.module.ts @@ -13,6 +13,7 @@ import { } from './exercise-details/exercise-details.component'; import { ExerciseMetadataItemComponent } from './exercise-details/exercise-metadata/exercise-metadata-item/exercise-metadata-item.component'; import { ExerciseMetadataComponent } from './exercise-details/exercise-metadata/exercise-metadata.component'; +import { ExerciseTreeComponent } from './exercise-details/exercise-tree/exercise-tree.component'; import { ReviewBadgeComponent } from './review-badge/review-badge.component'; @NgModule({ @@ -39,6 +40,7 @@ import { ReviewBadgeComponent } from './review-badge/review-badge.component'; ExerciseMetadataItemComponent, ExerciseHeaderComponent, ExerciseBodyComponent, + ExerciseTreeComponent, ExerciseDetailsNonModalComponent, MarkDownViewerComponent, ReviewBadgeComponent, diff --git a/src/main/webapp/i18n/de/exercise.json b/src/main/webapp/i18n/de/exercise.json index 3ea6fac532ed5d78e7545c377c522bd7dbb5454c..9a16b3199255b77fc323068b69531616cdfabbdf 100644 --- a/src/main/webapp/i18n/de/exercise.json +++ b/src/main/webapp/i18n/de/exercise.json @@ -5,6 +5,7 @@ "notAccessible": "Fehler: Die Aufgabe mit der Id {{param}} ist nicht zugreifbar. Versuchen Sie sich anzumelden.", "details": { "details": "Details", + "collectionInfo": "Informationen zur Aufgabensammlung", "accuracy": "Trefferübereinstimmung", "rating": "Diese Metrik zeigt wie gut die Suchanfrage mit den gefundenen Treffern übereinstimmt.", "bookmark": "Merken", @@ -141,7 +142,7 @@ }, "open": "Aufgabe öffnen", "more": "Mehr ...", - "collection": "Aufgabensammlung", + "collection": "Sammlung", "collectionContains": "Diese Sammlung enthält", "containedIn": "Diese Aufgabe ist enthalten in", "close": "Schließen", diff --git a/src/main/webapp/i18n/en/exercise.json b/src/main/webapp/i18n/en/exercise.json index 526fd6d43440dccb9ed872df60ed70a7d1a008fe..6c9b89eb0cb11bba7b976ebcfdfbdf5e9eb63440 100644 --- a/src/main/webapp/i18n/en/exercise.json +++ b/src/main/webapp/i18n/en/exercise.json @@ -6,6 +6,7 @@ "details": { "details": "Details", "accuracy": "Search accuracy", + "collectionInfo": "Information about the collection", "rating": "This metric shows how well the search term overlaps with the found resources!", "bookmark": "Bookmark", "withChildren": "with children", diff --git a/src/test/java/at/ac/uibk/gitsearch/service/dto/VariousDTOTest.java b/src/test/java/at/ac/uibk/gitsearch/service/dto/VariousDTOTest.java index ba2a28a0dad1c3ffd11aa825322dd9a3d0ca5f26..294ae22d6c82a322ddcc42f34d254bc969971d8e 100644 --- a/src/test/java/at/ac/uibk/gitsearch/service/dto/VariousDTOTest.java +++ b/src/test/java/at/ac/uibk/gitsearch/service/dto/VariousDTOTest.java @@ -39,6 +39,11 @@ class VariousDTOTest { propertiesTester.testProperties(MessageDTO.class); } + @Test + void testBroadCastMessageDTO() { + propertiesTester.testProperties(BroadCastMessageDTO.class); + } + @Test void testMetadataUserDTO() { MetadataUserDTO user = new MetadataUserDTO("name", "affiliation", "abc@def.de"); diff --git a/src/test/java/at/ac/uibk/gitsearch/service/vocabulary/VocabularyServiceTest.java b/src/test/java/at/ac/uibk/gitsearch/service/vocabulary/VocabularyServiceTest.java index c82695fe495749d27d9863f7cbe859f45aa17b70..d5e509a04d7d51090fe07420bf9a39240ccbf1e4 100644 --- a/src/test/java/at/ac/uibk/gitsearch/service/vocabulary/VocabularyServiceTest.java +++ b/src/test/java/at/ac/uibk/gitsearch/service/vocabulary/VocabularyServiceTest.java @@ -105,11 +105,20 @@ class VocabularyServiceTest { @Timeout(60000) void checkAllURIs(String key, URI target) { VocabularyItem[] items = vocabularyRepository.getVocabularyItemsFor(key); - if (!key.equals("Creators") && !key.equals("Contributors") && !key.equals("Publisher")) { + if ( + !key.equals("Creators") && + !key.equals("Contributors") && + !key.equals("Publisher") && + !key.equals("Educational Use") && + !key.equals("Educational Framework") && + !key.equals("Educational Level") + ) { Assert.assertTrue( "vocabulary for " + key + "[" + target.toASCIIString() + "] should contain at least one item", items.length > 0 ); + } else { + Assert.assertEquals("vocabulary for " + key + "[" + target.toASCIIString() + "] expected no items", 0, items.length); } }