From 4d79273664e2cca95c47459a5197f2bb52cbce25 Mon Sep 17 00:00:00 2001 From: "stuart.woodman" Date: Mon, 6 Feb 2023 09:39:09 +1100 Subject: [PATCH 1/3] Added login functionality to portal (AAF, Google and Github currently). --- angular.json | 3 +- proxy.conf.json | 16 +- src/app/app-routing.module.ts | 28 +++ src/app/app.component.html | 1 + src/app/app.component.ts | 17 ++ src/app/app.module.ts | 37 +++- .../menupanel/login/logged-in.component.ts | 19 ++ .../menupanel/login/login-menu.component.html | 14 ++ .../menupanel/login/login-menu.component.scss | 28 +++ .../menupanel/login/login-menu.component.ts | 47 +++++ src/app/menupanel/login/login.component.html | 12 ++ src/app/menupanel/login/login.component.scss | 37 ++++ src/app/menupanel/login/login.component.ts | 37 ++++ src/app/models/user.model.ts | 9 + src/app/portal/portal.component.html | 92 +++++++++ src/app/portal/portal.component.ts | 14 ++ src/app/router.animations.ts | 65 ++++++ src/app/services/api/auscope-api.service.ts | 11 +- src/app/services/auth/auth.guard.ts | 39 ++++ src/app/services/auth/auth.service.ts | 63 ++++++ src/app/services/user/user-state.service.ts | 39 ++++ src/environments/environment.ts | 1 + src/extension/img/GitHub_Logo.png | Bin 0 -> 7899 bytes src/extension/img/aaf_service_223x54.png | Bin 0 -> 5047 bytes .../img/btn_google_signin_dark_normal_web.png | Bin 0 -> 3983 bytes src/index.htm | 194 ++++-------------- src/styles.scss | 6 - 27 files changed, 647 insertions(+), 182 deletions(-) create mode 100644 src/app/app-routing.module.ts create mode 100644 src/app/app.component.html create mode 100644 src/app/app.component.ts create mode 100644 src/app/menupanel/login/logged-in.component.ts create mode 100644 src/app/menupanel/login/login-menu.component.html create mode 100644 src/app/menupanel/login/login-menu.component.scss create mode 100644 src/app/menupanel/login/login-menu.component.ts create mode 100644 src/app/menupanel/login/login.component.html create mode 100644 src/app/menupanel/login/login.component.scss create mode 100644 src/app/menupanel/login/login.component.ts create mode 100644 src/app/models/user.model.ts create mode 100644 src/app/portal/portal.component.html create mode 100644 src/app/portal/portal.component.ts create mode 100644 src/app/router.animations.ts create mode 100644 src/app/services/auth/auth.guard.ts create mode 100644 src/app/services/auth/auth.service.ts create mode 100644 src/app/services/user/user-state.service.ts create mode 100644 src/extension/img/GitHub_Logo.png create mode 100644 src/extension/img/aaf_service_223x54.png create mode 100644 src/extension/img/btn_google_signin_dark_normal_web.png diff --git a/angular.json b/angular.json index e578c02c5..c40c1ea22 100644 --- a/angular.json +++ b/angular.json @@ -127,7 +127,8 @@ "serve": { "builder": "@angular-builders/custom-webpack:dev-server", "options": { - "browserTarget": "project:build" + "browserTarget": "project:build", + "proxyConfig": "./proxy.conf.json" }, "configurations": { "audev": { diff --git a/proxy.conf.json b/proxy.conf.json index be91f5a1b..85aecaf03 100644 --- a/proxy.conf.json +++ b/proxy.conf.json @@ -1,8 +1,10 @@ { - "/": { - "target": "http://localhost:8080/AuScope-Portal", - "secure": false, - "pathRewrite": {"^/" : ""}, - "logLevel":"debug" - } -} \ No newline at end of file + "context": [ + "/AuScope-Portal/**", + "/api/**" + ], + "target": "http://localhost:8080/", + "secure": false, + "logLevel": "debug", + "changeOrigin": true +} diff --git a/src/app/app-routing.module.ts b/src/app/app-routing.module.ts new file mode 100644 index 000000000..7d8622b84 --- /dev/null +++ b/src/app/app-routing.module.ts @@ -0,0 +1,28 @@ +import { NgModule } from '@angular/core'; +import { Routes, RouterModule } from '@angular/router'; +import { environment } from '../environments/environment'; +import { LoggedInComponent } from './menupanel/login/logged-in.component'; +import { LoginComponent } from './menupanel/login/login.component'; +import { PortalComponent } from './portal/portal.component'; + + +const baseUrl = environment.portalBaseUrl.replace(/^\/|\/$/g, ''); + +/** + * Application routes. + * Add the following to a route to ensure only authenticated users can access: + * canActivate: [AuthGuard] + */ +const routes: Routes = [ + { path: '', pathMatch: 'full', component: PortalComponent }, + { path: 'login', component: LoginComponent }, + { path: 'login/loggedIn', component: LoggedInComponent }, + { path: baseUrl, redirectTo: '/', pathMatch: 'full' }, + { path: '**', redirectTo: '/' } +]; + +@NgModule({ + imports: [RouterModule.forRoot(routes)], + exports: [RouterModule] +}) +export class AppRoutingModule {} diff --git a/src/app/app.component.html b/src/app/app.component.html new file mode 100644 index 000000000..90c6b6463 --- /dev/null +++ b/src/app/app.component.html @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/app/app.component.ts b/src/app/app.component.ts new file mode 100644 index 000000000..dc0cf3d64 --- /dev/null +++ b/src/app/app.component.ts @@ -0,0 +1,17 @@ +import { Component } from '@angular/core'; +/** + * Top level application component, holds the router outlet + * + * @export + * @class AppComponent + */ +@Component({ + selector: 'app-component', + templateUrl: './app.component.html' +}) + +export class AppComponent { + + constructor() {} + +} diff --git a/src/app/app.module.ts b/src/app/app.module.ts index 953b77714..0a49e2a2a 100644 --- a/src/app/app.module.ts +++ b/src/app/app.module.ts @@ -4,11 +4,17 @@ import { NgModule } from '@angular/core'; import { ModalModule } from 'ngx-bootstrap/modal'; import { NouisliderModule } from 'ng2-nouislider'; import { CommonModule } from '@angular/common'; +import { BrowserModule } from '@angular/platform-browser'; // Cesium icons import { MatTooltipModule } from '@angular/material/tooltip'; // Components +import { AppComponent } from './app.component'; +import { PortalComponent } from './portal/portal.component'; +import { LoginMenuComponent } from './menupanel/login/login-menu.component'; +import { LoginComponent } from './menupanel/login/login.component'; +import { LoggedInComponent } from './menupanel/login/logged-in.component'; import { CsMapComponent } from './cesium-map/csmap.component'; import { CesiumMapPreviewComponent } from './menupanel/common/infopanel/cesiummappreview/cesium.preview.component'; import { LayerPanelComponent } from './menupanel/layerpanel/layerpanel.component'; @@ -93,11 +99,25 @@ import { NgxColorsModule } from 'ngx-colors'; import {MatProgressBarModule} from '@angular/material/progress-bar'; import {MatCardModule} from '@angular/material/card'; import { getSaver, SAVER } from './modalwindow/layeranalytic/nvcl/saver.provider'; + +// Routing +import { AppRoutingModule } from './app-routing.module'; + +// Auth +import { UserStateService } from './services/user/user-state.service'; +import { AuthGuard } from './services/auth/auth.guard'; +import { AuthService } from './services/auth/auth.service'; + PlotlyModule.plotlyjs = PlotlyJS; @NgModule({ declarations: [ + AppComponent, + PortalComponent, + LoginComponent, + LoggedInComponent, + LoginMenuComponent, CsMapComponent, CesiumMapPreviewComponent, LayerPanelComponent, @@ -136,10 +156,12 @@ PlotlyModule.plotlyjs = PlotlyJS; RecordModalComponent ], providers: [ AuscopeApiService, FilterService, RectanglesEditorService, AdvancedComponentService, SearchService, - NVCLService, MSCLService, BoundsService, GraceService, { provide: SAVER, useFactory: getSaver } + NVCLService, MSCLService, BoundsService, GraceService, { provide: SAVER, useFactory: getSaver }, + UserStateService, AuthGuard, AuthService ], imports: [ PortalCoreModule.forRoot(environment, config), + AppRoutingModule, PortalCorePipesModule, ClipboardModule, ModalModule.forRoot(), @@ -167,18 +189,11 @@ PlotlyModule.plotlyjs = PlotlyJS; AngularCesiumWidgetsModule, NgbModule, FormsModule, - ReactiveFormsModule + ReactiveFormsModule, + BrowserModule ], bootstrap: [ - CsMapComponent, - LayerPanelComponent, - CustomPanelComponent, - ActiveLayersPanelComponent, - PermanentLinkComponent, - DataExplorerComponent, - PortalDetailsPanelComponent, - HelpMenuComponent, - SearchPanelComponent + AppComponent ] }) export class AppModule { } diff --git a/src/app/menupanel/login/logged-in.component.ts b/src/app/menupanel/login/logged-in.component.ts new file mode 100644 index 000000000..e92dd6c71 --- /dev/null +++ b/src/app/menupanel/login/logged-in.component.ts @@ -0,0 +1,19 @@ +import { Component, OnInit } from '@angular/core'; + +import { AuthService } from '../../services/auth/auth.service'; + +@Component({ + selector: 'app-logged-in', + template: ``, + styles: [] +}) +export class LoggedInComponent implements OnInit { + + constructor(private authService: AuthService) {} + + ngOnInit() { + // Inform the auth service + this.authService.onLoggedIn(); + } + +} diff --git a/src/app/menupanel/login/login-menu.component.html b/src/app/menupanel/login/login-menu.component.html new file mode 100644 index 000000000..87f27984d --- /dev/null +++ b/src/app/menupanel/login/login-menu.component.html @@ -0,0 +1,14 @@ +
+
+ + +
+
+
+
+ +
+
\ No newline at end of file diff --git a/src/app/menupanel/login/login-menu.component.scss b/src/app/menupanel/login/login-menu.component.scss new file mode 100644 index 000000000..0596f4475 --- /dev/null +++ b/src/app/menupanel/login/login-menu.component.scss @@ -0,0 +1,28 @@ +@import '../../globals'; + +.user-menu { + color: $text-colour; +} + +.user-button { + color: #d8d9d7; +} + +.user-button:hover { + color: white; +} + +.user-menu:hover { + color: $text-highlight-colour; +} + +::ng-deep { + .mat-menu-panel { + margin-top: 10px; + } + + .mat-menu-content { + background-color: $background-colour; + } + +} \ No newline at end of file diff --git a/src/app/menupanel/login/login-menu.component.ts b/src/app/menupanel/login/login-menu.component.ts new file mode 100644 index 000000000..6a7ed40a5 --- /dev/null +++ b/src/app/menupanel/login/login-menu.component.ts @@ -0,0 +1,47 @@ +import { Component } from '@angular/core'; +import { Router } from '@angular/router'; +import { AuthService } from 'app/services/auth/auth.service'; +import { UserStateService } from '../../services/user/user-state.service'; + +@Component({ + selector: '[app-login-menu]', + templateUrl: './login-menu.component.html', + styleUrls: ['./login-menu.component.scss'] +}) +export class LoginMenuComponent { + + username: string; + + constructor(private router: Router, private authService: AuthService, private userStateService: UserStateService) { + this.authService.checkServerLogin(); + this.userStateService.user.subscribe(user => { + if (user) { + this.username = user.fullName; + } + }); + } + + /** + * Check is a user is logged in. + * + * @returns true if user logged in, false otherwise + */ + isUserLoggedIn(): boolean { + return this.authService.isLoggedIn; + } + + /** + * Redirect to login page + */ + login() { + this.router.navigate(['login']); + } + + /** + * Logout current user + */ + logOut() { + this.authService.logout(); + } + +} diff --git a/src/app/menupanel/login/login.component.html b/src/app/menupanel/login/login.component.html new file mode 100644 index 000000000..4c1a26c54 --- /dev/null +++ b/src/app/menupanel/login/login.component.html @@ -0,0 +1,12 @@ +
+
+
+

AuScope Discovery Portal

+ +

Please log in using one of the options below

+
+
+ +
+
+
diff --git a/src/app/menupanel/login/login.component.scss b/src/app/menupanel/login/login.component.scss new file mode 100644 index 000000000..2d1a5c5dd --- /dev/null +++ b/src/app/menupanel/login/login.component.scss @@ -0,0 +1,37 @@ +@import 'app/globals'; + +.login-page { + position: absolute; + top: 0; + left: 0; + right: 0; + bottom: 0; + overflow: auto; + background: $auscope-royal-blue; + text-align: center; + color: #fff; + padding: 3em; + + .user-avatar { + -webkit-border-radius: 50%; + border-radius: 50%; + border: 2px solid #fff; + } + + .login-message { + margin: 10px 0 20px 0; + } + + .login-button { + cursor: pointer; + } + + img { + margin-bottom: 10px; + } + + i { + margin-bottom: 10px; + } + +} diff --git a/src/app/menupanel/login/login.component.ts b/src/app/menupanel/login/login.component.ts new file mode 100644 index 000000000..4f4f1500b --- /dev/null +++ b/src/app/menupanel/login/login.component.ts @@ -0,0 +1,37 @@ +import { Component } from '@angular/core'; +import { routerTransition } from '../../router.animations'; +import { environment } from '../../../environments/environment'; + +@Component({ + selector: 'app-login', + templateUrl: './login.component.html', + styleUrls: ['./login.component.scss'], + animations: [routerTransition()] +}) +export class LoginComponent { + constructor() {} + + // TODO: We may need to store some user site settings to local storage if not saved to DB for post-auth + + /** + * Google login + */ + loginGoogle() { + window.location.href = environment.portalBaseUrl + 'oauth2/authorization/google'; + } + + /** + * Australia Access Federation (AAF) login + */ + loginAaf() { + window.location.href = environment.portalBaseUrl + 'login/aaf'; + } + + /** + * Github login + */ + loginGithub() { + window.location.href = environment.portalBaseUrl + 'oauth2/authorization/github'; + } + +} diff --git a/src/app/models/user.model.ts b/src/app/models/user.model.ts new file mode 100644 index 000000000..4875d55b0 --- /dev/null +++ b/src/app/models/user.model.ts @@ -0,0 +1,9 @@ +/** + * User Models + */ +export interface User { + fullName: string; + email: string; + // Currently unused + acceptedTermsConditions: number; +} diff --git a/src/app/portal/portal.component.html b/src/app/portal/portal.component.html new file mode 100644 index 000000000..be1ac1580 --- /dev/null +++ b/src/app/portal/portal.component.html @@ -0,0 +1,92 @@ + + + + + + + + + +
+ +
+ +
+ +
+ + + + + diff --git a/src/app/portal/portal.component.ts b/src/app/portal/portal.component.ts new file mode 100644 index 000000000..0ece36de6 --- /dev/null +++ b/src/app/portal/portal.component.ts @@ -0,0 +1,14 @@ +import { Component } from '@angular/core'; +/** + * Defines the overall app layout + */ +@Component({ + selector: 'app-portal', + templateUrl: './portal.component.html', + styleUrls: ['../../styles.scss'] +}) +export class PortalComponent { + + constructor() {} + +} diff --git a/src/app/router.animations.ts b/src/app/router.animations.ts new file mode 100644 index 000000000..197500631 --- /dev/null +++ b/src/app/router.animations.ts @@ -0,0 +1,65 @@ +import { animate, state, style, transition, trigger } from '@angular/animations'; + +export function routerTransition() { + return slideToTop(); +} + +export function slideToRight() { + return trigger('routerTransition', [ + state('void', style({})), + state('*', style({})), + transition(':enter', [ + style({ transform: 'translateX(-100%)' }), + animate('0.5s ease-in-out', style({ transform: 'translateX(0%)' })) + ]), + transition(':leave', [ + style({ transform: 'translateX(0%)' }), + animate('0.5s ease-in-out', style({ transform: 'translateX(100%)' })) + ]) + ]); +} + +export function slideToLeft() { + return trigger('routerTransition', [ + state('void', style({})), + state('*', style({})), + transition(':enter', [ + style({ transform: 'translateX(100%)' }), + animate('0.5s ease-in-out', style({ transform: 'translateX(0%)' })) + ]), + transition(':leave', [ + style({ transform: 'translateX(0%)' }), + animate('0.5s ease-in-out', style({ transform: 'translateX(-100%)' })) + ]) + ]); +} + +export function slideToBottom() { + return trigger('routerTransition', [ + state('void', style({})), + state('*', style({})), + transition(':enter', [ + style({ transform: 'translateY(-100%)' }), + animate('0.5s ease-in-out', style({ transform: 'translateY(0%)' })) + ]), + transition(':leave', [ + style({ transform: 'translateY(0%)' }), + animate('0.5s ease-in-out', style({ transform: 'translateY(100%)' })) + ]) + ]); +} + +export function slideToTop() { + return trigger('routerTransition', [ + state('void', style({})), + state('*', style({})), + transition(':enter', [ + style({ transform: 'translateY(100%)' }), + animate('0.5s ease-in-out', style({ transform: 'translateY(0%)' })) + ]), + transition(':leave', [ + style({ transform: 'translateY(0%)' }), + animate('0.5s ease-in-out', style({ transform: 'translateY(-100%)' })) + ]) + ]); +} diff --git a/src/app/services/api/auscope-api.service.ts b/src/app/services/api/auscope-api.service.ts index 2a55bb5aa..517863b32 100644 --- a/src/app/services/api/auscope-api.service.ts +++ b/src/app/services/api/auscope-api.service.ts @@ -1,5 +1,6 @@ import { HttpClient, HttpParams } from '@angular/common/http'; import { Injectable } from '@angular/core'; +import { User } from 'app/models/user.model'; import { Observable, of, throwError } from 'rxjs'; import { switchMap } from 'rxjs/operators'; import { environment } from '../../../environments/environment'; @@ -34,8 +35,7 @@ export class AuscopeApiService { } private apiPost(endpoint: string, params = {}, options = {}): Observable { - const url = environment.portalBaseUrl + endpoint; - + const url = environment.portalProxyUrl + endpoint; const body = new FormData(); for (const key in params) { const val = params[key]; @@ -52,9 +52,8 @@ export class AuscopeApiService { } private apiGet(endpoint: string, params = {}, options?): Observable { - const url = environment.portalBaseUrl + endpoint; + const url = environment.portalProxyUrl + endpoint; const opts: { observe: 'body' } = { ...options, observe: 'body', params: params }; - return this.http.get>(url, opts).pipe(switchMap(apiData)); } @@ -105,4 +104,8 @@ export class AuscopeApiService { return this.apiGet('suggestTerms.do', params); } + public get user(): Observable { + return this.apiGet('secure/getUser.do'); + } + } diff --git a/src/app/services/auth/auth.guard.ts b/src/app/services/auth/auth.guard.ts new file mode 100644 index 000000000..dc8643297 --- /dev/null +++ b/src/app/services/auth/auth.guard.ts @@ -0,0 +1,39 @@ +import { Injectable } from '@angular/core'; +import { CanActivate, Router, ActivatedRouteSnapshot, RouterStateSnapshot } from '@angular/router'; +import { Observable } from 'rxjs'; +import { map } from 'rxjs/operators'; +import { AuthService } from './auth.service'; +import { UserStateService } from '../user/user-state.service'; + +/** + * AuthGuard is not so useful at the moment with all pages available to the user, + * but may become useful down the track if we want to limit access to pages (by adding + * "canActivate [AuthGuard]" to the route deinfintion). + */ +@Injectable() +export class AuthGuard implements CanActivate { + + constructor(private router: Router, + private authService: AuthService, + private userStateService: UserStateService) {} + + canActivate(router: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable | boolean { + const url: string = state.url; + return this.checkLogin(url); + } + + checkLogin(url: string): Observable | boolean { + if (this.authService.isLoggedIn) { + return this.userStateService.user.pipe(map( + // Later we can use 'user' to check T&C's etc. if necessary + user => { + return true; + } + )); + } + // Navigate to the login page + this.router.navigate(['/login']); + return false; + } + +} diff --git a/src/app/services/auth/auth.service.ts b/src/app/services/auth/auth.service.ts new file mode 100644 index 000000000..46fb0152c --- /dev/null +++ b/src/app/services/auth/auth.service.ts @@ -0,0 +1,63 @@ +import { Injectable } from '@angular/core'; +import { HttpClient } from '@angular/common/http'; +import { Router } from '@angular/router'; +import { UserStateService } from '../user/user-state.service'; +import { environment } from 'environments/environment'; + +@Injectable() +export class AuthService { + + constructor(private userStateService: UserStateService, + private router: Router, + private http: HttpClient) {} + + logout(): void { + localStorage.removeItem('isLoggedIn'); + this.http.get(environment.portalProxyUrl + 'logout').subscribe(() => { + this.userStateService.removeUser(); + this.router.navigate(['']); + }); + } + + /** + * Update the local user object to see if the User has been logged out of + * the server, but the front end doesn't know it yet + */ + checkServerLogin(): void { + this.userStateService.updateUser().subscribe(user => { + if (user === null) { + localStorage.removeItem('isLoggedIn'); + } else { + localStorage.setItem('isLoggedIn', 'true'); + } + }, () => { + localStorage.removeItem('isLoggedIn'); + }); + } + + onLoggedIn() { + this.userStateService.updateUser().subscribe(user => { + localStorage.setItem('isLoggedIn', 'true'); + this.router.navigate(['/']); + }); + } + + public get isLoggedIn(): boolean { + return localStorage.getItem('isLoggedIn') === 'true'; + } + + public get redirectUrl(): string { + return localStorage.getItem('redirectUrl'); + } + + public set redirectUrl(url: string) { + localStorage.setItem('redirectUrl', url); + } + + resetRedirectUrl(): string { + const url = localStorage.getItem('redirectUrl'); + localStorage.removeItem('redirectUrl'); + return url; + } + +} diff --git a/src/app/services/user/user-state.service.ts b/src/app/services/user/user-state.service.ts new file mode 100644 index 000000000..85a3129a0 --- /dev/null +++ b/src/app/services/user/user-state.service.ts @@ -0,0 +1,39 @@ +import { Injectable } from '@angular/core'; +import { BehaviorSubject, Observable } from 'rxjs'; +import { map } from 'rxjs/operators'; +import { User } from '../../models/user.model'; +import { AuscopeApiService } from '../api/auscope-api.service'; + + +@Injectable() +export class UserStateService { + + private _user: BehaviorSubject = new BehaviorSubject(null); + public readonly user: Observable = this._user.asObservable(); + + constructor(private apiService: AuscopeApiService) {} + + public updateUser(): Observable { + return this.apiService.user.pipe( + map((user: User) => { + // If full name is empty (as with AAF login), use email address as name + if (user.fullName === undefined || user.fullName === '') { + user.fullName = user.email; + } + this._user.next(user); + + // TODO: Update bookmarks when we have them + + return user; + }, + // Failure to retrieve User means no User logged in + () => { + this.removeUser(); + })); + } + + public removeUser() { + this._user.next(null); + } + +} diff --git a/src/environments/environment.ts b/src/environments/environment.ts index c45caa61b..894ec5da9 100644 --- a/src/environments/environment.ts +++ b/src/environments/environment.ts @@ -13,6 +13,7 @@ export const environment = { getCSWRecordEndP: 'getKnownLayers.do', // portalBaseUrl: 'http://localhost:8080/AuScope-Portal/', portalBaseUrl: 'https://au-portal-dev.it.csiro.au/api/', + portalProxyUrl: '/api/', hostUrl: 'http://localhost:4200/index.htm', nVCLAnalyticalUrl: 'https://nvclanalytics.azurewebsites.net/NVCLAnalyticalServices/', googleAnalyticsKey: null, diff --git a/src/extension/img/GitHub_Logo.png b/src/extension/img/GitHub_Logo.png new file mode 100644 index 0000000000000000000000000000000000000000..812b7f22706a330c45408fe4642afd6209c16eaa GIT binary patch literal 7899 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 literal 0 HcmV?d00001 diff --git a/src/extension/img/aaf_service_223x54.png b/src/extension/img/aaf_service_223x54.png new file mode 100644 index 0000000000000000000000000000000000000000..8f001b666ab8e7e37c7443f34e5dcc0d1a3d572b GIT binary patch literal 5047 zcmV;o6G-fdP)pIRT(}rckk}fcH2Ug zK0sLeu&q%jpcQ$@Y9W9|vejrLDlH}orl=T+3MPsgEzu}wFdB`apvXg$u5SpfT~v^O zKnp5`K3qZCrHYpA?za25GydOy&Oi6e+?o5>d++XA&VMtRduPs^IdkU!&i9}H{O4Sy zoH%jf#EBCpPMkPtx_Gr-5Qbrwt2QT2N~F*8yuOBmj{vuxcAubam8)YXPBP^nZ9TMo zu^J$H6+j2D-Ey>pZuFjwD(v6sRL9B0O~?f+RLhDr%I{n)hXBCqCj>qNylu42rSC^| z#f{v$P6d6BI1O@Ax8w+Xt=(T$-rN)La}xpZ@~pIe2Jkv<;2rIo3Z2q8X^a&59#%)b zw^jw(l3Gvtb#+bPBe@a43xYslJLNf{JC(Quh*gYHZb*|FR%Bq-eJ=15Qpu(5Q$BC0>nVcbFwA) znKqBT*St-skwdh9s|sJ*r@WKi(Cl`~X^NAk$oTdi)!Mnbvs&;?N?#~^W}8w6o{j;F zkF|H`J>;8M57v^Y;uw2~H{?qgiA3!B(phWWGJ!*{F-gk3M!8twsZaMQZ2FBqVR>!Wd z%CtB4sB~R;^qW za4A5MTXB9#%ZV1YTq{@ei#c;$40x9Z%O}M%7xq6D zyH>!Trqp;=8+d$->cnf`Se7jtA@$ThKhyf#)RCLkG`*Y?1|N?*Hwt|8klsuCHE^DN zR3F>bTQoIcQoia8rJj3Kg|B<3^8WHm4N6J#!1s%0C+)+xeM+e_F4g%O*7NSTLWLjS zHt`o)Lt3q59g&G5N{Cjqk>JabsTrAc58cKga8XN*P$tvz158IYt&3gTzW!F_8Q{X* zU8!6Ep5%eN+ppYQR;hEVx??6P(lIEgOekwJrQZ4%0D=Lh8i0Nl6-;Qn?BG5P7Gfm+ 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 - - - AuScope Discovery Portal - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- -
- -
- -
- - - - - - - - - - - - - - - - - + + + AuScope Discovery Portal + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/styles.scss b/src/styles.scss index b1821a382..cdbf41082 100644 --- a/src/styles.scss +++ b/src/styles.scss @@ -306,12 +306,6 @@ label { } /* top level menu control in sidebar */ - - .menuBtn + h5 > ul { - /*opacity: 0.01; max-height: 0px; transition: opacity 1s, max-height 1s;*/ - display: none; - } - .menuBtn:checked + h5 > ul { /*opacity: 1; max-height: 999px; transition: opacity 1s, max-height 1s;*/ display: block; From 437cdc87d951810b7e96126f46ad1f68c7dc45cb Mon Sep 17 00:00:00 2001 From: "stuart.woodman" Date: Mon, 6 Feb 2023 10:08:10 +1100 Subject: [PATCH 2/3] Added portalProxyUrl to other dev/production config files. --- src/environments/environment.au2.ts | 1 + src/environments/environment.dev.ts | 1 + src/environments/environment.prod-https.ts | 1 + src/environments/environment.prod.ts | 1 + 4 files changed, 4 insertions(+) diff --git a/src/environments/environment.au2.ts b/src/environments/environment.au2.ts index 914b95b21..58f8d6bf5 100644 --- a/src/environments/environment.au2.ts +++ b/src/environments/environment.au2.ts @@ -13,6 +13,7 @@ export const environment = { production: true, getCSWRecordEndP: 'getKnownLayers.do', portalBaseUrl: 'https://au-portal-2.it.csiro.au/api/', + portalProxyUrl: '/api/', hostUrl: 'https://au-portal-2.it.csiro.au/portal/index.htm', nVCLAnalyticalUrl: 'https://nvclanalytics.azurewebsites.net/NVCLAnalyticalServices/', googleAnalyticsKey: null, diff --git a/src/environments/environment.dev.ts b/src/environments/environment.dev.ts index e4ed4a4be..131c734ff 100644 --- a/src/environments/environment.dev.ts +++ b/src/environments/environment.dev.ts @@ -13,6 +13,7 @@ export const environment = { production: false, getCSWRecordEndP: 'getKnownLayers.do', portalBaseUrl: 'https://au-portal-dev.it.csiro.au/api/', + portalProxyUrl: '/api/', hostUrl: 'https://au-portal-dev.it.csiro.au/index.htm', nVCLAnalyticalUrl: 'https://nvclanalytics.azurewebsites.net/NVCLAnalyticalServices/', googleAnalyticsKey: null, diff --git a/src/environments/environment.prod-https.ts b/src/environments/environment.prod-https.ts index d18b51183..29cd83d98 100644 --- a/src/environments/environment.prod-https.ts +++ b/src/environments/environment.prod-https.ts @@ -13,6 +13,7 @@ export const environment = { production: true, getCSWRecordEndP: 'getKnownLayers.do', portalBaseUrl: 'https://auscope-portal.geoanalytics.csiro.au/api/', + portalProxyUrl: '/api/', hostUrl: 'https://auscope-portal.geoanalytics.csiro.au/index.htm', nVCLAnalyticalUrl: 'https://nvclanalytics.azurewebsites.net/NVCLAnalyticalServices/', googleAnalyticsKey: null, diff --git a/src/environments/environment.prod.ts b/src/environments/environment.prod.ts index 41cdafea1..238a1218b 100644 --- a/src/environments/environment.prod.ts +++ b/src/environments/environment.prod.ts @@ -13,6 +13,7 @@ export const environment = { production: true, getCSWRecordEndP: 'getKnownLayers.do', portalBaseUrl: 'http://portal.auscope.org.au/api/', + portalProxyUrl: '/api/', hostUrl: 'http://portal.auscope.org.au/index.htm', nVCLAnalyticalUrl: 'https://nvclanalytics.azurewebsites.net/NVCLAnalyticalServices/', googleAnalyticsKey: null, From 2bb85632bb871a6212656402012822d251dc56be Mon Sep 17 00:00:00 2001 From: "stuart.woodman" Date: Mon, 6 Feb 2023 10:15:33 +1100 Subject: [PATCH 3/3] Bump version --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 774aaf5e7..cd5ab2d75 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "auscope-portal", - "version": "6.2.0", + "version": "6.2.1", "license": "MIT", "scripts": { "ng": "ng",