Merge branch 'main' of github.com:DSpace/dspace-angular into 1422-deploy-time-config
@@ -269,7 +269,9 @@ E2E tests (aka integration tests) use [Cypress.io](https://www.cypress.io/). Con
|
|||||||
|
|
||||||
The test files can be found in the `./cypress/integration/` folder.
|
The test files can be found in the `./cypress/integration/` folder.
|
||||||
|
|
||||||
Before you can run e2e tests, you MUST have a running backend (i.e. REST API). By default, the e2e tests look for this at http://localhost:8080/server/ or whatever `rest` backend is defined in your `config.prod.yml` or `config.yml`. You may override this using env variables, see [Configuring](#configuring).
|
Before you can run e2e tests, two things are required:
|
||||||
|
1. You MUST have a running backend (i.e. REST API). By default, the e2e tests look for this at http://localhost:8080/server/ or whatever `rest` backend is defined in your `config.prod.yml` or `config.yml`. You may override this using env variables, see [Configuring](#configuring).
|
||||||
|
2. Your backend MUST include our Entities Test Data set. Some tests run against a (currently hardcoded) Community/Collection/Item UUID. These UUIDs are all valid for our Entities Test Data set. The Entities Test Data set may be installed easily via Docker, see https://github.com/DSpace/DSpace/tree/main/dspace/src/main/docker-compose#ingest-option-2-ingest-entities-test-data
|
||||||
|
|
||||||
Run `ng e2e` to kick off the tests. This will start Cypress and allow you to select the browser you wish to use, as well as whether you wish to run all tests or an individual test file. Once you click run on test(s), this opens the [Cypress Test Runner](https://docs.cypress.io/guides/core-concepts/test-runner) to run your test(s) and show you the results.
|
Run `ng e2e` to kick off the tests. This will start Cypress and allow you to select the browser you wish to use, as well as whether you wish to run all tests or an individual test file. Once you click run on test(s), this opens the [Cypress Test Runner](https://docs.cypress.io/guides/core-concepts/test-runner) to run your test(s) and show you the results.
|
||||||
|
|
||||||
|
@@ -5,5 +5,6 @@
|
|||||||
"screenshotsFolder": "cypress/screenshots",
|
"screenshotsFolder": "cypress/screenshots",
|
||||||
"pluginsFile": "cypress/plugins/index.ts",
|
"pluginsFile": "cypress/plugins/index.ts",
|
||||||
"fixturesFolder": "cypress/fixtures",
|
"fixturesFolder": "cypress/fixtures",
|
||||||
"baseUrl": "http://localhost:4000"
|
"baseUrl": "http://localhost:4000",
|
||||||
|
"retries": 2
|
||||||
}
|
}
|
@@ -64,7 +64,7 @@ services:
|
|||||||
dspacesolr:
|
dspacesolr:
|
||||||
container_name: dspacesolr
|
container_name: dspacesolr
|
||||||
# Uses official Solr image at https://hub.docker.com/_/solr/
|
# Uses official Solr image at https://hub.docker.com/_/solr/
|
||||||
image: solr:8.8
|
image: solr:8.11-slim
|
||||||
# Needs main 'dspace' container to start first to guarantee access to solr_configs
|
# Needs main 'dspace' container to start first to guarantee access to solr_configs
|
||||||
depends_on:
|
depends_on:
|
||||||
- dspace
|
- dspace
|
||||||
|
@@ -62,7 +62,7 @@ services:
|
|||||||
dspacesolr:
|
dspacesolr:
|
||||||
container_name: dspacesolr
|
container_name: dspacesolr
|
||||||
# Uses official Solr image at https://hub.docker.com/_/solr/
|
# Uses official Solr image at https://hub.docker.com/_/solr/
|
||||||
image: solr:8.8
|
image: solr:8.11-slim
|
||||||
# Needs main 'dspace' container to start first to guarantee access to solr_configs
|
# Needs main 'dspace' container to start first to guarantee access to solr_configs
|
||||||
depends_on:
|
depends_on:
|
||||||
- dspace
|
- dspace
|
||||||
|
@@ -97,9 +97,9 @@
|
|||||||
"jwt-decode": "^3.1.2",
|
"jwt-decode": "^3.1.2",
|
||||||
"klaro": "^0.7.10",
|
"klaro": "^0.7.10",
|
||||||
"lodash": "^4.17.21",
|
"lodash": "^4.17.21",
|
||||||
"mirador": "^3.0.0",
|
"mirador": "^3.3.0",
|
||||||
"mirador-dl-plugin": "^0.13.0",
|
"mirador-dl-plugin": "^0.13.0",
|
||||||
"mirador-share-plugin": "^0.10.0",
|
"mirador-share-plugin": "^0.11.0",
|
||||||
"moment": "^2.29.1",
|
"moment": "^2.29.1",
|
||||||
"morgan": "^1.10.0",
|
"morgan": "^1.10.0",
|
||||||
"ng-mocks": "11.11.2",
|
"ng-mocks": "11.11.2",
|
||||||
@@ -112,8 +112,6 @@
|
|||||||
"nouislider": "^14.6.3",
|
"nouislider": "^14.6.3",
|
||||||
"pem": "1.14.4",
|
"pem": "1.14.4",
|
||||||
"postcss-cli": "^8.3.0",
|
"postcss-cli": "^8.3.0",
|
||||||
"react": "^16.14.0",
|
|
||||||
"react-dom": "^16.14.0",
|
|
||||||
"reflect-metadata": "^0.1.13",
|
"reflect-metadata": "^0.1.13",
|
||||||
"rxjs": "^6.6.3",
|
"rxjs": "^6.6.3",
|
||||||
"sortablejs": "1.13.0",
|
"sortablejs": "1.13.0",
|
||||||
@@ -175,6 +173,8 @@
|
|||||||
"protractor": "^7.0.0",
|
"protractor": "^7.0.0",
|
||||||
"protractor-istanbul-plugin": "2.0.0",
|
"protractor-istanbul-plugin": "2.0.0",
|
||||||
"raw-loader": "0.5.1",
|
"raw-loader": "0.5.1",
|
||||||
|
"react": "^16.14.0",
|
||||||
|
"react-dom": "^16.14.0",
|
||||||
"rimraf": "^3.0.2",
|
"rimraf": "^3.0.2",
|
||||||
"rxjs-spy": "^7.5.3",
|
"rxjs-spy": "^7.5.3",
|
||||||
"sass-resources-loader": "^2.1.1",
|
"sass-resources-loader": "^2.1.1",
|
||||||
|
@@ -203,7 +203,6 @@ import { GroupAdministratorGuard } from './core/data/feature-authorization/featu
|
|||||||
]}
|
]}
|
||||||
],{
|
],{
|
||||||
onSameUrlNavigation: 'reload',
|
onSameUrlNavigation: 'reload',
|
||||||
relativeLinkResolution: 'legacy'
|
|
||||||
})
|
})
|
||||||
],
|
],
|
||||||
exports: [RouterModule],
|
exports: [RouterModule],
|
||||||
|
@@ -174,7 +174,8 @@ describe('App component', () => {
|
|||||||
TestBed.configureTestingModule(getDefaultTestBedConf());
|
TestBed.configureTestingModule(getDefaultTestBedConf());
|
||||||
TestBed.overrideProvider(ThemeService, {useValue: getMockThemeService('custom')});
|
TestBed.overrideProvider(ThemeService, {useValue: getMockThemeService('custom')});
|
||||||
document = TestBed.inject(DOCUMENT);
|
document = TestBed.inject(DOCUMENT);
|
||||||
headSpy = jasmine.createSpyObj('head', ['appendChild']);
|
headSpy = jasmine.createSpyObj('head', ['appendChild', 'getElementsByClassName']);
|
||||||
|
headSpy.getElementsByClassName.and.returnValue([]);
|
||||||
spyOn(document, 'getElementsByTagName').and.returnValue([headSpy]);
|
spyOn(document, 'getElementsByTagName').and.returnValue([headSpy]);
|
||||||
fixture = TestBed.createComponent(AppComponent);
|
fixture = TestBed.createComponent(AppComponent);
|
||||||
comp = fixture.componentInstance;
|
comp = fixture.componentInstance;
|
||||||
|
@@ -34,12 +34,12 @@ import { AuthService } from './core/auth/auth.service';
|
|||||||
import { CSSVariableService } from './shared/sass-helper/sass-helper.service';
|
import { CSSVariableService } from './shared/sass-helper/sass-helper.service';
|
||||||
import { MenuService } from './shared/menu/menu.service';
|
import { MenuService } from './shared/menu/menu.service';
|
||||||
import { HostWindowService } from './shared/host-window.service';
|
import { HostWindowService } from './shared/host-window.service';
|
||||||
import { ThemeConfig } from '../config/theme.model';
|
import { HeadTagConfig, ThemeConfig } from '../config/theme.model';
|
||||||
import { Angulartics2DSpace } from './statistics/angulartics/dspace-provider';
|
import { Angulartics2DSpace } from './statistics/angulartics/dspace-provider';
|
||||||
import { environment } from '../environments/environment';
|
import { environment } from '../environments/environment';
|
||||||
import { models } from './core/core.module';
|
import { models } from './core/core.module';
|
||||||
import { LocaleService } from './core/locale/locale.service';
|
import { LocaleService } from './core/locale/locale.service';
|
||||||
import { hasValue, isNotEmpty } from './shared/empty.util';
|
import { hasNoValue, hasValue, isNotEmpty } from './shared/empty.util';
|
||||||
import { KlaroService } from './shared/cookies/klaro.service';
|
import { KlaroService } from './shared/cookies/klaro.service';
|
||||||
import { GoogleAnalyticsService } from './statistics/google-analytics.service';
|
import { GoogleAnalyticsService } from './statistics/google-analytics.service';
|
||||||
import { ThemeService } from './shared/theme-support/theme.service';
|
import { ThemeService } from './shared/theme-support/theme.service';
|
||||||
@@ -124,13 +124,13 @@ export class AppComponent implements OnInit, AfterViewInit {
|
|||||||
this.isThemeCSSLoading$.next(true);
|
this.isThemeCSSLoading$.next(true);
|
||||||
}
|
}
|
||||||
if (hasValue(themeName)) {
|
if (hasValue(themeName)) {
|
||||||
this.setThemeCss(themeName);
|
this.loadGlobalThemeConfig(themeName);
|
||||||
} else {
|
} else {
|
||||||
const defaultThemeConfig = getDefaultThemeConfig();
|
const defaultThemeConfig = getDefaultThemeConfig();
|
||||||
if (hasValue(defaultThemeConfig)) {
|
if (hasValue(defaultThemeConfig)) {
|
||||||
this.setThemeCss(defaultThemeConfig.name);
|
this.loadGlobalThemeConfig(defaultThemeConfig.name);
|
||||||
} else {
|
} else {
|
||||||
this.setThemeCss(BASE_THEME_NAME);
|
this.loadGlobalThemeConfig(BASE_THEME_NAME);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@@ -245,6 +245,11 @@ export class AppComponent implements OnInit, AfterViewInit {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private loadGlobalThemeConfig(themeName: string): void {
|
||||||
|
this.setThemeCss(themeName);
|
||||||
|
this.setHeadTags(themeName);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Update the theme css file in <head>
|
* Update the theme css file in <head>
|
||||||
*
|
*
|
||||||
@@ -253,9 +258,13 @@ export class AppComponent implements OnInit, AfterViewInit {
|
|||||||
*/
|
*/
|
||||||
private setThemeCss(themeName: string): void {
|
private setThemeCss(themeName: string): void {
|
||||||
const head = this.document.getElementsByTagName('head')[0];
|
const head = this.document.getElementsByTagName('head')[0];
|
||||||
|
if (hasNoValue(head)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// Array.from to ensure we end up with an array, not an HTMLCollection, which would be
|
// Array.from to ensure we end up with an array, not an HTMLCollection, which would be
|
||||||
// automatically updated if we add nodes later
|
// automatically updated if we add nodes later
|
||||||
const currentThemeLinks = Array.from(this.document.getElementsByClassName('theme-css'));
|
const currentThemeLinks = Array.from(head.getElementsByClassName('theme-css'));
|
||||||
const link = this.document.createElement('link');
|
const link = this.document.createElement('link');
|
||||||
link.setAttribute('rel', 'stylesheet');
|
link.setAttribute('rel', 'stylesheet');
|
||||||
link.setAttribute('type', 'text/css');
|
link.setAttribute('type', 'text/css');
|
||||||
@@ -277,6 +286,78 @@ export class AppComponent implements OnInit, AfterViewInit {
|
|||||||
head.appendChild(link);
|
head.appendChild(link);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private setHeadTags(themeName: string): void {
|
||||||
|
const head = this.document.getElementsByTagName('head')[0];
|
||||||
|
if (hasNoValue(head)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// clear head tags
|
||||||
|
const currentHeadTags = Array.from(head.getElementsByClassName('theme-head-tag'));
|
||||||
|
if (hasValue(currentHeadTags)) {
|
||||||
|
currentHeadTags.forEach((currentHeadTag: any) => currentHeadTag.remove());
|
||||||
|
}
|
||||||
|
|
||||||
|
// create new head tags (not yet added to DOM)
|
||||||
|
const headTagFragment = this.document.createDocumentFragment();
|
||||||
|
this.createHeadTags(themeName)
|
||||||
|
.forEach(newHeadTag => headTagFragment.appendChild(newHeadTag));
|
||||||
|
|
||||||
|
// add new head tags to DOM
|
||||||
|
head.appendChild(headTagFragment);
|
||||||
|
}
|
||||||
|
|
||||||
|
private createHeadTags(themeName: string): HTMLElement[] {
|
||||||
|
const themeConfig = this.themeService.getThemeConfigFor(themeName);
|
||||||
|
const headTagConfigs = themeConfig?.headTags;
|
||||||
|
|
||||||
|
if (hasNoValue(headTagConfigs)) {
|
||||||
|
const parentThemeName = themeConfig?.extends;
|
||||||
|
if (hasValue(parentThemeName)) {
|
||||||
|
// inherit the head tags of the parent theme
|
||||||
|
return this.createHeadTags(parentThemeName);
|
||||||
|
}
|
||||||
|
const defaultThemeConfig = getDefaultThemeConfig();
|
||||||
|
const defaultThemeName = defaultThemeConfig.name;
|
||||||
|
if (
|
||||||
|
hasNoValue(defaultThemeName) ||
|
||||||
|
themeName === defaultThemeName ||
|
||||||
|
themeName === BASE_THEME_NAME
|
||||||
|
) {
|
||||||
|
// last resort, use fallback favicon.ico
|
||||||
|
return [
|
||||||
|
this.createHeadTag({
|
||||||
|
'tagName': 'link',
|
||||||
|
'attributes': {
|
||||||
|
'rel': 'icon',
|
||||||
|
'href': 'assets/images/favicon.ico',
|
||||||
|
'sizes': 'any',
|
||||||
|
}
|
||||||
|
})
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
// inherit the head tags of the default theme
|
||||||
|
return this.createHeadTags(defaultThemeConfig.name);
|
||||||
|
}
|
||||||
|
|
||||||
|
return headTagConfigs.map(this.createHeadTag.bind(this));
|
||||||
|
}
|
||||||
|
|
||||||
|
private createHeadTag(headTagConfig: HeadTagConfig): HTMLElement {
|
||||||
|
const tag = this.document.createElement(headTagConfig.tagName);
|
||||||
|
|
||||||
|
if (hasValue(headTagConfig.attributes)) {
|
||||||
|
Object.entries(headTagConfig.attributes)
|
||||||
|
.forEach(([key, value]) => tag.setAttribute(key, value));
|
||||||
|
}
|
||||||
|
|
||||||
|
// 'class' attribute should always be 'theme-head-tag' for removal
|
||||||
|
tag.setAttribute('class', 'theme-head-tag');
|
||||||
|
|
||||||
|
return tag;
|
||||||
|
}
|
||||||
|
|
||||||
private trackIdleModal() {
|
private trackIdleModal() {
|
||||||
const isIdle$ = this.authService.isUserIdle();
|
const isIdle$ = this.authService.isUserIdle();
|
||||||
const isAuthenticated$ = this.authService.isAuthenticated();
|
const isAuthenticated$ = this.authService.isAuthenticated();
|
||||||
|
@@ -10,11 +10,11 @@
|
|||||||
</nav>
|
</nav>
|
||||||
|
|
||||||
<ng-template #breadcrumb let-text="text" let-url="url">
|
<ng-template #breadcrumb let-text="text" let-url="url">
|
||||||
<li class="breadcrumb-item"><a [routerLink]="url">{{text | translate}}</a></li>
|
<li class="breadcrumb-item"><div class="breadcrumb-item-limiter"><a [routerLink]="url" class="text-truncate">{{text | translate}}</a></div></li>
|
||||||
</ng-template>
|
</ng-template>
|
||||||
|
|
||||||
<ng-template #activeBreadcrumb let-text="text">
|
<ng-template #activeBreadcrumb let-text="text">
|
||||||
<li class="breadcrumb-item active" aria-current="page">{{text | translate}}</li>
|
<li class="breadcrumb-item active" aria-current="page"><div class="breadcrumb-item-limiter"><div class="text-truncate">{{text | translate}}</div></div></li>
|
||||||
</ng-template>
|
</ng-template>
|
||||||
</ng-container>
|
</ng-container>
|
||||||
|
|
||||||
|
@@ -10,6 +10,19 @@
|
|||||||
background-color: var(--ds-breadcrumb-bg);
|
background-color: var(--ds-breadcrumb-bg);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
li.breadcrumb-item {
|
||||||
|
display: flex;
|
||||||
|
}
|
||||||
|
|
||||||
|
.breadcrumb-item-limiter {
|
||||||
|
display: inline-block;
|
||||||
|
max-width: var(--ds-breadcrumb-max-length);
|
||||||
|
> * {
|
||||||
|
max-width: 100%;
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
li.breadcrumb-item > a {
|
li.breadcrumb-item > a {
|
||||||
color: var(--ds-breadcrumb-link-color) !important;
|
color: var(--ds-breadcrumb-link-color) !important;
|
||||||
}
|
}
|
||||||
@@ -18,5 +31,6 @@ li.breadcrumb-item.active {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.breadcrumb-item+ .breadcrumb-item::before {
|
.breadcrumb-item+ .breadcrumb-item::before {
|
||||||
|
display: block;
|
||||||
content: quote("•") !important;
|
content: quote("•") !important;
|
||||||
}
|
}
|
||||||
|
@@ -72,7 +72,7 @@ describe('BreadcrumbsComponent', () => {
|
|||||||
expect(breadcrumbs.length).toBe(3);
|
expect(breadcrumbs.length).toBe(3);
|
||||||
expectBreadcrumb(breadcrumbs[0], 'home.breadcrumbs', '/');
|
expectBreadcrumb(breadcrumbs[0], 'home.breadcrumbs', '/');
|
||||||
expectBreadcrumb(breadcrumbs[1], 'bc 1', '/example.com');
|
expectBreadcrumb(breadcrumbs[1], 'bc 1', '/example.com');
|
||||||
expectBreadcrumb(breadcrumbs[2], 'bc 2', null);
|
expectBreadcrumb(breadcrumbs[2].query(By.css('.text-truncate')), 'bc 2', null);
|
||||||
});
|
});
|
||||||
|
|
||||||
});
|
});
|
||||||
|
@@ -1,10 +1,10 @@
|
|||||||
<ds-type-badge *ngIf="showLabel" [object]="dso"></ds-type-badge>
|
<ds-type-badge *ngIf="showLabel" [object]="dso"></ds-type-badge>
|
||||||
<ds-truncatable [id]="dso.id">
|
<ds-truncatable [id]="dso.id">
|
||||||
<a *ngIf="linkType != linkTypes.None" [target]="(linkType == linkTypes.ExternalLink) ? '_blank' : '_self'" rel="noopener noreferrer"
|
<a *ngIf="linkType != linkTypes.None" [target]="(linkType == linkTypes.ExternalLink) ? '_blank' : '_self'" rel="noopener noreferrer"
|
||||||
[routerLink]="[itemPageRoute]" class="lead item-list-title"
|
[routerLink]="[itemPageRoute]" class="lead item-list-title dont-break-out"
|
||||||
[innerHTML]="dsoTitle"></a>
|
[innerHTML]="dsoTitle"></a>
|
||||||
<span *ngIf="linkType == linkTypes.None"
|
<span *ngIf="linkType == linkTypes.None"
|
||||||
class="lead item-list-title"
|
class="lead item-list-title dont-break-out"
|
||||||
[innerHTML]="dsoTitle"></span>
|
[innerHTML]="dsoTitle"></span>
|
||||||
<span class="text-muted">
|
<span class="text-muted">
|
||||||
<ds-truncatable-part [id]="dso.id" [minLines]="1">
|
<ds-truncatable-part [id]="dso.id" [minLines]="1">
|
||||||
|
@@ -1,10 +1,10 @@
|
|||||||
<ds-type-badge *ngIf="showLabel" [object]="dso"></ds-type-badge>
|
<ds-type-badge *ngIf="showLabel" [object]="dso"></ds-type-badge>
|
||||||
<ds-truncatable [id]="dso.id">
|
<ds-truncatable [id]="dso.id">
|
||||||
<a *ngIf="linkType != linkTypes.None" [target]="(linkType == linkTypes.ExternalLink) ? '_blank' : '_self'" rel="noopener noreferrer"
|
<a *ngIf="linkType != linkTypes.None" [target]="(linkType == linkTypes.ExternalLink) ? '_blank' : '_self'" rel="noopener noreferrer"
|
||||||
[routerLink]="[itemPageRoute]" class="lead item-list-title"
|
[routerLink]="[itemPageRoute]" class="lead item-list-title dont-break-out"
|
||||||
[innerHTML]="dsoTitle"></a>
|
[innerHTML]="dsoTitle"></a>
|
||||||
<span *ngIf="linkType == linkTypes.None"
|
<span *ngIf="linkType == linkTypes.None"
|
||||||
class="lead item-list-title"
|
class="lead item-list-title dont-break-out"
|
||||||
[innerHTML]="dsoTitle"></span>
|
[innerHTML]="dsoTitle"></span>
|
||||||
<span class="text-muted">
|
<span class="text-muted">
|
||||||
<ds-truncatable-part [id]="dso.id" [minLines]="1">
|
<ds-truncatable-part [id]="dso.id" [minLines]="1">
|
||||||
|
@@ -1,10 +1,10 @@
|
|||||||
<ds-type-badge *ngIf="showLabel" [object]="dso"></ds-type-badge>
|
<ds-type-badge *ngIf="showLabel" [object]="dso"></ds-type-badge>
|
||||||
<ds-truncatable [id]="dso.id">
|
<ds-truncatable [id]="dso.id">
|
||||||
<a *ngIf="linkType != linkTypes.None" [target]="(linkType == linkTypes.ExternalLink) ? '_blank' : '_self'" rel="noopener noreferrer"
|
<a *ngIf="linkType != linkTypes.None" [target]="(linkType == linkTypes.ExternalLink) ? '_blank' : '_self'" rel="noopener noreferrer"
|
||||||
[routerLink]="[itemPageRoute]" class="lead item-list-title"
|
[routerLink]="[itemPageRoute]" class="lead item-list-title dont-break-out"
|
||||||
[innerHTML]="dsoTitle"></a>
|
[innerHTML]="dsoTitle"></a>
|
||||||
<span *ngIf="linkType == linkTypes.None"
|
<span *ngIf="linkType == linkTypes.None"
|
||||||
class="lead item-list-title"
|
class="lead item-list-title dont-break-out"
|
||||||
[innerHTML]="dsoTitle"></span>
|
[innerHTML]="dsoTitle"></span>
|
||||||
<span class="text-muted">
|
<span class="text-muted">
|
||||||
<ds-truncatable-part [id]="dso.id" [minLines]="1">
|
<ds-truncatable-part [id]="dso.id" [minLines]="1">
|
||||||
|
@@ -1,10 +1,10 @@
|
|||||||
<ds-truncatable [id]="dso.id">
|
<ds-truncatable [id]="dso.id">
|
||||||
<ds-type-badge *ngIf="showLabel" [object]="dso"></ds-type-badge>
|
<ds-type-badge *ngIf="showLabel" [object]="dso"></ds-type-badge>
|
||||||
<a *ngIf="linkType != linkTypes.None" [target]="(linkType == linkTypes.ExternalLink) ? '_blank' : '_self'" rel="noopener noreferrer"
|
<a *ngIf="linkType != linkTypes.None" [target]="(linkType == linkTypes.ExternalLink) ? '_blank' : '_self'" rel="noopener noreferrer"
|
||||||
[routerLink]="[itemPageRoute]" class="lead item-list-title"
|
[routerLink]="[itemPageRoute]" class="lead item-list-title dont-break-out"
|
||||||
[innerHTML]="dsoTitle"></a>
|
[innerHTML]="dsoTitle"></a>
|
||||||
<span *ngIf="linkType == linkTypes.None"
|
<span *ngIf="linkType == linkTypes.None"
|
||||||
class="lead item-list-title"
|
class="lead item-list-title dont-break-out"
|
||||||
[innerHTML]="dsoTitle"></span>
|
[innerHTML]="dsoTitle"></span>
|
||||||
<!--<span class="text-muted">-->
|
<!--<span class="text-muted">-->
|
||||||
<!--<ds-truncatable-part [id]="dso.id" [minLines]="1">-->
|
<!--<ds-truncatable-part [id]="dso.id" [minLines]="1">-->
|
||||||
|
@@ -1,18 +1,24 @@
|
|||||||
<ng-template #bitstreamView>
|
<ng-template #bitstreamView>
|
||||||
<div class="{{columnSizes.columns[0].buildClasses()}} row-element d-flex">
|
<div class="{{columnSizes.columns[0].buildClasses()}} row-element d-flex">
|
||||||
<ng-content select="[slot=drag-handle]"></ng-content>
|
<ng-content select="[slot=drag-handle]"></ng-content>
|
||||||
<div class="float-left d-flex align-items-center">
|
<div class="float-left d-flex align-items-center overflow-hidden">
|
||||||
{{ bitstreamName }}
|
<span class="text-truncate">
|
||||||
|
{{ bitstreamName }}
|
||||||
|
</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="{{columnSizes.columns[1].buildClasses()}} row-element d-flex align-items-center">
|
<div class="{{columnSizes.columns[1].buildClasses()}} row-element d-flex align-items-center">
|
||||||
<div class="w-100">
|
<div class="w-100">
|
||||||
|
<span class="text-truncate">
|
||||||
{{ bitstream?.firstMetadataValue('dc.description') }}
|
{{ bitstream?.firstMetadataValue('dc.description') }}
|
||||||
|
</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="{{columnSizes.columns[2].buildClasses()}} row-element d-flex align-items-center">
|
<div class="{{columnSizes.columns[2].buildClasses()}} row-element d-flex align-items-center">
|
||||||
<div class="text-center w-100">
|
<div class="text-center w-100">
|
||||||
{{ (format$ | async)?.shortDescription }}
|
<span class="text-truncate">
|
||||||
|
{{ (format$ | async)?.shortDescription }}
|
||||||
|
</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="{{columnSizes.columns[3].buildClasses()}} row-element d-flex align-items-center">
|
<div class="{{columnSizes.columns[3].buildClasses()}} row-element d-flex align-items-center">
|
||||||
|
@@ -26,7 +26,7 @@
|
|||||||
<td class="w-100">
|
<td class="w-100">
|
||||||
<div class="value-field">
|
<div class="value-field">
|
||||||
<div *ngIf="!(editable | async)">
|
<div *ngIf="!(editable | async)">
|
||||||
<span>{{metadata?.value}}</span>
|
<span class="dont-break-out">{{metadata?.value}}</span>
|
||||||
</div>
|
</div>
|
||||||
<div *ngIf="(editable | async)" class="field-container">
|
<div *ngIf="(editable | async)" class="field-container">
|
||||||
<textarea class="form-control" type="textarea" attr.aria-labelledby="fieldValue" [(ngModel)]="metadata.value" [dsDebounce]
|
<textarea class="form-control" type="textarea" attr.aria-labelledby="fieldValue" [(ngModel)]="metadata.value" [dsDebounce]
|
||||||
|
@@ -1,5 +1,5 @@
|
|||||||
<ds-metadata-field-wrapper [label]="label | translate">
|
<ds-metadata-field-wrapper [label]="label | translate">
|
||||||
<a *ngFor="let mdValue of mdValues; let last=last;" [href]="mdValue.value">
|
<a class="dont-break-out" *ngFor="let mdValue of mdValues; let last=last;" [href]="mdValue.value">
|
||||||
{{ linktext || mdValue.value }}<span *ngIf="!last" [innerHTML]="separator"></span>
|
{{ linktext || mdValue.value }}<span *ngIf="!last" [innerHTML]="separator"></span>
|
||||||
</a>
|
</a>
|
||||||
</ds-metadata-field-wrapper>
|
</ds-metadata-field-wrapper>
|
||||||
|
@@ -1,5 +1,5 @@
|
|||||||
<ds-metadata-field-wrapper [label]="label | translate">
|
<ds-metadata-field-wrapper [label]="label | translate">
|
||||||
<span *ngFor="let mdValue of mdValues; let last=last;">
|
<span class="dont-break-out" *ngFor="let mdValue of mdValues; let last=last;">
|
||||||
{{mdValue.value}}<span *ngIf="!last" [innerHTML]="separator"></span>
|
{{mdValue.value}}<span *ngIf="!last" [innerHTML]="separator"></span>
|
||||||
</span>
|
</span>
|
||||||
</ds-metadata-field-wrapper>
|
</ds-metadata-field-wrapper>
|
||||||
|
@@ -4,7 +4,7 @@
|
|||||||
<ds-search-sidebar *ngIf="!(isXsOrSm$ | async)" class="col-3 sidebar-md-sticky"
|
<ds-search-sidebar *ngIf="!(isXsOrSm$ | async)" class="col-3 sidebar-md-sticky"
|
||||||
id="search-sidebar"
|
id="search-sidebar"
|
||||||
[configurationList]="(configurationList$ | async)"
|
[configurationList]="(configurationList$ | async)"
|
||||||
[resultCount]="(resultsRD$ | async)?.payload.totalElements"
|
[resultCount]="(resultsRD$ | async)?.payload?.totalElements"
|
||||||
[viewModeList]="viewModeList"
|
[viewModeList]="viewModeList"
|
||||||
[searchOptions]="(searchOptions$ | async)"
|
[searchOptions]="(searchOptions$ | async)"
|
||||||
[sortOptions]="(sortOptions$ | async)"
|
[sortOptions]="(sortOptions$ | async)"
|
||||||
@@ -27,7 +27,7 @@
|
|||||||
<ds-search-sidebar *ngIf="(isXsOrSm$ | async)" class="col-12"
|
<ds-search-sidebar *ngIf="(isXsOrSm$ | async)" class="col-12"
|
||||||
id="search-sidebar-sm"
|
id="search-sidebar-sm"
|
||||||
[configurationList]="(configurationList$ | async)"
|
[configurationList]="(configurationList$ | async)"
|
||||||
[resultCount]="(resultsRD$ | async)?.payload.totalElements"
|
[resultCount]="(resultsRD$ | async)?.payload?.totalElements"
|
||||||
(toggleSidebar)="closeSidebar()"
|
(toggleSidebar)="closeSidebar()"
|
||||||
[ngClass]="{'active': !(isSidebarCollapsed() | async)}"
|
[ngClass]="{'active': !(isSidebarCollapsed() | async)}"
|
||||||
[searchOptions]="(searchOptions$ | async)"
|
[searchOptions]="(searchOptions$ | async)"
|
||||||
|
@@ -19,7 +19,7 @@ import { PaginatedSearchOptions } from '../shared/search/paginated-search-option
|
|||||||
import { SearchService } from '../core/shared/search/search.service';
|
import { SearchService } from '../core/shared/search/search.service';
|
||||||
import { SidebarService } from '../shared/sidebar/sidebar.service';
|
import { SidebarService } from '../shared/sidebar/sidebar.service';
|
||||||
import { hasValue } from '../shared/empty.util';
|
import { hasValue } from '../shared/empty.util';
|
||||||
import { getFirstSucceededRemoteData } from '../core/shared/operators';
|
import { getFirstCompletedRemoteData } from '../core/shared/operators';
|
||||||
import { MyDSpaceResponseParsingService } from '../core/data/mydspace-response-parsing.service';
|
import { MyDSpaceResponseParsingService } from '../core/data/mydspace-response-parsing.service';
|
||||||
import { SearchConfigurationOption } from '../shared/search/search-switch-configuration/search-configuration-option.model';
|
import { SearchConfigurationOption } from '../shared/search/search-switch-configuration/search-configuration-option.model';
|
||||||
import { RoleType } from '../core/roles/role-types';
|
import { RoleType } from '../core/roles/role-types';
|
||||||
@@ -30,7 +30,7 @@ import { MyDSpaceRequest } from '../core/data/request.models';
|
|||||||
import { SearchResult } from '../shared/search/search-result.model';
|
import { SearchResult } from '../shared/search/search-result.model';
|
||||||
import { Context } from '../core/shared/context.model';
|
import { Context } from '../core/shared/context.model';
|
||||||
import { SortOptions } from '../core/cache/models/sort-options.model';
|
import { SortOptions } from '../core/cache/models/sort-options.model';
|
||||||
import { RouteService } from '../core/services/route.service';
|
import { SearchObjects } from '../shared/search/search-objects.model';
|
||||||
|
|
||||||
export const MYDSPACE_ROUTE = '/mydspace';
|
export const MYDSPACE_ROUTE = '/mydspace';
|
||||||
export const SEARCH_CONFIG_SERVICE: InjectionToken<SearchConfigurationService> = new InjectionToken<SearchConfigurationService>('searchConfigurationService');
|
export const SEARCH_CONFIG_SERVICE: InjectionToken<SearchConfigurationService> = new InjectionToken<SearchConfigurationService>('searchConfigurationService');
|
||||||
@@ -111,8 +111,7 @@ export class MyDSpacePageComponent implements OnInit {
|
|||||||
constructor(private service: SearchService,
|
constructor(private service: SearchService,
|
||||||
private sidebarService: SidebarService,
|
private sidebarService: SidebarService,
|
||||||
private windowService: HostWindowService,
|
private windowService: HostWindowService,
|
||||||
@Inject(SEARCH_CONFIG_SERVICE) public searchConfigService: MyDSpaceConfigurationService,
|
@Inject(SEARCH_CONFIG_SERVICE) public searchConfigService: MyDSpaceConfigurationService) {
|
||||||
private routeService: RouteService) {
|
|
||||||
this.isXsOrSm$ = this.windowService.isXsOrSm();
|
this.isXsOrSm$ = this.windowService.isXsOrSm();
|
||||||
this.service.setServiceOptions(MyDSpaceResponseParsingService, MyDSpaceRequest);
|
this.service.setServiceOptions(MyDSpaceResponseParsingService, MyDSpaceRequest);
|
||||||
}
|
}
|
||||||
@@ -134,8 +133,8 @@ export class MyDSpacePageComponent implements OnInit {
|
|||||||
this.searchOptions$ = this.searchConfigService.paginatedSearchOptions;
|
this.searchOptions$ = this.searchConfigService.paginatedSearchOptions;
|
||||||
this.sub = this.searchOptions$.pipe(
|
this.sub = this.searchOptions$.pipe(
|
||||||
tap(() => this.resultsRD$.next(null)),
|
tap(() => this.resultsRD$.next(null)),
|
||||||
switchMap((options: PaginatedSearchOptions) => this.service.search(options).pipe(getFirstSucceededRemoteData())))
|
switchMap((options: PaginatedSearchOptions) => this.service.search(options).pipe(getFirstCompletedRemoteData())))
|
||||||
.subscribe((results) => {
|
.subscribe((results: RemoteData<SearchObjects<DSpaceObject>>) => {
|
||||||
this.resultsRD$.next(results);
|
this.resultsRD$.next(results);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@@ -10,5 +10,5 @@
|
|||||||
</ds-viewable-collection>
|
</ds-viewable-collection>
|
||||||
</div>
|
</div>
|
||||||
<ds-loading *ngIf="isLoading()" message="{{'loading.mydspace-results' | translate}}"></ds-loading>
|
<ds-loading *ngIf="isLoading()" message="{{'loading.mydspace-results' | translate}}"></ds-loading>
|
||||||
<ds-error *ngIf="searchResults?.hasFailed && (!searchResults?.errorMessage || searchResults?.statusCode != 400)" message="{{'error.search-results' | translate}}"></ds-error>
|
<ds-error *ngIf="showError()" message="{{errorMessageLabel() | translate}}"></ds-error>
|
||||||
<h3 *ngIf="searchResults?.payload?.page.length == 0" class="text-center text-muted" ><span>{{'mydspace.results.no-results' | translate}}</span></h3>
|
<h3 *ngIf="searchResults?.payload?.page.length == 0" class="text-center text-muted" ><span>{{'mydspace.results.no-results' | translate}}</span></h3>
|
||||||
|
@@ -40,9 +40,19 @@ describe('MyDSpaceResultsComponent', () => {
|
|||||||
expect(fixture.debugElement.query(By.css('a'))).toBeNull();
|
expect(fixture.debugElement.query(By.css('a'))).toBeNull();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should display error message if error is != 400', () => {
|
it('should display error message if error is 500', () => {
|
||||||
(comp as any).searchResults = { hasFailed: true, error: { statusCode: 500 } };
|
(comp as any).searchResults = { hasFailed: true, statusCode: 500 };
|
||||||
fixture.detectChanges();
|
fixture.detectChanges();
|
||||||
|
expect(comp.showError()).toBeTrue();
|
||||||
|
expect(comp.errorMessageLabel()).toBe('error.search-results');
|
||||||
|
expect(fixture.debugElement.query(By.css('ds-error'))).not.toBeNull();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should display error message if error is 422', () => {
|
||||||
|
(comp as any).searchResults = { hasFailed: true, statusCode: 422 };
|
||||||
|
fixture.detectChanges();
|
||||||
|
expect(comp.showError()).toBeTrue();
|
||||||
|
expect(comp.errorMessageLabel()).toBe('error.invalid-search-query');
|
||||||
expect(fixture.debugElement.query(By.css('ds-error'))).not.toBeNull();
|
expect(fixture.debugElement.query(By.css('ds-error'))).not.toBeNull();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@@ -58,4 +58,12 @@ export class MyDSpaceResultsComponent {
|
|||||||
isLoading() {
|
isLoading() {
|
||||||
return !this.searchResults || isEmpty(this.searchResults) || this.searchResults.isLoading;
|
return !this.searchResults || isEmpty(this.searchResults) || this.searchResults.isLoading;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
showError(): boolean {
|
||||||
|
return this.searchResults?.hasFailed && (!this.searchResults?.errorMessage || this.searchResults?.statusCode !== 400);
|
||||||
|
}
|
||||||
|
|
||||||
|
errorMessageLabel(): string {
|
||||||
|
return (this.searchResults?.statusCode === 422) ? 'error.invalid-search-query' : 'error.search-results';
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@@ -8,7 +8,7 @@ import { pushInOut } from '../shared/animations/push';
|
|||||||
import { HostWindowService } from '../shared/host-window.service';
|
import { HostWindowService } from '../shared/host-window.service';
|
||||||
import { SidebarService } from '../shared/sidebar/sidebar.service';
|
import { SidebarService } from '../shared/sidebar/sidebar.service';
|
||||||
import { hasValue, isEmpty } from '../shared/empty.util';
|
import { hasValue, isEmpty } from '../shared/empty.util';
|
||||||
import { getFirstSucceededRemoteData } from '../core/shared/operators';
|
import { getFirstCompletedRemoteData } from '../core/shared/operators';
|
||||||
import { RouteService } from '../core/services/route.service';
|
import { RouteService } from '../core/services/route.service';
|
||||||
import { SEARCH_CONFIG_SERVICE } from '../my-dspace-page/my-dspace-page.component';
|
import { SEARCH_CONFIG_SERVICE } from '../my-dspace-page/my-dspace-page.component';
|
||||||
import { PaginatedSearchOptions } from '../shared/search/paginated-search-options.model';
|
import { PaginatedSearchOptions } from '../shared/search/paginated-search-options.model';
|
||||||
@@ -126,12 +126,12 @@ export class SearchComponent implements OnInit {
|
|||||||
this.searchOptions$ = this.getSearchOptions();
|
this.searchOptions$ = this.getSearchOptions();
|
||||||
this.sub = this.searchOptions$.pipe(
|
this.sub = this.searchOptions$.pipe(
|
||||||
switchMap((options) => this.service.search(
|
switchMap((options) => this.service.search(
|
||||||
options, undefined, true, true, followLink<Item>('thumbnail', { isOptional: true })
|
options, undefined, false, true, followLink<Item>('thumbnail', { isOptional: true })
|
||||||
).pipe(getFirstSucceededRemoteData(), startWith(undefined))
|
).pipe(getFirstCompletedRemoteData(), startWith(undefined))
|
||||||
)
|
)
|
||||||
).subscribe((results) => {
|
).subscribe((results) => {
|
||||||
this.resultsRD$.next(results);
|
this.resultsRD$.next(results);
|
||||||
});
|
});
|
||||||
|
|
||||||
if (isEmpty(this.configuration$)) {
|
if (isEmpty(this.configuration$)) {
|
||||||
this.configuration$ = this.searchConfigService.getCurrentConfiguration('default');
|
this.configuration$ = this.searchConfigService.getCurrentConfiguration('default');
|
||||||
|
@@ -11,8 +11,8 @@
|
|||||||
aria-labelledby="dropdownMenuButton"
|
aria-labelledby="dropdownMenuButton"
|
||||||
(scroll)="onScroll($event)"
|
(scroll)="onScroll($event)"
|
||||||
infiniteScroll
|
infiniteScroll
|
||||||
[infiniteScrollDistance]="5"
|
[infiniteScrollDistance]="1.5"
|
||||||
[infiniteScrollThrottle]="300"
|
[infiniteScrollThrottle]="0"
|
||||||
[infiniteScrollUpDistance]="1.5"
|
[infiniteScrollUpDistance]="1.5"
|
||||||
[fromRoot]="true"
|
[fromRoot]="true"
|
||||||
[scrollWindow]="false"
|
[scrollWindow]="false"
|
||||||
@@ -21,7 +21,7 @@
|
|||||||
<button class="dropdown-item disabled" *ngIf="searchListCollection?.length == 0 && !(isLoading | async)">
|
<button class="dropdown-item disabled" *ngIf="searchListCollection?.length == 0 && !(isLoading | async)">
|
||||||
{{'submission.sections.general.no-collection' | translate}}
|
{{'submission.sections.general.no-collection' | translate}}
|
||||||
</button>
|
</button>
|
||||||
<ng-container *ngIf="searchListCollection?.length > 0 && !(isLoading | async)">
|
<ng-container *ngIf="searchListCollection?.length > 0">
|
||||||
<button *ngFor="let listItem of searchListCollection"
|
<button *ngFor="let listItem of searchListCollection"
|
||||||
class="dropdown-item collection-item"
|
class="dropdown-item collection-item"
|
||||||
title="{{ listItem.collection.name }}"
|
title="{{ listItem.collection.name }}"
|
||||||
|
@@ -223,20 +223,20 @@ export class CollectionDropdownComponent implements OnInit, OnDestroy {
|
|||||||
switchMap((collectionsRD: RemoteData<PaginatedList<Collection>>) => {
|
switchMap((collectionsRD: RemoteData<PaginatedList<Collection>>) => {
|
||||||
this.searchComplete.emit();
|
this.searchComplete.emit();
|
||||||
if (collectionsRD.hasSucceeded && collectionsRD.payload.totalElements > 0) {
|
if (collectionsRD.hasSucceeded && collectionsRD.payload.totalElements > 0) {
|
||||||
if ( (this.searchListCollection.length + findOptions.elementsPerPage) >= collectionsRD.payload.totalElements ) {
|
if (this.searchListCollection.length >= collectionsRD.payload.totalElements) {
|
||||||
this.hasNextPage = false;
|
this.hasNextPage = false;
|
||||||
this.emitSelectionEvents(collectionsRD);
|
|
||||||
return observableFrom(collectionsRD.payload.page).pipe(
|
|
||||||
mergeMap((collection: Collection) => collection.parentCommunity.pipe(
|
|
||||||
getFirstSucceededRemoteDataPayload(),
|
|
||||||
map((community: Community) => ({
|
|
||||||
communities: [{ id: community.id, name: community.name }],
|
|
||||||
collection: { id: collection.id, uuid: collection.id, name: collection.name }
|
|
||||||
})
|
|
||||||
))),
|
|
||||||
reduce((acc: any, value: any) => [...acc, value], []),
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
this.emitSelectionEvents(collectionsRD);
|
||||||
|
return observableFrom(collectionsRD.payload.page).pipe(
|
||||||
|
mergeMap((collection: Collection) => collection.parentCommunity.pipe(
|
||||||
|
getFirstSucceededRemoteDataPayload(),
|
||||||
|
map((community: Community) => ({
|
||||||
|
communities: [{ id: community.id, name: community.name }],
|
||||||
|
collection: { id: collection.id, uuid: collection.id, name: collection.name }
|
||||||
|
})
|
||||||
|
))),
|
||||||
|
reduce((acc: any, value: any) => [...acc, value], []),
|
||||||
|
);
|
||||||
} else {
|
} else {
|
||||||
this.hasNextPage = false;
|
this.hasNextPage = false;
|
||||||
return observableOf([]);
|
return observableOf([]);
|
||||||
|
@@ -1,3 +1,4 @@
|
|||||||
<div>
|
<ds-alert [type]="AlertTypeEnum.Error" [dismissible]="false">
|
||||||
<label>{{ message }}</label>
|
<!-- Using [innerHTML] instead of {{message}} allows to render HTML code -->
|
||||||
</div>
|
<span [innerHTML]="message"></span>
|
||||||
|
</ds-alert>
|
||||||
|
@@ -36,7 +36,7 @@ describe('ErrorComponent (inline template)', () => {
|
|||||||
comp = fixture.componentInstance; // ErrorComponent test instance
|
comp = fixture.componentInstance; // ErrorComponent test instance
|
||||||
|
|
||||||
// query for the message <label> by CSS element selector
|
// query for the message <label> by CSS element selector
|
||||||
de = fixture.debugElement.query(By.css('label'));
|
de = fixture.debugElement.query(By.css('ds-alert'));
|
||||||
el = de.nativeElement;
|
el = de.nativeElement;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@@ -3,6 +3,7 @@ import { Component, Input } from '@angular/core';
|
|||||||
import { TranslateService } from '@ngx-translate/core';
|
import { TranslateService } from '@ngx-translate/core';
|
||||||
|
|
||||||
import { Subscription } from 'rxjs';
|
import { Subscription } from 'rxjs';
|
||||||
|
import { AlertType } from '../alert/aletr-type';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'ds-error',
|
selector: 'ds-error',
|
||||||
@@ -13,6 +14,12 @@ export class ErrorComponent {
|
|||||||
|
|
||||||
@Input() message = 'Error...';
|
@Input() message = 'Error...';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The AlertType enumeration
|
||||||
|
* @type {AlertType}
|
||||||
|
*/
|
||||||
|
public AlertTypeEnum = AlertType;
|
||||||
|
|
||||||
private subscription: Subscription;
|
private subscription: Subscription;
|
||||||
|
|
||||||
constructor(private translate: TranslateService) {
|
constructor(private translate: TranslateService) {
|
||||||
|
@@ -1,4 +1,4 @@
|
|||||||
<a [routerLink]="(bitstreamPath$| async)?.routerLink" [queryParams]="(bitstreamPath$| async)?.queryParams" [target]="isBlank ? '_blank': '_self'" [ngClass]="cssClasses">
|
<a [routerLink]="(bitstreamPath$| async)?.routerLink" class="dont-break-out" [queryParams]="(bitstreamPath$| async)?.queryParams" [target]="isBlank ? '_blank': '_self'" [ngClass]="cssClasses">
|
||||||
<span *ngIf="!(canDownload$ |async)"><i class="fas fa-lock"></i></span>
|
<span *ngIf="!(canDownload$ |async)"><i class="fas fa-lock"></i></span>
|
||||||
<ng-container *ngTemplateOutlet="content"></ng-container>
|
<ng-container *ngTemplateOutlet="content"></ng-container>
|
||||||
</a>
|
</a>
|
||||||
|
@@ -1,3 +1,3 @@
|
|||||||
<div>
|
<div>
|
||||||
<span>{{metadataRepresentation.getValue()}}</span>
|
<span class="dont-break-out">{{metadataRepresentation.getValue()}}</span>
|
||||||
</div>
|
</div>
|
||||||
|
@@ -2,9 +2,9 @@
|
|||||||
|
|
||||||
<ds-truncatable [id]="dso.id" *ngIf="object !== undefined && object !== null">
|
<ds-truncatable [id]="dso.id" *ngIf="object !== undefined && object !== null">
|
||||||
<a *ngIf="linkType != linkTypes.None" [target]="(linkType == linkTypes.ExternalLink) ? '_blank' : '_self'" rel="noopener noreferrer"
|
<a *ngIf="linkType != linkTypes.None" [target]="(linkType == linkTypes.ExternalLink) ? '_blank' : '_self'" rel="noopener noreferrer"
|
||||||
[routerLink]="[itemPageRoute]" class="lead item-list-title"
|
[routerLink]="[itemPageRoute]" class="lead item-list-title dont-break-out"
|
||||||
[innerHTML]="dsoTitle"></a>
|
[innerHTML]="dsoTitle"></a>
|
||||||
<span *ngIf="linkType == linkTypes.None" class="lead item-list-title"
|
<span *ngIf="linkType == linkTypes.None" class="lead item-list-title dont-break-out"
|
||||||
[innerHTML]="dsoTitle"></span>
|
[innerHTML]="dsoTitle"></span>
|
||||||
<span class="text-muted">
|
<span class="text-muted">
|
||||||
<ds-truncatable-part [id]="dso.id" [minLines]="1">
|
<ds-truncatable-part [id]="dso.id" [minLines]="1">
|
||||||
|
@@ -281,7 +281,7 @@ export class ResourcePoliciesComponent implements OnInit, OnDestroy {
|
|||||||
* Redirect to resource policy creation page
|
* Redirect to resource policy creation page
|
||||||
*/
|
*/
|
||||||
redirectToResourcePolicyCreatePage(): void {
|
redirectToResourcePolicyCreatePage(): void {
|
||||||
this.router.navigate([`../create`], {
|
this.router.navigate([`./create`], {
|
||||||
relativeTo: this.route,
|
relativeTo: this.route,
|
||||||
queryParams: {
|
queryParams: {
|
||||||
policyTargetId: this.resourceUUID,
|
policyTargetId: this.resourceUUID,
|
||||||
@@ -296,7 +296,7 @@ export class ResourcePoliciesComponent implements OnInit, OnDestroy {
|
|||||||
* @param policy The resource policy
|
* @param policy The resource policy
|
||||||
*/
|
*/
|
||||||
redirectToResourcePolicyEditPage(policy: ResourcePolicy): void {
|
redirectToResourcePolicyEditPage(policy: ResourcePolicy): void {
|
||||||
this.router.navigate([`../edit`], {
|
this.router.navigate([`./edit`], {
|
||||||
relativeTo: this.route,
|
relativeTo: this.route,
|
||||||
queryParams: {
|
queryParams: {
|
||||||
policyId: policy.id
|
policyId: policy.id
|
||||||
|
@@ -169,7 +169,7 @@ export class SearchFilterComponent implements OnInit {
|
|||||||
return this.searchService.getFacetValuesFor(this.filter, 1, options).pipe(
|
return this.searchService.getFacetValuesFor(this.filter, 1, options).pipe(
|
||||||
filter((RD) => !RD.isLoading),
|
filter((RD) => !RD.isLoading),
|
||||||
map((valuesRD) => {
|
map((valuesRD) => {
|
||||||
return valuesRD.payload.totalElements > 0;
|
return valuesRD.payload?.totalElements > 0;
|
||||||
}),);
|
}),);
|
||||||
}
|
}
|
||||||
));
|
));
|
||||||
|
@@ -16,11 +16,11 @@
|
|||||||
</ds-viewable-collection>
|
</ds-viewable-collection>
|
||||||
</div>
|
</div>
|
||||||
<ds-loading
|
<ds-loading
|
||||||
*ngIf="hasNoValue(searchResults) || hasNoValue(searchResults.payload) || searchResults.isLoading"
|
*ngIf="!showError() && (hasNoValue(searchResults) || hasNoValue(searchResults.payload) || searchResults.isLoading)"
|
||||||
message="{{'loading.search-results' | translate}}"></ds-loading>
|
message="{{'loading.search-results' | translate}}"></ds-loading>
|
||||||
<ds-error
|
<ds-error
|
||||||
*ngIf="searchResults?.hasFailed && (!searchResults?.errorMessage || searchResults?.statusCode != 400)"
|
*ngIf="showError()"
|
||||||
message="{{'error.search-results' | translate}}"></ds-error>
|
message="{{errorMessageLabel() | translate}}"></ds-error>
|
||||||
<div *ngIf="searchResults?.payload?.page.length == 0 || searchResults?.statusCode == 400">
|
<div *ngIf="searchResults?.payload?.page.length == 0 || searchResults?.statusCode == 400">
|
||||||
{{ 'search.results.no-results' | translate }}
|
{{ 'search.results.no-results' | translate }}
|
||||||
<a [routerLink]="['/search']"
|
<a [routerLink]="['/search']"
|
||||||
|
@@ -45,9 +45,19 @@ describe('SearchResultsComponent', () => {
|
|||||||
expect(fixture.debugElement.query(By.css('a'))).toBeNull();
|
expect(fixture.debugElement.query(By.css('a'))).toBeNull();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should display error message if error is != 400', () => {
|
it('should display error message if error is 500', () => {
|
||||||
(comp as any).searchResults = createFailedRemoteDataObject('Error', 500);
|
(comp as any).searchResults = createFailedRemoteDataObject('Error', 500);
|
||||||
fixture.detectChanges();
|
fixture.detectChanges();
|
||||||
|
expect(comp.showError()).toBeTrue();
|
||||||
|
expect(comp.errorMessageLabel()).toBe('error.search-results');
|
||||||
|
expect(fixture.debugElement.query(By.css('ds-error'))).not.toBeNull();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should display error message if error is 422', () => {
|
||||||
|
(comp as any).searchResults = createFailedRemoteDataObject('Error', 422);
|
||||||
|
fixture.detectChanges();
|
||||||
|
expect(comp.showError()).toBeTrue();
|
||||||
|
expect(comp.errorMessageLabel()).toBe('error.invalid-search-query');
|
||||||
expect(fixture.debugElement.query(By.css('ds-error'))).not.toBeNull();
|
expect(fixture.debugElement.query(By.css('ds-error'))).not.toBeNull();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@@ -78,6 +78,14 @@ export class SearchResultsComponent {
|
|||||||
|
|
||||||
@Output() selectObject: EventEmitter<ListableObject> = new EventEmitter<ListableObject>();
|
@Output() selectObject: EventEmitter<ListableObject> = new EventEmitter<ListableObject>();
|
||||||
|
|
||||||
|
showError(): boolean {
|
||||||
|
return this.searchResults?.hasFailed && (!this.searchResults?.errorMessage || this.searchResults?.statusCode !== 400);
|
||||||
|
}
|
||||||
|
|
||||||
|
errorMessageLabel(): string {
|
||||||
|
return (this.searchResults?.statusCode === 422) ? 'error.invalid-search-query' : 'error.search-results';
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Method to change the given string by surrounding it by quotes if not already present.
|
* Method to change the given string by surrounding it by quotes if not already present.
|
||||||
*/
|
*/
|
||||||
|
@@ -1,5 +1,5 @@
|
|||||||
<div class="clamp-{{background}}-{{lines}} min-{{minLines}} {{type}} {{fixedHeight ? 'fixedHeight' : ''}}">
|
<div class="clamp-{{background}}-{{lines}} min-{{minLines}} {{type}} {{fixedHeight ? 'fixedHeight' : ''}}">
|
||||||
<div class="content">
|
<div class="content dont-break-out">
|
||||||
<ng-content></ng-content>
|
<ng-content></ng-content>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@@ -2182,7 +2182,7 @@
|
|||||||
"item.edit.bitstreams.headers.name": "Name",
|
"item.edit.bitstreams.headers.name": "Name",
|
||||||
|
|
||||||
// "item.edit.bitstreams.notifications.discarded.content": "Your changes were discarded. To reinstate your changes click the 'Undo' button",
|
// "item.edit.bitstreams.notifications.discarded.content": "Your changes were discarded. To reinstate your changes click the 'Undo' button",
|
||||||
"item.edit.bitstreams.notifications.discarded.content": "Ihre Änderungen wurden verworfen. Um Ihre Änderungen wiederherzustellen, klicken Sie auf die Schaltfläche 'Rückgängig',
|
"item.edit.bitstreams.notifications.discarded.content": "Ihre Änderungen wurden verworfen. Um Ihre Änderungen wiederherzustellen, klicken Sie auf die Schaltfläche 'Rückgängig'",
|
||||||
|
|
||||||
// "item.edit.bitstreams.notifications.discarded.title": "Changes discarded",
|
// "item.edit.bitstreams.notifications.discarded.title": "Changes discarded",
|
||||||
"item.edit.bitstreams.notifications.discarded.title": "Änderungen verworfen",
|
"item.edit.bitstreams.notifications.discarded.title": "Änderungen verworfen",
|
||||||
|
@@ -1331,6 +1331,8 @@
|
|||||||
|
|
||||||
"error.search-results": "Error fetching search results",
|
"error.search-results": "Error fetching search results",
|
||||||
|
|
||||||
|
"error.invalid-search-query": "Search query is not valid. Please check <a href=\"https://solr.apache.org/guide/query-syntax-and-parsing.html\" target=\"_blank\">Solr query syntax</a> best practices for further information about this error.",
|
||||||
|
|
||||||
"error.sub-collections": "Error fetching sub-collections",
|
"error.sub-collections": "Error fetching sub-collections",
|
||||||
|
|
||||||
"error.sub-communities": "Error fetching sub-communities",
|
"error.sub-communities": "Error fetching sub-communities",
|
||||||
|
Before Width: | Height: | Size: 3.2 KiB After Width: | Height: | Size: 15 KiB |
@@ -292,11 +292,48 @@ export class DefaultAppConfig implements AppConfig {
|
|||||||
|
|
||||||
{
|
{
|
||||||
// The default dspace theme
|
// The default dspace theme
|
||||||
name: 'dspace'
|
name: 'dspace',
|
||||||
}
|
// Whenever this theme is active, the following tags will be injected into the <head> of the page.
|
||||||
|
// Example use case: set the favicon based on the active theme.
|
||||||
|
headTags: [
|
||||||
|
{
|
||||||
|
// Insert <link rel="icon" href="assets/dspace/images/favicons/favicon.ico" sizes="any"/> into the <head> of the page.
|
||||||
|
tagName: 'link',
|
||||||
|
attributes: {
|
||||||
|
'rel': 'icon',
|
||||||
|
'href': 'assets/dspace/images/favicons/favicon.ico',
|
||||||
|
'sizes': 'any',
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
// Insert <link rel="icon" href="assets/dspace/images/favicons/favicon.svg" type="image/svg+xml"/> into the <head> of the page.
|
||||||
|
tagName: 'link',
|
||||||
|
attributes: {
|
||||||
|
'rel': 'icon',
|
||||||
|
'href': 'assets/dspace/images/favicons/favicon.svg',
|
||||||
|
'type': 'image/svg+xml',
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
// Insert <link rel="apple-touch-icon" href="assets/dspace/images/favicons/apple-touch-icon.png"/> into the <head> of the page.
|
||||||
|
tagName: 'link',
|
||||||
|
attributes: {
|
||||||
|
'rel': 'apple-touch-icon',
|
||||||
|
'href': 'assets/dspace/images/favicons/apple-touch-icon.png',
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
// Insert <link rel="manifest" href="assets/dspace/images/favicons/manifest.webmanifest"/> into the <head> of the page.
|
||||||
|
tagName: 'link',
|
||||||
|
attributes: {
|
||||||
|
'rel': 'manifest',
|
||||||
|
'href': 'assets/dspace/images/favicons/manifest.webmanifest',
|
||||||
|
}
|
||||||
|
},
|
||||||
|
]
|
||||||
|
},
|
||||||
];
|
];
|
||||||
|
// Whether to enable media viewer for image and/or video Bitstreams (i.e. Bitstreams whose MIME type starts with "image" or "video").
|
||||||
// Whether to enable media viewer for image and/or video Bitstreams (i.e. Bitstreams whose MIME type starts with 'image' or 'video').
|
|
||||||
// For images, this enables a gallery viewer where you can zoom or page through images.
|
// For images, this enables a gallery viewer where you can zoom or page through images.
|
||||||
// For videos, this enables embedded video streaming
|
// For videos, this enables embedded video streaming
|
||||||
mediaViewer: MediaViewerConfig = {
|
mediaViewer: MediaViewerConfig = {
|
||||||
|
@@ -12,6 +12,28 @@ export interface NamedThemeConfig extends Config {
|
|||||||
* its ancestor theme(s) will be checked recursively before falling back to the default theme.
|
* its ancestor theme(s) will be checked recursively before falling back to the default theme.
|
||||||
*/
|
*/
|
||||||
extends?: string;
|
extends?: string;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A list of HTML tags that should be added to the HEAD section of the document, whenever this theme is active.
|
||||||
|
*/
|
||||||
|
headTags?: HeadTagConfig[];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Interface that represents a single theme-specific HTML tag in the HEAD section of the page.
|
||||||
|
*/
|
||||||
|
export interface HeadTagConfig extends Config {
|
||||||
|
/**
|
||||||
|
* The name of the HTML tag
|
||||||
|
*/
|
||||||
|
tagName: string;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The attributes on the HTML tag
|
||||||
|
*/
|
||||||
|
attributes?: {
|
||||||
|
[key: string]: string;
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface RegExThemeConfig extends NamedThemeConfig {
|
export interface RegExThemeConfig extends NamedThemeConfig {
|
||||||
|
@@ -6,7 +6,6 @@
|
|||||||
<base href="/">
|
<base href="/">
|
||||||
<title>DSpace</title>
|
<title>DSpace</title>
|
||||||
<meta name="viewport" content="width=device-width,minimum-scale=1">
|
<meta name="viewport" content="width=device-width,minimum-scale=1">
|
||||||
<link rel="icon" type="image/x-icon" href="assets/images/favicon.ico" />
|
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
<body>
|
<body>
|
||||||
|
@@ -6,7 +6,6 @@
|
|||||||
<base href="/">
|
<base href="/">
|
||||||
<title>DSpace</title>
|
<title>DSpace</title>
|
||||||
<meta name="viewport" content="width=device-width,minimum-scale=1">
|
<meta name="viewport" content="width=device-width,minimum-scale=1">
|
||||||
<link rel="icon" type="image/x-icon" href="assets/images/favicon.ico" />
|
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
<body>
|
<body>
|
||||||
|
@@ -79,6 +79,7 @@
|
|||||||
--ds-breadcrumb-bg: #{$gray-200} !important;
|
--ds-breadcrumb-bg: #{$gray-200} !important;
|
||||||
--ds-breadcrumb-link-color: #{$cyan};
|
--ds-breadcrumb-link-color: #{$cyan};
|
||||||
--ds-breadcrumb-link-active-color: #{darken($cyan, 30%)};
|
--ds-breadcrumb-link-active-color: #{darken($cyan, 30%)};
|
||||||
|
--ds-breadcrumb-max-length: 200px;
|
||||||
|
|
||||||
--ds-slider-color: #{$green};
|
--ds-slider-color: #{$green};
|
||||||
--ds-slider-handle-width: 18px;
|
--ds-slider-handle-width: 18px;
|
||||||
|
@@ -74,3 +74,21 @@ ngb-modal-backdrop {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.dont-break-out {
|
||||||
|
/* These are technically the same, but use both */
|
||||||
|
overflow-wrap: break-word;
|
||||||
|
word-wrap: break-word;
|
||||||
|
|
||||||
|
-ms-word-break: break-all;
|
||||||
|
/* This is the dangerous one in WebKit, as it breaks things wherever */
|
||||||
|
word-break: break-all;
|
||||||
|
/* Instead use this non-standard one: */
|
||||||
|
word-break: break-word;
|
||||||
|
|
||||||
|
/* Adds a hyphen where the word breaks, if supported (No Blink) */
|
||||||
|
-ms-hyphens: auto;
|
||||||
|
-moz-hyphens: auto;
|
||||||
|
-webkit-hyphens: auto;
|
||||||
|
hyphens: auto;
|
||||||
|
|
||||||
|
}
|
||||||
|
After Width: | Height: | Size: 12 KiB |
After Width: | Height: | Size: 33 KiB |
BIN
src/themes/dspace/assets/images/favicons/apple-touch-icon.png
Normal file
After Width: | Height: | Size: 2.3 KiB |
BIN
src/themes/dspace/assets/images/favicons/favicon.ico
Normal file
After Width: | Height: | Size: 15 KiB |
7
src/themes/dspace/assets/images/favicons/favicon.svg
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||||
|
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||||
|
<svg width="100%" height="100%" viewBox="0 0 98 98" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xml:space="preserve" xmlns:serif="http://www.serif.com/" style="fill-rule:evenodd;clip-rule:evenodd;stroke-linejoin:round;stroke-miterlimit:2;">
|
||||||
|
<g transform="matrix(1,0,0,1,0.019,2.867)">
|
||||||
|
<path d="M53.561,58.569L53.67,58.563L53.786,58.553L53.892,58.54L54.002,58.53L54.112,58.507L54.221,58.488L54.327,58.465L54.433,58.436L54.538,58.413L54.644,58.38L54.747,58.346L54.844,58.311L54.948,58.271L55.049,58.229L55.149,58.187L55.149,58.186L55.245,58.141L55.34,58.097L55.437,58.048L55.528,57.992L55.528,57.991L55.622,57.941L55.622,57.939L55.712,57.883L55.712,57.882L55.805,57.822L55.888,57.766L55.888,57.765L55.973,57.702L56.061,57.637L56.061,57.635L56.148,57.573L56.148,57.572C56.964,56.889 57.541,55.926 57.709,54.834L57.722,54.73L57.722,54.72L57.736,54.619L57.736,54.608L57.745,54.51L57.745,54.498L57.754,54.397L57.754,54.38L57.759,54.285L57.759,54.269L57.761,54.164L57.761,37.704L57.759,37.599L57.759,37.583L57.754,37.488L57.754,37.475L57.745,37.374L57.745,37.362L57.736,37.264L57.736,37.253L57.722,37.153L57.722,37.143L57.709,37.039C57.542,35.947 56.965,34.982 56.148,34.301L56.148,34.3L56.061,34.237L56.061,34.235L55.973,34.17L55.888,34.107L55.888,34.106L55.805,34.05L55.712,33.989L55.622,33.933L55.622,33.93L55.528,33.88L55.528,33.879L55.437,33.823L55.34,33.774L55.245,33.731L55.245,33.73L55.149,33.685L55.149,33.684L55.049,33.641L54.948,33.599L54.844,33.559L54.747,33.524L54.644,33.493L54.538,33.457L54.433,33.434L54.327,33.406L54.221,33.382L54.112,33.363L54.002,33.34L53.892,33.331L53.786,33.317L53.67,33.307L53.561,33.301L53.447,33.296L45.557,33.296C35.841,33.296 29.699,25.458 29.699,16.146L29.699,6.92C29.699,3.108 26.597,0.005 22.785,0.005L6.92,0.005C3.107,0.005 -0,3.111 -0,6.92L-0,23.602C-0,27.408 3.104,30.511 6.92,30.511L15.334,30.511C24.503,30.511 32.24,36.461 32.48,45.914L32.48,45.954C32.24,55.407 24.502,61.356 15.334,61.356L6.92,61.356C3.105,61.356 -0,64.459 -0,68.265L-0,84.947C-0,88.757 3.106,91.862 6.92,91.862L22.785,91.862C26.597,91.862 29.699,88.758 29.699,84.947L29.699,75.724C29.699,66.412 35.843,58.575 45.557,58.575L53.447,58.575L53.561,58.569ZM87.607,9.956C81.466,3.814 72.985,0 63.651,0L48.627,0L48.627,17.424L63.651,17.424C68.177,17.424 72.298,19.282 75.291,22.273C78.281,25.263 80.14,29.385 80.14,33.912L80.14,57.954C80.14,62.492 78.287,66.619 75.308,69.609L75.291,69.593C72.3,72.584 68.178,74.442 63.651,74.442L48.627,74.442L48.627,91.866L63.651,91.866C72.984,91.866 81.465,88.052 87.607,81.91L87.607,81.877C93.749,75.734 97.562,67.263 97.562,57.954L97.562,33.912C97.562,24.578 93.749,16.097 87.607,9.956Z" style="fill:rgb(146,198,66);fill-rule:nonzero;"/>
|
||||||
|
</g>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 2.8 KiB |
@@ -0,0 +1,19 @@
|
|||||||
|
{
|
||||||
|
"name": "DSpace",
|
||||||
|
"short_name": "DSpace",
|
||||||
|
"icons": [
|
||||||
|
{
|
||||||
|
"src": "android-chrome-192x192.png",
|
||||||
|
"sizes": "192x192",
|
||||||
|
"type": "image/png"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"src": "android-chrome-512x512.png",
|
||||||
|
"sizes": "512x512",
|
||||||
|
"type": "image/png"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"theme_color": "#091119",
|
||||||
|
"background_color": "#091119",
|
||||||
|
"display": "standalone"
|
||||||
|
}
|
10
yarn.lock
@@ -9111,12 +9111,12 @@ mirador-dl-plugin@^0.13.0:
|
|||||||
resolved "https://registry.yarnpkg.com/mirador-dl-plugin/-/mirador-dl-plugin-0.13.0.tgz#9a6cb0fa3c566a2a1ebe1ad9caa1ff590ff22689"
|
resolved "https://registry.yarnpkg.com/mirador-dl-plugin/-/mirador-dl-plugin-0.13.0.tgz#9a6cb0fa3c566a2a1ebe1ad9caa1ff590ff22689"
|
||||||
integrity sha512-I/6etIvpTtO1zgjxx2uEUFoyB9NxQ43JWg8CMkKmZqblW7AAeFqRn1/zUlQH7N8KFZft9Rah6D8qxtuNAo9jmA==
|
integrity sha512-I/6etIvpTtO1zgjxx2uEUFoyB9NxQ43JWg8CMkKmZqblW7AAeFqRn1/zUlQH7N8KFZft9Rah6D8qxtuNAo9jmA==
|
||||||
|
|
||||||
mirador-share-plugin@^0.10.0:
|
mirador-share-plugin@^0.11.0:
|
||||||
version "0.10.0"
|
version "0.11.0"
|
||||||
resolved "https://registry.yarnpkg.com/mirador-share-plugin/-/mirador-share-plugin-0.10.0.tgz#82cde27faedc440fab648db137e62849d6542420"
|
resolved "https://registry.yarnpkg.com/mirador-share-plugin/-/mirador-share-plugin-0.11.0.tgz#13e2f654e38839044382acad42d9329e91a8cd5e"
|
||||||
integrity sha512-hC9hG0H04WAR6JNfLDnQICtxwWV3K+cmqnArtOvAIGGnbgXWs5tmQyfdY55z05jzbeL40rd7z1K094hHV3R4WQ==
|
integrity sha512-fHcdDXyrtfy5pn1zdQNX9BvE5Tjup66eQwyNippE5PMaP8ImUcrFaSL+mStdn+v6agsHcsdRqLhseZ0XWgEuAw==
|
||||||
|
|
||||||
mirador@^3.0.0:
|
mirador@^3.3.0:
|
||||||
version "3.3.0"
|
version "3.3.0"
|
||||||
resolved "https://registry.yarnpkg.com/mirador/-/mirador-3.3.0.tgz#7a957a1db1a5388b2b8cafab00db4eb9f97557b9"
|
resolved "https://registry.yarnpkg.com/mirador/-/mirador-3.3.0.tgz#7a957a1db1a5388b2b8cafab00db4eb9f97557b9"
|
||||||
integrity sha512-BmGfRnWJ45B+vtiAwcFT7n9nKialfejE9UvuUK0NorO37ShArpsKr3yVSD4jQASwSR4DRRpPEG21jOk4WN7H3w==
|
integrity sha512-BmGfRnWJ45B+vtiAwcFT7n9nKialfejE9UvuUK0NorO37ShArpsKr3yVSD4jQASwSR4DRRpPEG21jOk4WN7H3w==
|
||||||
|