diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
index c58e09edf2..f3b7aff689 100644
--- a/.github/workflows/build.yml
+++ b/.github/workflows/build.yml
@@ -15,17 +15,19 @@ jobs:
env:
# The ci step will test the dspace-angular code against DSpace REST.
# Direct that step to utilize a DSpace REST service that has been started in docker.
- DSPACE_REST_HOST: localhost
+ DSPACE_REST_HOST: 127.0.0.1
DSPACE_REST_PORT: 8080
DSPACE_REST_NAMESPACE: '/server'
DSPACE_REST_SSL: false
+ # Spin up UI on 127.0.0.1 to avoid host resolution issues in e2e tests with Node 18+
+ DSPACE_UI_HOST: 127.0.0.1
# When Chrome version is specified, we pin to a specific version of Chrome
# Comment this out to use the latest release
#CHROME_VERSION: "90.0.4430.212-1"
strategy:
# Create a matrix of Node versions to test against (in parallel)
matrix:
- node-version: [14.x, 16.x]
+ node-version: [16.x, 18.x]
# Do NOT exit immediately if one matrix job fails
fail-fast: false
# These are the actual CI steps to perform per job
@@ -112,7 +114,7 @@ jobs:
start: yarn run serve:ssr
# Wait for backend & frontend to be available
# NOTE: We use the 'sites' REST endpoint to also ensure the database is ready
- wait-on: http://localhost:8080/server/api/core/sites, http://localhost:4000
+ wait-on: http://127.0.0.1:8080/server/api/core/sites, http://127.0.0.1:4000
# Wait for 2 mins max for everything to respond
wait-on-timeout: 120
@@ -147,7 +149,7 @@ jobs:
run: |
nohup yarn run serve:ssr &
printf 'Waiting for app to start'
- until curl --output /dev/null --silent --head --fail http://localhost:4000/home; do
+ until curl --output /dev/null --silent --head --fail http://127.0.0.1:4000/home; do
printf '.'
sleep 2
done
@@ -158,7 +160,7 @@ jobs:
# This step also prints entire HTML of homepage for easier debugging if grep fails.
- name: Verify SSR (server-side rendering)
run: |
- result=$(wget -O- -q http://localhost:4000/home)
+ result=$(wget -O- -q http://127.0.0.1:4000/home)
echo "$result"
echo "$result" | grep -oE "]*>" | grep DSpace
diff --git a/Dockerfile b/Dockerfile
index a7c1640d0b..61d960e7d3 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -1,11 +1,15 @@
# This image will be published as dspace/dspace-angular
# See https://github.com/DSpace/dspace-angular/tree/main/docker for usage details
-FROM node:14-alpine
+FROM node:18-alpine
WORKDIR /app
ADD . /app/
EXPOSE 4000
+# Ensure Python and other build tools are available
+# These are needed to install some node modules, especially on linux/arm64
+RUN apk add --update python3 make g++ && rm -rf /var/cache/apk/*
+
# We run yarn install with an increased network timeout (5min) to avoid "ESOCKETTIMEDOUT" errors from hub.docker.com
# See, for example https://github.com/yarnpkg/yarn/issues/5540
RUN yarn install --network-timeout 300000
diff --git a/README.md b/README.md
index 3867759ab7..c90dc1d08f 100644
--- a/README.md
+++ b/README.md
@@ -35,7 +35,7 @@ https://wiki.lyrasis.org/display/DSDOC7x/Installing+DSpace
Quick start
-----------
-**Ensure you're running [Node](https://nodejs.org) `v14.x` or `v16.x`, [npm](https://www.npmjs.com/) >= `v5.x` and [yarn](https://yarnpkg.com) == `v1.x`**
+**Ensure you're running [Node](https://nodejs.org) `v16.x` or `v18.x`, [npm](https://www.npmjs.com/) >= `v5.x` and [yarn](https://yarnpkg.com) == `v1.x`**
```bash
# clone the repo
@@ -90,7 +90,7 @@ Requirements
------------
- [Node.js](https://nodejs.org) and [yarn](https://yarnpkg.com)
-- Ensure you're running node `v14.x` or `v16.x` and yarn == `v1.x`
+- Ensure you're running node `v16.x` or `v18.x` and yarn == `v1.x`
If you have [`nvm`](https://github.com/creationix/nvm#install-script) or [`nvm-windows`](https://github.com/coreybutler/nvm-windows) installed, which is highly recommended, you can run `nvm install --lts && nvm use` to install and start using the latest Node LTS.
diff --git a/angular.json b/angular.json
index 2ece0c5e7d..b32670ad77 100644
--- a/angular.json
+++ b/angular.json
@@ -25,12 +25,10 @@
}
},
"allowedCommonJsDependencies": [
- "angular2-text-mask",
"cerialize",
"core-js",
"lodash",
"jwt-decode",
- "url-parse",
"uuid",
"webfontloader",
"zone.js"
diff --git a/config/config.example.yml b/config/config.example.yml
index a6673457ab..1e55dfda5f 100644
--- a/config/config.example.yml
+++ b/config/config.example.yml
@@ -210,6 +210,11 @@ item:
undoTimeout: 10000 # 10 seconds
# Show the item access status label in items lists
showAccessStatuses: false
+ bitstream:
+ # Number of entries in the bitstream list in the item view page.
+ # Rounded to the nearest size in the list of selectable sizes on the
+ # settings menu. See pageSizeOptions in 'pagination-component-options.model.ts'.
+ pageSize: 5
# Collection Page Config
collection:
@@ -298,4 +303,4 @@ info:
# display in supported metadata fields. By default, only dc.description.abstract is supported.
markdown:
enabled: false
- mathjax: false
\ No newline at end of file
+ mathjax: false
diff --git a/cypress.json b/cypress.json
index 80358eb6dd..3adf7839c2 100644
--- a/cypress.json
+++ b/cypress.json
@@ -5,7 +5,7 @@
"screenshotsFolder": "cypress/screenshots",
"pluginsFile": "cypress/plugins/index.ts",
"fixturesFolder": "cypress/fixtures",
- "baseUrl": "http://localhost:4000",
+ "baseUrl": "http://127.0.0.1:4000",
"retries": {
"runMode": 2,
"openMode": 0
@@ -22,4 +22,4 @@
"DSPACE_TEST_SUBMIT_USER": "dspacedemo+submit@gmail.com",
"DSPACE_TEST_SUBMIT_USER_PASSWORD": "dspace"
}
-}
\ No newline at end of file
+}
diff --git a/docker/docker-compose-ci.yml b/docker/docker-compose-ci.yml
index dbe9500499..ef84c14f43 100644
--- a/docker/docker-compose-ci.yml
+++ b/docker/docker-compose-ci.yml
@@ -24,8 +24,8 @@ services:
# __D__ => "-" (e.g. google__D__metadata => google-metadata)
# dspace.dir, dspace.server.url and dspace.ui.url
dspace__P__dir: /dspace
- dspace__P__server__P__url: http://localhost:8080/server
- dspace__P__ui__P__url: http://localhost:4000
+ dspace__P__server__P__url: http://127.0.0.1:8080/server
+ dspace__P__ui__P__url: http://127.0.0.1:4000
# db.url: Ensure we are using the 'dspacedb' image for our database
db__P__url: 'jdbc:postgresql://dspacedb:5432/dspace'
# solr.server: Ensure we are using the 'dspacesolr' image for Solr
diff --git a/package.json b/package.json
index 705dc8e345..52b089be37 100644
--- a/package.json
+++ b/package.json
@@ -54,18 +54,18 @@
"ts-node": "10.2.1"
},
"dependencies": {
- "@angular/animations": "~13.2.6",
+ "@angular/animations": "~13.3.12",
"@angular/cdk": "^13.2.6",
- "@angular/common": "~13.2.6",
- "@angular/compiler": "~13.2.6",
- "@angular/core": "~13.2.6",
- "@angular/forms": "~13.2.6",
- "@angular/localize": "13.2.6",
- "@angular/platform-browser": "~13.2.6",
- "@angular/platform-browser-dynamic": "~13.2.6",
- "@angular/platform-server": "~13.2.6",
- "@angular/router": "~13.2.6",
- "@babel/runtime": "^7.17.2",
+ "@angular/common": "~13.3.12",
+ "@angular/compiler": "~13.3.12",
+ "@angular/core": "~13.3.12",
+ "@angular/forms": "~13.3.12",
+ "@angular/localize": "13.3.12",
+ "@angular/platform-browser": "~13.3.12",
+ "@angular/platform-browser-dynamic": "~13.3.12",
+ "@angular/platform-server": "~13.3.12",
+ "@angular/router": "~13.3.12",
+ "@babel/runtime": "7.17.2",
"@kolkov/ngx-gallery": "^2.0.1",
"@material-ui/core": "^4.11.0",
"@material-ui/icons": "^4.9.1",
@@ -77,15 +77,15 @@
"@ngrx/store": "^13.0.2",
"@nguniversal/express-engine": "^13.0.2",
"@ngx-translate/core": "^13.0.0",
- "@nicky-lenaers/ngx-scroll-to": "^9.0.0",
+ "@nicky-lenaers/ngx-scroll-to": "^13.0.0",
"@types/grecaptcha": "^3.0.4",
"angular-idle-preload": "3.0.0",
"angulartics2": "^12.0.0",
"axios": "^0.27.2",
- "bootstrap": "4.3.1",
- "caniuse-lite": "^1.0.30001165",
+ "bootstrap": "^4.6.1",
"cerialize": "0.1.18",
"cli-progress": "^3.8.0",
+ "colors": "^1.4.0",
"compression": "^1.7.4",
"cookie-parser": "1.4.5",
"core-js": "^3.7.0",
@@ -95,11 +95,8 @@
"express": "^4.17.1",
"express-rate-limit": "^5.1.3",
"fast-json-patch": "^3.0.0-1",
- "file-saver": "^2.0.5",
"filesize": "^6.1.0",
- "font-awesome": "4.7.0",
"http-proxy-middleware": "^1.0.5",
- "https": "1.0.0",
"js-cookie": "2.2.1",
"js-yaml": "^4.1.0",
"json5": "^2.1.3",
@@ -119,43 +116,38 @@
"ngx-infinite-scroll": "^10.0.1",
"ngx-pagination": "5.0.0",
"ngx-sortablejs": "^11.1.0",
- "ngx-ui-switch": "^11.0.1",
+ "ngx-ui-switch": "^13.0.2",
"nouislider": "^14.6.3",
"pem": "1.14.4",
- "postcss-cli": "^9.1.0",
"prop-types": "^15.7.2",
"react-copy-to-clipboard": "^5.0.1",
"reflect-metadata": "^0.1.13",
"rxjs": "^7.5.5",
"sanitize-html": "^2.7.2",
"sortablejs": "1.13.0",
- "tslib": "^2.0.0",
- "url-parse": "^1.5.6",
"uuid": "^8.3.2",
"webfontloader": "1.6.28",
"zone.js": "~0.11.5"
},
"devDependencies": {
"@angular-builders/custom-webpack": "~13.1.0",
- "@angular-devkit/build-angular": "~13.2.6",
+ "@angular-devkit/build-angular": "~13.3.10",
"@angular-eslint/builder": "13.1.0",
"@angular-eslint/eslint-plugin": "13.1.0",
"@angular-eslint/eslint-plugin-template": "13.1.0",
"@angular-eslint/schematics": "13.1.0",
"@angular-eslint/template-parser": "13.1.0",
- "@angular/cli": "~13.2.6",
- "@angular/compiler-cli": "~13.2.6",
- "@angular/language-service": "~13.2.6",
+ "@angular/cli": "~13.3.10",
+ "@angular/compiler-cli": "~13.3.12",
+ "@angular/language-service": "~13.3.12",
"@cypress/schematic": "^1.5.0",
- "@fortawesome/fontawesome-free": "^5.5.0",
+ "@fortawesome/fontawesome-free": "^6.2.1",
"@ngrx/store-devtools": "^13.0.2",
"@ngtools/webpack": "^13.2.6",
- "@nguniversal/builders": "^13.0.2",
+ "@nguniversal/builders": "^13.1.1",
"@types/deep-freeze": "0.1.2",
"@types/express": "^4.17.9",
- "@types/file-saver": "^2.0.1",
"@types/jasmine": "~3.6.0",
- "@types/jasminewd2": "~2.0.8",
"@types/js-cookie": "2.2.6",
"@types/lodash": "^4.14.165",
"@types/node": "^14.14.9",
@@ -166,26 +158,18 @@
"compression-webpack-plugin": "^9.2.0",
"copy-webpack-plugin": "^6.4.1",
"cross-env": "^7.0.3",
- "css-loader": "^6.2.0",
- "css-minimizer-webpack-plugin": "^3.4.1",
- "cssnano": "^5.0.6",
"cypress": "9.7.0",
"cypress-axe": "^0.14.0",
- "debug-loader": "^0.0.1",
"deep-freeze": "0.0.1",
- "dotenv": "^8.2.0",
"eslint": "^8.2.0",
"eslint-plugin-deprecation": "^1.3.2",
"eslint-plugin-import": "^2.25.4",
- "eslint-plugin-jsdoc": "^38.0.6",
+ "eslint-plugin-jsdoc": "^39.6.4",
"eslint-plugin-lodash": "^7.4.0",
"eslint-plugin-unused-imports": "^2.0.0",
"express-static-gzip": "^2.1.5",
- "fork-ts-checker-webpack-plugin": "^6.0.3",
- "html-loader": "^1.3.2",
"jasmine-core": "^3.8.0",
"jasmine-marbles": "0.9.2",
- "jasmine-spec-reporter": "~5.0.0",
"karma": "^6.3.14",
"karma-chrome-launcher": "~3.1.0",
"karma-coverage-istanbul-reporter": "~3.0.2",
@@ -193,26 +177,20 @@
"karma-jasmine-html-reporter": "^1.5.0",
"karma-mocha-reporter": "2.2.5",
"ngx-mask": "^13.1.7",
- "nodemon": "^2.0.15",
+ "nodemon": "^2.0.20",
"postcss": "^8.1",
"postcss-apply": "0.12.0",
"postcss-import": "^14.0.0",
"postcss-loader": "^4.0.3",
"postcss-preset-env": "^7.4.2",
"postcss-responsive-type": "1.0.0",
- "protractor": "^7.0.0",
- "protractor-istanbul-plugin": "2.0.0",
- "raw-loader": "0.5.1",
"react": "^16.14.0",
"react-dom": "^16.14.0",
"rimraf": "^3.0.2",
"rxjs-spy": "^8.0.2",
- "sass": "~1.32.6",
+ "sass": "~1.33.0",
"sass-loader": "^12.6.0",
"sass-resources-loader": "^2.1.1",
- "string-replace-loader": "^3.1.0",
- "terser-webpack-plugin": "^2.3.1",
- "ts-loader": "^5.2.0",
"ts-node": "^8.10.2",
"typescript": "~4.5.5",
"webpack": "^5.69.1",
diff --git a/src/app/access-control/access-control.module.ts b/src/app/access-control/access-control.module.ts
index 891238bbed..afb92a9111 100644
--- a/src/app/access-control/access-control.module.ts
+++ b/src/app/access-control/access-control.module.ts
@@ -10,6 +10,16 @@ import { MembersListComponent } from './group-registry/group-form/members-list/m
import { SubgroupsListComponent } from './group-registry/group-form/subgroup-list/subgroups-list.component';
import { GroupsRegistryComponent } from './group-registry/groups-registry.component';
import { FormModule } from '../shared/form/form.module';
+import { DYNAMIC_ERROR_MESSAGES_MATCHER, DynamicErrorMessagesMatcher } from '@ng-dynamic-forms/core';
+import { AbstractControl } from '@angular/forms';
+
+/**
+ * Condition for displaying error messages on email form field
+ */
+export const ValidateEmailErrorStateMatcher: DynamicErrorMessagesMatcher =
+ (control: AbstractControl, model: any, hasFocus: boolean) => {
+ return (control.touched && !hasFocus) || (control.errors?.emailTaken && hasFocus);
+ };
@NgModule({
imports: [
@@ -26,6 +36,12 @@ import { FormModule } from '../shared/form/form.module';
GroupFormComponent,
SubgroupsListComponent,
MembersListComponent
+ ],
+ providers: [
+ {
+ provide: DYNAMIC_ERROR_MESSAGES_MATCHER,
+ useValue: ValidateEmailErrorStateMatcher
+ },
]
})
/**
diff --git a/src/app/admin/admin.module.ts b/src/app/admin/admin.module.ts
index 0ddbefd253..dff2e506c3 100644
--- a/src/app/admin/admin.module.ts
+++ b/src/app/admin/admin.module.ts
@@ -10,6 +10,7 @@ import { AdminSearchModule } from './admin-search-page/admin-search.module';
import { AdminSidebarSectionComponent } from './admin-sidebar/admin-sidebar-section/admin-sidebar-section.component';
import { ExpandableAdminSidebarSectionComponent } from './admin-sidebar/expandable-admin-sidebar-section/expandable-admin-sidebar-section.component';
import { BatchImportPageComponent } from './admin-import-batch-page/batch-import-page.component';
+import { UploadModule } from '../shared/upload/upload.module';
const ENTRY_COMPONENTS = [
// put only entry components that use custom decorator
@@ -25,7 +26,8 @@ const ENTRY_COMPONENTS = [
AccessControlModule,
AdminSearchModule.withEntryComponents(),
AdminWorkflowModuleModule.withEntryComponents(),
- SharedModule
+ SharedModule,
+ UploadModule,
],
declarations: [
AdminCurationTasksComponent,
diff --git a/src/app/app.module.ts b/src/app/app.module.ts
index 392969d041..750d63beda 100644
--- a/src/app/app.module.ts
+++ b/src/app/app.module.ts
@@ -1,14 +1,12 @@
import { APP_BASE_HREF, CommonModule, DOCUMENT } from '@angular/common';
import { HTTP_INTERCEPTORS, HttpClientModule } from '@angular/common/http';
import { NgModule } from '@angular/core';
-import { AbstractControl } from '@angular/forms';
import { BrowserModule } from '@angular/platform-browser';
import { NgbModule } from '@ng-bootstrap/ng-bootstrap';
import { EffectsModule } from '@ngrx/effects';
import { RouterStateSerializer, StoreRouterConnectingModule } from '@ngrx/router-store';
import { MetaReducer, StoreModule, USER_PROVIDED_META_REDUCERS } from '@ngrx/store';
-import { DYNAMIC_ERROR_MESSAGES_MATCHER, DYNAMIC_MATCHER_PROVIDERS, DynamicErrorMessagesMatcher } from '@ng-dynamic-forms/core';
import { TranslateModule } from '@ngx-translate/core';
import { ScrollToModule } from '@nicky-lenaers/ngx-scroll-to';
import { AppRoutingModule } from './app-routing.module';
@@ -28,7 +26,6 @@ import { XsrfInterceptor } from './core/xsrf/xsrf.interceptor';
import { LogInterceptor } from './core/log/log.interceptor';
import { EagerThemesModule } from '../themes/eager-themes.module';
import { APP_CONFIG, AppConfig } from '../config/app-config.interface';
-import { NgxMaskModule } from 'ngx-mask';
import { StoreDevModules } from '../config/store/devtools';
import { RootModule } from './root.module';
@@ -46,14 +43,6 @@ export function getMetaReducers(appConfig: AppConfig): MetaReducer[] {
return appConfig.debug ? [...appMetaReducers, ...debugMetaReducers] : appMetaReducers;
}
-/**
- * Condition for displaying error messages on email form field
- */
-export const ValidateEmailErrorStateMatcher: DynamicErrorMessagesMatcher =
- (control: AbstractControl, model: any, hasFocus: boolean) => {
- return (control.touched && !hasFocus) || (control.errors?.emailTaken && hasFocus);
- };
-
const IMPORTS = [
CommonModule,
SharedModule,
@@ -64,7 +53,6 @@ const IMPORTS = [
ScrollToModule.forRoot(),
NgbModule,
TranslateModule.forRoot(),
- NgxMaskModule.forRoot(),
EffectsModule.forRoot(appEffects),
StoreModule.forRoot(appReducers, storeModuleConfig),
StoreRouterConnectingModule.forRoot(),
@@ -113,11 +101,6 @@ const PROVIDERS = [
useClass: LogInterceptor,
multi: true
},
- {
- provide: DYNAMIC_ERROR_MESSAGES_MATCHER,
- useValue: ValidateEmailErrorStateMatcher
- },
- ...DYNAMIC_MATCHER_PROVIDERS,
];
const DECLARATIONS = [
diff --git a/src/app/breadcrumbs/breadcrumbs.component.html b/src/app/breadcrumbs/breadcrumbs.component.html
index 51524fde48..bff792eeff 100644
--- a/src/app/breadcrumbs/breadcrumbs.component.html
+++ b/src/app/breadcrumbs/breadcrumbs.component.html
@@ -10,7 +10,7 @@
-
+
diff --git a/src/app/collection-page/collection-page-routing.module.ts b/src/app/collection-page/collection-page-routing.module.ts
index 678c745c01..819ee8ca16 100644
--- a/src/app/collection-page/collection-page-routing.module.ts
+++ b/src/app/collection-page/collection-page-routing.module.ts
@@ -72,6 +72,7 @@ import { MenuItemType } from '../shared/menu/menu-item-type.model';
id: 'statistics_collection_:id',
active: true,
visible: true,
+ index: 2,
model: {
type: MenuItemType.LINK,
text: 'menu.section.statistics',
diff --git a/src/app/collection-page/collection-page.module.ts b/src/app/collection-page/collection-page.module.ts
index c35ebf9021..ff49b983ff 100644
--- a/src/app/collection-page/collection-page.module.ts
+++ b/src/app/collection-page/collection-page.module.ts
@@ -25,7 +25,7 @@ import { ComcolModule } from '../shared/comcol/comcol.module';
StatisticsModule.forRoot(),
EditItemPageModule,
CollectionFormModule,
- ComcolModule
+ ComcolModule,
],
declarations: [
CollectionPageComponent,
@@ -38,7 +38,7 @@ import { ComcolModule } from '../shared/comcol/comcol.module';
],
providers: [
SearchService,
- ]
+ ],
})
export class CollectionPageModule {
diff --git a/src/app/collection-page/edit-collection-page/edit-collection-page.module.ts b/src/app/collection-page/edit-collection-page/edit-collection-page.module.ts
index 45612be41a..18f7feb699 100644
--- a/src/app/collection-page/edit-collection-page/edit-collection-page.module.ts
+++ b/src/app/collection-page/edit-collection-page/edit-collection-page.module.ts
@@ -25,7 +25,7 @@ import { ComcolModule } from '../../shared/comcol/comcol.module';
CollectionFormModule,
ResourcePoliciesModule,
FormModule,
- ComcolModule
+ ComcolModule,
],
declarations: [
EditCollectionPageComponent,
diff --git a/src/app/community-page/community-page-routing.module.ts b/src/app/community-page/community-page-routing.module.ts
index 25326448a8..4870d52dd9 100644
--- a/src/app/community-page/community-page-routing.module.ts
+++ b/src/app/community-page/community-page-routing.module.ts
@@ -55,6 +55,7 @@ import { MenuItemType } from '../shared/menu/menu-item-type.model';
id: 'statistics_community_:id',
active: true,
visible: true,
+ index: 2,
model: {
type: MenuItemType.LINK,
text: 'menu.section.statistics',
diff --git a/src/app/community-page/community-page.module.ts b/src/app/community-page/community-page.module.ts
index 7cf2c8db8a..1dd9e82499 100644
--- a/src/app/community-page/community-page.module.ts
+++ b/src/app/community-page/community-page.module.ts
@@ -36,7 +36,7 @@ const DECLARATIONS = [CommunityPageComponent,
CommunityPageRoutingModule,
StatisticsModule.forRoot(),
CommunityFormModule,
- ComcolModule
+ ComcolModule,
],
declarations: [
...DECLARATIONS
diff --git a/src/app/community-page/edit-community-page/edit-community-page.module.ts b/src/app/community-page/edit-community-page/edit-community-page.module.ts
index 2b0fc73f2a..0479ea6bc6 100644
--- a/src/app/community-page/edit-community-page/edit-community-page.module.ts
+++ b/src/app/community-page/edit-community-page/edit-community-page.module.ts
@@ -21,7 +21,7 @@ import { ComcolModule } from '../../shared/comcol/comcol.module';
EditCommunityPageRoutingModule,
CommunityFormModule,
ComcolModule,
- ResourcePoliciesModule
+ ResourcePoliciesModule,
],
declarations: [
EditCommunityPageComponent,
diff --git a/src/app/core/core.module.ts b/src/app/core/core.module.ts
index 90cefd54c7..ede23ba43b 100644
--- a/src/app/core/core.module.ts
+++ b/src/app/core/core.module.ts
@@ -2,15 +2,12 @@ import { CommonModule } from '@angular/common';
import { HttpClient } from '@angular/common/http';
import { ModuleWithProviders, NgModule, Optional, SkipSelf } from '@angular/core';
-import { DynamicFormLayoutService, DynamicFormService, DynamicFormValidationService } from '@ng-dynamic-forms/core';
import { EffectsModule } from '@ngrx/effects';
import { Action, StoreConfig, StoreModule } from '@ngrx/store';
import { MyDSpaceGuard } from '../my-dspace-page/my-dspace.guard';
import { isNotEmpty } from '../shared/empty.util';
-import { FormBuilderService } from '../shared/form/builder/form-builder.service';
-import { FormService } from '../shared/form/form.service';
import { HostWindowService } from '../shared/host-window.service';
import { MenuService } from '../shared/menu/menu.service';
import { EndpointMockingRestService } from '../shared/mocks/dspace-rest/endpoint-mocking-rest.service';
@@ -24,8 +21,6 @@ import { SelectableListService } from '../shared/object-list/selectable-list/sel
import { ObjectSelectService } from '../shared/object-select/object-select.service';
import { PaginationComponentOptions } from '../shared/pagination/pagination-component-options.model';
import { SidebarService } from '../shared/sidebar/sidebar.service';
-import { UploaderService } from '../shared/uploader/uploader.service';
-import { SectionFormOperationsService } from '../submission/sections/form/section-form-operations.service';
import { AuthenticatedGuard } from './auth/authenticated.guard';
import { AuthStatus } from './auth/models/auth-status.model';
import { BrowseService } from './browse/browse.service';
@@ -137,9 +132,6 @@ import {
import { Registration } from './shared/registration.model';
import { MetadataSchemaDataService } from './data/metadata-schema-data.service';
import { MetadataFieldDataService } from './data/metadata-field-data.service';
-import {
- DsDynamicTypeBindRelationService
-} from '../shared/form/builder/ds-dynamic-form-ui/ds-dynamic-type-bind-relation.service';
import { TokenResponseParsingService } from './auth/token-response-parsing.service';
import { SubmissionCcLicenseDataService } from './submission/submission-cc-license-data.service';
import { SubmissionCcLicence } from './submission/models/submission-cc-license.model';
@@ -149,7 +141,6 @@ import { VocabularyEntry } from './submission/vocabularies/models/vocabulary-ent
import { Vocabulary } from './submission/vocabularies/models/vocabulary.model';
import { VocabularyEntryDetail } from './submission/vocabularies/models/vocabulary-entry-detail.model';
import { VocabularyService } from './submission/vocabularies/vocabulary.service';
-import { VocabularyTreeviewService } from '../shared/vocabulary-treeview/vocabulary-treeview.service';
import { ConfigurationDataService } from './data/configuration-data.service';
import { ConfigurationProperty } from './shared/configuration-property.model';
import { ReloadGuard } from './reload/reload.guard';
@@ -210,12 +201,6 @@ const PROVIDERS = [
DSOResponseParsingService,
{ provide: MOCK_RESPONSE_MAP, useValue: mockResponseMap },
{ provide: DspaceRestService, useFactory: restServiceFactory, deps: [MOCK_RESPONSE_MAP, HttpClient] },
- DynamicFormLayoutService,
- DynamicFormService,
- DynamicFormValidationService,
- FormBuilderService,
- SectionFormOperationsService,
- FormService,
EPersonDataService,
LinkHeadService,
HALEndpointService,
@@ -244,12 +229,10 @@ const PROVIDERS = [
SubmissionResponseParsingService,
SubmissionJsonPatchOperationsService,
JsonPatchOperationsBuilder,
- UploaderService,
UUIDService,
NotificationsService,
WorkspaceitemDataService,
WorkflowItemDataService,
- UploaderService,
DSpaceObjectDataService,
ConfigurationDataService,
DSOChangeAnalyzer,
@@ -266,7 +249,6 @@ const PROVIDERS = [
ClaimedTaskDataService,
PoolTaskDataService,
BitstreamDataService,
- DsDynamicTypeBindRelationService,
EntityTypeDataService,
ContentSourceResponseParsingService,
ItemTemplateDataService,
@@ -302,7 +284,6 @@ const PROVIDERS = [
VocabularyService,
VocabularyDataService,
VocabularyEntryDetailsDataService,
- VocabularyTreeviewService,
SequenceService,
GroupDataService,
FeedbackDataService,
diff --git a/src/app/shared/uploader/uploader.service.ts b/src/app/core/drag.service.ts
similarity index 53%
rename from src/app/shared/uploader/uploader.service.ts
rename to src/app/core/drag.service.ts
index 548de34f9c..d5f329d362 100644
--- a/src/app/shared/uploader/uploader.service.ts
+++ b/src/app/core/drag.service.ts
@@ -1,7 +1,17 @@
+/**
+ * The contents of this file are subject to the license and copyright
+ * detailed in the LICENSE and NOTICE files at the root of the source
+ * tree and available online at
+ *
+ * http://www.dspace.org/license/
+ */
+
import { Injectable } from '@angular/core';
-@Injectable()
-export class UploaderService {
+@Injectable({
+ providedIn: 'root'
+})
+export class DragService {
private _overrideDragOverPage = false;
public overrideDragOverPage() {
diff --git a/src/app/core/locale/locale.service.ts b/src/app/core/locale/locale.service.ts
index 68d2839d42..16a35b8ae5 100644
--- a/src/app/core/locale/locale.service.ts
+++ b/src/app/core/locale/locale.service.ts
@@ -40,7 +40,7 @@ export class LocaleService {
protected translate: TranslateService,
protected authService: AuthService,
protected routeService: RouteService,
- @Inject(DOCUMENT) private document: any
+ @Inject(DOCUMENT) protected document: any
) {
}
diff --git a/src/app/core/locale/server-locale.service.ts b/src/app/core/locale/server-locale.service.ts
index f438643e49..556619b946 100644
--- a/src/app/core/locale/server-locale.service.ts
+++ b/src/app/core/locale/server-locale.service.ts
@@ -1,12 +1,31 @@
import { LANG_ORIGIN, LocaleService } from './locale.service';
-import { Injectable } from '@angular/core';
+import { Inject, Injectable } from '@angular/core';
import { combineLatest, Observable, of as observableOf } from 'rxjs';
import { map, mergeMap, take } from 'rxjs/operators';
-import { isEmpty, isNotEmpty } from '../../shared/empty.util';
+import { hasValue, isEmpty, isNotEmpty } from '../../shared/empty.util';
+import { NativeWindowRef, NativeWindowService } from '../services/window.service';
+import { REQUEST } from '@nguniversal/express-engine/tokens';
+import { CookieService } from '../services/cookie.service';
+import { TranslateService } from '@ngx-translate/core';
+import { AuthService } from '../auth/auth.service';
+import { RouteService } from '../services/route.service';
+import { DOCUMENT } from '@angular/common';
@Injectable()
export class ServerLocaleService extends LocaleService {
+ constructor(
+ @Inject(NativeWindowService) protected _window: NativeWindowRef,
+ @Inject(REQUEST) protected req: Request,
+ protected cookie: CookieService,
+ protected translate: TranslateService,
+ protected authService: AuthService,
+ protected routeService: RouteService,
+ @Inject(DOCUMENT) protected document: any
+ ) {
+ super(_window, cookie, translate, authService, routeService, document);
+ }
+
/**
* Get the languages list of the user in Accept-Language format
*
@@ -50,6 +69,10 @@ export class ServerLocaleService extends LocaleService {
if (isNotEmpty(epersonLang)) {
languages.push(...epersonLang);
}
+ if (hasValue(this.req.headers['accept-language'])) {
+ languages.push(...this.req.headers['accept-language'].split(',')
+ );
+ }
return languages;
})
);
diff --git a/src/app/item-page/bitstreams/upload/upload-bitstream.component.ts b/src/app/item-page/bitstreams/upload/upload-bitstream.component.ts
index 1e5295a347..74019de7cc 100644
--- a/src/app/item-page/bitstreams/upload/upload-bitstream.component.ts
+++ b/src/app/item-page/bitstreams/upload/upload-bitstream.component.ts
@@ -4,7 +4,7 @@ import { RemoteData } from '../../../core/data/remote-data';
import { Item } from '../../../core/shared/item.model';
import { map, take, switchMap } from 'rxjs/operators';
import { ActivatedRoute, Router } from '@angular/router';
-import { UploaderOptions } from '../../../shared/uploader/uploader-options.model';
+import { UploaderOptions } from '../../../shared/upload/uploader/uploader-options.model';
import { hasValue, isEmpty, isNotEmpty } from '../../../shared/empty.util';
import { ItemDataService } from '../../../core/data/item-data.service';
import { AuthService } from '../../../core/auth/auth.service';
@@ -14,7 +14,7 @@ import { PaginatedList } from '../../../core/data/paginated-list.model';
import { Bundle } from '../../../core/shared/bundle.model';
import { BundleDataService } from '../../../core/data/bundle-data.service';
import { getFirstSucceededRemoteDataPayload, getFirstCompletedRemoteData } from '../../../core/shared/operators';
-import { UploaderComponent } from '../../../shared/uploader/uploader.component';
+import { UploaderComponent } from '../../../shared/upload/uploader/uploader.component';
import { RequestService } from '../../../core/data/request.service';
import { getBitstreamModuleRoute } from '../../../app-routing-paths';
import { getEntityEditRoute } from '../../item-page-routing-paths';
diff --git a/src/app/item-page/full/field-components/file-section/full-file-section.component.spec.ts b/src/app/item-page/full/field-components/file-section/full-file-section.component.spec.ts
index 1059ed12da..6f0e97513f 100644
--- a/src/app/item-page/full/field-components/file-section/full-file-section.component.spec.ts
+++ b/src/app/item-page/full/field-components/file-section/full-file-section.component.spec.ts
@@ -18,6 +18,8 @@ import { NotificationsService } from '../../../../shared/notifications/notificat
import { NotificationsServiceStub } from '../../../../shared/testing/notifications-service.stub';
import { PaginationService } from '../../../../core/pagination/pagination.service';
import { PaginationServiceStub } from '../../../../shared/testing/pagination-service.stub';
+import { APP_CONFIG } from 'src/config/app-config.interface';
+import { environment } from 'src/environments/environment';
describe('FullFileSectionComponent', () => {
let comp: FullFileSectionComponent;
@@ -69,7 +71,8 @@ describe('FullFileSectionComponent', () => {
providers: [
{ provide: BitstreamDataService, useValue: bitstreamDataService },
{ provide: NotificationsService, useValue: new NotificationsServiceStub() },
- { provide: PaginationService, useValue: paginationService }
+ { provide: PaginationService, useValue: paginationService },
+ { provide: APP_CONFIG, useValue: environment },
],
schemas: [NO_ERRORS_SCHEMA]
diff --git a/src/app/item-page/full/field-components/file-section/full-file-section.component.ts b/src/app/item-page/full/field-components/file-section/full-file-section.component.ts
index e21c1a32eb..3be0d58c81 100644
--- a/src/app/item-page/full/field-components/file-section/full-file-section.component.ts
+++ b/src/app/item-page/full/field-components/file-section/full-file-section.component.ts
@@ -1,4 +1,4 @@
-import { Component, Input, OnInit } from '@angular/core';
+import { Component, Inject, Input, OnInit } from '@angular/core';
import { Observable } from 'rxjs';
import { BitstreamDataService } from '../../../../core/data/bitstream-data.service';
@@ -14,6 +14,7 @@ import { NotificationsService } from '../../../../shared/notifications/notificat
import { TranslateService } from '@ngx-translate/core';
import { hasValue, isEmpty } from '../../../../shared/empty.util';
import { PaginationService } from '../../../../core/pagination/pagination.service';
+import { AppConfig, APP_CONFIG } from 'src/config/app-config.interface';
/**
* This component renders the file section of the item
@@ -34,26 +35,26 @@ export class FullFileSectionComponent extends FileSectionComponent implements On
originals$: Observable>>;
licenses$: Observable>>;
- pageSize = 5;
originalOptions = Object.assign(new PaginationComponentOptions(), {
id: 'obo',
currentPage: 1,
- pageSize: this.pageSize
+ pageSize: this.appConfig.item.bitstream.pageSize
});
licenseOptions = Object.assign(new PaginationComponentOptions(), {
id: 'lbo',
currentPage: 1,
- pageSize: this.pageSize
+ pageSize: this.appConfig.item.bitstream.pageSize
});
constructor(
bitstreamDataService: BitstreamDataService,
protected notificationsService: NotificationsService,
protected translateService: TranslateService,
- protected paginationService: PaginationService
+ protected paginationService: PaginationService,
+ @Inject(APP_CONFIG) protected appConfig: AppConfig
) {
- super(bitstreamDataService, notificationsService, translateService);
+ super(bitstreamDataService, notificationsService, translateService, appConfig);
}
ngOnInit(): void {
diff --git a/src/app/item-page/item-page-routing.module.ts b/src/app/item-page/item-page-routing.module.ts
index add2c3d768..ac14ca8402 100644
--- a/src/app/item-page/item-page-routing.module.ts
+++ b/src/app/item-page/item-page-routing.module.ts
@@ -67,6 +67,7 @@ import { OrcidPageGuard } from './orcid-page/orcid-page.guard';
id: 'statistics_item_:id',
active: true,
visible: true,
+ index: 2,
model: {
type: MenuItemType.LINK,
text: 'menu.section.statistics',
diff --git a/src/app/item-page/item-page.module.ts b/src/app/item-page/item-page.module.ts
index c4e86a37fb..de9f2f60c5 100644
--- a/src/app/item-page/item-page.module.ts
+++ b/src/app/item-page/item-page.module.ts
@@ -46,6 +46,7 @@ import { OrcidPageComponent } from './orcid-page/orcid-page.component';
import { NgbAccordionModule } from '@ng-bootstrap/ng-bootstrap';
import { OrcidSyncSettingsComponent } from './orcid-page/orcid-sync-settings/orcid-sync-settings.component';
import { OrcidQueueComponent } from './orcid-page/orcid-queue/orcid-queue.component';
+import { UploadModule } from '../shared/upload/upload.module';
const ENTRY_COMPONENTS = [
@@ -94,7 +95,8 @@ const DECLARATIONS = [
JournalEntitiesModule.withEntryComponents(),
ResearchEntitiesModule.withEntryComponents(),
NgxGalleryModule,
- NgbAccordionModule
+ NgbAccordionModule,
+ UploadModule,
],
declarations: [
...DECLARATIONS,
diff --git a/src/app/item-page/simple/field-components/file-section/file-section.component.spec.ts b/src/app/item-page/simple/field-components/file-section/file-section.component.spec.ts
index 2d185aef9c..83f92d5af8 100644
--- a/src/app/item-page/simple/field-components/file-section/file-section.component.spec.ts
+++ b/src/app/item-page/simple/field-components/file-section/file-section.component.spec.ts
@@ -17,6 +17,8 @@ import { MetadataFieldWrapperComponent } from '../../../field-components/metadat
import { createPaginatedList } from '../../../../shared/testing/utils.test';
import { NotificationsService } from '../../../../shared/notifications/notifications.service';
import { NotificationsServiceStub } from '../../../../shared/testing/notifications-service.stub';
+import { APP_CONFIG } from 'src/config/app-config.interface';
+import { environment } from 'src/environments/environment';
describe('FileSectionComponent', () => {
let comp: FileSectionComponent;
@@ -65,7 +67,8 @@ describe('FileSectionComponent', () => {
declarations: [FileSectionComponent, VarDirective, FileSizePipe, MetadataFieldWrapperComponent],
providers: [
{ provide: BitstreamDataService, useValue: bitstreamDataService },
- { provide: NotificationsService, useValue: new NotificationsServiceStub() }
+ { provide: NotificationsService, useValue: new NotificationsServiceStub() },
+ { provide: APP_CONFIG, useValue: environment }
],
schemas: [NO_ERRORS_SCHEMA]
diff --git a/src/app/item-page/simple/field-components/file-section/file-section.component.ts b/src/app/item-page/simple/field-components/file-section/file-section.component.ts
index d28b579996..08e792fc8b 100644
--- a/src/app/item-page/simple/field-components/file-section/file-section.component.ts
+++ b/src/app/item-page/simple/field-components/file-section/file-section.component.ts
@@ -1,4 +1,4 @@
-import { Component, Input, OnInit } from '@angular/core';
+import { Component, Inject, Input, OnInit } from '@angular/core';
import { BehaviorSubject } from 'rxjs';
import { BitstreamDataService } from '../../../../core/data/bitstream-data.service';
@@ -10,6 +10,7 @@ import { PaginatedList } from '../../../../core/data/paginated-list.model';
import { NotificationsService } from '../../../../shared/notifications/notifications.service';
import { TranslateService } from '@ngx-translate/core';
import { getFirstCompletedRemoteData } from '../../../../core/shared/operators';
+import { AppConfig, APP_CONFIG } from 'src/config/app-config.interface';
/**
* This component renders the file section of the item
@@ -35,13 +36,15 @@ export class FileSectionComponent implements OnInit {
isLastPage: boolean;
- pageSize = 5;
+ pageSize: number;
constructor(
protected bitstreamDataService: BitstreamDataService,
protected notificationsService: NotificationsService,
- protected translateService: TranslateService
+ protected translateService: TranslateService,
+ @Inject(APP_CONFIG) protected appConfig: AppConfig
) {
+ this.pageSize = this.appConfig.item.bitstream.pageSize;
}
ngOnInit(): void {
diff --git a/src/app/my-dspace-page/collection-selector/collection-selector.component.html b/src/app/my-dspace-page/collection-selector/collection-selector.component.html
index a87118fc4e..6e2a1925c5 100644
--- a/src/app/my-dspace-page/collection-selector/collection-selector.component.html
+++ b/src/app/my-dspace-page/collection-selector/collection-selector.component.html
@@ -5,7 +5,7 @@
-
-
+
+
diff --git a/src/app/my-dspace-page/collection-selector/collection-selector.component.spec.ts b/src/app/my-dspace-page/collection-selector/collection-selector.component.spec.ts
index ce54d326fc..af043b447b 100644
--- a/src/app/my-dspace-page/collection-selector/collection-selector.component.spec.ts
+++ b/src/app/my-dspace-page/collection-selector/collection-selector.component.spec.ts
@@ -128,10 +128,13 @@ describe('CollectionSelectorComponent', () => {
beforeEach(() => {
scheduler = getTestScheduler();
- fixture = TestBed.createComponent(CollectionSelectorComponent);
+ fixture = TestBed.overrideComponent(CollectionSelectorComponent, {
+ set: {
+ template: ''
+ }
+ }).createComponent(CollectionSelectorComponent);
component = fixture.componentInstance;
fixture.detectChanges();
-
});
it('should create', () => {
diff --git a/src/app/my-dspace-page/my-dspace-new-submission/my-dspace-new-submission.component.spec.ts b/src/app/my-dspace-page/my-dspace-new-submission/my-dspace-new-submission.component.spec.ts
index fb43c253eb..ed61fab1d6 100644
--- a/src/app/my-dspace-page/my-dspace-new-submission/my-dspace-new-submission.component.spec.ts
+++ b/src/app/my-dspace-page/my-dspace-new-submission/my-dspace-new-submission.component.spec.ts
@@ -16,10 +16,10 @@ import { TranslateLoaderMock } from '../../shared/mocks/translate-loader.mock';
import { NotificationsService } from '../../shared/notifications/notifications.service';
import { NotificationsServiceStub } from '../../shared/testing/notifications-service.stub';
import { getMockScrollToService } from '../../shared/mocks/scroll-to-service.mock';
-import { UploaderService } from '../../shared/uploader/uploader.service';
+import { DragService } from '../../core/drag.service';
import { HostWindowService } from '../../shared/host-window.service';
import { HostWindowServiceStub } from '../../shared/testing/host-window-service.stub';
-import { UploaderComponent } from '../../shared/uploader/uploader.component';
+import { UploaderComponent } from '../../shared/upload/uploader/uploader.component';
import { HttpXsrfTokenExtractor } from '@angular/common/http';
import { CookieService } from '../../core/services/cookie.service';
import { CookieServiceMock } from '../../shared/mocks/cookie.service.mock';
@@ -59,7 +59,7 @@ describe('MyDSpaceNewSubmissionComponent test', () => {
NgbModal,
ChangeDetectorRef,
MyDSpaceNewSubmissionComponent,
- UploaderService,
+ DragService,
{ provide: HttpXsrfTokenExtractor, useValue: new HttpXsrfTokenExtractorMock('mock-token') },
{ provide: CookieService, useValue: new CookieServiceMock() },
{ provide: HostWindowService, useValue: new HostWindowServiceStub(800) },
diff --git a/src/app/my-dspace-page/my-dspace-new-submission/my-dspace-new-submission.component.ts b/src/app/my-dspace-page/my-dspace-new-submission/my-dspace-new-submission.component.ts
index b2ba6fe2af..0694fc63bf 100644
--- a/src/app/my-dspace-page/my-dspace-new-submission/my-dspace-new-submission.component.ts
+++ b/src/app/my-dspace-page/my-dspace-new-submission/my-dspace-new-submission.component.ts
@@ -8,13 +8,13 @@ import { NgbModal } from '@ng-bootstrap/ng-bootstrap';
import { AuthService } from '../../core/auth/auth.service';
import { DSpaceObject } from '../../core/shared/dspace-object.model';
import { NotificationsService } from '../../shared/notifications/notifications.service';
-import { UploaderOptions } from '../../shared/uploader/uploader-options.model';
+import { UploaderOptions } from '../../shared/upload/uploader/uploader-options.model';
import { HALEndpointService } from '../../core/shared/hal-endpoint.service';
import { hasValue } from '../../shared/empty.util';
import { SearchResult } from '../../shared/search/models/search-result.model';
import { CollectionSelectorComponent } from '../collection-selector/collection-selector.component';
-import { UploaderComponent } from '../../shared/uploader/uploader.component';
-import { UploaderError } from '../../shared/uploader/uploader-error.model';
+import { UploaderComponent } from '../../shared/upload/uploader/uploader.component';
+import { UploaderError } from '../../shared/upload/uploader/uploader-error.model';
import { Router } from '@angular/router';
/**
diff --git a/src/app/my-dspace-page/my-dspace-page.module.ts b/src/app/my-dspace-page/my-dspace-page.module.ts
index 2ccddd87f7..6ad50af96a 100644
--- a/src/app/my-dspace-page/my-dspace-page.module.ts
+++ b/src/app/my-dspace-page/my-dspace-page.module.ts
@@ -14,6 +14,7 @@ import { MyDSpaceNewSubmissionDropdownComponent } from './my-dspace-new-submissi
import { MyDSpaceNewExternalDropdownComponent } from './my-dspace-new-submission/my-dspace-new-external-dropdown/my-dspace-new-external-dropdown.component';
import { ThemedMyDSpacePageComponent } from './themed-my-dspace-page.component';
import { SearchModule } from '../shared/search/search.module';
+import { UploadModule } from '../shared/upload/upload.module';
const DECLARATIONS = [
MyDSpacePageComponent,
@@ -30,7 +31,8 @@ const DECLARATIONS = [
SharedModule,
SearchModule,
MyDspacePageRoutingModule,
- MyDspaceSearchModule.withEntryComponents()
+ MyDspaceSearchModule.withEntryComponents(),
+ UploadModule,
],
declarations: DECLARATIONS,
providers: [
diff --git a/src/app/register-email-form/register-email-form.component.spec.ts b/src/app/register-email-form/register-email-form.component.spec.ts
index bac922c73b..cf3b4b13d2 100644
--- a/src/app/register-email-form/register-email-form.component.spec.ts
+++ b/src/app/register-email-form/register-email-form.component.spec.ts
@@ -95,6 +95,10 @@ describe('RegisterEmailComponent', () => {
comp.form.patchValue({email: 'valid@email.org'});
expect(comp.form.invalid).toBeFalse();
});
+ it('should be valid when uppercase letters are used', () => {
+ comp.form.patchValue({email: 'VALID@email.org'});
+ expect(comp.form.invalid).toBeFalse();
+ });
});
describe('register', () => {
it('should send a registration to the service and on success display a message and return to home', () => {
diff --git a/src/app/register-email-form/register-email-form.component.ts b/src/app/register-email-form/register-email-form.component.ts
index ced87b9e75..561bd53e67 100644
--- a/src/app/register-email-form/register-email-form.component.ts
+++ b/src/app/register-email-form/register-email-form.component.ts
@@ -79,7 +79,9 @@ export class RegisterEmailFormComponent implements OnInit {
this.form = this.formBuilder.group({
email: new FormControl('', {
validators: [Validators.required,
- Validators.pattern('^[a-z0-9._%+-]+@[a-z0-9.-]+\.[a-z]{2,4}$')
+ // Regex pattern borrowed from HTML5 specs for a valid email address:
+ // https://html.spec.whatwg.org/multipage/input.html#valid-e-mail-address
+ Validators.pattern('^[a-zA-Z0-9.!#$%&\'*+\/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$')
],
})
});
diff --git a/src/app/shared/collection-dropdown/themed-collection-dropdown.component.ts b/src/app/shared/collection-dropdown/themed-collection-dropdown.component.ts
new file mode 100644
index 0000000000..27c883099d
--- /dev/null
+++ b/src/app/shared/collection-dropdown/themed-collection-dropdown.component.ts
@@ -0,0 +1,33 @@
+import { CollectionDropdownComponent, CollectionListEntry } from './collection-dropdown.component';
+import { ThemedComponent } from '../theme-support/themed.component';
+import { Component, Input, Output, EventEmitter } from '@angular/core';
+
+@Component({
+ selector: 'ds-themed-collection-dropdown',
+ styleUrls: [],
+ templateUrl: '../../shared/theme-support/themed.component.html',
+})
+export class ThemedCollectionDropdownComponent extends ThemedComponent {
+
+ @Input() entityType: string;
+
+ @Output() searchComplete = new EventEmitter();
+
+ @Output() theOnlySelectable = new EventEmitter();
+
+ @Output() selectionChange = new EventEmitter();
+
+ protected inAndOutputNames: (keyof CollectionDropdownComponent & keyof this)[] = ['entityType', 'searchComplete', 'theOnlySelectable', 'selectionChange'];
+
+ protected getComponentName(): string {
+ return 'CollectionDropdownComponent';
+ }
+
+ protected importThemedComponent(themeName: string): Promise {
+ return import(`../../../themes/${themeName}/app/shared/collection-dropdown/collection-dropdown.component`);
+ }
+
+ protected importUnthemedComponent(): Promise {
+ return import(`./collection-dropdown.component`);
+ }
+}
diff --git a/src/app/shared/comcol/comcol-forms/comcol-form/comcol-form.component.ts b/src/app/shared/comcol/comcol-forms/comcol-form/comcol-form.component.ts
index 29be240753..23dfca8616 100644
--- a/src/app/shared/comcol/comcol-forms/comcol-form/comcol-form.component.ts
+++ b/src/app/shared/comcol/comcol-forms/comcol-form/comcol-form.component.ts
@@ -17,8 +17,8 @@ import { MetadataMap, MetadataValue } from '../../../../core/shared/metadata.mod
import { ResourceType } from '../../../../core/shared/resource-type';
import { hasValue, isNotEmpty } from '../../../empty.util';
import { NotificationsService } from '../../../notifications/notifications.service';
-import { UploaderOptions } from '../../../uploader/uploader-options.model';
-import { UploaderComponent } from '../../../uploader/uploader.component';
+import { UploaderOptions } from '../../../upload/uploader/uploader-options.model';
+import { UploaderComponent } from '../../../upload/uploader/uploader.component';
import { Operation } from 'fast-json-patch';
import { NoContent } from '../../../../core/shared/NoContent.model';
import { getFirstCompletedRemoteData } from '../../../../core/shared/operators';
diff --git a/src/app/shared/comcol/comcol.module.ts b/src/app/shared/comcol/comcol.module.ts
index 094387929a..efbcedf2c6 100644
--- a/src/app/shared/comcol/comcol.module.ts
+++ b/src/app/shared/comcol/comcol.module.ts
@@ -15,6 +15,7 @@ import { ThemedComcolPageBrowseByComponent } from './comcol-page-browse-by/theme
import { ComcolRoleComponent } from './comcol-forms/edit-comcol-page/comcol-role/comcol-role.component';
import { SharedModule } from '../shared.module';
import { FormModule } from '../form/form.module';
+import { UploadModule } from '../upload/upload.module';
const COMPONENTS = [
ComcolPageContentComponent,
@@ -28,9 +29,7 @@ const COMPONENTS = [
ComcolPageBrowseByComponent,
ThemedComcolPageBrowseByComponent,
ComcolRoleComponent,
-
ThemedComcolPageHandleComponent
-
];
@NgModule({
@@ -40,10 +39,12 @@ const COMPONENTS = [
imports: [
CommonModule,
FormModule,
- SharedModule
+ SharedModule,
+ UploadModule,
],
exports: [
- ...COMPONENTS
+ ...COMPONENTS,
+ UploadModule,
]
})
export class ComcolModule { }
diff --git a/src/app/shared/cookies/browser-klaro.service.ts b/src/app/shared/cookies/browser-klaro.service.ts
index 56e371242b..3e830811b3 100644
--- a/src/app/shared/cookies/browser-klaro.service.ts
+++ b/src/app/shared/cookies/browser-klaro.service.ts
@@ -1,5 +1,4 @@
-import { Injectable } from '@angular/core';
-import { setup, show } from 'klaro/dist/klaro-no-translations';
+import { Inject, Injectable, InjectionToken } from '@angular/core';
import { combineLatest as observableCombineLatest, Observable, of as observableOf } from 'rxjs';
import { AuthService } from '../../core/auth/auth.service';
import { TranslateService } from '@ngx-translate/core';
@@ -43,6 +42,17 @@ const cookiePurposeMessagePrefix = 'cookies.consent.purpose.';
*/
const updateDebounce = 300;
+/**
+ * By using this injection token instead of importing directly we can keep Klaro out of the main bundle
+ */
+const LAZY_KLARO = new InjectionToken>(
+ 'Lazily loaded Klaro',
+ {
+ providedIn: 'root',
+ factory: async () => (await import('klaro')),
+ }
+);
+
/**
* Browser implementation for the KlaroService, representing a service for handling Klaro consent preferences and UI
*/
@@ -65,7 +75,9 @@ export class BrowserKlaroService extends KlaroService {
private authService: AuthService,
private ePersonService: EPersonDataService,
private configService: ConfigurationDataService,
- private cookieService: CookieService) {
+ private cookieService: CookieService,
+ @Inject(LAZY_KLARO) private lazyKlaro: Promise,
+ ) {
super();
}
@@ -135,8 +147,7 @@ export class BrowserKlaroService extends KlaroService {
this.translateConfiguration();
this.klaroConfig.services = this.filterConfigServices(servicesToHide);
-
- setup(this.klaroConfig);
+ this.lazyKlaro.then(({ setup }) => setup(this.klaroConfig));
});
}
@@ -220,7 +231,7 @@ export class BrowserKlaroService extends KlaroService {
* Show the cookie consent form
*/
showSettings() {
- show(this.klaroConfig);
+ this.lazyKlaro.then(({show}) => show(this.klaroConfig));
}
/**
diff --git a/src/app/shared/dso-selector/modal-wrappers/create-community-parent-selector/create-community-parent-selector.component.html b/src/app/shared/dso-selector/modal-wrappers/create-community-parent-selector/create-community-parent-selector.component.html
index 84fdd34c01..4a22672988 100644
--- a/src/app/shared/dso-selector/modal-wrappers/create-community-parent-selector/create-community-parent-selector.component.html
+++ b/src/app/shared/dso-selector/modal-wrappers/create-community-parent-selector/create-community-parent-selector.component.html
@@ -9,7 +9,7 @@
diff --git a/src/app/shared/form/builder/ds-dynamic-form-ui/models/lookup/dynamic-lookup.component.spec.ts b/src/app/shared/form/builder/ds-dynamic-form-ui/models/lookup/dynamic-lookup.component.spec.ts
index dd97bb74ab..27029ff2be 100644
--- a/src/app/shared/form/builder/ds-dynamic-form-ui/models/lookup/dynamic-lookup.component.spec.ts
+++ b/src/app/shared/form/builder/ds-dynamic-form-ui/models/lookup/dynamic-lookup.component.spec.ts
@@ -20,7 +20,7 @@ import { FormFieldMetadataValueObject } from '../../../models/form-field-metadat
import { VocabularyEntry } from '../../../../../../core/submission/vocabularies/models/vocabulary-entry.model';
import { createTestComponent } from '../../../../../testing/utils.test';
import { DynamicLookupNameModel } from './dynamic-lookup-name.model';
-import { AuthorityConfidenceStateDirective } from '../../../../../authority-confidence/authority-confidence-state.directive';
+import { AuthorityConfidenceStateDirective } from '../../../../directives/authority-confidence-state.directive';
import { ObjNgFor } from '../../../../../utils/object-ngfor.pipe';
import {
mockDynamicFormLayoutService,
diff --git a/src/app/shared/form/builder/ds-dynamic-form-ui/models/onebox/dynamic-onebox.component.spec.ts b/src/app/shared/form/builder/ds-dynamic-form-ui/models/onebox/dynamic-onebox.component.spec.ts
index 1102ae2e74..cf417145a7 100644
--- a/src/app/shared/form/builder/ds-dynamic-form-ui/models/onebox/dynamic-onebox.component.spec.ts
+++ b/src/app/shared/form/builder/ds-dynamic-form-ui/models/onebox/dynamic-onebox.component.spec.ts
@@ -21,11 +21,11 @@ import { DsDynamicOneboxComponent } from './dynamic-onebox.component';
import { DynamicOneboxModel } from './dynamic-onebox.model';
import { FormFieldMetadataValueObject } from '../../../models/form-field-metadata-value.model';
import { createTestComponent } from '../../../../../testing/utils.test';
-import { AuthorityConfidenceStateDirective } from '../../../../../authority-confidence/authority-confidence-state.directive';
+import { AuthorityConfidenceStateDirective } from '../../../../directives/authority-confidence-state.directive';
import { ObjNgFor } from '../../../../../utils/object-ngfor.pipe';
import { VocabularyEntry } from '../../../../../../core/submission/vocabularies/models/vocabulary-entry.model';
import { createSuccessfulRemoteDataObject$ } from '../../../../../remote-data.utils';
-import { VocabularyTreeviewComponent } from '../../../../../vocabulary-treeview/vocabulary-treeview.component';
+import { VocabularyTreeviewComponent } from '../../../../vocabulary-treeview/vocabulary-treeview.component';
import {
mockDynamicFormLayoutService,
mockDynamicFormValidationService
diff --git a/src/app/shared/form/builder/ds-dynamic-form-ui/models/onebox/dynamic-onebox.component.ts b/src/app/shared/form/builder/ds-dynamic-form-ui/models/onebox/dynamic-onebox.component.ts
index 9d2799177c..008328bf73 100644
--- a/src/app/shared/form/builder/ds-dynamic-form-ui/models/onebox/dynamic-onebox.component.ts
+++ b/src/app/shared/form/builder/ds-dynamic-form-ui/models/onebox/dynamic-onebox.component.ts
@@ -30,7 +30,7 @@ import { VocabularyEntry } from '../../../../../../core/submission/vocabularies/
import { PageInfo } from '../../../../../../core/shared/page-info.model';
import { DsDynamicVocabularyComponent } from '../dynamic-vocabulary.component';
import { Vocabulary } from '../../../../../../core/submission/vocabularies/models/vocabulary.model';
-import { VocabularyTreeviewComponent } from '../../../../../vocabulary-treeview/vocabulary-treeview.component';
+import { VocabularyTreeviewComponent } from '../../../../vocabulary-treeview/vocabulary-treeview.component';
import { VocabularyEntryDetail } from '../../../../../../core/submission/vocabularies/models/vocabulary-entry-detail.model';
/**
diff --git a/src/app/shared/form/builder/ds-dynamic-form-ui/models/relation-group/dynamic-relation-group.component.spec.ts b/src/app/shared/form/builder/ds-dynamic-form-ui/models/relation-group/dynamic-relation-group.component.spec.ts
index 83baeaaeaa..733758fd27 100644
--- a/src/app/shared/form/builder/ds-dynamic-form-ui/models/relation-group/dynamic-relation-group.component.spec.ts
+++ b/src/app/shared/form/builder/ds-dynamic-form-ui/models/relation-group/dynamic-relation-group.component.spec.ts
@@ -16,7 +16,7 @@ import { FormFieldModel } from '../../../models/form-field.model';
import { FormBuilderService } from '../../../form-builder.service';
import { FormService } from '../../../../form.service';
import { FormComponent } from '../../../../form.component';
-import { Chips } from '../../../../../chips/models/chips.model';
+import { Chips } from '../../../../chips/models/chips.model';
import { FormFieldMetadataValueObject } from '../../../models/form-field-metadata-value.model';
import { DsDynamicInputModel } from '../ds-dynamic-input.model';
import { createTestComponent } from '../../../../../testing/utils.test';
diff --git a/src/app/shared/form/builder/ds-dynamic-form-ui/models/relation-group/dynamic-relation-group.components.ts b/src/app/shared/form/builder/ds-dynamic-form-ui/models/relation-group/dynamic-relation-group.components.ts
index fe495419f0..fd111e44c2 100644
--- a/src/app/shared/form/builder/ds-dynamic-form-ui/models/relation-group/dynamic-relation-group.components.ts
+++ b/src/app/shared/form/builder/ds-dynamic-form-ui/models/relation-group/dynamic-relation-group.components.ts
@@ -19,10 +19,10 @@ import { FormBuilderService } from '../../../form-builder.service';
import { SubmissionFormsModel } from '../../../../../../core/config/models/config-submission-forms.model';
import { FormService } from '../../../../form.service';
import { FormComponent } from '../../../../form.component';
-import { Chips } from '../../../../../chips/models/chips.model';
+import { Chips } from '../../../../chips/models/chips.model';
import { hasValue, isEmpty, isNotEmpty, isNotNull } from '../../../../../empty.util';
import { shrinkInOut } from '../../../../../animations/shrink';
-import { ChipsItem } from '../../../../../chips/models/chips-item.model';
+import { ChipsItem } from '../../../../chips/models/chips-item.model';
import { hasOnlyEmptyProperties } from '../../../../../object.util';
import { VocabularyService } from '../../../../../../core/submission/vocabularies/vocabulary.service';
import { FormFieldMetadataValueObject } from '../../../models/form-field-metadata-value.model';
diff --git a/src/app/shared/form/builder/ds-dynamic-form-ui/models/tag/dynamic-tag.component.spec.ts b/src/app/shared/form/builder/ds-dynamic-form-ui/models/tag/dynamic-tag.component.spec.ts
index 7b2c416699..162d9c3cec 100644
--- a/src/app/shared/form/builder/ds-dynamic-form-ui/models/tag/dynamic-tag.component.spec.ts
+++ b/src/app/shared/form/builder/ds-dynamic-form-ui/models/tag/dynamic-tag.component.spec.ts
@@ -13,7 +13,7 @@ import { VocabularyService } from '../../../../../../core/submission/vocabularie
import { VocabularyServiceStub } from '../../../../../testing/vocabulary-service.stub';
import { DsDynamicTagComponent } from './dynamic-tag.component';
import { DynamicTagModel } from './dynamic-tag.model';
-import { Chips } from '../../../../../chips/models/chips.model';
+import { Chips } from '../../../../chips/models/chips.model';
import { FormFieldMetadataValueObject } from '../../../models/form-field-metadata-value.model';
import { VocabularyEntry } from '../../../../../../core/submission/vocabularies/models/vocabulary-entry.model';
import { createTestComponent } from '../../../../../testing/utils.test';
diff --git a/src/app/shared/form/builder/ds-dynamic-form-ui/models/tag/dynamic-tag.component.ts b/src/app/shared/form/builder/ds-dynamic-form-ui/models/tag/dynamic-tag.component.ts
index 4978af970b..1c015be747 100644
--- a/src/app/shared/form/builder/ds-dynamic-form-ui/models/tag/dynamic-tag.component.ts
+++ b/src/app/shared/form/builder/ds-dynamic-form-ui/models/tag/dynamic-tag.component.ts
@@ -9,7 +9,7 @@ import isEqual from 'lodash/isEqual';
import { VocabularyService } from '../../../../../../core/submission/vocabularies/vocabulary.service';
import { DynamicTagModel } from './dynamic-tag.model';
-import { Chips } from '../../../../../chips/models/chips.model';
+import { Chips } from '../../../../chips/models/chips.model';
import { hasValue, isNotEmpty } from '../../../../../empty.util';
import { environment } from '../../../../../../../environments/environment';
import { getFirstSucceededRemoteDataPayload } from '../../../../../../core/shared/operators';
diff --git a/src/app/shared/form/builder/ds-dynamic-form-ui/relation-lookup-modal/external-source-tab/dynamic-lookup-relation-external-source-tab.component.spec.ts b/src/app/shared/form/builder/ds-dynamic-form-ui/relation-lookup-modal/external-source-tab/dynamic-lookup-relation-external-source-tab.component.spec.ts
index 3c9b575027..cfffc5ddb7 100644
--- a/src/app/shared/form/builder/ds-dynamic-form-ui/relation-lookup-modal/external-source-tab/dynamic-lookup-relation-external-source-tab.component.spec.ts
+++ b/src/app/shared/form/builder/ds-dynamic-form-ui/relation-lookup-modal/external-source-tab/dynamic-lookup-relation-external-source-tab.component.spec.ts
@@ -1,4 +1,6 @@
-import { DsDynamicLookupRelationExternalSourceTabComponent } from './dynamic-lookup-relation-external-source-tab.component';
+import {
+ DsDynamicLookupRelationExternalSourceTabComponent
+} from './dynamic-lookup-relation-external-source-tab.component';
import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing';
import { VarDirective } from '../../../../../utils/var.directive';
import { TranslateModule } from '@ngx-translate/core';
@@ -6,7 +8,7 @@ import { RouterTestingModule } from '@angular/router/testing';
import { EventEmitter, NO_ERRORS_SCHEMA } from '@angular/core';
import { PaginatedSearchOptions } from '../../../../../search/models/paginated-search-options.model';
import { SearchConfigurationService } from '../../../../../../core/shared/search/search-configuration.service';
-import { of as observableOf } from 'rxjs';
+import { of as observableOf, EMPTY } from 'rxjs';
import {
createFailedRemoteDataObject$,
createPendingRemoteDataObject$,
@@ -22,11 +24,13 @@ import { SelectableListService } from '../../../../../object-list/selectable-lis
import { Item } from '../../../../../../core/shared/item.model';
import { Collection } from '../../../../../../core/shared/collection.model';
import { RelationshipOptions } from '../../../models/relationship-options.model';
-import { ExternalSourceEntryImportModalComponent } from './external-source-entry-import-modal/external-source-entry-import-modal.component';
import { createPaginatedList } from '../../../../../testing/utils.test';
import { PaginationService } from '../../../../../../core/pagination/pagination.service';
import { PaginationServiceStub } from '../../../../../testing/pagination-service.stub';
import { ItemType } from '../../../../../../core/shared/item-relationships/item-type.model';
+import {
+ ThemedExternalSourceEntryImportModalComponent
+} from './external-source-entry-import-modal/themed-external-source-entry-import-modal.component';
describe('DsDynamicLookupRelationExternalSourceTabComponent', () => {
let component: DsDynamicLookupRelationExternalSourceTabComponent;
@@ -187,12 +191,13 @@ describe('DsDynamicLookupRelationExternalSourceTabComponent', () => {
describe('import', () => {
beforeEach(() => {
- spyOn(modalService, 'open').and.returnValue(Object.assign({ componentInstance: Object.assign({ importedObject: new EventEmitter() }) }));
+ spyOn(modalService, 'open').and.returnValue(Object.assign({ componentInstance: Object.assign({ importedObject: new EventEmitter(), compRef$: EMPTY }) }));
+ component.modalRef = modalService.open(ThemedExternalSourceEntryImportModalComponent, { size: 'lg', container: 'ds-dynamic-lookup-relation-modal' });
component.import(externalEntries[0]);
});
it('should open a new ExternalSourceEntryImportModalComponent', () => {
- expect(modalService.open).toHaveBeenCalledWith(ExternalSourceEntryImportModalComponent, jasmine.any(Object));
+ expect(modalService.open).toHaveBeenCalledWith(ThemedExternalSourceEntryImportModalComponent, jasmine.any(Object));
});
});
});
diff --git a/src/app/shared/form/builder/ds-dynamic-form-ui/relation-lookup-modal/external-source-tab/dynamic-lookup-relation-external-source-tab.component.ts b/src/app/shared/form/builder/ds-dynamic-form-ui/relation-lookup-modal/external-source-tab/dynamic-lookup-relation-external-source-tab.component.ts
index e5ea98e537..22fcc4e8bb 100644
--- a/src/app/shared/form/builder/ds-dynamic-form-ui/relation-lookup-modal/external-source-tab/dynamic-lookup-relation-external-source-tab.component.ts
+++ b/src/app/shared/form/builder/ds-dynamic-form-ui/relation-lookup-modal/external-source-tab/dynamic-lookup-relation-external-source-tab.component.ts
@@ -1,4 +1,4 @@
-import { Component, EventEmitter, Input, OnDestroy, OnInit, Output } from '@angular/core';
+import { Component, EventEmitter, Input, OnDestroy, OnInit, Output, ComponentRef } from '@angular/core';
import { SEARCH_CONFIG_SERVICE } from '../../../../../../my-dspace-page/my-dspace-page.component';
import { SearchConfigurationService } from '../../../../../../core/shared/search/search-configuration.service';
import { Router } from '@angular/router';
@@ -16,7 +16,8 @@ import { PaginationComponentOptions } from '../../../../../pagination/pagination
import { RelationshipOptions } from '../../../models/relationship-options.model';
import { NgbModal, NgbModalRef } from '@ng-bootstrap/ng-bootstrap';
import { ExternalSourceEntryImportModalComponent } from './external-source-entry-import-modal/external-source-entry-import-modal.component';
-import { hasValue } from '../../../../../empty.util';
+import { ThemedExternalSourceEntryImportModalComponent } from './external-source-entry-import-modal/themed-external-source-entry-import-modal.component';
+import { hasValue, hasValueOperator } from '../../../../../empty.util';
import { SelectableListService } from '../../../../../object-list/selectable-list/selectable-list.service';
import { Item } from '../../../../../../core/shared/item.model';
import { Collection } from '../../../../../../core/shared/collection.model';
@@ -114,9 +115,9 @@ export class DsDynamicLookupRelationExternalSourceTabComponent implements OnInit
modalRef: NgbModalRef;
/**
- * Subscription to the modal's importedObject event-emitter
+ * Array to track all subscriptions and unsubscribe them onDestroy
*/
- importObjectSub: Subscription;
+ protected subs: Subscription[] = [];
/**
* The entity types compatible with the given external source
@@ -161,30 +162,40 @@ export class DsDynamicLookupRelationExternalSourceTabComponent implements OnInit
* @param entry The entry to import
*/
import(entry) {
- this.modalRef = this.modalService.open(ExternalSourceEntryImportModalComponent, {
+ this.modalRef = this.modalService.open(ThemedExternalSourceEntryImportModalComponent, {
size: 'lg',
container: 'ds-dynamic-lookup-relation-modal'
});
- const modalComp = this.modalRef.componentInstance;
- modalComp.externalSourceEntry = entry;
- modalComp.item = this.item;
- modalComp.collection = this.collection;
- modalComp.relationship = this.relationship;
- modalComp.label = this.label;
- modalComp.relatedEntityType = this.relatedEntityType;
- this.importObjectSub = modalComp.importedObject.subscribe((object) => {
+
+ const modalComp$ = this.modalRef.componentInstance.compRef$.pipe(
+ hasValueOperator(),
+ map((compRef: ComponentRef) => compRef.instance)
+ );
+
+ this.subs.push(modalComp$.subscribe((modalComp: ExternalSourceEntryImportModalComponent) => {
+ modalComp.externalSourceEntry = entry;
+ modalComp.item = this.item;
+ // modalComp.collection = this.collection;
+ modalComp.relationship = this.relationship;
+ modalComp.label = this.label;
+ modalComp.relatedEntityType = this.relatedEntityType;
+ }));
+
+ this.subs.push(modalComp$.pipe(
+ switchMap((modalComp: ExternalSourceEntryImportModalComponent) => modalComp.importedObject)
+ ).subscribe((object) => {
this.selectableListService.selectSingle(this.listId, object);
this.importedObject.emit(object);
- });
+ }));
}
/**
* Unsubscribe from open subscriptions
*/
ngOnDestroy(): void {
- if (hasValue(this.importObjectSub)) {
- this.importObjectSub.unsubscribe();
- }
+ this.subs
+ .filter((sub) => hasValue(sub))
+ .forEach((sub) => sub.unsubscribe());
}
/**
diff --git a/src/app/shared/form/builder/ds-dynamic-form-ui/relation-lookup-modal/external-source-tab/external-source-entry-import-modal/themed-external-source-entry-import-modal.component.ts b/src/app/shared/form/builder/ds-dynamic-form-ui/relation-lookup-modal/external-source-tab/external-source-entry-import-modal/themed-external-source-entry-import-modal.component.ts
new file mode 100644
index 0000000000..26e6097c2d
--- /dev/null
+++ b/src/app/shared/form/builder/ds-dynamic-form-ui/relation-lookup-modal/external-source-tab/external-source-entry-import-modal/themed-external-source-entry-import-modal.component.ts
@@ -0,0 +1,22 @@
+import { ExternalSourceEntryImportModalComponent } from './external-source-entry-import-modal.component';
+import { ThemedComponent } from '../../../../../../theme-support/themed.component';
+import { Component } from '@angular/core';
+
+@Component({
+ selector: 'ds-themed-external-source-entry-import-modal',
+ styleUrls: [],
+ templateUrl: '../../../../../../../shared/theme-support/themed.component.html',
+})
+export class ThemedExternalSourceEntryImportModalComponent extends ThemedComponent {
+ protected getComponentName(): string {
+ return 'ExternalSourceEntryImportModalComponent';
+ }
+
+ protected importThemedComponent(themeName: string): Promise {
+ return import(`../../../../../../../../themes/${themeName}/app/shared/form/builder/ds-dynamic-form-ui/relation-lookup-modal/external-source-tab/external-source-entry-import-modal/external-source-entry-import-modal.component`);
+ }
+
+ protected importUnthemedComponent(): Promise {
+ return import(`./external-source-entry-import-modal.component`);
+ }
+}
diff --git a/src/app/shared/chips/chips.component.html b/src/app/shared/form/chips/chips.component.html
similarity index 100%
rename from src/app/shared/chips/chips.component.html
rename to src/app/shared/form/chips/chips.component.html
diff --git a/src/app/shared/chips/chips.component.scss b/src/app/shared/form/chips/chips.component.scss
similarity index 100%
rename from src/app/shared/chips/chips.component.scss
rename to src/app/shared/form/chips/chips.component.scss
diff --git a/src/app/shared/chips/chips.component.spec.ts b/src/app/shared/form/chips/chips.component.spec.ts
similarity index 92%
rename from src/app/shared/chips/chips.component.spec.ts
rename to src/app/shared/form/chips/chips.component.spec.ts
index 6f9b948002..2b8a469bd1 100644
--- a/src/app/shared/chips/chips.component.spec.ts
+++ b/src/app/shared/form/chips/chips.component.spec.ts
@@ -3,17 +3,16 @@ import { ChangeDetectorRef, Component, CUSTOM_ELEMENTS_SCHEMA } from '@angular/c
import { ComponentFixture, fakeAsync, inject, TestBed, tick, waitForAsync, } from '@angular/core/testing';
import { Chips } from './models/chips.model';
-import { UploaderService } from '../uploader/uploader.service';
import { ChipsComponent } from './chips.component';
import { NgbModule } from '@ng-bootstrap/ng-bootstrap';
import { By } from '@angular/platform-browser';
-import { FormFieldMetadataValueObject } from '../form/builder/models/form-field-metadata-value.model';
-import { createTestComponent } from '../testing/utils.test';
-import { AuthorityConfidenceStateDirective } from '../authority-confidence/authority-confidence-state.directive';
+import { FormFieldMetadataValueObject } from '../builder/models/form-field-metadata-value.model';
+import { createTestComponent } from '../../testing/utils.test';
+import { AuthorityConfidenceStateDirective } from '../directives/authority-confidence-state.directive';
import { TranslateModule } from '@ngx-translate/core';
-import { ConfidenceType } from '../../core/shared/confidence-type';
+import { ConfidenceType } from '../../../core/shared/confidence-type';
import { SortablejsModule } from 'ngx-sortablejs';
-import { environment } from '../../../environments/environment';
+import { environment } from '../../../../environments/environment';
describe('ChipsComponent test suite', () => {
@@ -41,7 +40,6 @@ describe('ChipsComponent test suite', () => {
providers: [
ChangeDetectorRef,
ChipsComponent,
- UploaderService
],
schemas: [CUSTOM_ELEMENTS_SCHEMA]
});
diff --git a/src/app/shared/chips/chips.component.ts b/src/app/shared/form/chips/chips.component.ts
similarity index 94%
rename from src/app/shared/chips/chips.component.ts
rename to src/app/shared/form/chips/chips.component.ts
index 94dfa00791..5166657582 100644
--- a/src/app/shared/chips/chips.component.ts
+++ b/src/app/shared/form/chips/chips.component.ts
@@ -5,7 +5,7 @@ import isObject from 'lodash/isObject';
import { Chips } from './models/chips.model';
import { ChipsItem } from './models/chips-item.model';
-import { UploaderService } from '../uploader/uploader.service';
+import { DragService } from '../../../core/drag.service';
import { TranslateService } from '@ngx-translate/core';
import { Options } from 'sortablejs';
import { BehaviorSubject } from 'rxjs';
@@ -33,7 +33,7 @@ export class ChipsComponent implements OnChanges {
constructor(
private cdr: ChangeDetectorRef,
- private uploaderService: UploaderService,
+ private dragService: DragService,
private translate: TranslateService) {
this.options = {
@@ -76,12 +76,12 @@ export class ChipsComponent implements OnChanges {
onDragStart(index) {
this.isDragging.next(true);
- this.uploaderService.overrideDragOverPage();
+ this.dragService.overrideDragOverPage();
this.dragged = index;
}
onDragEnd(event) {
- this.uploaderService.allowDragOverPage();
+ this.dragService.allowDragOverPage();
this.dragged = -1;
this.chips.updateOrder();
this.isDragging.next(false);
diff --git a/src/app/shared/chips/models/chips-item.model.spec.ts b/src/app/shared/form/chips/models/chips-item.model.spec.ts
similarity index 94%
rename from src/app/shared/chips/models/chips-item.model.spec.ts
rename to src/app/shared/form/chips/models/chips-item.model.spec.ts
index c85ff83c31..27b4840d46 100644
--- a/src/app/shared/chips/models/chips-item.model.spec.ts
+++ b/src/app/shared/form/chips/models/chips-item.model.spec.ts
@@ -1,5 +1,5 @@
import { ChipsItem, ChipsItemIcon } from './chips-item.model';
-import { FormFieldMetadataValueObject } from '../../form/builder/models/form-field-metadata-value.model';
+import { FormFieldMetadataValueObject } from '../../builder/models/form-field-metadata-value.model';
describe('ChipsItem model test suite', () => {
let item: ChipsItem;
diff --git a/src/app/shared/chips/models/chips-item.model.ts b/src/app/shared/form/chips/models/chips-item.model.ts
similarity index 89%
rename from src/app/shared/chips/models/chips-item.model.ts
rename to src/app/shared/form/chips/models/chips-item.model.ts
index 9afeafd479..277e6477ce 100644
--- a/src/app/shared/chips/models/chips-item.model.ts
+++ b/src/app/shared/form/chips/models/chips-item.model.ts
@@ -1,9 +1,9 @@
import isObject from 'lodash/isObject';
import uniqueId from 'lodash/uniqueId';
-import { hasValue, isNotEmpty } from '../../empty.util';
-import { FormFieldMetadataValueObject } from '../../form/builder/models/form-field-metadata-value.model';
-import { ConfidenceType } from '../../../core/shared/confidence-type';
-import { PLACEHOLDER_PARENT_METADATA } from '../../form/builder/ds-dynamic-form-ui/ds-dynamic-form-constants';
+import { hasValue, isNotEmpty } from '../../../empty.util';
+import { FormFieldMetadataValueObject } from '../../builder/models/form-field-metadata-value.model';
+import { ConfidenceType } from '../../../../core/shared/confidence-type';
+import { PLACEHOLDER_PARENT_METADATA } from '../../builder/ds-dynamic-form-ui/ds-dynamic-form-constants';
export interface ChipsItemIcon {
metadata: string;
diff --git a/src/app/shared/chips/models/chips.model.spec.ts b/src/app/shared/form/chips/models/chips.model.spec.ts
similarity index 96%
rename from src/app/shared/chips/models/chips.model.spec.ts
rename to src/app/shared/form/chips/models/chips.model.spec.ts
index a6cf5faf81..c86ff55c6f 100644
--- a/src/app/shared/chips/models/chips.model.spec.ts
+++ b/src/app/shared/form/chips/models/chips.model.spec.ts
@@ -1,6 +1,6 @@
import { Chips } from './chips.model';
import { ChipsItem } from './chips-item.model';
-import { FormFieldMetadataValueObject } from '../../form/builder/models/form-field-metadata-value.model';
+import { FormFieldMetadataValueObject } from '../../builder/models/form-field-metadata-value.model';
describe('Chips model test suite', () => {
let items: any[];
diff --git a/src/app/shared/chips/models/chips.model.ts b/src/app/shared/form/chips/models/chips.model.ts
similarity index 90%
rename from src/app/shared/chips/models/chips.model.ts
rename to src/app/shared/form/chips/models/chips.model.ts
index 2979c3dc09..daeb363d82 100644
--- a/src/app/shared/chips/models/chips.model.ts
+++ b/src/app/shared/form/chips/models/chips.model.ts
@@ -3,11 +3,11 @@ import isEqual from 'lodash/isEqual';
import isObject from 'lodash/isObject';
import { BehaviorSubject } from 'rxjs';
import { ChipsItem, ChipsItemIcon } from './chips-item.model';
-import { hasValue, isNotEmpty } from '../../empty.util';
-import { MetadataIconConfig } from '../../../../config/submission-config.interface';
-import { FormFieldMetadataValueObject } from '../../form/builder/models/form-field-metadata-value.model';
-import { VocabularyEntry } from '../../../core/submission/vocabularies/models/vocabulary-entry.model';
-import { PLACEHOLDER_PARENT_METADATA } from '../../form/builder/ds-dynamic-form-ui/ds-dynamic-form-constants';
+import { hasValue, isNotEmpty } from '../../../empty.util';
+import { MetadataIconConfig } from '../../../../../config/submission-config.interface';
+import { FormFieldMetadataValueObject } from '../../builder/models/form-field-metadata-value.model';
+import { VocabularyEntry } from '../../../../core/submission/vocabularies/models/vocabulary-entry.model';
+import { PLACEHOLDER_PARENT_METADATA } from '../../builder/ds-dynamic-form-ui/ds-dynamic-form-constants';
export class Chips {
chipsItems: BehaviorSubject;
diff --git a/src/app/shared/authority-confidence/authority-confidence-state.directive.ts b/src/app/shared/form/directives/authority-confidence-state.directive.ts
similarity index 83%
rename from src/app/shared/authority-confidence/authority-confidence-state.directive.ts
rename to src/app/shared/form/directives/authority-confidence-state.directive.ts
index bf6f949575..49eee5ae8f 100644
--- a/src/app/shared/authority-confidence/authority-confidence-state.directive.ts
+++ b/src/app/shared/form/directives/authority-confidence-state.directive.ts
@@ -1,3 +1,11 @@
+/**
+ * The contents of this file are subject to the license and copyright
+ * detailed in the LICENSE and NOTICE files at the root of the source
+ * tree and available online at
+ *
+ * http://www.dspace.org/license/
+ */
+
import {
AfterViewInit,
Directive,
@@ -13,13 +21,13 @@ import {
import findIndex from 'lodash/findIndex';
-import { VocabularyEntry } from '../../core/submission/vocabularies/models/vocabulary-entry.model';
-import { FormFieldMetadataValueObject } from '../form/builder/models/form-field-metadata-value.model';
-import { ConfidenceType } from '../../core/shared/confidence-type';
-import { isNotEmpty, isNull } from '../empty.util';
-import { ConfidenceIconConfig } from '../../../config/submission-config.interface';
-import { environment } from '../../../environments/environment';
-import { VocabularyEntryDetail } from '../../core/submission/vocabularies/models/vocabulary-entry-detail.model';
+import { VocabularyEntry } from '../../../core/submission/vocabularies/models/vocabulary-entry.model';
+import { FormFieldMetadataValueObject } from '../builder/models/form-field-metadata-value.model';
+import { ConfidenceType } from '../../../core/shared/confidence-type';
+import { isNotEmpty, isNull } from '../../empty.util';
+import { ConfidenceIconConfig } from '../../../../config/submission-config.interface';
+import { environment } from '../../../../environments/environment';
+import { VocabularyEntryDetail } from '../../../core/submission/vocabularies/models/vocabulary-entry-detail.model';
/**
* Directive to add to the element a bootstrap utility class based on metadata confidence value
diff --git a/src/app/shared/form/form.module.ts b/src/app/shared/form/form.module.ts
index 62ab5bd647..c3062b4231 100644
--- a/src/app/shared/form/form.module.ts
+++ b/src/app/shared/form/form.module.ts
@@ -2,10 +2,7 @@ import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { FormComponent } from './form.component';
import { DsDynamicFormComponent } from './builder/ds-dynamic-form-ui/ds-dynamic-form.component';
-import {
- DsDynamicFormControlContainerComponent,
- dsDynamicFormControlMapFn
-} from './builder/ds-dynamic-form-ui/ds-dynamic-form-control-container.component';
+import { DsDynamicFormControlContainerComponent, dsDynamicFormControlMapFn } from './builder/ds-dynamic-form-ui/ds-dynamic-form-control-container.component';
import { DsDynamicListComponent } from './builder/ds-dynamic-form-ui/models/list/dynamic-list.component';
import { DsDynamicLookupComponent } from './builder/ds-dynamic-form-ui/models/lookup/dynamic-lookup.component';
import { DsDynamicDisabledComponent } from './builder/ds-dynamic-form-ui/models/disabled/dynamic-disabled.component';
@@ -24,12 +21,23 @@ import { DsDynamicLookupRelationExternalSourceTabComponent } from './builder/ds-
import { SharedModule } from '../shared.module';
import { TranslateModule } from '@ngx-translate/core';
import { SearchModule } from '../search/search.module';
-import { DYNAMIC_FORM_CONTROL_MAP_FN, DynamicFormsCoreModule } from '@ng-dynamic-forms/core';
+import { DYNAMIC_FORM_CONTROL_MAP_FN, DYNAMIC_MATCHER_PROVIDERS, DynamicFormLayoutService, DynamicFormsCoreModule, DynamicFormService, DynamicFormValidationService } from '@ng-dynamic-forms/core';
import { ExistingMetadataListElementComponent } from './builder/ds-dynamic-form-ui/existing-metadata-list-element/existing-metadata-list-element.component';
import { ExistingRelationListElementComponent } from './builder/ds-dynamic-form-ui/existing-relation-list-element/existing-relation-list-element.component';
import { ExternalSourceEntryImportModalComponent } from './builder/ds-dynamic-form-ui/relation-lookup-modal/external-source-tab/external-source-entry-import-modal/external-source-entry-import-modal.component';
import { CustomSwitchComponent } from './builder/ds-dynamic-form-ui/models/custom-switch/custom-switch.component';
import { DynamicFormsNGBootstrapUIModule } from '@ng-dynamic-forms/ui-ng-bootstrap';
+import { ChipsComponent } from './chips/chips.component';
+import { NumberPickerComponent } from './number-picker/number-picker.component';
+import { AuthorityConfidenceStateDirective } from './directives/authority-confidence-state.directive';
+import { SortablejsModule } from 'ngx-sortablejs';
+import { VocabularyTreeviewComponent } from './vocabulary-treeview/vocabulary-treeview.component';
+import { VocabularyTreeviewService } from './vocabulary-treeview/vocabulary-treeview.service';
+import { FormBuilderService } from './builder/form-builder.service';
+import { DsDynamicTypeBindRelationService } from './builder/ds-dynamic-form-ui/ds-dynamic-type-bind-relation.service';
+import { FormService } from './form.service';
+import { NgxMaskModule } from 'ngx-mask';
+import { ThemedExternalSourceEntryImportModalComponent } from './builder/ds-dynamic-form-ui/relation-lookup-modal/external-source-tab/external-source-entry-import-modal/themed-external-source-entry-import-modal.component';
const COMPONENTS = [
CustomSwitchComponent,
@@ -53,12 +61,21 @@ const COMPONENTS = [
ExistingMetadataListElementComponent,
ExistingRelationListElementComponent,
ExternalSourceEntryImportModalComponent,
- FormComponent
+ FormComponent,
+ ChipsComponent,
+ NumberPickerComponent,
+ VocabularyTreeviewComponent,
+ ThemedExternalSourceEntryImportModalComponent
+];
+
+const DIRECTIVES = [
+ AuthorityConfidenceStateDirective,
];
@NgModule({
declarations: [
- ...COMPONENTS
+ ...COMPONENTS,
+ ...DIRECTIVES,
],
imports: [
CommonModule,
@@ -66,16 +83,27 @@ const COMPONENTS = [
DynamicFormsNGBootstrapUIModule,
SearchModule,
SharedModule,
- TranslateModule
+ TranslateModule,
+ SortablejsModule,
+ NgxMaskModule.forRoot(),
],
exports: [
- ...COMPONENTS
+ ...COMPONENTS,
+ ...DIRECTIVES,
],
providers: [
{
provide: DYNAMIC_FORM_CONTROL_MAP_FN,
useValue: dsDynamicFormControlMapFn
- }
+ },
+ ...DYNAMIC_MATCHER_PROVIDERS,
+ VocabularyTreeviewService,
+ DynamicFormLayoutService,
+ DynamicFormService,
+ DynamicFormValidationService,
+ FormBuilderService,
+ DsDynamicTypeBindRelationService,
+ FormService,
]
})
export class FormModule {
diff --git a/src/app/shared/number-picker/number-picker.component.html b/src/app/shared/form/number-picker/number-picker.component.html
similarity index 100%
rename from src/app/shared/number-picker/number-picker.component.html
rename to src/app/shared/form/number-picker/number-picker.component.html
diff --git a/src/app/shared/number-picker/number-picker.component.scss b/src/app/shared/form/number-picker/number-picker.component.scss
similarity index 100%
rename from src/app/shared/number-picker/number-picker.component.scss
rename to src/app/shared/form/number-picker/number-picker.component.scss
diff --git a/src/app/shared/number-picker/number-picker.component.spec.ts b/src/app/shared/form/number-picker/number-picker.component.spec.ts
similarity index 96%
rename from src/app/shared/number-picker/number-picker.component.spec.ts
rename to src/app/shared/form/number-picker/number-picker.component.spec.ts
index 0cc073644e..d4484dbfa3 100644
--- a/src/app/shared/number-picker/number-picker.component.spec.ts
+++ b/src/app/shared/form/number-picker/number-picker.component.spec.ts
@@ -2,12 +2,11 @@
import { ChangeDetectorRef, Component, CUSTOM_ELEMENTS_SCHEMA } from '@angular/core';
import { ComponentFixture, inject, TestBed, waitForAsync, } from '@angular/core/testing';
-import { UploaderService } from '../uploader/uploader.service';
import { NgbModule } from '@ng-bootstrap/ng-bootstrap';
import { By } from '@angular/platform-browser';
import { NumberPickerComponent } from './number-picker.component';
import { FormsModule, ReactiveFormsModule } from '@angular/forms';
-import { createTestComponent } from '../testing/utils.test';
+import { createTestComponent } from '../../testing/utils.test';
describe('NumberPickerComponent test suite', () => {
@@ -33,7 +32,6 @@ describe('NumberPickerComponent test suite', () => {
providers: [
ChangeDetectorRef,
NumberPickerComponent,
- UploaderService
],
schemas: [CUSTOM_ELEMENTS_SCHEMA]
});
diff --git a/src/app/shared/number-picker/number-picker.component.ts b/src/app/shared/form/number-picker/number-picker.component.ts
similarity index 98%
rename from src/app/shared/number-picker/number-picker.component.ts
rename to src/app/shared/form/number-picker/number-picker.component.ts
index 465e905713..0df1e050cd 100644
--- a/src/app/shared/number-picker/number-picker.component.ts
+++ b/src/app/shared/form/number-picker/number-picker.component.ts
@@ -1,6 +1,6 @@
import { ChangeDetectorRef, Component, EventEmitter, Input, OnInit, Output, SimpleChanges, } from '@angular/core';
import { ControlValueAccessor, FormBuilder, NG_VALUE_ACCESSOR } from '@angular/forms';
-import { isEmpty } from '../empty.util';
+import { isEmpty } from '../../empty.util';
@Component({
selector: 'ds-number-picker',
diff --git a/src/app/shared/vocabulary-treeview/vocabulary-tree-flat-data-source.ts b/src/app/shared/form/vocabulary-treeview/vocabulary-tree-flat-data-source.ts
similarity index 100%
rename from src/app/shared/vocabulary-treeview/vocabulary-tree-flat-data-source.ts
rename to src/app/shared/form/vocabulary-treeview/vocabulary-tree-flat-data-source.ts
diff --git a/src/app/shared/vocabulary-treeview/vocabulary-tree-flattener.ts b/src/app/shared/form/vocabulary-treeview/vocabulary-tree-flattener.ts
similarity index 100%
rename from src/app/shared/vocabulary-treeview/vocabulary-tree-flattener.ts
rename to src/app/shared/form/vocabulary-treeview/vocabulary-tree-flattener.ts
diff --git a/src/app/shared/vocabulary-treeview/vocabulary-treeview-node.model.ts b/src/app/shared/form/vocabulary-treeview/vocabulary-treeview-node.model.ts
similarity index 88%
rename from src/app/shared/vocabulary-treeview/vocabulary-treeview-node.model.ts
rename to src/app/shared/form/vocabulary-treeview/vocabulary-treeview-node.model.ts
index 63039e6c75..c167328cab 100644
--- a/src/app/shared/vocabulary-treeview/vocabulary-treeview-node.model.ts
+++ b/src/app/shared/form/vocabulary-treeview/vocabulary-treeview-node.model.ts
@@ -1,7 +1,7 @@
/* eslint-disable max-classes-per-file */
import { BehaviorSubject } from 'rxjs';
-import { VocabularyEntryDetail } from '../../core/submission/vocabularies/models/vocabulary-entry-detail.model';
-import { PageInfo } from '../../core/shared/page-info.model';
+import { VocabularyEntryDetail } from '../../../core/submission/vocabularies/models/vocabulary-entry-detail.model';
+import { PageInfo } from '../../../core/shared/page-info.model';
export const LOAD_MORE = 'LOAD_MORE';
export const LOAD_MORE_ROOT = 'LOAD_MORE_ROOT';
diff --git a/src/app/shared/vocabulary-treeview/vocabulary-treeview.component.html b/src/app/shared/form/vocabulary-treeview/vocabulary-treeview.component.html
similarity index 100%
rename from src/app/shared/vocabulary-treeview/vocabulary-treeview.component.html
rename to src/app/shared/form/vocabulary-treeview/vocabulary-treeview.component.html
diff --git a/src/app/shared/vocabulary-treeview/vocabulary-treeview.component.scss b/src/app/shared/form/vocabulary-treeview/vocabulary-treeview.component.scss
similarity index 100%
rename from src/app/shared/vocabulary-treeview/vocabulary-treeview.component.scss
rename to src/app/shared/form/vocabulary-treeview/vocabulary-treeview.component.scss
diff --git a/src/app/shared/vocabulary-treeview/vocabulary-treeview.component.spec.ts b/src/app/shared/form/vocabulary-treeview/vocabulary-treeview.component.spec.ts
similarity index 91%
rename from src/app/shared/vocabulary-treeview/vocabulary-treeview.component.spec.ts
rename to src/app/shared/form/vocabulary-treeview/vocabulary-treeview.component.spec.ts
index 1bdf4b9df7..cf8fbd8c49 100644
--- a/src/app/shared/vocabulary-treeview/vocabulary-treeview.component.spec.ts
+++ b/src/app/shared/form/vocabulary-treeview/vocabulary-treeview.component.spec.ts
@@ -8,18 +8,18 @@ import { TranslateModule } from '@ngx-translate/core';
import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap';
import { provideMockStore } from '@ngrx/store/testing';
-import { createTestComponent } from '../testing/utils.test';
+import { createTestComponent } from '../../testing/utils.test';
import { VocabularyTreeviewComponent } from './vocabulary-treeview.component';
import { VocabularyTreeviewService } from './vocabulary-treeview.service';
-import { VocabularyEntryDetail } from '../../core/submission/vocabularies/models/vocabulary-entry-detail.model';
+import { VocabularyEntryDetail } from '../../../core/submission/vocabularies/models/vocabulary-entry-detail.model';
import { TreeviewFlatNode } from './vocabulary-treeview-node.model';
-import { FormFieldMetadataValueObject } from '../form/builder/models/form-field-metadata-value.model';
-import { VocabularyOptions } from '../../core/submission/vocabularies/models/vocabulary-options.model';
-import { PageInfo } from '../../core/shared/page-info.model';
-import { VocabularyEntry } from '../../core/submission/vocabularies/models/vocabulary-entry.model';
-import { AuthTokenInfo } from '../../core/auth/models/auth-token-info.model';
-import { authReducer } from '../../core/auth/auth.reducer';
-import { storeModuleConfig } from '../../app.reducer';
+import { FormFieldMetadataValueObject } from '../builder/models/form-field-metadata-value.model';
+import { VocabularyOptions } from '../../../core/submission/vocabularies/models/vocabulary-options.model';
+import { PageInfo } from '../../../core/shared/page-info.model';
+import { VocabularyEntry } from '../../../core/submission/vocabularies/models/vocabulary-entry.model';
+import { AuthTokenInfo } from '../../../core/auth/models/auth-token-info.model';
+import { authReducer } from '../../../core/auth/auth.reducer';
+import { storeModuleConfig } from '../../../app.reducer';
describe('VocabularyTreeviewComponent test suite', () => {
diff --git a/src/app/shared/vocabulary-treeview/vocabulary-treeview.component.ts b/src/app/shared/form/vocabulary-treeview/vocabulary-treeview.component.ts
similarity index 93%
rename from src/app/shared/vocabulary-treeview/vocabulary-treeview.component.ts
rename to src/app/shared/form/vocabulary-treeview/vocabulary-treeview.component.ts
index 9850947f60..408d656f42 100644
--- a/src/app/shared/vocabulary-treeview/vocabulary-treeview.component.ts
+++ b/src/app/shared/form/vocabulary-treeview/vocabulary-treeview.component.ts
@@ -7,17 +7,17 @@ import { Observable, Subscription } from 'rxjs';
import { select, Store } from '@ngrx/store';
import { TranslateService } from '@ngx-translate/core';
-import { VocabularyEntryDetail } from '../../core/submission/vocabularies/models/vocabulary-entry-detail.model';
-import { hasValue, isEmpty, isNotEmpty } from '../empty.util';
-import { isAuthenticated } from '../../core/auth/selectors';
+import { VocabularyEntryDetail } from '../../../core/submission/vocabularies/models/vocabulary-entry-detail.model';
+import { hasValue, isEmpty, isNotEmpty } from '../../empty.util';
+import { isAuthenticated } from '../../../core/auth/selectors';
import { VocabularyTreeviewService } from './vocabulary-treeview.service';
import { LOAD_MORE, LOAD_MORE_ROOT, TreeviewFlatNode, TreeviewNode } from './vocabulary-treeview-node.model';
-import { VocabularyOptions } from '../../core/submission/vocabularies/models/vocabulary-options.model';
-import { PageInfo } from '../../core/shared/page-info.model';
-import { VocabularyEntry } from '../../core/submission/vocabularies/models/vocabulary-entry.model';
+import { VocabularyOptions } from '../../../core/submission/vocabularies/models/vocabulary-options.model';
+import { PageInfo } from '../../../core/shared/page-info.model';
+import { VocabularyEntry } from '../../../core/submission/vocabularies/models/vocabulary-entry.model';
import { VocabularyTreeFlattener } from './vocabulary-tree-flattener';
import { VocabularyTreeFlatDataSource } from './vocabulary-tree-flat-data-source';
-import { CoreState } from '../../core/core-state.model';
+import { CoreState } from '../../../core/core-state.model';
/**
* Component that show a hierarchical vocabulary in a tree view
diff --git a/src/app/shared/vocabulary-treeview/vocabulary-treeview.service.spec.ts b/src/app/shared/form/vocabulary-treeview/vocabulary-treeview.service.spec.ts
similarity index 94%
rename from src/app/shared/vocabulary-treeview/vocabulary-treeview.service.spec.ts
rename to src/app/shared/form/vocabulary-treeview/vocabulary-treeview.service.spec.ts
index c1c64c80bd..752ef10fee 100644
--- a/src/app/shared/vocabulary-treeview/vocabulary-treeview.service.spec.ts
+++ b/src/app/shared/form/vocabulary-treeview/vocabulary-treeview.service.spec.ts
@@ -5,15 +5,15 @@ import { TranslateLoader, TranslateModule, TranslateService } from '@ngx-transla
import { cold, getTestScheduler, hot } from 'jasmine-marbles';
import { VocabularyTreeviewService } from './vocabulary-treeview.service';
-import { VocabularyService } from '../../core/submission/vocabularies/vocabulary.service';
-import { TranslateLoaderMock } from '../mocks/translate-loader.mock';
-import { VocabularyOptions } from '../../core/submission/vocabularies/models/vocabulary-options.model';
+import { VocabularyService } from '../../../core/submission/vocabularies/vocabulary.service';
+import { TranslateLoaderMock } from '../../mocks/translate-loader.mock';
+import { VocabularyOptions } from '../../../core/submission/vocabularies/models/vocabulary-options.model';
import { LOAD_MORE_NODE, LOAD_MORE_ROOT_NODE, TreeviewFlatNode, TreeviewNode } from './vocabulary-treeview-node.model';
-import { PageInfo } from '../../core/shared/page-info.model';
-import { VocabularyEntryDetail } from '../../core/submission/vocabularies/models/vocabulary-entry-detail.model';
-import { buildPaginatedList } from '../../core/data/paginated-list.model';
-import { createSuccessfulRemoteDataObject } from '../remote-data.utils';
-import { VocabularyEntry } from '../../core/submission/vocabularies/models/vocabulary-entry.model';
+import { PageInfo } from '../../../core/shared/page-info.model';
+import { VocabularyEntryDetail } from '../../../core/submission/vocabularies/models/vocabulary-entry-detail.model';
+import { buildPaginatedList } from '../../../core/data/paginated-list.model';
+import { createSuccessfulRemoteDataObject } from '../../remote-data.utils';
+import { VocabularyEntry } from '../../../core/submission/vocabularies/models/vocabulary-entry.model';
import { expand, map, switchMap } from 'rxjs/operators';
import { from as observableFrom } from 'rxjs';
diff --git a/src/app/shared/vocabulary-treeview/vocabulary-treeview.service.ts b/src/app/shared/form/vocabulary-treeview/vocabulary-treeview.service.ts
similarity index 94%
rename from src/app/shared/vocabulary-treeview/vocabulary-treeview.service.ts
rename to src/app/shared/form/vocabulary-treeview/vocabulary-treeview.service.ts
index f04cd1bc28..8804716927 100644
--- a/src/app/shared/vocabulary-treeview/vocabulary-treeview.service.ts
+++ b/src/app/shared/form/vocabulary-treeview/vocabulary-treeview.service.ts
@@ -10,17 +10,17 @@ import {
TreeviewFlatNode,
TreeviewNode
} from './vocabulary-treeview-node.model';
-import { VocabularyEntry } from '../../core/submission/vocabularies/models/vocabulary-entry.model';
-import { VocabularyService } from '../../core/submission/vocabularies/vocabulary.service';
-import { PageInfo } from '../../core/shared/page-info.model';
-import { isEmpty, isNotEmpty } from '../empty.util';
-import { VocabularyOptions } from '../../core/submission/vocabularies/models/vocabulary-options.model';
+import { VocabularyEntry } from '../../../core/submission/vocabularies/models/vocabulary-entry.model';
+import { VocabularyService } from '../../../core/submission/vocabularies/vocabulary.service';
+import { PageInfo } from '../../../core/shared/page-info.model';
+import { isEmpty, isNotEmpty } from '../../empty.util';
+import { VocabularyOptions } from '../../../core/submission/vocabularies/models/vocabulary-options.model';
import {
getFirstSucceededRemoteDataPayload,
getFirstSucceededRemoteListPayload
-} from '../../core/shared/operators';
-import { PaginatedList } from '../../core/data/paginated-list.model';
-import { VocabularyEntryDetail } from '../../core/submission/vocabularies/models/vocabulary-entry-detail.model';
+} from '../../../core/shared/operators';
+import { PaginatedList } from '../../../core/data/paginated-list.model';
+import { VocabularyEntryDetail } from '../../../core/submission/vocabularies/models/vocabulary-entry-detail.model';
/**
* A service that provides methods to deal with vocabulary tree
diff --git a/src/app/shared/menu/menu.module.ts b/src/app/shared/menu/menu.module.ts
index 1d186a9b7d..c007af517d 100644
--- a/src/app/shared/menu/menu.module.ts
+++ b/src/app/shared/menu/menu.module.ts
@@ -12,8 +12,11 @@ import { ExternalLinkMenuItemComponent } from './menu-item/external-link-menu-it
const COMPONENTS = [
MenuSectionComponent,
MenuComponent,
- LinkMenuItemComponent,
+];
+
+const ENTRY_COMPONENTS = [
TextMenuItemComponent,
+ LinkMenuItemComponent,
OnClickMenuItemComponent,
ExternalLinkMenuItemComponent,
];
@@ -32,10 +35,12 @@ const PROVIDERS = [
...MODULES
],
declarations: [
- ...COMPONENTS
+ ...COMPONENTS,
+ ...ENTRY_COMPONENTS,
],
providers: [
- ...PROVIDERS
+ ...PROVIDERS,
+ ...ENTRY_COMPONENTS,
],
exports: [
...COMPONENTS
diff --git a/src/app/shared/search-form/scope-selector-modal/scope-selector-modal.component.html b/src/app/shared/search-form/scope-selector-modal/scope-selector-modal.component.html
index bf5c15e963..e7165a9213 100644
--- a/src/app/shared/search-form/scope-selector-modal/scope-selector-modal.component.html
+++ b/src/app/shared/search-form/scope-selector-modal/scope-selector-modal.component.html
@@ -9,7 +9,7 @@
diff --git a/src/app/shared/shared.module.ts b/src/app/shared/shared.module.ts
index 0c849a36ba..6228fef3a3 100644
--- a/src/app/shared/shared.module.ts
+++ b/src/app/shared/shared.module.ts
@@ -17,7 +17,6 @@ import {
} from '@ng-bootstrap/ng-bootstrap';
import { MissingTranslationHandler, TranslateModule } from '@ngx-translate/core';
import { NgxPaginationModule } from 'ngx-pagination';
-import { FileUploadModule } from 'ng2-file-upload';
import { InfiniteScrollModule } from 'ngx-infinite-scroll';
import { ConfirmationModalComponent } from './confirmation-modal/confirmation-modal.component';
import {
@@ -29,7 +28,6 @@ import {
import {
ImportBatchSelectorComponent
} from './dso-selector/modal-wrappers/import-batch-selector/import-batch-selector.component';
-import { FileDropzoneNoUploaderComponent } from './file-dropzone-no-uploader/file-dropzone-no-uploader.component';
import { ItemListElementComponent } from './object-list/item-list-element/item-types/item/item-list-element.component';
import { EnumKeysPipe } from './utils/enum-keys-pipe';
import { FileSizePipe } from './utils/file-size-pipe';
@@ -72,9 +70,6 @@ import { TruncatePipe } from './utils/truncate.pipe';
import { TruncatableComponent } from './truncatable/truncatable.component';
import { TruncatableService } from './truncatable/truncatable.service';
import { TruncatablePartComponent } from './truncatable/truncatable-part/truncatable-part.component';
-import { UploaderComponent } from './uploader/uploader.component';
-import { ChipsComponent } from './chips/chips.component';
-import { NumberPickerComponent } from './number-picker/number-picker.component';
import { MockAdminGuard } from './mocks/admin-guard.service.mock';
import { AlertComponent } from './alert/alert.component';
import {
@@ -110,7 +105,6 @@ import { EmphasizePipe } from './utils/emphasize.pipe';
import { InputSuggestionsComponent } from './input-suggestions/input-suggestions.component';
import { CapitalizePipe } from './utils/capitalize.pipe';
import { ObjectKeysPipe } from './utils/object-keys-pipe';
-import { AuthorityConfidenceStateDirective } from './authority-confidence/authority-confidence-state.directive';
import { LangSwitchComponent } from './lang-switch/lang-switch.component';
import {
PlainTextMetadataListElementComponent
@@ -231,7 +225,6 @@ import {
ImportableListItemControlComponent
} from './object-collection/shared/importable-list-item-control/importable-list-item-control.component';
import { ItemVersionsComponent } from './item/item-versions/item-versions.component';
-import { SortablejsModule } from 'ngx-sortablejs';
import { LogInContainerComponent } from './log-in/container/log-in-container.component';
import { LogInShibbolethComponent } from './log-in/methods/shibboleth/log-in-shibboleth.component';
import { LogInPasswordComponent } from './log-in/methods/password/log-in-password.component';
@@ -257,7 +250,6 @@ import { NgForTrackByIdDirective } from './ng-for-track-by-id.directive';
import { FileDownloadLinkComponent } from './file-download-link/file-download-link.component';
import { CollectionDropdownComponent } from './collection-dropdown/collection-dropdown.component';
import { EntityDropdownComponent } from './entity-dropdown/entity-dropdown.component';
-import { VocabularyTreeviewComponent } from './vocabulary-treeview/vocabulary-treeview.component';
import { CurationFormComponent } from '../curation-form/curation-form.component';
import {
PublicationSidebarSearchListElementComponent
@@ -292,9 +284,6 @@ import {
MetadataRepresentationListComponent
} from '../item-page/simple/metadata-representation-list/metadata-representation-list.component';
import { RelatedItemsComponent } from '../item-page/simple/related-items/related-items-component';
-import { LinkMenuItemComponent } from './menu/menu-item/link-menu-item.component';
-import { OnClickMenuItemComponent } from './menu/menu-item/onclick-menu-item.component';
-import { TextMenuItemComponent } from './menu/menu-item/text-menu-item.component';
import { SearchNavbarComponent } from '../search-navbar/search-navbar.component';
import { ThemedSearchNavbarComponent } from '../search-navbar/themed-search-navbar.component';
import {
@@ -311,7 +300,6 @@ import { DsSelectComponent } from './ds-select/ds-select.component';
import { LogInOidcComponent } from './log-in/methods/oidc/log-in-oidc.component';
import { ThemedItemListPreviewComponent } from './object-list/my-dspace-result-list-element/item-list-preview/themed-item-list-preview.component';
import { RSSComponent } from './rss-feed/rss.component';
-import { ExternalLinkMenuItemComponent } from './menu/menu-item/external-link-menu-item.component';
import { DsoPageOrcidButtonComponent } from './dso-page/dso-page-orcid-button/dso-page-orcid-button.component';
import { LogInOrcidComponent } from './log-in/methods/orcid/log-in-orcid.component';
import { BrowserOnlyPipe } from './utils/browser-only.pipe';
@@ -323,16 +311,16 @@ import {
} from '../item-page/simple/field-components/specific-field/title/item-page-title-field.component';
import { MarkdownPipe } from './utils/markdown.pipe';
import { GoogleRecaptchaModule } from '../core/google-recaptcha/google-recaptcha.module';
+import { MenuModule } from './menu/menu.module';
import {
ListableNotificationObjectComponent
} from './object-list/listable-notification-object/listable-notification-object.component';
-import { ItemBackButtonComponent } from './item-back-button/item-back-button.component';
import { ThemedItemBackButtonComponent } from './item-back-button/themed-item-back-button.component';
+import { ThemedCollectionDropdownComponent } from './collection-dropdown/themed-collection-dropdown.component';
+
const MODULES = [
CommonModule,
- SortablejsModule,
- FileUploadModule,
FormsModule,
InfiniteScrollModule,
NgbNavModule,
@@ -349,6 +337,7 @@ const MODULES = [
DragDropModule,
CdkTreeModule,
GoogleRecaptchaModule,
+ MenuModule,
];
const ROOT_MODULES = [
@@ -380,7 +369,6 @@ const COMPONENTS = [
AuthNavMenuComponent,
ThemedAuthNavMenuComponent,
UserMenuComponent,
- ChipsComponent,
DsSelectComponent,
ErrorComponent,
FileSectionComponent,
@@ -389,7 +377,6 @@ const COMPONENTS = [
ThemedLoadingComponent,
LogInComponent,
LogOutComponent,
- NumberPickerComponent,
ObjectListComponent,
ThemedObjectListComponent,
ObjectDetailComponent,
@@ -404,21 +391,14 @@ const COMPONENTS = [
SidebarFilterComponent,
SidebarFilterSelectedOptionComponent,
ThumbnailComponent,
- UploaderComponent,
- FileDropzoneNoUploaderComponent,
ItemListPreviewComponent,
ThemedItemListPreviewComponent,
MyDSpaceItemStatusComponent,
ItemSubmitterComponent,
ItemDetailPreviewComponent,
ItemDetailPreviewFieldComponent,
- ItemBackButtonComponent,
ThemedItemBackButtonComponent,
ClaimedTaskActionsComponent,
- ClaimedTaskActionsApproveComponent,
- ClaimedTaskActionsRejectComponent,
- ClaimedTaskActionsReturnToPoolComponent,
- ClaimedTaskActionsEditMetadataComponent,
ClaimedTaskActionsLoaderComponent,
ItemActionsComponent,
PoolTaskActionsComponent,
@@ -433,88 +413,30 @@ const COMPONENTS = [
ValidationSuggestionsComponent,
DsoInputSuggestionsComponent,
DSOSelectorComponent,
- CreateCommunityParentSelectorComponent,
- ThemedCreateCommunityParentSelectorComponent,
- CreateCollectionParentSelectorComponent,
- ThemedCreateCollectionParentSelectorComponent,
- CreateItemParentSelectorComponent,
- ThemedCreateItemParentSelectorComponent,
- EditCommunitySelectorComponent,
- ThemedEditCommunitySelectorComponent,
- EditCollectionSelectorComponent,
- ThemedEditCollectionSelectorComponent,
- EditItemSelectorComponent,
- ThemedEditItemSelectorComponent,
- CommunitySearchResultListElementComponent,
- CollectionSearchResultListElementComponent,
- BrowseByComponent,
-
- CollectionSearchResultGridElementComponent,
- CommunitySearchResultGridElementComponent,
SearchExportCsvComponent,
PageSizeSelectorComponent,
ListableObjectComponentLoaderComponent,
- CollectionListElementComponent,
- CommunityListElementComponent,
- CollectionGridElementComponent,
- CommunityGridElementComponent,
- BrowseByComponent,
AbstractTrackableComponent,
ComcolMetadataComponent,
TypeBadgeComponent,
AccessStatusBadgeComponent,
- BrowseByComponent,
- AbstractTrackableComponent,
-
ItemSelectComponent,
CollectionSelectComponent,
MetadataRepresentationLoaderComponent,
SelectableListItemControlComponent,
-
ImportableListItemControlComponent,
-
- LogInShibbolethComponent,
- LogInOidcComponent,
- LogInOrcidComponent,
- LogInPasswordComponent,
LogInContainerComponent,
ItemVersionsComponent,
- ItemSearchResultListElementComponent,
ItemVersionsNoticeComponent,
ModifyItemOverviewComponent,
ImpersonateNavbarComponent,
- FileDownloadLinkComponent,
- BitstreamDownloadPageComponent,
- BitstreamRequestACopyPageComponent,
- CollectionDropdownComponent,
EntityDropdownComponent,
ExportMetadataSelectorComponent,
ImportBatchSelectorComponent,
ExportBatchSelectorComponent,
ConfirmationModalComponent,
- VocabularyTreeviewComponent,
AuthorizedCollectionSelectorComponent,
- CurationFormComponent,
- SearchResultListElementComponent,
- SearchResultGridElementComponent,
- ItemListElementComponent,
- ItemGridElementComponent,
- ItemSearchResultGridElementComponent,
- BrowseEntryListElementComponent,
- SearchResultDetailElementComponent,
- PlainTextMetadataListElementComponent,
- ItemMetadataListElementComponent,
- MetadataRepresentationListElementComponent,
- ItemMetadataRepresentationListElementComponent,
- BundleListElementComponent,
- StartsWithDateComponent,
- StartsWithTextComponent,
- SidebarSearchListElementComponent,
- PublicationSidebarSearchListElementComponent,
- CollectionSidebarSearchListElementComponent,
- CommunitySidebarSearchListElementComponent,
SearchNavbarComponent,
- ScopeSelectorModalComponent,
ItemPageTitleFieldComponent,
ThemedSearchNavbarComponent,
ListableNotificationObjectComponent,
@@ -566,6 +488,7 @@ const ENTRY_COMPONENTS = [
ClaimedTaskActionsReturnToPoolComponent,
ClaimedTaskActionsEditMetadataComponent,
CollectionDropdownComponent,
+ ThemedCollectionDropdownComponent,
FileDownloadLinkComponent,
BitstreamDownloadPageComponent,
BitstreamRequestACopyPageComponent,
@@ -574,16 +497,11 @@ const ENTRY_COMPONENTS = [
ImportBatchSelectorComponent,
ExportBatchSelectorComponent,
ConfirmationModalComponent,
- VocabularyTreeviewComponent,
SidebarSearchListElementComponent,
PublicationSidebarSearchListElementComponent,
CollectionSidebarSearchListElementComponent,
CommunitySidebarSearchListElementComponent,
- LinkMenuItemComponent,
- OnClickMenuItemComponent,
- TextMenuItemComponent,
ScopeSelectorModalComponent,
- ExternalLinkMenuItemComponent,
ListableNotificationObjectComponent,
];
@@ -612,7 +530,6 @@ const DIRECTIVES = [
DragClickDirective,
DebounceDirective,
ClickOutsideDirective,
- AuthorityConfidenceStateDirective,
InListValidator,
AutoFocusDirective,
RoleDirective,
@@ -635,6 +552,7 @@ const DIRECTIVES = [
declarations: [
...PIPES,
...COMPONENTS,
+ ...ENTRY_COMPONENTS,
...DIRECTIVES,
...SHARED_ITEM_PAGE_COMPONENTS,
ItemVersionsSummaryModalComponent,
@@ -647,9 +565,10 @@ const DIRECTIVES = [
...MODULES,
...PIPES,
...COMPONENTS,
+ ...ENTRY_COMPONENTS,
...SHARED_ITEM_PAGE_COMPONENTS,
...DIRECTIVES,
- TranslateModule
+ TranslateModule,
]
})
diff --git a/src/app/shared/theme-support/themed.component.spec.ts b/src/app/shared/theme-support/themed.component.spec.ts
index 404e970a7a..7776e60379 100644
--- a/src/app/shared/theme-support/themed.component.spec.ts
+++ b/src/app/shared/theme-support/themed.component.spec.ts
@@ -71,6 +71,12 @@ describe('ThemedComponent', () => {
expect((component as any).compRef.instance.testInput).toEqual('changed');
});
}));
+
+ it(`should set usedTheme to the name of the matched theme`, waitForAsync(() => {
+ fixture.whenStable().then(() => {
+ expect(component.usedTheme).toEqual('custom');
+ });
+ }));
});
describe('when the current theme doesn\'t match a themed component', () => {
@@ -92,6 +98,12 @@ describe('ThemedComponent', () => {
expect((component as any).compRef.instance.testInput).toEqual('changed');
});
}));
+
+ it(`should set usedTheme to the name of the base theme`, waitForAsync(() => {
+ fixture.whenStable().then(() => {
+ expect(component.usedTheme).toEqual('base');
+ });
+ }));
});
describe('and it extends another theme', () => {
@@ -117,6 +129,12 @@ describe('ThemedComponent', () => {
expect((component as any).compRef.instance.testInput).toEqual('changed');
});
}));
+
+ it(`should set usedTheme to the name of the base theme`, waitForAsync(() => {
+ fixture.whenStable().then(() => {
+ expect(component.usedTheme).toEqual('base');
+ });
+ }));
});
describe('that does match it', () => {
@@ -141,6 +159,12 @@ describe('ThemedComponent', () => {
expect((component as any).compRef.instance.testInput).toEqual('changed');
});
}));
+
+ it(`should set usedTheme to the name of the matched theme`, waitForAsync(() => {
+ fixture.whenStable().then(() => {
+ expect(component.usedTheme).toEqual('custom');
+ });
+ }));
});
describe('that extends another theme that doesn\'t match it either', () => {
@@ -167,6 +191,12 @@ describe('ThemedComponent', () => {
expect((component as any).compRef.instance.testInput).toEqual('changed');
});
}));
+
+ it(`should set usedTheme to the name of the base theme`, waitForAsync(() => {
+ fixture.whenStable().then(() => {
+ expect(component.usedTheme).toEqual('base');
+ });
+ }));
});
describe('that extends another theme that does match it', () => {
@@ -193,6 +223,12 @@ describe('ThemedComponent', () => {
expect((component as any).compRef.instance.testInput).toEqual('changed');
});
}));
+
+ it(`should set usedTheme to the name of the matched theme`, waitForAsync(() => {
+ fixture.whenStable().then(() => {
+ expect(component.usedTheme).toEqual('custom');
+ });
+ }));
});
});
});
diff --git a/src/app/shared/theme-support/themed.component.ts b/src/app/shared/theme-support/themed.component.ts
index 87f182a5ff..995122d284 100644
--- a/src/app/shared/theme-support/themed.component.ts
+++ b/src/app/shared/theme-support/themed.component.ts
@@ -8,13 +8,15 @@ import {
OnDestroy,
ComponentFactoryResolver,
ChangeDetectorRef,
- OnChanges
+ OnChanges,
+ HostBinding
} from '@angular/core';
import { hasValue, isNotEmpty } from '../empty.util';
-import { from as fromPromise, Observable, of as observableOf, Subscription } from 'rxjs';
+import { from as fromPromise, Observable, of as observableOf, Subscription, BehaviorSubject } from 'rxjs';
import { ThemeService } from './theme.service';
-import { catchError, switchMap, map } from 'rxjs/operators';
+import { catchError, switchMap, map, tap } from 'rxjs/operators';
import { GenericConstructor } from '../../core/shared/generic-constructor';
+import { BASE_THEME_NAME } from './theme.constants';
@Component({
selector: 'ds-themed',
@@ -25,11 +27,22 @@ export abstract class ThemedComponent implements OnInit, OnDestroy, OnChanges
@ViewChild('vcr', { read: ViewContainerRef }) vcr: ViewContainerRef;
protected compRef: ComponentRef;
+ /**
+ * A reference to the themed component. Will start as undefined and emit every time the themed
+ * component is rendered
+ */
+ public compRef$: BehaviorSubject> = new BehaviorSubject(undefined);
+
protected lazyLoadSub: Subscription;
protected themeSub: Subscription;
protected inAndOutputNames: (keyof T & keyof this)[] = [];
+ /**
+ * A data attribute on the ThemedComponent to indicate which theme the rendered component came from.
+ */
+ @HostBinding('attr.data-used-theme') usedTheme: string;
+
constructor(
protected resolver: ComponentFactoryResolver,
protected cdr: ChangeDetectorRef,
@@ -80,6 +93,7 @@ export abstract class ThemedComponent implements OnInit, OnDestroy, OnChanges
} else {
// otherwise import and return the default component
return fromPromise(this.importUnthemedComponent()).pipe(
+ tap(() => this.usedTheme = BASE_THEME_NAME),
map((unthemedFile: any) => {
return unthemedFile[this.getComponentName()];
})
@@ -90,6 +104,7 @@ export abstract class ThemedComponent implements OnInit, OnDestroy, OnChanges
const factory = this.resolver.resolveComponentFactory(constructor);
this.compRef = this.vcr.createComponent(factory);
this.connectInputsAndOutputs();
+ this.compRef$.next(this.compRef);
this.cdr.markForCheck();
});
}
@@ -123,6 +138,7 @@ export abstract class ThemedComponent implements OnInit, OnDestroy, OnChanges
private resolveThemedComponent(themeName?: string, checkedThemeNames: string[] = []): Observable {
if (isNotEmpty(themeName)) {
return fromPromise(this.importThemedComponent(themeName)).pipe(
+ tap(() => this.usedTheme = themeName),
catchError(() => {
// Try the next ancestor theme instead
const nextTheme = this.themeService.getThemeConfigFor(themeName)?.extends;
diff --git a/src/app/shared/file-dropzone-no-uploader/file-dropzone-no-uploader.component.html b/src/app/shared/upload/file-dropzone-no-uploader/file-dropzone-no-uploader.component.html
similarity index 100%
rename from src/app/shared/file-dropzone-no-uploader/file-dropzone-no-uploader.component.html
rename to src/app/shared/upload/file-dropzone-no-uploader/file-dropzone-no-uploader.component.html
diff --git a/src/app/shared/file-dropzone-no-uploader/file-dropzone-no-uploader.component.ts b/src/app/shared/upload/file-dropzone-no-uploader/file-dropzone-no-uploader.component.ts
similarity index 100%
rename from src/app/shared/file-dropzone-no-uploader/file-dropzone-no-uploader.component.ts
rename to src/app/shared/upload/file-dropzone-no-uploader/file-dropzone-no-uploader.component.ts
diff --git a/src/app/shared/file-dropzone-no-uploader/file-dropzone-no-uploader.scss b/src/app/shared/upload/file-dropzone-no-uploader/file-dropzone-no-uploader.scss
similarity index 100%
rename from src/app/shared/file-dropzone-no-uploader/file-dropzone-no-uploader.scss
rename to src/app/shared/upload/file-dropzone-no-uploader/file-dropzone-no-uploader.scss
diff --git a/src/app/shared/upload/upload.module.ts b/src/app/shared/upload/upload.module.ts
new file mode 100644
index 0000000000..9f2895d7ac
--- /dev/null
+++ b/src/app/shared/upload/upload.module.ts
@@ -0,0 +1,38 @@
+/**
+ * The contents of this file are subject to the license and copyright
+ * detailed in the LICENSE and NOTICE files at the root of the source
+ * tree and available online at
+ *
+ * http://www.dspace.org/license/
+ */
+import { NgModule } from '@angular/core';
+import { CommonModule } from '@angular/common';
+import { SharedModule } from '../shared.module';
+import { FileUploadModule } from 'ng2-file-upload';
+import { UploaderComponent } from './uploader/uploader.component';
+import { FileDropzoneNoUploaderComponent } from './file-dropzone-no-uploader/file-dropzone-no-uploader.component';
+
+const COMPONENTS = [
+ UploaderComponent,
+ FileDropzoneNoUploaderComponent,
+];
+
+@NgModule({
+ imports: [
+ CommonModule,
+ SharedModule,
+ FileUploadModule,
+ ],
+ declarations: [
+ ...COMPONENTS,
+ ],
+ providers: [
+ ...COMPONENTS,
+ ],
+ exports: [
+ ...COMPONENTS,
+ FileUploadModule,
+ ]
+})
+export class UploadModule {
+}
diff --git a/src/app/shared/uploader/uploader-error.model.ts b/src/app/shared/upload/uploader/uploader-error.model.ts
similarity index 100%
rename from src/app/shared/uploader/uploader-error.model.ts
rename to src/app/shared/upload/uploader/uploader-error.model.ts
diff --git a/src/app/shared/uploader/uploader-options.model.ts b/src/app/shared/upload/uploader/uploader-options.model.ts
similarity index 86%
rename from src/app/shared/uploader/uploader-options.model.ts
rename to src/app/shared/upload/uploader/uploader-options.model.ts
index 959e5c3295..559fb0485b 100644
--- a/src/app/shared/uploader/uploader-options.model.ts
+++ b/src/app/shared/upload/uploader/uploader-options.model.ts
@@ -1,4 +1,4 @@
-import { RestRequestMethod } from '../../core/data/rest-request-method';
+import { RestRequestMethod } from '../../../core/data/rest-request-method';
export class UploaderOptions {
/**
diff --git a/src/app/shared/uploader/uploader-properties.model.ts b/src/app/shared/upload/uploader/uploader-properties.model.ts
similarity index 83%
rename from src/app/shared/uploader/uploader-properties.model.ts
rename to src/app/shared/upload/uploader/uploader-properties.model.ts
index bc0376b809..b84ae30bf8 100644
--- a/src/app/shared/uploader/uploader-properties.model.ts
+++ b/src/app/shared/upload/uploader/uploader-properties.model.ts
@@ -1,4 +1,4 @@
-import { MetadataMap } from '../../core/shared/metadata.models';
+import { MetadataMap } from '../../../core/shared/metadata.models';
/**
* Properties to send to the REST API for uploading a bitstream
diff --git a/src/app/shared/uploader/uploader.component.html b/src/app/shared/upload/uploader/uploader.component.html
similarity index 100%
rename from src/app/shared/uploader/uploader.component.html
rename to src/app/shared/upload/uploader/uploader.component.html
diff --git a/src/app/shared/uploader/uploader.component.scss b/src/app/shared/upload/uploader/uploader.component.scss
similarity index 100%
rename from src/app/shared/uploader/uploader.component.scss
rename to src/app/shared/upload/uploader/uploader.component.scss
diff --git a/src/app/shared/uploader/uploader.component.spec.ts b/src/app/shared/upload/uploader/uploader.component.spec.ts
similarity index 86%
rename from src/app/shared/uploader/uploader.component.spec.ts
rename to src/app/shared/upload/uploader/uploader.component.spec.ts
index 84fee2e147..8ea23c8acb 100644
--- a/src/app/shared/uploader/uploader.component.spec.ts
+++ b/src/app/shared/upload/uploader/uploader.component.spec.ts
@@ -4,16 +4,16 @@ import { ComponentFixture, inject, TestBed, waitForAsync, } from '@angular/core/
import { ScrollToService } from '@nicky-lenaers/ngx-scroll-to';
-import { UploaderService } from './uploader.service';
+import { DragService } from '../../../core/drag.service';
import { UploaderOptions } from './uploader-options.model';
import { UploaderComponent } from './uploader.component';
import { FileUploadModule } from 'ng2-file-upload';
import { TranslateModule } from '@ngx-translate/core';
-import { createTestComponent } from '../testing/utils.test';
+import { createTestComponent } from '../../testing/utils.test';
import { HttpXsrfTokenExtractor } from '@angular/common/http';
-import { CookieService } from '../../core/services/cookie.service';
-import { CookieServiceMock } from '../mocks/cookie.service.mock';
-import { HttpXsrfTokenExtractorMock } from '../mocks/http-xsrf-token-extractor.mock';
+import { CookieService } from '../../../core/services/cookie.service';
+import { CookieServiceMock } from '../../mocks/cookie.service.mock';
+import { HttpXsrfTokenExtractorMock } from '../../mocks/http-xsrf-token-extractor.mock';
describe('Chips component', () => {
@@ -37,7 +37,7 @@ describe('Chips component', () => {
ChangeDetectorRef,
ScrollToService,
UploaderComponent,
- UploaderService,
+ DragService,
{ provide: HttpXsrfTokenExtractor, useValue: new HttpXsrfTokenExtractorMock('mock-token') },
{ provide: CookieService, useValue: new CookieServiceMock() },
],
diff --git a/src/app/shared/uploader/uploader.component.ts b/src/app/shared/upload/uploader/uploader.component.ts
similarity index 93%
rename from src/app/shared/uploader/uploader.component.ts
rename to src/app/shared/upload/uploader/uploader.component.ts
index 3cbf033b17..14b1ca9b94 100644
--- a/src/app/shared/uploader/uploader.component.ts
+++ b/src/app/shared/upload/uploader/uploader.component.ts
@@ -6,12 +6,12 @@ import uniqueId from 'lodash/uniqueId';
import { ScrollToService } from '@nicky-lenaers/ngx-scroll-to';
import { UploaderOptions } from './uploader-options.model';
-import { hasValue, isNotEmpty, isUndefined } from '../empty.util';
-import { UploaderService } from './uploader.service';
+import { hasValue, isNotEmpty, isUndefined } from '../../empty.util';
import { UploaderProperties } from './uploader-properties.model';
import { HttpXsrfTokenExtractor } from '@angular/common/http';
-import { XSRF_COOKIE, XSRF_REQUEST_HEADER, XSRF_RESPONSE_HEADER } from '../../core/xsrf/xsrf.interceptor';
-import { CookieService } from '../../core/services/cookie.service';
+import { XSRF_COOKIE, XSRF_REQUEST_HEADER, XSRF_RESPONSE_HEADER } from '../../../core/xsrf/xsrf.interceptor';
+import { CookieService } from '../../../core/services/cookie.service';
+import { DragService } from '../../../core/drag.service';
@Component({
selector: 'ds-uploader',
@@ -76,7 +76,7 @@ export class UploaderComponent {
@HostListener('window:dragover', ['$event'])
onDragOver(event: any) {
- if (this.enableDragOverDocument && this.uploaderService.isAllowedDragOverPage()) {
+ if (this.enableDragOverDocument && this.dragService.isAllowedDragOverPage()) {
// Show drop area on the page
event.preventDefault();
if ((event.target as any).tagName !== 'HTML') {
@@ -85,9 +85,13 @@ export class UploaderComponent {
}
}
- constructor(private cdr: ChangeDetectorRef, private scrollToService: ScrollToService,
- private uploaderService: UploaderService, private tokenExtractor: HttpXsrfTokenExtractor,
- private cookieService: CookieService) {
+ constructor(
+ private cdr: ChangeDetectorRef,
+ private scrollToService: ScrollToService,
+ private dragService: DragService,
+ private tokenExtractor: HttpXsrfTokenExtractor,
+ private cookieService: CookieService
+ ) {
}
/**
diff --git a/src/app/statistics-page/statistics-table/statistics-table.component.html b/src/app/statistics-page/statistics-table/statistics-table.component.html
index 3ecd256812..fb042b25c3 100644
--- a/src/app/statistics-page/statistics-table/statistics-table.component.html
+++ b/src/app/statistics-page/statistics-table/statistics-table.component.html
@@ -10,7 +10,7 @@
- |
+ |