Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

v3.10.2 #135

Merged
merged 34 commits into from
Sep 12, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
34 commits
Select commit Hold shift + click to select a range
ae46323
Convert usages of bypassSecurityTrustHtml to sanitize and reworked so…
sei-bstein May 12, 2023
36259dd
- For missing toc.json/settings.json
sei-bstein May 17, 2023
b659314
Bump socket.io-parser from 4.2.2 to 4.2.3
dependabot[bot] May 26, 2023
bcf9277
Merge pull request #124 from cmu-sei/dependabot/npm_and_yarn/socket.i…
sei-bstein May 26, 2023
d8f2d75
- For missing toc.json/settings.json
sei-bstein May 17, 2023
eeb51f7
Add eslint, configure rules, and fix critical issues.
sei-bstein May 18, 2023
a9c66df
Allow root-relative imports in gameboard-ui
sei-bstein May 30, 2023
455eb04
Add custom input component to allow for custom components with ngmodel
sei-bstein Jun 1, 2023
d1129eb
Update angular to 15.2.5
sei-bstein Jun 12, 2023
e239f0e
- For missing toc.json/settings.json
sei-bstein May 17, 2023
76c6232
Bump socket.io-parser from 4.2.2 to 4.2.3
dependabot[bot] May 26, 2023
d1fea03
- For missing toc.json/settings.json
sei-bstein May 17, 2023
4856aa9
Add eslint, configure rules, and fix critical issues.
sei-bstein May 18, 2023
ad0f859
Allow root-relative imports in gameboard-ui
sei-bstein May 30, 2023
326eb0f
Add custom input component to allow for custom components with ngmodel
sei-bstein Jun 1, 2023
030ed2e
Update angular to 15.2.5
sei-bstein Jun 12, 2023
fb44c73
Client-side defense for illegal file uploads
sei-bstein Jun 13, 2023
da67afe
Improved error handling for illegal mime types. Support area formatti…
sei-bstein Jun 13, 2023
b976e8e
Merge branch 'next' into new/ticket-security-enhancements
sei-bstein Jun 13, 2023
0b28af1
Merge pull request #125 from cmu-sei/new/ticket-security-enhancements
sei-bstein Jun 13, 2023
3656244
Allow escaped html to render in support tickets.
sei-bstein Jun 14, 2023
ea4adea
Added a component that allows inline rendering of links while preserv…
sei-bstein Jun 14, 2023
25ccf0c
Merge pull request #127 from cmu-sei/new/render-links-in-tickets
sei-bstein Jun 14, 2023
e7a51dc
Revise error message on bad file upload.
sei-bstein Jun 14, 2023
5edb2ba
Merge branch 'main' into next
sei-bstein Jun 14, 2023
453b054
Added handling for multiline strings in ticket comments.
sei-bstein Jun 15, 2023
e437c08
Merge branch 'main' into next
sei-bstein Jun 30, 2023
4abaf48
Pin gh actions for vs code extension. Add tap and log tool. Update ng…
sei-bstein Jul 27, 2023
f6adbe4
Remove unused property of game model
sei-bstein Jul 28, 2023
d0f29af
Add game engine mode enum
sei-bstein Jul 28, 2023
3b40004
Addresses GBAPI #236.
sei-bstein Aug 25, 2023
58a5293
Merge from issue/236
sei-bstein Sep 12, 2023
590a5a5
- Fixed a bug that caused 'game over' to appear before a player sessi…
sei-bstein Sep 12, 2023
b0c7d0f
simplify display logic for enrollment panel.
sei-bstein Sep 12, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions projects/gameboard-ui/src/app/api/user.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,12 @@ export class UserService {
return this.http.put<void>(`${this.url}/user/login`, {});
}

public canEnrollAndPlayOutsideExecutionWindow(user: ApiUser) {
return user && (
user.isAdmin || user.isTester || user.isRegistrar
);
}

