This is the codeAbility Sharing Platform! Learn more about the codeAbility Sharing Platform.

Skip to content
Snippets Groups Projects
exercise-details.component.ts 10.3 KiB
Newer Older
import { HttpResponse } from '@angular/common/http';
import { AfterViewInit, Component, EventEmitter, Input, OnDestroy, OnInit, Output } from '@angular/core';
import { faArrowRight, faFolderOpen } from '@fortawesome/free-solid-svg-icons';
Eduard Frankford's avatar
Eduard Frankford committed
import { ReviewManagementService } from 'app/admin/review-management/review-management.service';
Daniel Rainer's avatar
Daniel Rainer committed
import { ApplicationInfoService } from 'app/core/application/applicationInfo.service';
import { Account } from 'app/core/auth/account.model';
Daniel Rainer's avatar
Daniel Rainer committed
import { AccountService } from 'app/core/auth/account.service';
import { AlertService } from 'app/core/util/alert.service';
import { LikesService } from 'app/entities/likes/likes.service';
import { ExerciseService } from 'app/exercise/service/exercise.service';
import { SearchService } from 'app/search/service/search-service';
import { ShoppingBasketInfo, ShoppingBasketRedirectInfoDTO } from 'app/shared/model/basket/shopping-basket-info.model';
import { Person } from 'app/shared/model/person.model';
import {
  ChildInfo,
  ExtendedSearchResultDTO,
  hasChildren,
  PluginActionInfo,
  SearchResultDTO,
} from 'app/shared/model/search/search-result-dto.model';
import { PluginService } from 'app/shared/service/plugin-service';
import { WatchlistManager } from 'app/shared/watchlist/watchlist-manager';
  selector: 'jhi-exercise-modal-details',
  templateUrl: './exercise-details-modal.component.html',
export class ExerciseDetailsModalComponent {
  @Output() exerciseChangedEvent = new EventEmitter<SearchResultDTO>();

  @Input() get referencedExercise(): SearchResultDTO | undefined {
    return this.exercise;
  }
  set referencedExercise(exercise: SearchResultDTO | undefined) {
    this.exercise = exercise as ExtendedSearchResultDTO;
  }

  exercise: ExtendedSearchResultDTO | undefined;
  constructor(private watchlistManager: WatchlistManager) {}

  handleExerciseChangedEvent(exercise: SearchResultDTO): void {
Michael Breu's avatar
Michael Breu committed
    this.exerciseChangedEvent.emit(exercise);
  }
Michael Breu's avatar
Michael Breu committed
  selector: 'jhi-exercise-nonmodal-details',
  templateUrl: './exercise-details-nonmodal.component.html',
})
export class ExerciseDetailsNonModalComponent {
  @Input() exercise: ExtendedSearchResultDTO | undefined;
Michael Breu's avatar
Michael Breu committed
}
Michael Breu's avatar
Michael Breu committed
  selector: 'jhi-exercise-header',
  templateUrl: './exerciseComponents/exercise-header.component.html',
  styleUrls: ['./exerciseComponents/exercise-header.component.scss'],
})
export class ExerciseHeaderComponent {
  @Input() exercise: ExtendedSearchResultDTO | undefined;
Michael Breu's avatar
Michael Breu committed

  /**
   * correct missing image urls
   */
  correctImageURL(event: Event): void {
    const srcElement = event.target as HTMLImageElement;
Michael Breu's avatar
Michael Breu committed
    if (srcElement) {
      srcElement.src = '/content/images/Logo_codeAbility_4c_300dpi_RGB3.gif';
    }
  }
Michael Breu's avatar
Michael Breu committed
  selector: 'jhi-exercise-body',
  templateUrl: './exerciseComponents/exercise-body.component.html',
  styleUrls: ['./exerciseComponents/exercise-body.component.scss'],
})
export class ExerciseBodyComponent implements OnInit, OnDestroy, AfterViewInit {
  @Output() exerciseChangedEvent = new EventEmitter<SearchResultDTO>();
  @Input() get referencedExercise(): SearchResultDTO | undefined {
    return this.exercise;
  }
  set referencedExercise(exercise: SearchResultDTO | undefined) {
    this.exercise = exercise as ExtendedSearchResultDTO;
  }
  exercise: ExtendedSearchResultDTO | undefined;
  parent: SearchResultDTO | undefined;

