Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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: 4 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,11 @@
"start:dev": "nodemon --exec \"cross-env NODE_ENV=development yarn run serve\"",
"start:prod": "yarn run build:prod && cross-env NODE_ENV=production yarn run serve:ssr",
"start:mirador:prod": "yarn run build:mirador && yarn run start:prod",
"serve": "ng serve -c development",
"preserve": "yarn base-href",
"serve": "ng serve --configuration development",
"serve:ssr": "node dist/server/main",
"analyze": "webpack-bundle-analyzer dist/browser/stats.json",
"build": "ng build -c development",
"build": "ng build --configuration development",
"build:stats": "ng build --stats-json",
"build:prod": "yarn run build:ssr",
"build:ssr": "ng build --configuration production && ng run dspace-angular:server:production",
Expand All @@ -37,6 +38,7 @@
"cypress:open": "cypress open",
"cypress:run": "cypress run",
"env:yaml": "ts-node --project ./tsconfig.ts-node.json scripts/env-to-yaml.ts",
"base-href": "ts-node --project ./tsconfig.ts-node.json scripts/base-href.ts",
"check-circ-deps": "npx madge --exclude '(bitstream|bundle|collection|config-submission-form|eperson|item|version)\\.model\\.ts$' --circular --extensions ts ./"
},
"browser": {
Expand Down
36 changes: 36 additions & 0 deletions scripts/base-href.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import * as fs from 'fs';
import { join } from 'path';

import { AppConfig } from '../src/config/app-config.interface';
import { buildAppConfig } from '../src/config/config.server';

/**
* Script to set baseHref as `ui.nameSpace` for development mode. Adds `baseHref` to angular.json build options.
*
* Usage (see package.json):
*
* yarn base-href
*/

const appConfig: AppConfig = buildAppConfig();

const angularJsonPath = join(process.cwd(), 'angular.json');

if (!fs.existsSync(angularJsonPath)) {
console.error(`Error:\n${angularJsonPath} does not exist\n`);
process.exit(1);
}

try {
const angularJson = require(angularJsonPath);

const baseHref = `${appConfig.ui.nameSpace}${appConfig.ui.nameSpace.endsWith('/') ? '' : '/'}`;

console.log(`Setting baseHref to ${baseHref} in angular.json`);

angularJson.projects['dspace-angular'].architect.build.options.baseHref = baseHref;

fs.writeFileSync(angularJsonPath, JSON.stringify(angularJson, null, 2) + '\n');
} catch (e) {
console.error(e);
}
32 changes: 26 additions & 6 deletions server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,8 @@ extendEnvironmentWithAppConfig(environment, appConfig);
// The Express app is exported so that it can be used by serverless Functions.
export function app() {

const router = express.Router();

/*
* Create a new express application
*/
Expand Down Expand Up @@ -138,7 +140,11 @@ export function app() {
/**
* Proxy the sitemaps
*/
server.use('/sitemap**', createProxyMiddleware({ target: `${environment.rest.baseUrl}/sitemaps`, changeOrigin: true }));
router.use('/sitemap**', createProxyMiddleware({
target: `${environment.rest.baseUrl}/sitemaps`,
pathRewrite: path => path.replace(environment.ui.nameSpace, '/'),
changeOrigin: true
}));

/**
* Checks if the rateLimiter property is present
Expand All @@ -157,7 +163,7 @@ export function app() {
* Serve static resources (images, i18n messages, …)
* Handle pre-compressed files with [express-static-gzip](https://github.com/tkoenig89/express-static-gzip)
*/
server.get('*.*', cacheControl, expressStaticGzip(DIST_FOLDER, {
router.get('*.*', cacheControl, expressStaticGzip(DIST_FOLDER, {
index: false,
enableBrotli: true,
orderPreference: ['br', 'gzip'],
Expand All @@ -166,10 +172,12 @@ export function app() {
/*
* Fallthrough to the IIIF viewer (must be included in the build).
*/
server.use('/iiif', express.static(IIIF_VIEWER, {index:false}));
router.use('/iiif', express.static(IIIF_VIEWER, { index: false }));

// Register the ngApp callback function to handle incoming requests
server.get('*', ngApp);
router.get('*', ngApp);

server.use(environment.ui.nameSpace, router);

return server;
}
Expand Down Expand Up @@ -203,13 +211,25 @@ function ngApp(req, res) {
if (hasValue(err)) {
console.warn('Error details : ', err);
}
res.sendFile(DIST_FOLDER + '/index.html');
res.render(indexHtml, {
req,
providers: [{
provide: APP_BASE_HREF,
useValue: req.baseUrl
}]
});
}
});
} else {
// If preboot is disabled, just serve the client
console.log('Universal off, serving for direct CSR');
res.sendFile(DIST_FOLDER + '/index.html');
res.render(indexHtml, {
req,
providers: [{
provide: APP_BASE_HREF,
useValue: req.baseUrl
}]
});
}
}

Expand Down
2 changes: 1 addition & 1 deletion src/app/app.component.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -187,7 +187,7 @@ describe('App component', () => {
link.setAttribute('rel', 'stylesheet');
link.setAttribute('type', 'text/css');
link.setAttribute('class', 'theme-css');
link.setAttribute('href', '/custom-theme.css');
link.setAttribute('href', 'custom-theme.css');

expect(headSpy.appendChild).toHaveBeenCalledWith(link);
});
Expand Down
2 changes: 1 addition & 1 deletion src/app/app.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -268,7 +268,7 @@ export class AppComponent implements OnInit, AfterViewInit {
link.setAttribute('rel', 'stylesheet');
link.setAttribute('type', 'text/css');
link.setAttribute('class', 'theme-css');
link.setAttribute('href', `/${encodeURIComponent(themeName)}-theme.css`);
link.setAttribute('href', `${encodeURIComponent(themeName)}-theme.css`);
// wait for the new css to download before removing the old one to prevent a
// flash of unstyled content
link.onload = () => {
Expand Down
14 changes: 8 additions & 6 deletions src/app/app.module.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { APP_BASE_HREF, CommonModule } from '@angular/common';
import { APP_BASE_HREF, CommonModule, DOCUMENT } from '@angular/common';
import { HTTP_INTERCEPTORS, HttpClientModule } from '@angular/common/http';
import { APP_INITIALIZER, NgModule } from '@angular/core';
import { AbstractControl } from '@angular/forms';
Expand Down Expand Up @@ -42,9 +42,11 @@ export function getConfig() {
return environment;
}

export function getBase(appConfig: AppConfig) {
return appConfig.ui.nameSpace;
}
const getBaseHref = (document: Document, appConfig: AppConfig): string => {
const baseTag = document.querySelector('head > base');
baseTag.setAttribute('href', `${appConfig.ui.nameSpace}${appConfig.ui.nameSpace.endsWith('/') ? '' : '/'}`);
return baseTag.getAttribute('href');
};

export function getMetaReducers(appConfig: AppConfig): MetaReducer<AppState>[] {
return appConfig.debug ? [...appMetaReducers, ...debugMetaReducers] : appMetaReducers;
Expand Down Expand Up @@ -84,8 +86,8 @@ const PROVIDERS = [
},
{
provide: APP_BASE_HREF,
useFactory: getBase,
deps: [APP_CONFIG]
useFactory: getBaseHref,
deps: [DOCUMENT, APP_CONFIG]
},
{
provide: USER_PROVIDED_META_REDUCERS,
Expand Down
8 changes: 4 additions & 4 deletions src/app/core/auth/auth.service.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -377,25 +377,25 @@ describe('AuthService test', () => {
it('should redirect to reload with redirect url', () => {
authService.navigateToRedirectUrl('/collection/123');
// Reload with redirect URL set to /collection/123
expect(hardRedirectService.redirect).toHaveBeenCalledWith(jasmine.stringMatching(new RegExp('/reload/[0-9]*\\?redirect=' + encodeURIComponent('/collection/123'))));
expect(hardRedirectService.redirect).toHaveBeenCalledWith(jasmine.stringMatching(new RegExp('reload/[0-9]*\\?redirect=' + encodeURIComponent('/collection/123'))));
});

it('should redirect to reload with /home', () => {
authService.navigateToRedirectUrl('/home');
// Reload with redirect URL set to /home
expect(hardRedirectService.redirect).toHaveBeenCalledWith(jasmine.stringMatching(new RegExp('/reload/[0-9]*\\?redirect=' + encodeURIComponent('/home'))));
expect(hardRedirectService.redirect).toHaveBeenCalledWith(jasmine.stringMatching(new RegExp('reload/[0-9]*\\?redirect=' + encodeURIComponent('/home'))));
});

it('should redirect to regular reload and not to /login', () => {
authService.navigateToRedirectUrl('/login');
// Reload without a redirect URL
expect(hardRedirectService.redirect).toHaveBeenCalledWith(jasmine.stringMatching(new RegExp('/reload/[0-9]*(?!\\?)$')));
expect(hardRedirectService.redirect).toHaveBeenCalledWith(jasmine.stringMatching(new RegExp('reload/[0-9]*(?!\\?)$')));
});

it('should redirect to regular reload when no redirect url is found', () => {
authService.navigateToRedirectUrl(undefined);
// Reload without a redirect URL
expect(hardRedirectService.redirect).toHaveBeenCalledWith(jasmine.stringMatching(new RegExp('/reload/[0-9]*(?!\\?)$')));
expect(hardRedirectService.redirect).toHaveBeenCalledWith(jasmine.stringMatching(new RegExp('reload/[0-9]*(?!\\?)$')));
});

describe('impersonate', () => {
Expand Down
4 changes: 2 additions & 2 deletions src/app/core/auth/auth.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -468,8 +468,8 @@ export class AuthService {
*/
public navigateToRedirectUrl(redirectUrl: string) {
// Don't do redirect if already on reload url
if (!hasValue(redirectUrl) || !redirectUrl.includes('/reload/')) {
let url = `/reload/${new Date().getTime()}`;
if (!hasValue(redirectUrl) || !redirectUrl.includes('reload/')) {
let url = `reload/${new Date().getTime()}`;
if (isNotEmpty(redirectUrl) && !redirectUrl.startsWith(LOGIN_ROUTE)) {
url += `?redirect=${encodeURIComponent(redirectUrl)}`;
}
Expand Down
2 changes: 1 addition & 1 deletion src/app/core/locale/locale.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -192,7 +192,7 @@ export class LocaleService {
this.routeService.getCurrentUrl().pipe(take(1)).subscribe((currentURL) => {
// Hard redirect to the reload page with a unique number behind it
// so that all state is definitely lost
this._window.nativeWindow.location.href = `/reload/${new Date().getTime()}?redirect=` + encodeURIComponent(currentURL);
this._window.nativeWindow.location.href = `reload/${new Date().getTime()}?redirect=` + encodeURIComponent(currentURL);
});

}
Expand Down
10 changes: 7 additions & 3 deletions src/app/core/reload/reload.guard.spec.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,17 @@
import { ReloadGuard } from './reload.guard';
import { Router } from '@angular/router';
import { AppConfig } from '../../../config/app-config.interface';
import { DefaultAppConfig } from '../../../config/default-app-config';
import { ReloadGuard } from './reload.guard';

describe('ReloadGuard', () => {
let guard: ReloadGuard;
let router: Router;
let appConfig: AppConfig;

beforeEach(() => {
router = jasmine.createSpyObj('router', ['parseUrl', 'createUrlTree']);
guard = new ReloadGuard(router);
appConfig = new DefaultAppConfig();
guard = new ReloadGuard(router, appConfig);
});

describe('canActivate', () => {
Expand All @@ -27,7 +31,7 @@ describe('ReloadGuard', () => {

it('should create a UrlTree with the redirect URL', () => {
guard.canActivate(route, undefined);
expect(router.parseUrl).toHaveBeenCalledWith(redirectUrl);
expect(router.parseUrl).toHaveBeenCalledWith(redirectUrl.substring(1));
});
});

Expand Down
13 changes: 10 additions & 3 deletions src/app/core/reload/reload.guard.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { Inject, Injectable } from '@angular/core';
import { ActivatedRouteSnapshot, CanActivate, Router, RouterStateSnapshot, UrlTree } from '@angular/router';
import { Injectable } from '@angular/core';
import { AppConfig, APP_CONFIG } from '../../../config/app-config.interface';
import { isNotEmpty } from '../../shared/empty.util';

/**
Expand All @@ -8,7 +9,10 @@ import { isNotEmpty } from '../../shared/empty.util';
*/
@Injectable()
export class ReloadGuard implements CanActivate {
constructor(private router: Router) {
constructor(
private router: Router,
@Inject(APP_CONFIG) private appConfig: AppConfig,
) {
}

/**
Expand All @@ -18,7 +22,10 @@ export class ReloadGuard implements CanActivate {
*/
canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): UrlTree {
if (isNotEmpty(route.queryParams.redirect)) {
return this.router.parseUrl(route.queryParams.redirect);
const url = route.queryParams.redirect.startsWith(this.appConfig.ui.nameSpace)
? route.queryParams.redirect.substring(this.appConfig.ui.nameSpace.length)
: route.queryParams.redirect;
return this.router.parseUrl(url);
} else {
return this.router.createUrlTree(['home']);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,6 @@ <h2><small>{{"500.page-internal-server-error" | translate}}</small></h2>
<p>{{"500.help" | translate}}</p>
<br/>
<p class="text-center">
<a href="/home" class="btn btn-primary">{{"500.link.home-page" | translate}}</a>
<a href="home" class="btn btn-primary">{{"500.link.home-page" | translate}}</a>
</p>
</div>
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ import { DebugElement, NO_ERRORS_SCHEMA } from '@angular/core';
import { By } from '@angular/platform-browser';
import { LinkMenuItemComponent } from './link-menu-item.component';
import { RouterLinkDirectiveStub } from '../../testing/router-link-directive.stub';
import { environment } from '../../../../environments/environment';
import { QueryParamsDirectiveStub } from '../../testing/query-params-directive.stub';
import { RouterStub } from '../../testing/router.stub';
import { Router } from '@angular/router';
Expand Down Expand Up @@ -58,7 +57,7 @@ describe('LinkMenuItemComponent', () => {
const routerLinkQuery = linkDes.map((de) => de.injector.get(RouterLinkDirectiveStub));

expect(routerLinkQuery.length).toBe(1);
expect(routerLinkQuery[0].routerLink).toBe(environment.ui.nameSpace + link);
expect(routerLinkQuery[0].routerLink).toBe(link);
});

it('should have the right queryParams attribute', () => {
Expand Down
3 changes: 1 addition & 2 deletions src/app/shared/menu/menu-item/link-menu-item.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ import { Component, Inject, Input, OnInit } from '@angular/core';
import { LinkMenuItemModel } from './models/link.model';
import { rendersMenuItemForType } from '../menu-item.decorator';
import { isNotEmpty } from '../../empty.util';
import { environment } from '../../../../environments/environment';
import { MenuItemType } from '../menu-item-type.model';
import { Router } from '@angular/router';

Expand Down Expand Up @@ -30,7 +29,7 @@ export class LinkMenuItemComponent implements OnInit {

getRouterLink() {
if (this.hasLink) {
return environment.ui.nameSpace + this.item.link;
return this.item.link;
}
return undefined;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,7 @@ describe('NotificationComponent', () => {
it('should have html content', () => {
fixture = TestBed.createComponent(NotificationComponent);
comp = fixture.componentInstance;
const htmlContent = '<a class="btn btn-link p-0 m-0 pb-1" href="/test"><strong>test</strong></a>';
const htmlContent = '<a class="btn btn-link p-0 m-0 pb-1" href="test"><strong>test</strong></a>';
comp.notification = {
id: '1',
type: NotificationType.Info,
Expand Down
2 changes: 1 addition & 1 deletion src/index.csr.html
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,6 @@
</body>

<!-- this is needed for CSR fallback -->
<script async src="/client.js"></script>
<script async src="client.js"></script>

</html>
4 changes: 3 additions & 1 deletion src/styles/_bootstrap_variables.scss
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,9 @@ $sidebar-items-width: 250px !default;
$total-sidebar-width: $collapsed-sidebar-width + $sidebar-items-width !default;

/* Fonts */
$fa-font-path: "/assets/fonts" !default;
// Starting this url with a caret (^) allows it to be a relative path based on UI's deployment path
// See https://github.com/angular/angular-cli/issues/12797#issuecomment-598534241
$fa-font-path: "^assets/fonts" !default;
/* Images */
$image-path: "../assets/images" !default;

Expand Down