private mapToTree(list: string[]): TreeNode {
const root: TreeNode = { name: '', path: `${this.config.apphost}doc`, folders: [], files: [] };
list.forEach(f => {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,13 @@
<div class="gameboard-performance-summary-component" *ngIf="ctx; else loading">
<div class="text-info card-text mb-2">
<span *ngIf="ctx.player.session?.isAfter || ((countdown$ | async) || 0 ) <= 0">Game Over </span>
<span *ngIf="ctx.player.session?.isDuring">Time Remaining: </span>
<span class="font-weight-bold" [class]="(countdown$ | async) || 0 | countdowncolor">
{{ (countdown$ | async) || 0 | countdown }}
<span
*ngIf="ctx.player.session && !ctx.player.session.isBefore && (ctx.player.session.isAfter || ((countdown$ | async) || 0 ) <= 0)">Game
Over
</span>
<span *ngIf="ctx.player.session?.isDuring">Time Remaining:
<span class="font-weight-bold" [class]="(countdown$ | async) || 0 | countdowncolor">
{{ (countdown$ | async) || 0 | countdown }}
</span>
</span>
</div>
<table class="table mt-2 text-center">
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ <h2>Status</h2>

<!-- if not session start -->
<ng-container
*ngIf="(ctx.user.isDesigner || !ctx.game.registration.isAfter) && (!ctx.player.id || !ctx.player.session || ctx.player.session.isBefore)">
*ngIf="(!ctx.game.registration.isAfter || (canAdminEnroll$ | async)) && (!ctx.player.id || !ctx.player.session || ctx.player.session.isBefore)">
<div class="col panel">
<h2>Enrollment</h2>
<app-player-enroll [ctx]="ctx" (onEnroll)="onEnroll($event)"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,12 @@
import { Component, OnDestroy } from '@angular/core';
import { ActivatedRoute, Router } from '@angular/router';
import { faCaretDown, faCaretRight, faExternalLinkAlt, faListOl } from '@fortawesome/free-solid-svg-icons';
import { BsModalService } from 'ngx-bootstrap/modal';
import { BehaviorSubject, combineLatest, merge, Observable, of, Subject, Subscription } from 'rxjs';
import { BehaviorSubject, combineLatest, merge, Observable, Subscription } from 'rxjs';
import { filter, first, map, startWith, switchMap, tap } from 'rxjs/operators';
import { ApiUser, PlayerRole } from '../../api/user-models';
import { GameService } from '../../api/game.service';
import { GameContext } from '../../api/models';
import { HubEvent, HubEventAction, HubState, NotificationService } from '../../services/notification.service';
import { ModalConfirmComponent } from '../../core/components/modal/modal-confirm.component';
import { HubEvent, HubEventAction, NotificationService } from '../../services/notification.service';
import { Player, TimeWindow } from '../../api/player-models';
import { PlayerService } from '../../api/player.service';
import { UserService as LocalUserService } from '../../utility/user.service';
Expand All @@ -21,6 +19,7 @@ import { BoardPlayer } from '../../api/board-models';
import { BoardService } from '../../api/board.service';
import { GameHubService } from '../../services/signalR/game-hub.service';
import { ModalConfirmService } from '@/services/modal-confirm.service';
import { UserService } from '@/api/user.service';

@Component({
selector: 'app-game-page',
Expand All @@ -37,11 +36,11 @@ export class GamePageComponent implements OnDestroy {
minDate = new Date(0);

protected boardPlayer?: BoardPlayer;
protected canAdminEnroll$: Observable<boolean>;
protected ctxIds: { userId?: string, gameId: string, playerId?: string } = { userId: '', gameId: '' };
protected playerSubject$ = new BehaviorSubject<Player | undefined>(undefined);

private isExternalGame = false;
private isSyncStartReady = false;
private syncStartChangedSubscription?: Subscription;
private syncStartGameStartedSubscription?: Subscription;
private hubEventsSubcription: Subscription;
Expand All @@ -50,18 +49,20 @@ export class GamePageComponent implements OnDestroy {
constructor(
router: Router,
route: ActivatedRoute,
apiGame: GameService,
apiBoards: BoardService,
apiPlayer: PlayerService,
localUser: LocalUserService,
userService: UserService,
private hub: NotificationService,
private apiGame: GameService,
private gameHubService: GameHubService,
private modalConfirmService: ModalConfirmService,
private windowService: WindowService
) {
const user$ = localUser.user$.pipe(
map(u => !!u ? u : {} as ApiUser)
);
this.canAdminEnroll$ = localUser.user$.pipe(map(u => !!u && userService.canEnrollAndPlayOutsideExecutionWindow(u)));

const game$ = route.params.pipe(
filter(p => !!p.id),
Expand All @@ -72,10 +73,6 @@ export class GamePageComponent implements OnDestroy {
this.syncStartChangedSubscription?.unsubscribe();

if (g.requireSynchronizedStart) {
this.syncStartChangedSubscription = this.gameHubService.syncStartChanged$.subscribe(state => {
this.isSyncStartReady = state.isReady;
});

this.syncStartGameStartedSubscription = this.gameHubService.syncStartGameStarted$.subscribe(startState => {
if (startState) {
router.navigateByUrl(`/game/${startState.game.id}/sync-start`);
Expand Down Expand Up @@ -106,7 +103,7 @@ export class GamePageComponent implements OnDestroy {
)
);
})
).subscribe(done => {
).subscribe(() => {
const currentPlayer = this.playerSubject$.getValue();
if (!currentPlayer?.id) {
this.boardPlayer = undefined;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
<!-- Copyright 2021 Carnegie Mellon University. All Rights Reserved. -->
<!-- Released under a MIT (SEI)-style license. See LICENSE.md in the project root for license information. -->

<ng-container *ngIf="ctx$ | async as ctx; else loading">

<div class="card" *ngIf="ctx">
Expand All @@ -16,7 +15,7 @@
<app-error-div [errors]="errors"></app-error-div>

<!-- not yet enrolled -->
<ng-container *ngIf="!ctx.player.id">
<ng-container *ngIf="!(isEnrolled$ | async)">
<div *ngIf="!ctx.user.name || !ctx.user.sponsor">
Ready to play? Before you do, you'll need to visit your
<a [routerLink]="['/profile']">profile</a> to...
Expand All @@ -27,7 +26,7 @@
</ul>
</div>

<ng-container *ngIf="!!(ctx.user.name && ctx.user.sponsor) && ctx.game.registration.isDuring">
<ng-container *ngIf="!!(ctx.user.name && ctx.user.sponsor) && (canAdminEnroll || canStandardEnroll)">
<!-- team -->
<div *ngIf="ctx.game.allowTeam">
<p>
Expand All @@ -50,22 +49,19 @@
<markdown [data]="ctx.game.registrationMarkdown"></markdown>
</div>
</ng-container>
</ng-container>

<!-- enrollment controls -->
<ng-container *ngIf="!ctx.player.id">
<!-- enrollment controls -->
<div class="d-flex align-items-center justify-content-center mt-5"
*ngIf="ctxDelayed$ | async as delay; else loading">
<div class="text-center" *ngIf="!!(ctx.user.name && ctx.user.sponsor) && ctx.game.registration.isDuring">
<div *ngIf="canStandardEnroll" class="text-center">
<app-confirm-button btnClass="btn btn-primary btn-lg mr-4"
(confirm)="handleEnroll(ctx.user.id, ctx.game.id)">
<fa-icon [icon]="faEdit"></fa-icon>
<span>Enroll</span>
</app-confirm-button>
</div>

<div *ngIf="ctx.user.isTester || (ctx.user.isRegistrar && !ctx.game.registration.isDuring)"
class="text-center">
<div *ngIf="canAdminEnroll" class="text-center">
<app-confirm-button btnClass="btn btn-warning btn-lg" (confirm)="handleEnroll(ctx.user.id, ctx.game.id)">
<fa-icon [icon]="faEdit"></fa-icon>
<span>Admin Enroll</span>
Expand All @@ -77,7 +73,8 @@
</ng-container>

<!-- already enrolled -->
<ng-container *ngIf="!!ctx.player.id && ctx.player.session?.isBefore && ctx.game.registration.isDuring">
<ng-container
*ngIf="(isEnrolled$ | async) && ctx.player.session?.isBefore && (ctx.game.registration.isDuring || canAdminEnroll)">
<div *ngIf="isManager$ | async" class="form-group mb-0">
<ng-container *ngIf="ctx.player.name !== ctx.player.approvedName">
<ng-container *ngIf="ctx.player.nameStatus == 'pending' && ctx.player.name != disallowedName">
Expand Down Expand Up @@ -114,8 +111,7 @@

<!--In team games before we start, managers can generate invitations and rename the team-->
<!--Non-managers can leave for different teams-->

<ng-container *ngIf="ctx.game.allowTeam && !ctx.player.session?.isDuring">
<ng-container *ngIf="ctx.game.allowTeam">
<div class="form-group d-flex flex-column">
<div class="mb-2" *ngIf="(isManager$ | async) || !(hasTeammates$ | async)">
<span class="lead">Manager</span>, generate an invitation for your teammates.<br />
Expand Down Expand Up @@ -148,27 +144,26 @@
</div>
</ng-container>
</ng-container>
</ng-container>

<!-- unenroll -->
<div
*ngIf="ctx.player.id && (!ctx.player.session || ctx.player.session.isBefore) && (!ctx.game.registration.isAfter || ctx.user.isTester)"
class="form-group text-center">
<div class="tooltip-holder" [tooltip]="unenrollTooltip" container="body" placement="top">
<app-confirm-button btnClass="btn btn-danger btn" (confirm)="handleUnenroll(ctx.player)"
[disabled]="!!unenrollTooltip">
<fa-icon [icon]="faTrash"></fa-icon>
<span>Unenroll</span>
</app-confirm-button>
</div>
<!-- unenroll -->
<div
*ngIf="(!ctx.player.session || ctx.player.session.isBefore) && (!ctx.game.registration.isAfter || ctx.user.isTester)"
class="form-group text-center">
<div class="tooltip-holder" [tooltip]="unenrollTooltip" container="body" placement="top">
<app-confirm-button btnClass="btn btn-danger btn" (confirm)="handleUnenroll(ctx.player)"
[disabled]="!!unenrollTooltip">
<fa-icon [icon]="faTrash"></fa-icon>
<span>Unenroll</span>
</app-confirm-button>
</div>

<ng-template *ngIf="!unenrollTooltip">
<p class="small">
You can unenroll either the enrollment period ends or your game session
starts.
</p>
</ng-template>
</div>
<ng-template *ngIf="!unenrollTooltip">
<p class="small">
You can unenroll until either the enrollment period ends or your game session starts.
</p>
</ng-template>
</div>
</ng-container>
</div>
</div>
</ng-container>
Expand All @@ -178,3 +173,13 @@
<app-spinner></app-spinner>
</div>
</ng-template>

<ng-template #setNameAndSponsor let-user>
Ready to play? Before you do, you'll need to visit your
<a [routerLink]="['/profile']">profile</a> to...

<ul class="profile-validation">
<li *ngIf="!user.name">Make sure you've entered your name</li>
<li *ngIf="!user.sponsor">Select your sponsor</li>
</ul>
</ng-template>
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
// Copyright 2021 Carnegie Mellon University. All Rights Reserved.
// Released under a MIT (SEI)-style license. See LICENSE.md in the project root for license information.

import { Component, EventEmitter, Input, OnChanges, OnDestroy, OnInit, Output } from '@angular/core';
import { Component, EventEmitter, Input, OnDestroy, OnInit, Output } from '@angular/core';
import { faCopy, faEdit, faPaste, faTrash, faUser } from '@fortawesome/free-solid-svg-icons';
import { Observable, of, Subject, Subscription, timer } from 'rxjs';
import { finalize, map, tap, delay, first } from 'rxjs/operators';
Expand All @@ -10,7 +10,8 @@ import { HubPlayer, NewPlayer, Player, PlayerEnlistment, PlayerRole, TeamInvitat
import { PlayerService } from '../../api/player.service';
import { ConfigService } from '../../utility/config.service';
import { NotificationService } from '../../services/notification.service';
import { UserService } from '../../utility/user.service';
import { UserService as LocalUserService } from '../../utility/user.service';
import { UserService } from '@/api/user.service';

@Component({
selector: 'app-player-enroll',
Expand All @@ -31,9 +32,12 @@ export class PlayerEnrollComponent implements OnInit, OnDestroy {
ctx$: Observable<GameContext>;
ctxDelayed$: Observable<GameContext>;

disallowedName: string | null = null;
disallowedReason: string | null = null;
protected canAdminEnroll = false;
protected canStandardEnroll = false;
protected disallowedName: string | null = null;
protected disallowedReason: string | null = null;
protected managerRole = PlayerRole.manager;
protected isEnrolled$: Observable<boolean>;
protected isManager$ = new Subject<boolean>();
protected hasTeammates$: Observable<boolean> = of(false);
protected unenrollTooltip?: string;
Expand All @@ -49,7 +53,8 @@ export class PlayerEnrollComponent implements OnInit, OnDestroy {
private api: PlayerService,
private config: ConfigService,
private hubService: NotificationService,
private localUser: UserService
private localUserService: LocalUserService,
private userService: UserService
) {
this.ctx$ = timer(0, 1000).pipe(
map(i => this.ctx),
Expand All @@ -65,6 +70,23 @@ export class PlayerEnrollComponent implements OnInit, OnDestroy {
this.disallowedReason = gc.player.nameStatus;
}
}
}),
tap(ctx => {
const localUser = this.localUserService.user$.value;
const hasPlayerSession = (!!ctx.player.id && !!ctx.player.session && !ctx.player.session.isBefore);

this.canAdminEnroll = !!localUser && !hasPlayerSession &&
(
this.ctx.game.registration.isDuring ||
this.userService.canEnrollAndPlayOutsideExecutionWindow(localUser)
);

this.canStandardEnroll = !!localUser && !hasPlayerSession &&
ctx.game.registration.isDuring && (
!ctx.player.id ||
!ctx.player.session ||
ctx.player.session?.isBefore
);
})
);

Expand All @@ -73,6 +95,7 @@ export class PlayerEnrollComponent implements OnInit, OnDestroy {
delay(this.delayMs)
);

this.isEnrolled$ = this.ctx$.pipe(map(ctx => !!ctx.player.id));
this.hasTeammates$ = this.hubService.actors$.pipe(map(actors => actors.length > 1));
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,8 @@

<app-error-div [errors]="errors"></app-error-div>

<ng-container *ngIf="ctx.player.session && ctx.player.session.isBefore && ctx.game.session.isDuring">
<ng-container
*ngIf="ctx.player.session && ctx.player.session.isBefore && (ctx.game.session.isDuring || canAdminStart)">
<app-session-start-controls [ctx]="ctx" (onRequestStart)="handleStart(ctx.player)"></app-session-start-controls>

<div class="card-text" *ngIf="!isDoubleChecking && !ctx.game.requireSynchronizedStart">
Expand Down
Loading
Loading