  bookmarked = false;
Michael Breu's avatar
Michael Breu committed

  downloadWithChildren = false;
  markDownExercise: SearchResultDTO | undefined;
Michael Breu's avatar
Michael Breu committed
  account: Account | null = null;
  authSubscription?: Subscription;
  hasLiked: boolean | null = null;
Michael Breu's avatar
Michael Breu committed
  likeSubscription?: Subscription;
  authenticated = false;

  treeIcon = faFolderOpen;
  faArrowRight = faArrowRight;

  oerLink?: string;
  oerExerciseMatch = /([a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12})$/;
Michael Breu's avatar
Michael Breu committed

  constructor(
Eduard Frankford's avatar
Eduard Frankford committed
    private reviewManagementService: ReviewManagementService,
Michael Breu's avatar
Michael Breu committed
    private accountService: AccountService,
    protected pluginService: PluginService,
    private searchService: SearchService,
    private likesService: LikesService,
    private jhiAlertService: AlertService,
    private watchlistManager: WatchlistManager,
    private exerciseService: ExerciseService,
    private applicationInfoService: ApplicationInfoService,
    private router: Router
  ) {}

  toggleWithChildren() {
    this.downloadWithChildren = !this.downloadWithChildren;
  }
  selectREADME(): void {
    this.markDownExercise = this.exercise;
  }

  hasChildren(): boolean {
Michael Breu's avatar
Michael Breu committed
    return hasChildren(this.exercise);
  getChildrenInfos(): ChildInfo[] {
    if (this.exercise?.childInfos) {
      return this.exercise?.childInfos;
    } else {
      this.loadChildrenInfoDisplayString();
      return [];
    }
  }

  loadChildrenInfoDisplayString(): string {
    if (!this.exercise?.childInfos) {
      this.exercise!.childInfos = [];
      this.exerciseService.populateExerciseWithChildrenInfo(this.exercise!).subscribe({
        next: () => {
          // nothing to do
        },
        error: () => console.warn('Could not load children info'),
      });
    }
    return '';
  }

  public startAction(action: PluginActionInfo, exercise: SearchResultDTO): void {
Michael Breu's avatar
Michael Breu committed
    const basketInfo: ShoppingBasketInfo = {
      plugin: action.plugin,
      action: action.action,
      itemInfos: [exercise],
Michael Breu's avatar
Michael Breu committed
    };
    this.pluginService.getRedirectLink(basketInfo).subscribe({
      next: (redirectInfo: ShoppingBasketRedirectInfoDTO) => {
Michael Breu's avatar
Michael Breu committed
        window.open(redirectInfo.redirectURL, action.action);
      },
      error: () => alert('Search failed'),
    });
Michael Breu's avatar
Michael Breu committed
  }

  public download(): void {
    this.exportProject(this.exercise!.exerciseId);
Michael Breu's avatar
Michael Breu committed
  }

  exportProject(exerciseId: string) {
    const recursion = this.downloadWithChildren ? 'WITH_DESCENDANTS' : 'JUST_PROJECT';
    return this.searchService.exportProject(exerciseId, recursion).subscribe({
      next: (response: HttpResponse<Blob>) => {
Michael Breu's avatar
Michael Breu committed
        this.jhiAlertService.addAlert({
          type: 'success',
          translationKey: 'artemisApp.programmingExercise.export.successMessage',
        });
Michael Breu's avatar
Michael Breu committed
        if (response.body) {
          const zipFile = new Blob([response.body], { type: 'application/zip' });
          const url = window.URL.createObjectURL(zipFile);
          const link = document.createElement('a');
          link.setAttribute('href', url);
          link.setAttribute('download', response.headers.get('filename')!);
          document.body.appendChild(link); // Required for FF
          link.click();
          window.URL.revokeObjectURL(url);
        }
      },
      error: () => alert('Unable to export exercise. Please log in to export, or check your git lab permissions.'),
    });
Eduard Frankford's avatar
Eduard Frankford committed
    return this.reviewManagementService.getGitLabUrl(this.exercise!);
Michael Breu's avatar
Michael Breu committed
  openLink(link: string): void {
    window.open(link);
  }

  searchChildren(parentId: string): void {
    this.router.navigate(['/search'], { queryParams: { p: parentId } });
  }

  toParent(parentId: string): void {
    this.toExercise(parentId);
  }

  toExercise(exerciseId: string): void {
    this.exerciseService.loadExercise(exerciseId).subscribe({
      next: searchResult => {
        this.exercise = this.exerciseService.populateExerciseWithData(searchResult);
        this.exerciseChangedEvent.emit(this.exercise);
        this.updateParent(this.exercise);
        alert(`exercise ${exerciseId} cannot be loaded`);
  isLoggedIn(): boolean {
    return this.accountService.isAuthenticated();
Michael Breu's avatar
Michael Breu committed
  }

  public isEditable(): boolean {
    if (!this.exercise) return false;
    if (!this.exercise.project.url) return false;
    const matchGroups = this.exercise.project.url.match(this.oerExerciseMatch);
Michael Breu's avatar
Michael Breu committed
    if (!matchGroups) return false;

    // Just for Testing: allow every edit
    if (this.oerLink) return true;

    const email = this.accountService.getUserEMail();
    if (!email || email == '') return false;

    // test all: creator, publisher
    let potentialEditors: Person[] = [];

    potentialEditors = potentialEditors.concat(this.exercise.metadata.creator);
    potentialEditors = potentialEditors.concat(this.exercise.metadata.publisher);
Michael Breu's avatar
Michael Breu committed

    for (const pE of potentialEditors) {
      if (pE.email.localeCompare(email, undefined, { sensitivity: 'base' }) === 0) return true;
Michael Breu's avatar
Michael Breu committed
    }
    return false;
  }

  public getOEResourceLink(): string {
    if (this.oerLink) {
      const matchGroups = this.exercise!.project.url.match(this.oerExerciseMatch);
Michael Breu's avatar
Michael Breu committed
      if (!matchGroups) return '';

      const guid = matchGroups[1];

      return this.oerLink.toString() + '/en/update/1/' + guid;
    }
    return '';
  }

  likeAction(): void {
    // to do call like service
    this.likesService.likeExercise(this.exercise!.exerciseId).subscribe(() =>
Michael Breu's avatar
Michael Breu committed
      // eslint-disable-next-line no-console
      console.log('Liked post' + this.exercise!.exerciseId)
Michael Breu's avatar
Michael Breu committed
    );

    this.exercise!.numberOfLikes = this.exercise!.numberOfLikes + 1;
    this.exercise!.userHasLiked = true;
  }

  unlikeAction(): void {
    // to do call like service
    this.likesService.unlikeExercise(this.exercise!.exerciseId);
Michael Breu's avatar
Michael Breu committed

    this.exercise!.numberOfLikes = this.exercise!.numberOfLikes - 1;
    this.exercise!.userHasLiked = false;
  }

  public isAuthenticated(): boolean {
    return this.accountService.isAuthenticated();
  }

  ngOnInit(): void {
    this.bookmarked = this.watchlistManager.isExerciseOnCurrentWatchlist(this.exercise!);
Michael Breu's avatar
Michael Breu committed
    this.authSubscription = this.accountService.getAuthenticationState().subscribe(account => (this.account = account));
    this.applicationInfoService.loadOerLink().subscribe({
      next: (res: string) => {
Michael Breu's avatar
Michael Breu committed
        this.oerLink = res;
      },
Michael Breu's avatar
Michael Breu committed
        // eslint-disable-next-line no-console
        console.error(error),
    });
  ngAfterViewInit(): void {
    this.updateParent(this.exercise!);
  }

  updateParent(exercise: ExtendedSearchResultDTO): void {
    if (this.exercise!.file.parentId) {
      this.exerciseService.loadExercise(this.exercise!.file.parentId).subscribe({
        next: searchResult => {
          this.parent = searchResult;
        },
        error: () => {
          alert(`exercise ${this.exercise!.file.parentId} cannot be loaded`);
        },
      });
    }
  }

Michael Breu's avatar
Michael Breu committed
  ngOnDestroy(): void {
    if (this.authSubscription) {
      this.authSubscription.unsubscribe();
    }
  }