mirror of
https://github.com/DSpace/dspace-angular.git
synced 2025-10-17 06:53:03 +00:00
Merge branch 'main' into w2p-101127_browse-by-controlled-vocabulary-7.6.0-next
Conflicts: src/assets/i18n/en.json5
This commit is contained in:
@@ -413,8 +413,7 @@ dspace-angular
|
|||||||
│ ├── merge-i18n-files.ts *
|
│ ├── merge-i18n-files.ts *
|
||||||
│ ├── serve.ts *
|
│ ├── serve.ts *
|
||||||
│ ├── sync-i18n-files.ts *
|
│ ├── sync-i18n-files.ts *
|
||||||
│ ├── test-rest.ts *
|
│ └── test-rest.ts *
|
||||||
│ └── webpack.js *
|
|
||||||
├── src * The source of the application
|
├── src * The source of the application
|
||||||
│ ├── app * The source code of the application, subdivided by module/page.
|
│ ├── app * The source code of the application, subdivided by module/page.
|
||||||
│ ├── assets * Folder for static resources
|
│ ├── assets * Folder for static resources
|
||||||
|
@@ -1,13 +0,0 @@
|
|||||||
const path = require('path');
|
|
||||||
const child_process = require('child_process');
|
|
||||||
|
|
||||||
const heapSize = 4096;
|
|
||||||
const webpackPath = path.join('node_modules', 'webpack', 'bin', 'webpack.js');
|
|
||||||
|
|
||||||
const params = [
|
|
||||||
'--max_old_space_size=' + heapSize,
|
|
||||||
webpackPath,
|
|
||||||
...process.argv.slice(2)
|
|
||||||
];
|
|
||||||
|
|
||||||
child_process.spawn('node', params, { stdio:'inherit' });
|
|
@@ -1,6 +1,5 @@
|
|||||||
import { NgModule } from '@angular/core';
|
import { NgModule } from '@angular/core';
|
||||||
import { RouterModule } from '@angular/router';
|
import { RouterModule } from '@angular/router';
|
||||||
import { EditBitstreamPageComponent } from './edit-bitstream-page/edit-bitstream-page.component';
|
|
||||||
import { AuthenticatedGuard } from '../core/auth/authenticated.guard';
|
import { AuthenticatedGuard } from '../core/auth/authenticated.guard';
|
||||||
import { BitstreamPageResolver } from './bitstream-page.resolver';
|
import { BitstreamPageResolver } from './bitstream-page.resolver';
|
||||||
import { BitstreamDownloadPageComponent } from './bitstream-download-page/bitstream-download-page.component';
|
import { BitstreamDownloadPageComponent } from './bitstream-download-page/bitstream-download-page.component';
|
||||||
@@ -13,6 +12,7 @@ import { LegacyBitstreamUrlResolver } from './legacy-bitstream-url.resolver';
|
|||||||
import { BitstreamBreadcrumbResolver } from '../core/breadcrumbs/bitstream-breadcrumb.resolver';
|
import { BitstreamBreadcrumbResolver } from '../core/breadcrumbs/bitstream-breadcrumb.resolver';
|
||||||
import { BitstreamBreadcrumbsService } from '../core/breadcrumbs/bitstream-breadcrumbs.service';
|
import { BitstreamBreadcrumbsService } from '../core/breadcrumbs/bitstream-breadcrumbs.service';
|
||||||
import { I18nBreadcrumbResolver } from '../core/breadcrumbs/i18n-breadcrumb.resolver';
|
import { I18nBreadcrumbResolver } from '../core/breadcrumbs/i18n-breadcrumb.resolver';
|
||||||
|
import { ThemedEditBitstreamPageComponent } from './edit-bitstream-page/themed-edit-bitstream-page.component';
|
||||||
|
|
||||||
const EDIT_BITSTREAM_PATH = ':id/edit';
|
const EDIT_BITSTREAM_PATH = ':id/edit';
|
||||||
const EDIT_BITSTREAM_AUTHORIZATIONS_PATH = ':id/authorizations';
|
const EDIT_BITSTREAM_AUTHORIZATIONS_PATH = ':id/authorizations';
|
||||||
@@ -49,7 +49,7 @@ const EDIT_BITSTREAM_AUTHORIZATIONS_PATH = ':id/authorizations';
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: EDIT_BITSTREAM_PATH,
|
path: EDIT_BITSTREAM_PATH,
|
||||||
component: EditBitstreamPageComponent,
|
component: ThemedEditBitstreamPageComponent,
|
||||||
resolve: {
|
resolve: {
|
||||||
bitstream: BitstreamPageResolver,
|
bitstream: BitstreamPageResolver,
|
||||||
breadcrumb: BitstreamBreadcrumbResolver,
|
breadcrumb: BitstreamBreadcrumbResolver,
|
||||||
|
@@ -7,6 +7,7 @@ import { BitstreamAuthorizationsComponent } from './bitstream-authorizations/bit
|
|||||||
import { FormModule } from '../shared/form/form.module';
|
import { FormModule } from '../shared/form/form.module';
|
||||||
import { ResourcePoliciesModule } from '../shared/resource-policies/resource-policies.module';
|
import { ResourcePoliciesModule } from '../shared/resource-policies/resource-policies.module';
|
||||||
import { BitstreamDownloadPageComponent } from './bitstream-download-page/bitstream-download-page.component';
|
import { BitstreamDownloadPageComponent } from './bitstream-download-page/bitstream-download-page.component';
|
||||||
|
import { ThemedEditBitstreamPageComponent } from './edit-bitstream-page/themed-edit-bitstream-page.component';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This module handles all components that are necessary for Bitstream related pages
|
* This module handles all components that are necessary for Bitstream related pages
|
||||||
@@ -22,6 +23,7 @@ import { BitstreamDownloadPageComponent } from './bitstream-download-page/bitstr
|
|||||||
declarations: [
|
declarations: [
|
||||||
BitstreamAuthorizationsComponent,
|
BitstreamAuthorizationsComponent,
|
||||||
EditBitstreamPageComponent,
|
EditBitstreamPageComponent,
|
||||||
|
ThemedEditBitstreamPageComponent,
|
||||||
BitstreamDownloadPageComponent,
|
BitstreamDownloadPageComponent,
|
||||||
]
|
]
|
||||||
})
|
})
|
||||||
|
@@ -0,0 +1,22 @@
|
|||||||
|
import { Component } from '@angular/core';
|
||||||
|
import { EditBitstreamPageComponent } from './edit-bitstream-page.component';
|
||||||
|
import { ThemedComponent } from '../../shared/theme-support/themed.component';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'ds-themed-edit-bitstream-page',
|
||||||
|
styleUrls: [],
|
||||||
|
templateUrl: '../../shared/theme-support/themed.component.html',
|
||||||
|
})
|
||||||
|
export class ThemedEditBitstreamPageComponent extends ThemedComponent<EditBitstreamPageComponent> {
|
||||||
|
protected getComponentName(): string {
|
||||||
|
return 'EditBitstreamPageComponent';
|
||||||
|
}
|
||||||
|
|
||||||
|
protected importThemedComponent(themeName: string): Promise<any> {
|
||||||
|
return import(`../../../themes/${themeName}/app/bitstream-page/edit-bitstream-page/edit-bitstream-page.component`);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected importUnthemedComponent(): Promise<any> {
|
||||||
|
return import('./edit-bitstream-page.component');
|
||||||
|
}
|
||||||
|
}
|
@@ -333,8 +333,9 @@ export class SearchService implements OnDestroy {
|
|||||||
* Send search event to rest api using angularitics
|
* Send search event to rest api using angularitics
|
||||||
* @param config Paginated search options used
|
* @param config Paginated search options used
|
||||||
* @param searchQueryResponse The response objects of the performed search
|
* @param searchQueryResponse The response objects of the performed search
|
||||||
|
* @param clickedObject Optional UUID of an object a search was performed and clicked for
|
||||||
*/
|
*/
|
||||||
trackSearch(config: PaginatedSearchOptions, searchQueryResponse: SearchObjects<DSpaceObject>) {
|
trackSearch(config: PaginatedSearchOptions, searchQueryResponse: SearchObjects<DSpaceObject>, clickedObject?: string) {
|
||||||
const filters: { filter: string, operator: string, value: string, label: string; }[] = [];
|
const filters: { filter: string, operator: string, value: string, label: string; }[] = [];
|
||||||
const appliedFilters = searchQueryResponse.appliedFilters || [];
|
const appliedFilters = searchQueryResponse.appliedFilters || [];
|
||||||
for (let i = 0, filtersLength = appliedFilters.length; i < filtersLength; i++) {
|
for (let i = 0, filtersLength = appliedFilters.length; i < filtersLength; i++) {
|
||||||
@@ -356,6 +357,7 @@ export class SearchService implements OnDestroy {
|
|||||||
order: config.sort.direction
|
order: config.sort.direction
|
||||||
},
|
},
|
||||||
filters: filters,
|
filters: filters,
|
||||||
|
clickedObject,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@@ -39,6 +39,8 @@ import { IdentifierDataComponent } from '../../shared/object-list/identifier-dat
|
|||||||
import { ItemRegisterDoiComponent } from './item-register-doi/item-register-doi.component';
|
import { ItemRegisterDoiComponent } from './item-register-doi/item-register-doi.component';
|
||||||
import { DsoSharedModule } from '../../dso-shared/dso-shared.module';
|
import { DsoSharedModule } from '../../dso-shared/dso-shared.module';
|
||||||
import { ItemCurateComponent } from './item-curate/item-curate.component';
|
import { ItemCurateComponent } from './item-curate/item-curate.component';
|
||||||
|
import { ThemedItemStatusComponent } from './item-status/themed-item-status.component';
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Module that contains all components related to the Edit Item page administrator functionality
|
* Module that contains all components related to the Edit Item page administrator functionality
|
||||||
@@ -67,6 +69,7 @@ import { ItemCurateComponent } from './item-curate/item-curate.component';
|
|||||||
ItemPublicComponent,
|
ItemPublicComponent,
|
||||||
ItemDeleteComponent,
|
ItemDeleteComponent,
|
||||||
ItemStatusComponent,
|
ItemStatusComponent,
|
||||||
|
ThemedItemStatusComponent,
|
||||||
ItemRelationshipsComponent,
|
ItemRelationshipsComponent,
|
||||||
ItemBitstreamsComponent,
|
ItemBitstreamsComponent,
|
||||||
ItemVersionHistoryComponent,
|
ItemVersionHistoryComponent,
|
||||||
@@ -89,6 +92,9 @@ import { ItemCurateComponent } from './item-curate/item-curate.component';
|
|||||||
IdentifierDataService,
|
IdentifierDataService,
|
||||||
ObjectValuesPipe
|
ObjectValuesPipe
|
||||||
],
|
],
|
||||||
|
exports: [
|
||||||
|
ItemOperationComponent,
|
||||||
|
]
|
||||||
})
|
})
|
||||||
export class EditItemPageModule {
|
export class EditItemPageModule {
|
||||||
|
|
||||||
|
@@ -6,7 +6,6 @@ import { ItemReinstateComponent } from './item-reinstate/item-reinstate.componen
|
|||||||
import { ItemPrivateComponent } from './item-private/item-private.component';
|
import { ItemPrivateComponent } from './item-private/item-private.component';
|
||||||
import { ItemPublicComponent } from './item-public/item-public.component';
|
import { ItemPublicComponent } from './item-public/item-public.component';
|
||||||
import { ItemDeleteComponent } from './item-delete/item-delete.component';
|
import { ItemDeleteComponent } from './item-delete/item-delete.component';
|
||||||
import { ItemStatusComponent } from './item-status/item-status.component';
|
|
||||||
import { ItemBitstreamsComponent } from './item-bitstreams/item-bitstreams.component';
|
import { ItemBitstreamsComponent } from './item-bitstreams/item-bitstreams.component';
|
||||||
import { ItemCollectionMapperComponent } from './item-collection-mapper/item-collection-mapper.component';
|
import { ItemCollectionMapperComponent } from './item-collection-mapper/item-collection-mapper.component';
|
||||||
import { ItemMoveComponent } from './item-move/item-move.component';
|
import { ItemMoveComponent } from './item-move/item-move.component';
|
||||||
@@ -42,6 +41,7 @@ import { ItemPageCollectionMapperGuard } from './item-page-collection-mapper.gua
|
|||||||
import { ThemedDsoEditMetadataComponent } from '../../dso-shared/dso-edit-metadata/themed-dso-edit-metadata.component';
|
import { ThemedDsoEditMetadataComponent } from '../../dso-shared/dso-edit-metadata/themed-dso-edit-metadata.component';
|
||||||
import { ItemPageRegisterDoiGuard } from './item-page-register-doi.guard';
|
import { ItemPageRegisterDoiGuard } from './item-page-register-doi.guard';
|
||||||
import { ItemCurateComponent } from './item-curate/item-curate.component';
|
import { ItemCurateComponent } from './item-curate/item-curate.component';
|
||||||
|
import { ThemedItemStatusComponent } from './item-status/themed-item-status.component';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Routing module that handles the routing for the Edit Item page administrator functionality
|
* Routing module that handles the routing for the Edit Item page administrator functionality
|
||||||
@@ -67,7 +67,7 @@ import { ItemCurateComponent } from './item-curate/item-curate.component';
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: 'status',
|
path: 'status',
|
||||||
component: ItemStatusComponent,
|
component: ThemedItemStatusComponent,
|
||||||
data: { title: 'item.edit.tabs.status.title', showBreadcrumbs: true },
|
data: { title: 'item.edit.tabs.status.title', showBreadcrumbs: true },
|
||||||
canActivate: [ItemPageStatusGuard]
|
canActivate: [ItemPageStatusGuard]
|
||||||
},
|
},
|
||||||
|
@@ -9,9 +9,9 @@ import { RemoteData } from '../../../core/data/remote-data';
|
|||||||
import { getItemEditRoute, getItemPageRoute } from '../../item-page-routing-paths';
|
import { getItemEditRoute, getItemPageRoute } from '../../item-page-routing-paths';
|
||||||
import { AuthorizationDataService } from '../../../core/data/feature-authorization/authorization-data.service';
|
import { AuthorizationDataService } from '../../../core/data/feature-authorization/authorization-data.service';
|
||||||
import { FeatureID } from '../../../core/data/feature-authorization/feature-id';
|
import { FeatureID } from '../../../core/data/feature-authorization/feature-id';
|
||||||
import { hasValue } from '../../../shared/empty.util';
|
import { hasValue, isNotEmpty } from '../../../shared/empty.util';
|
||||||
import {
|
import {
|
||||||
getAllSucceededRemoteDataPayload, getFirstSucceededRemoteData, getRemoteDataPayload,
|
getAllSucceededRemoteDataPayload, getFirstCompletedRemoteData, getFirstSucceededRemoteData, getRemoteDataPayload,
|
||||||
} from '../../../core/shared/operators';
|
} from '../../../core/shared/operators';
|
||||||
import { IdentifierDataService } from '../../../core/data/identifier-data.service';
|
import { IdentifierDataService } from '../../../core/data/identifier-data.service';
|
||||||
import { Identifier } from '../../../shared/object-list/identifier-data/identifier.model';
|
import { Identifier } from '../../../shared/object-list/identifier-data/identifier.model';
|
||||||
@@ -105,12 +105,13 @@ export class ItemStatusComponent implements OnInit {
|
|||||||
|
|
||||||
// Observable for configuration determining whether the Register DOI feature is enabled
|
// Observable for configuration determining whether the Register DOI feature is enabled
|
||||||
let registerConfigEnabled$: Observable<boolean> = this.configurationService.findByPropertyName('identifiers.item-status.register-doi').pipe(
|
let registerConfigEnabled$: Observable<boolean> = this.configurationService.findByPropertyName('identifiers.item-status.register-doi').pipe(
|
||||||
getFirstSucceededRemoteData(),
|
getFirstCompletedRemoteData(),
|
||||||
getRemoteDataPayload(),
|
map((rd: RemoteData<ConfigurationProperty>) => {
|
||||||
map((enabled: ConfigurationProperty) => {
|
// If the config property is exposed via rest and has a value set, return it
|
||||||
if (enabled !== undefined && enabled.values) {
|
if (rd.hasSucceeded && hasValue(rd.payload) && isNotEmpty(rd.payload.values)) {
|
||||||
return true;
|
return rd.payload.values[0] === 'true';
|
||||||
}
|
}
|
||||||
|
// Otherwise, return false
|
||||||
return false;
|
return false;
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
|
@@ -0,0 +1,23 @@
|
|||||||
|
import { Component } from '@angular/core';
|
||||||
|
import { ThemedComponent } from '../../../shared/theme-support/themed.component';
|
||||||
|
import { ItemStatusComponent } from './item-status.component';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'ds-themed-item-status',
|
||||||
|
styleUrls: [],
|
||||||
|
templateUrl: '../../../shared/theme-support/themed.component.html',
|
||||||
|
})
|
||||||
|
export class ThemedItemStatusComponent extends ThemedComponent<ItemStatusComponent> {
|
||||||
|
protected getComponentName(): string {
|
||||||
|
return 'ItemStatusComponent';
|
||||||
|
}
|
||||||
|
|
||||||
|
protected importThemedComponent(themeName: string): Promise<any> {
|
||||||
|
return import(`../../../../themes/${themeName}/app/item-page/edit-item-page/item-status/item-status.component`);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected importUnthemedComponent(): Promise<any> {
|
||||||
|
return import('./item-status.component');
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@@ -4,7 +4,6 @@ import { CoreModule } from '../core/core.module';
|
|||||||
import { SharedModule } from '../shared/shared.module';
|
import { SharedModule } from '../shared/shared.module';
|
||||||
import { SidebarService } from '../shared/sidebar/sidebar.service';
|
import { SidebarService } from '../shared/sidebar/sidebar.service';
|
||||||
import { ConfigurationSearchPageGuard } from './configuration-search-page.guard';
|
import { ConfigurationSearchPageGuard } from './configuration-search-page.guard';
|
||||||
import { SearchTrackerComponent } from './search-tracker.component';
|
|
||||||
import { StatisticsModule } from '../statistics/statistics.module';
|
import { StatisticsModule } from '../statistics/statistics.module';
|
||||||
import { SearchPageComponent } from './search-page.component';
|
import { SearchPageComponent } from './search-page.component';
|
||||||
import { SearchFilterService } from '../core/shared/search/search-filter.service';
|
import { SearchFilterService } from '../core/shared/search/search-filter.service';
|
||||||
@@ -16,7 +15,6 @@ import { SearchModule } from '../shared/search/search.module';
|
|||||||
|
|
||||||
const components = [
|
const components = [
|
||||||
SearchPageComponent,
|
SearchPageComponent,
|
||||||
SearchTrackerComponent,
|
|
||||||
ThemedSearchPageComponent
|
ThemedSearchPageComponent
|
||||||
];
|
];
|
||||||
|
|
||||||
|
@@ -1 +0,0 @@
|
|||||||
|
|
@@ -1,3 +0,0 @@
|
|||||||
:host {
|
|
||||||
display: none
|
|
||||||
}
|
|
@@ -1,83 +0,0 @@
|
|||||||
import { Component, Inject, OnInit } from '@angular/core';
|
|
||||||
import { Angulartics2 } from 'angulartics2';
|
|
||||||
import { map, switchMap } from 'rxjs/operators';
|
|
||||||
import { SearchComponent } from '../shared/search/search.component';
|
|
||||||
import { SidebarService } from '../shared/sidebar/sidebar.service';
|
|
||||||
import { HostWindowService } from '../shared/host-window.service';
|
|
||||||
import { SEARCH_CONFIG_SERVICE } from '../my-dspace-page/my-dspace-page.component';
|
|
||||||
import { RouteService } from '../core/services/route.service';
|
|
||||||
import { SearchConfigurationService } from '../core/shared/search/search-configuration.service';
|
|
||||||
import { SearchService } from '../core/shared/search/search.service';
|
|
||||||
import { PaginatedSearchOptions } from '../shared/search/models/paginated-search-options.model';
|
|
||||||
import { SearchObjects } from '../shared/search/models/search-objects.model';
|
|
||||||
import { Router } from '@angular/router';
|
|
||||||
import { RemoteData } from '../core/data/remote-data';
|
|
||||||
import { DSpaceObject } from '../core/shared/dspace-object.model';
|
|
||||||
import { getFirstSucceededRemoteData } from '../core/shared/operators';
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This component triggers a page view statistic
|
|
||||||
*/
|
|
||||||
@Component({
|
|
||||||
selector: 'ds-search-tracker',
|
|
||||||
styleUrls: ['./search-tracker.component.scss'],
|
|
||||||
templateUrl: './search-tracker.component.html',
|
|
||||||
providers: [
|
|
||||||
{
|
|
||||||
provide: SEARCH_CONFIG_SERVICE,
|
|
||||||
useClass: SearchConfigurationService
|
|
||||||
}
|
|
||||||
]
|
|
||||||
})
|
|
||||||
export class SearchTrackerComponent extends SearchComponent implements OnInit {
|
|
||||||
|
|
||||||
constructor(
|
|
||||||
protected service: SearchService,
|
|
||||||
protected sidebarService: SidebarService,
|
|
||||||
protected windowService: HostWindowService,
|
|
||||||
@Inject(SEARCH_CONFIG_SERVICE) public searchConfigService: SearchConfigurationService,
|
|
||||||
protected routeService: RouteService,
|
|
||||||
public angulartics2: Angulartics2,
|
|
||||||
protected router: Router
|
|
||||||
) {
|
|
||||||
super(service, sidebarService, windowService, searchConfigService, routeService, router);
|
|
||||||
}
|
|
||||||
|
|
||||||
ngOnInit(): void {
|
|
||||||
// super.ngOnInit();
|
|
||||||
this.getSearchOptions().pipe(
|
|
||||||
switchMap((options: PaginatedSearchOptions) =>
|
|
||||||
this.service.searchEntries(options).pipe(
|
|
||||||
getFirstSucceededRemoteData(),
|
|
||||||
map((rd: RemoteData<SearchObjects<DSpaceObject>>) => ({
|
|
||||||
config: options,
|
|
||||||
searchQueryResponse: rd.payload
|
|
||||||
}))
|
|
||||||
)),
|
|
||||||
).subscribe(({ config, searchQueryResponse }) => {
|
|
||||||
const filters: { filter: string, operator: string, value: string, label: string; }[] = [];
|
|
||||||
const appliedFilters = searchQueryResponse.appliedFilters || [];
|
|
||||||
for (let i = 0, filtersLength = appliedFilters.length; i < filtersLength; i++) {
|
|
||||||
const appliedFilter = appliedFilters[i];
|
|
||||||
filters.push(appliedFilter);
|
|
||||||
}
|
|
||||||
this.angulartics2.eventTrack.next({
|
|
||||||
action: 'search',
|
|
||||||
properties: {
|
|
||||||
searchOptions: config,
|
|
||||||
page: {
|
|
||||||
size: config.pagination.size, // same as searchQueryResponse.page.elementsPerPage
|
|
||||||
totalElements: searchQueryResponse.pageInfo.totalElements,
|
|
||||||
totalPages: searchQueryResponse.pageInfo.totalPages,
|
|
||||||
number: config.pagination.currentPage, // same as searchQueryResponse.page.currentPage
|
|
||||||
},
|
|
||||||
sort: {
|
|
||||||
by: config.sort.field,
|
|
||||||
order: config.sort.direction
|
|
||||||
},
|
|
||||||
filters: filters,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
@@ -2,7 +2,7 @@
|
|||||||
<div class="preserve-line-breaks ds-context-help-content">
|
<div class="preserve-line-breaks ds-context-help-content">
|
||||||
<ng-container *ngFor="let elem of (parsedContent$ | async)">
|
<ng-container *ngFor="let elem of (parsedContent$ | async)">
|
||||||
<ng-container *ngIf="elem.href">
|
<ng-container *ngIf="elem.href">
|
||||||
<a href="{{elem.href}}" target="_blank">{{elem.text}}</a>
|
<a href="{{elem.href}}" target="_blank" rel="noopener noreferrer">{{elem.text}}</a>
|
||||||
</ng-container>
|
</ng-container>
|
||||||
<ng-container *ngIf="elem.href === undefined">
|
<ng-container *ngIf="elem.href === undefined">
|
||||||
{{ elem }}
|
{{ elem }}
|
||||||
|
@@ -567,4 +567,41 @@ describe('MenuService', () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe(`resolveSubstitutions`, () => {
|
||||||
|
let linkPrefix;
|
||||||
|
let link;
|
||||||
|
let uuid;
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
linkPrefix = 'statistics_collection_';
|
||||||
|
link = `${linkPrefix}:id`;
|
||||||
|
uuid = 'f7cc3ca4-3c2c-464d-8af8-add9f84f711c';
|
||||||
|
});
|
||||||
|
|
||||||
|
it(`shouldn't do anything when there are no params`, () => {
|
||||||
|
let result = (service as any).resolveSubstitutions(link, undefined);
|
||||||
|
expect(result).toEqual(link);
|
||||||
|
result = (service as any).resolveSubstitutions(link, null);
|
||||||
|
expect(result).toEqual(link);
|
||||||
|
result = (service as any).resolveSubstitutions(link, {});
|
||||||
|
expect(result).toEqual(link);
|
||||||
|
});
|
||||||
|
|
||||||
|
it(`should replace link params that are also route params`, () => {
|
||||||
|
const result = (service as any).resolveSubstitutions(link,{ 'id': uuid });
|
||||||
|
expect(result).toEqual(linkPrefix + uuid);
|
||||||
|
});
|
||||||
|
|
||||||
|
it(`should not replace link params that aren't route params`, () => {
|
||||||
|
const result = (service as any).resolveSubstitutions(link,{ 'something': 'else' });
|
||||||
|
expect(result).toEqual(link);
|
||||||
|
});
|
||||||
|
|
||||||
|
it(`should gracefully deal with routes that contain the name of the route param`, () => {
|
||||||
|
const selfReferentialParam = `:id:something`;
|
||||||
|
const result = (service as any).resolveSubstitutions(link,{ 'id': selfReferentialParam });
|
||||||
|
expect(result).toEqual(linkPrefix + selfReferentialParam);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
});
|
});
|
||||||
|
@@ -17,7 +17,7 @@ import {
|
|||||||
ToggleActiveMenuSectionAction,
|
ToggleActiveMenuSectionAction,
|
||||||
ToggleMenuAction,
|
ToggleMenuAction,
|
||||||
} from './menu.actions';
|
} from './menu.actions';
|
||||||
import { hasNoValue, hasValue, hasValueOperator, isNotEmpty } from '../empty.util';
|
import { hasNoValue, hasValue, hasValueOperator, isNotEmpty, isEmpty } from '../empty.util';
|
||||||
import { MenuState } from './menu-state.model';
|
import { MenuState } from './menu-state.model';
|
||||||
import { MenuSections } from './menu-sections.model';
|
import { MenuSections } from './menu-sections.model';
|
||||||
import { MenuSection } from './menu-section.model';
|
import { MenuSection } from './menu-section.model';
|
||||||
@@ -409,20 +409,14 @@ export class MenuService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
protected resolveSubstitutions(object, params) {
|
protected resolveSubstitutions(object, params) {
|
||||||
|
|
||||||
let resolved;
|
let resolved;
|
||||||
if (typeof object === 'string') {
|
if (isEmpty(params)) {
|
||||||
resolved = object;
|
resolved = object;
|
||||||
let match: RegExpMatchArray;
|
} else if (typeof object === 'string') {
|
||||||
do {
|
resolved = object;
|
||||||
match = resolved.match(/:(\w+)/);
|
Object.entries(params).forEach(([key, value]: [string, string]) =>
|
||||||
if (match) {
|
resolved = resolved.replaceAll(`:${key}`, value)
|
||||||
const substitute = params[match[1]];
|
);
|
||||||
if (hasValue(substitute)) {
|
|
||||||
resolved = resolved.replace(match[0], `${substitute}`);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} while (match);
|
|
||||||
} else if (Array.isArray(object)) {
|
} else if (Array.isArray(object)) {
|
||||||
resolved = [];
|
resolved = [];
|
||||||
object.forEach((entry, index) => {
|
object.forEach((entry, index) => {
|
||||||
|
@@ -1668,11 +1668,7 @@ export const mockFileFormData = {
|
|||||||
],
|
],
|
||||||
endDate: [
|
endDate: [
|
||||||
{
|
{
|
||||||
value: {
|
value: new Date('2019-01-16'),
|
||||||
year: 2019,
|
|
||||||
month: 1,
|
|
||||||
day: 16
|
|
||||||
},
|
|
||||||
language: null,
|
language: null,
|
||||||
authority: null,
|
authority: null,
|
||||||
display: {
|
display: {
|
||||||
@@ -1694,7 +1690,7 @@ export const mockFileFormData = {
|
|||||||
value: 'embargo',
|
value: 'embargo',
|
||||||
language: null,
|
language: null,
|
||||||
authority: null,
|
authority: null,
|
||||||
display: 'lease',
|
display: 'embargo',
|
||||||
confidence: -1,
|
confidence: -1,
|
||||||
place: 0,
|
place: 0,
|
||||||
otherInformation: null
|
otherInformation: null
|
||||||
@@ -1702,11 +1698,7 @@ export const mockFileFormData = {
|
|||||||
],
|
],
|
||||||
startDate: [
|
startDate: [
|
||||||
{
|
{
|
||||||
value: {
|
value: new Date('2019-01-16'),
|
||||||
year: 2019,
|
|
||||||
month: 1,
|
|
||||||
day: 16
|
|
||||||
},
|
|
||||||
language: null,
|
language: null,
|
||||||
authority: null,
|
authority: null,
|
||||||
display: {
|
display: {
|
||||||
|
@@ -31,6 +31,8 @@ import { SearchObjects } from './models/search-objects.model';
|
|||||||
import { DSpaceObject } from '../../core/shared/dspace-object.model';
|
import { DSpaceObject } from '../../core/shared/dspace-object.model';
|
||||||
import { SearchFilterConfig } from './models/search-filter-config.model';
|
import { SearchFilterConfig } from './models/search-filter-config.model';
|
||||||
import { FilterType } from './models/filter-type.model';
|
import { FilterType } from './models/filter-type.model';
|
||||||
|
import { getCommunityPageRoute } from '../../community-page/community-page-routing-paths';
|
||||||
|
import { getCollectionPageRoute } from '../../collection-page/collection-page-routing-paths';
|
||||||
|
|
||||||
let comp: SearchComponent;
|
let comp: SearchComponent;
|
||||||
let fixture: ComponentFixture<SearchComponent>;
|
let fixture: ComponentFixture<SearchComponent>;
|
||||||
@@ -101,8 +103,9 @@ const searchServiceStub = jasmine.createSpyObj('SearchService', {
|
|||||||
search: mockResultsRD$,
|
search: mockResultsRD$,
|
||||||
getSearchLink: '/search',
|
getSearchLink: '/search',
|
||||||
getScopes: observableOf(['test-scope']),
|
getScopes: observableOf(['test-scope']),
|
||||||
getSearchConfigurationFor: createSuccessfulRemoteDataObject$(searchConfig)
|
getSearchConfigurationFor: createSuccessfulRemoteDataObject$(searchConfig),
|
||||||
});
|
trackSearch: {},
|
||||||
|
}) as SearchService;
|
||||||
const configurationParam = 'default';
|
const configurationParam = 'default';
|
||||||
const queryParam = 'test query';
|
const queryParam = 'test query';
|
||||||
const scopeParam = '7669c72a-3f2a-451f-a3b9-9210e7a4c02f';
|
const scopeParam = '7669c72a-3f2a-451f-a3b9-9210e7a4c02f';
|
||||||
@@ -327,4 +330,64 @@ describe('SearchComponent', () => {
|
|||||||
}));
|
}));
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('getDsoUUIDFromUrl', () => {
|
||||||
|
let url: string;
|
||||||
|
let result: string;
|
||||||
|
|
||||||
|
describe('when the navigated URL is an entity route', () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
url = '/entities/publication/9a364471-3f19-4e7b-916a-a24a44ff48e3';
|
||||||
|
result = (comp as any).getDsoUUIDFromUrl(url);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should return the UUID', () => {
|
||||||
|
expect(result).toEqual('9a364471-3f19-4e7b-916a-a24a44ff48e3');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('when the navigated URL is a community route', () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
url = `${getCommunityPageRoute('9a364471-3f19-4e7b-916a-a24a44ff48e3')}`;
|
||||||
|
result = (comp as any).getDsoUUIDFromUrl(url);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should return the UUID', () => {
|
||||||
|
expect(result).toEqual('9a364471-3f19-4e7b-916a-a24a44ff48e3');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('when the navigated URL is a collection route', () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
url = `${getCollectionPageRoute('9a364471-3f19-4e7b-916a-a24a44ff48e3')}`;
|
||||||
|
result = (comp as any).getDsoUUIDFromUrl(url);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should return the UUID', () => {
|
||||||
|
expect(result).toEqual('9a364471-3f19-4e7b-916a-a24a44ff48e3');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('when the navigated URL is an item route', () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
url = '/items/9a364471-3f19-4e7b-916a-a24a44ff48e3';
|
||||||
|
result = (comp as any).getDsoUUIDFromUrl(url);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should return the UUID', () => {
|
||||||
|
expect(result).toEqual('9a364471-3f19-4e7b-916a-a24a44ff48e3');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('when the navigated URL is an invalid route', () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
url = '/invalid/object/route/9a364471-3f19-4e7b-916a-a24a44ff48e3';
|
||||||
|
result = (comp as any).getDsoUUIDFromUrl(url);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should return null', () => {
|
||||||
|
expect(result).toBeNull();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
@@ -1,5 +1,5 @@
|
|||||||
import { ChangeDetectionStrategy, Component, EventEmitter, Inject, Input, OnInit, Output } from '@angular/core';
|
import { ChangeDetectionStrategy, Component, EventEmitter, Inject, Input, OnInit, Output } from '@angular/core';
|
||||||
import { Router } from '@angular/router';
|
import { NavigationStart, Router } from '@angular/router';
|
||||||
|
|
||||||
import { BehaviorSubject, combineLatest, Observable, Subscription } from 'rxjs';
|
import { BehaviorSubject, combineLatest, Observable, Subscription } from 'rxjs';
|
||||||
import { debounceTime, distinctUntilChanged, filter, map, switchMap } from 'rxjs/operators';
|
import { debounceTime, distinctUntilChanged, filter, map, switchMap } from 'rxjs/operators';
|
||||||
@@ -11,7 +11,7 @@ import { DSpaceObject } from '../../core/shared/dspace-object.model';
|
|||||||
import { pushInOut } from '../animations/push';
|
import { pushInOut } from '../animations/push';
|
||||||
import { HostWindowService } from '../host-window.service';
|
import { HostWindowService } from '../host-window.service';
|
||||||
import { SidebarService } from '../sidebar/sidebar.service';
|
import { SidebarService } from '../sidebar/sidebar.service';
|
||||||
import { hasValue } from '../empty.util';
|
import { hasValue, hasValueOperator, isNotEmpty } from '../empty.util';
|
||||||
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 './models/paginated-search-options.model';
|
import { PaginatedSearchOptions } from './models/paginated-search-options.model';
|
||||||
@@ -35,6 +35,9 @@ import { environment } from 'src/environments/environment';
|
|||||||
import { SubmissionObject } from '../../core/submission/models/submission-object.model';
|
import { SubmissionObject } from '../../core/submission/models/submission-object.model';
|
||||||
import { SearchFilterConfig } from './models/search-filter-config.model';
|
import { SearchFilterConfig } from './models/search-filter-config.model';
|
||||||
import { WorkspaceItem } from '../..//core/submission/models/workspaceitem.model';
|
import { WorkspaceItem } from '../..//core/submission/models/workspaceitem.model';
|
||||||
|
import { ITEM_MODULE_PATH } from '../../item-page/item-page-routing-paths';
|
||||||
|
import { COLLECTION_MODULE_PATH } from '../../collection-page/collection-page-routing-paths';
|
||||||
|
import { COMMUNITY_MODULE_PATH } from '../../community-page/community-page-routing-paths';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'ds-search',
|
selector: 'ds-search',
|
||||||
@@ -229,9 +232,21 @@ export class SearchComponent implements OnInit {
|
|||||||
searchLink: string;
|
searchLink: string;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Subscription to unsubscribe from
|
* Regex to match UUIDs
|
||||||
*/
|
*/
|
||||||
sub: Subscription;
|
uuidRegex = /\b[0-9a-f]{8}\b-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-\b[0-9a-f]{12}\b/g;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* List of paths that are considered to be the start of a route to an object page (excluding "/", e.g. "items")
|
||||||
|
* These are expected to end on an object UUID
|
||||||
|
* If they match the route we're navigating to, an object property will be added to the search event sent
|
||||||
|
*/
|
||||||
|
allowedObjectPaths: string[] = ['entities', ITEM_MODULE_PATH, COLLECTION_MODULE_PATH, COMMUNITY_MODULE_PATH];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Subscriptions to unsubscribe from
|
||||||
|
*/
|
||||||
|
subs: Subscription[] = [];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Emits an event with the current search result entries
|
* Emits an event with the current search result entries
|
||||||
@@ -301,7 +316,7 @@ export class SearchComponent implements OnInit {
|
|||||||
);
|
);
|
||||||
const searchOptions$: Observable<PaginatedSearchOptions> = this.getSearchOptions().pipe(distinctUntilChanged());
|
const searchOptions$: Observable<PaginatedSearchOptions> = this.getSearchOptions().pipe(distinctUntilChanged());
|
||||||
|
|
||||||
this.sub = combineLatest([configuration$, searchSortOptions$, searchOptions$, sortOption$]).pipe(
|
this.subs.push(combineLatest([configuration$, searchSortOptions$, searchOptions$, sortOption$]).pipe(
|
||||||
filter(([configuration, searchSortOptions, searchOptions, sortOption]: [string, SortOptions[], PaginatedSearchOptions, SortOptions]) => {
|
filter(([configuration, searchSortOptions, searchOptions, sortOption]: [string, SortOptions[], PaginatedSearchOptions, SortOptions]) => {
|
||||||
// filter for search options related to instanced paginated id
|
// filter for search options related to instanced paginated id
|
||||||
return searchOptions.pagination.id === this.paginationId;
|
return searchOptions.pagination.id === this.paginationId;
|
||||||
@@ -332,7 +347,9 @@ export class SearchComponent implements OnInit {
|
|||||||
this.retrieveSearchResults(newSearchOptions);
|
this.retrieveSearchResults(newSearchOptions);
|
||||||
this.retrieveFilters(searchOptions);
|
this.retrieveFilters(searchOptions);
|
||||||
}
|
}
|
||||||
});
|
}));
|
||||||
|
|
||||||
|
this.subscribeToRoutingEvents();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -374,12 +391,10 @@ export class SearchComponent implements OnInit {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Unsubscribe from the subscription
|
* Unsubscribe from the subscriptions
|
||||||
*/
|
*/
|
||||||
ngOnDestroy(): void {
|
ngOnDestroy(): void {
|
||||||
if (hasValue(this.sub)) {
|
this.subs.filter((sub) => hasValue(sub)).forEach((sub) => sub.unsubscribe());
|
||||||
this.sub.unsubscribe();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -440,6 +455,43 @@ export class SearchComponent implements OnInit {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Subscribe to routing events to detect when a user moves away from the search page
|
||||||
|
* When the user is routing to an object page, it needs to send out a separate search event containing that object's UUID
|
||||||
|
* This method should only be called once and is essentially what SearchTrackingComponent used to do (now removed)
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
private subscribeToRoutingEvents() {
|
||||||
|
this.subs.push(
|
||||||
|
this.router.events.pipe(
|
||||||
|
filter((event) => event instanceof NavigationStart),
|
||||||
|
map((event: NavigationStart) => this.getDsoUUIDFromUrl(event.url)),
|
||||||
|
hasValueOperator(),
|
||||||
|
).subscribe((uuid) => {
|
||||||
|
if (this.resultsRD$.value.hasSucceeded) {
|
||||||
|
this.service.trackSearch(this.searchOptions$.value, this.resultsRD$.value.payload as SearchObjects<DSpaceObject>, uuid);
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the UUID from a DSO url
|
||||||
|
* Return null if the url isn't an object page (allowedObjectPaths) or the UUID couldn't be found
|
||||||
|
* @param url
|
||||||
|
*/
|
||||||
|
private getDsoUUIDFromUrl(url: string): string {
|
||||||
|
if (isNotEmpty(url)) {
|
||||||
|
if (this.allowedObjectPaths.some((path) => url.startsWith(`/${path}`))) {
|
||||||
|
const uuid = url.substring(url.lastIndexOf('/') + 1);
|
||||||
|
if (uuid.match(this.uuidRegex)) {
|
||||||
|
return uuid;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Check if the sidebar is collapsed
|
* Check if the sidebar is collapsed
|
||||||
* @returns {Observable<boolean>} emits true if the sidebar is currently collapsed, false if it is expanded
|
* @returns {Observable<boolean>} emits true if the sidebar is currently collapsed, false if it is expanded
|
||||||
|
@@ -131,7 +131,6 @@ export class StartsWithDateComponent extends StartsWithAbstractComponent {
|
|||||||
} else {
|
} else {
|
||||||
this.startsWithYear = +startsWith;
|
this.startsWithYear = +startsWith;
|
||||||
}
|
}
|
||||||
this.setStartsWithParam(false);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@@ -70,7 +70,6 @@ export abstract class StartsWithAbstractComponent implements OnInit, OnDestroy {
|
|||||||
*/
|
*/
|
||||||
setStartsWith(startsWith: string) {
|
setStartsWith(startsWith: string) {
|
||||||
this.startsWith = startsWith;
|
this.startsWith = startsWith;
|
||||||
this.setStartsWithParam(false);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@@ -1,7 +1,7 @@
|
|||||||
<form class="w-100" [formGroup]="formData" (ngSubmit)="submitForm(formData.value)">
|
<form class="w-100" [formGroup]="formData" (ngSubmit)="submitForm(formData.value)">
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="form-group input-group col-8 col-sm-12 col-md-6">
|
<div class="form-group input-group col-8 col-sm-12 col-md-6">
|
||||||
<input class="form-control" placeholder="{{'browse.startsWith.type_text' | translate}}" type="text" name="startsWith" formControlName="startsWith" [value]="getStartsWith()" />
|
<input class="form-control" [attr.aria-label]="'browse.startsWith.input' | translate" placeholder="{{'browse.startsWith.type_text' | translate}}" type="text" name="startsWith" formControlName="startsWith" [value]="getStartsWith()" />
|
||||||
<span class="input-group-append">
|
<span class="input-group-append">
|
||||||
<button class="btn btn-primary" type="submit"><i class="fas fa-book-open"></i> {{'browse.startsWith.submit' | translate}}</button>
|
<button class="btn btn-primary" type="submit"><i class="fas fa-book-open"></i> {{'browse.startsWith.submit' | translate}}</button>
|
||||||
</span>
|
</span>
|
||||||
|
@@ -31,7 +31,8 @@ export class Angulartics2DSpace {
|
|||||||
event.properties.searchOptions,
|
event.properties.searchOptions,
|
||||||
event.properties.page,
|
event.properties.page,
|
||||||
event.properties.sort,
|
event.properties.sort,
|
||||||
event.properties.filters
|
event.properties.filters,
|
||||||
|
event.properties.clickedObject,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -45,12 +45,14 @@ export class StatisticsService {
|
|||||||
* @param page: An object that describes the pagination status
|
* @param page: An object that describes the pagination status
|
||||||
* @param sort: An object that describes the sort status
|
* @param sort: An object that describes the sort status
|
||||||
* @param filters: An array of search filters used to filter the result set
|
* @param filters: An array of search filters used to filter the result set
|
||||||
|
* @param clickedObject: UUID of object clicked
|
||||||
*/
|
*/
|
||||||
trackSearchEvent(
|
trackSearchEvent(
|
||||||
searchOptions: SearchOptions,
|
searchOptions: SearchOptions,
|
||||||
page: { size: number, totalElements: number, totalPages: number, number: number },
|
page: { size: number, totalElements: number, totalPages: number, number: number },
|
||||||
sort: { by: string, order: string },
|
sort: { by: string, order: string },
|
||||||
filters?: { filter: string, operator: string, value: string, label: string }[]
|
filters?: { filter: string, operator: string, value: string, label: string }[],
|
||||||
|
clickedObject?: string,
|
||||||
) {
|
) {
|
||||||
const body = {
|
const body = {
|
||||||
query: searchOptions.query,
|
query: searchOptions.query,
|
||||||
@@ -87,6 +89,9 @@ export class StatisticsService {
|
|||||||
}
|
}
|
||||||
Object.assign(body, { appliedFilters: bodyFilters });
|
Object.assign(body, { appliedFilters: bodyFilters });
|
||||||
}
|
}
|
||||||
|
if (hasValue(clickedObject)) {
|
||||||
|
Object.assign(body, { clickedObject });
|
||||||
|
}
|
||||||
this.sendEvent('/statistics/searchevents', body);
|
this.sendEvent('/statistics/searchevents', body);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -25,7 +25,7 @@
|
|||||||
class="btn btn-outline-primary"
|
class="btn btn-outline-primary"
|
||||||
(blur)="onClose()"
|
(blur)="onClose()"
|
||||||
(click)="onClose()"
|
(click)="onClose()"
|
||||||
[disabled]="(processingChange$ | async) || collectionModifiable == false"
|
[disabled]="(processingChange$ | async) || collectionModifiable == false || isReadonly"
|
||||||
ngbDropdownToggle>
|
ngbDropdownToggle>
|
||||||
<span *ngIf="(processingChange$ | async)"><i class='fas fa-circle-notch fa-spin'></i></span>
|
<span *ngIf="(processingChange$ | async)"><i class='fas fa-circle-notch fa-spin'></i></span>
|
||||||
<span *ngIf="!(processingChange$ | async)">{{ selectedCollectionName$ | async }}</span>
|
<span *ngIf="!(processingChange$ | async)">{{ selectedCollectionName$ | async }}</span>
|
||||||
|
@@ -252,6 +252,12 @@ describe('SubmissionFormCollectionComponent Component', () => {
|
|||||||
expect(dropDown).toBeFalsy();
|
expect(dropDown).toBeFalsy();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('the dropdown button should be disabled when isReadonly is true', () => {
|
||||||
|
comp.isReadonly = true;
|
||||||
|
fixture.detectChanges();
|
||||||
|
expect(dropdowBtn.nativeNode.attributes.disabled).toBeDefined();
|
||||||
|
});
|
||||||
|
|
||||||
it('should be simulated when the drop-down menu is closed', () => {
|
it('should be simulated when the drop-down menu is closed', () => {
|
||||||
spyOn(comp, 'onClose');
|
spyOn(comp, 'onClose');
|
||||||
comp.onClose();
|
comp.onClose();
|
||||||
|
@@ -65,6 +65,11 @@ export class SubmissionFormCollectionComponent implements OnChanges, OnInit {
|
|||||||
*/
|
*/
|
||||||
@Input() submissionId;
|
@Input() submissionId;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Flag to indicate if the submission dropdown is read only
|
||||||
|
*/
|
||||||
|
@Input() isReadonly = false;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* An event fired when a different collection is selected.
|
* An event fired when a different collection is selected.
|
||||||
* Event's payload equals to new SubmissionObject.
|
* Event's payload equals to new SubmissionObject.
|
||||||
|
@@ -8,12 +8,15 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="submission-form-header-item mb-3 mb-sm-0 flex-sm-grow-1 flex-md-grow-0">
|
<div class="submission-form-header-item mb-3 mb-sm-0 flex-sm-grow-1 flex-md-grow-0">
|
||||||
|
<ng-container *ngIf="!isSectionHidden">
|
||||||
<ds-submission-form-collection [currentCollectionId]="collectionId"
|
<ds-submission-form-collection [currentCollectionId]="collectionId"
|
||||||
[currentDefinition]="definitionId"
|
[currentDefinition]="definitionId"
|
||||||
[submissionId]="submissionId"
|
[submissionId]="submissionId"
|
||||||
[collectionModifiable]="collectionModifiable"
|
[collectionModifiable]="collectionModifiable"
|
||||||
|
[isReadonly]="isSectionReadonly"
|
||||||
(collectionChange)="onCollectionChange($event)">
|
(collectionChange)="onCollectionChange($event)">
|
||||||
</ds-submission-form-collection>
|
</ds-submission-form-collection>
|
||||||
|
</ng-container>
|
||||||
</div>
|
</div>
|
||||||
<div class="submission-form-header-item text-right">
|
<div class="submission-form-header-item text-right">
|
||||||
<ds-submission-form-section-add [collectionId]="collectionId"
|
<ds-submission-form-section-add [collectionId]="collectionId"
|
||||||
|
@@ -25,6 +25,7 @@ import { createTestComponent } from '../../shared/testing/utils.test';
|
|||||||
import { Item } from '../../core/shared/item.model';
|
import { Item } from '../../core/shared/item.model';
|
||||||
import { TestScheduler } from 'rxjs/testing';
|
import { TestScheduler } from 'rxjs/testing';
|
||||||
import { SectionsService } from '../sections/sections.service';
|
import { SectionsService } from '../sections/sections.service';
|
||||||
|
import { VisibilityType } from '../sections/visibility-type';
|
||||||
|
|
||||||
describe('SubmissionFormComponent Component', () => {
|
describe('SubmissionFormComponent Component', () => {
|
||||||
|
|
||||||
@@ -156,6 +157,32 @@ describe('SubmissionFormComponent Component', () => {
|
|||||||
done();
|
done();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should return the visibility object of the collection section', () => {
|
||||||
|
comp.submissionDefinition = submissionDefinition;
|
||||||
|
fixture.detectChanges();
|
||||||
|
const result = compAsAny.getCollectionVisibility();
|
||||||
|
expect(result).toEqual({
|
||||||
|
main: VisibilityType.HIDDEN,
|
||||||
|
other: VisibilityType.HIDDEN,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should return true if collection section visibility is hidden', () => {
|
||||||
|
comp.submissionDefinition = submissionDefinition;
|
||||||
|
fixture.detectChanges();
|
||||||
|
expect(comp.isSectionHidden).toBe(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should return false for isSectionReadonly when collection section visibility is not READONLY', () => {
|
||||||
|
const visibility = {
|
||||||
|
main: VisibilityType.READONLY,
|
||||||
|
other: VisibilityType.READONLY,
|
||||||
|
};
|
||||||
|
comp.submissionDefinition = Object.assign({}, submissionDefinition, { visibility: visibility });
|
||||||
|
fixture.detectChanges();
|
||||||
|
expect(comp.isSectionReadonly).toBe(false);
|
||||||
|
});
|
||||||
|
|
||||||
it('should update properly on collection change', (done) => {
|
it('should update properly on collection change', (done) => {
|
||||||
comp.collectionId = collectionId;
|
comp.collectionId = collectionId;
|
||||||
comp.submissionId = submissionId;
|
comp.submissionId = submissionId;
|
||||||
|
@@ -9,7 +9,7 @@ import { HALEndpointService } from '../../core/shared/hal-endpoint.service';
|
|||||||
import { SubmissionObject } from '../../core/submission/models/submission-object.model';
|
import { SubmissionObject } from '../../core/submission/models/submission-object.model';
|
||||||
import { WorkspaceitemSectionsObject } from '../../core/submission/models/workspaceitem-sections.model';
|
import { WorkspaceitemSectionsObject } from '../../core/submission/models/workspaceitem-sections.model';
|
||||||
|
|
||||||
import { hasValue, isNotEmpty } from '../../shared/empty.util';
|
import { hasValue, isNotEmpty, isNotUndefined } from '../../shared/empty.util';
|
||||||
import { UploaderOptions } from '../../shared/upload/uploader/uploader-options.model';
|
import { UploaderOptions } from '../../shared/upload/uploader/uploader-options.model';
|
||||||
import { SubmissionObjectEntry } from '../objects/submission-objects.reducer';
|
import { SubmissionObjectEntry } from '../objects/submission-objects.reducer';
|
||||||
import { SectionDataObject } from '../sections/models/section-data.model';
|
import { SectionDataObject } from '../sections/models/section-data.model';
|
||||||
@@ -18,6 +18,10 @@ import { Item } from '../../core/shared/item.model';
|
|||||||
import { SectionsType } from '../sections/sections-type';
|
import { SectionsType } from '../sections/sections-type';
|
||||||
import { SectionsService } from '../sections/sections.service';
|
import { SectionsService } from '../sections/sections.service';
|
||||||
import { SubmissionError } from '../objects/submission-error.model';
|
import { SubmissionError } from '../objects/submission-error.model';
|
||||||
|
import { SubmissionSectionVisibility } from './../../core/config/models/config-submission-section.model';
|
||||||
|
import { SubmissionSectionModel } from './../../core/config/models/config-submission-section.model';
|
||||||
|
import { VisibilityType } from '../sections/visibility-type';
|
||||||
|
import isEqual from 'lodash/isEqual';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This component represents the submission form.
|
* This component represents the submission form.
|
||||||
@@ -188,6 +192,42 @@ export class SubmissionFormComponent implements OnChanges, OnDestroy {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the visibility object of the collection section
|
||||||
|
*/
|
||||||
|
private getCollectionVisibility(): SubmissionSectionVisibility {
|
||||||
|
const submissionSectionModel: SubmissionSectionModel =
|
||||||
|
this.submissionDefinition.sections.page.find(
|
||||||
|
(section) => isEqual(section.sectionType, SectionsType.Collection)
|
||||||
|
);
|
||||||
|
|
||||||
|
return isNotUndefined(submissionSectionModel.visibility) ? submissionSectionModel.visibility : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Getter to see if the collection section visibility is hidden
|
||||||
|
*/
|
||||||
|
get isSectionHidden(): boolean {
|
||||||
|
const visibility = this.getCollectionVisibility();
|
||||||
|
return (
|
||||||
|
hasValue(visibility) &&
|
||||||
|
isEqual(visibility.main, VisibilityType.HIDDEN) &&
|
||||||
|
isEqual(visibility.other, VisibilityType.HIDDEN)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Getter to see if the collection section visibility is readonly
|
||||||
|
*/
|
||||||
|
get isSectionReadonly(): boolean {
|
||||||
|
const visibility = this.getCollectionVisibility();
|
||||||
|
return (
|
||||||
|
hasValue(visibility) &&
|
||||||
|
isEqual(visibility.main, VisibilityType.READONLY) &&
|
||||||
|
isEqual(visibility.other, VisibilityType.READONLY)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Unsubscribe from all subscriptions, destroy instance variables
|
* Unsubscribe from all subscriptions, destroy instance variables
|
||||||
* and reset submission state
|
* and reset submission state
|
||||||
@@ -239,6 +279,8 @@ export class SubmissionFormComponent implements OnChanges, OnDestroy {
|
|||||||
protected getSectionsList(): Observable<any> {
|
protected getSectionsList(): Observable<any> {
|
||||||
return this.submissionService.getSubmissionSections(this.submissionId).pipe(
|
return this.submissionService.getSubmissionSections(this.submissionId).pipe(
|
||||||
filter((sections: SectionDataObject[]) => isNotEmpty(sections)),
|
filter((sections: SectionDataObject[]) => isNotEmpty(sections)),
|
||||||
map((sections: SectionDataObject[]) => sections));
|
map((sections: SectionDataObject[]) =>
|
||||||
|
sections.filter((section: SectionDataObject) => !isEqual(section.sectionType,SectionsType.Collection))),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -8,4 +8,5 @@ export enum SectionsType {
|
|||||||
AccessesCondition = 'accessCondition',
|
AccessesCondition = 'accessCondition',
|
||||||
SherpaPolicies = 'sherpaPolicy',
|
SherpaPolicies = 'sherpaPolicy',
|
||||||
Identifiers = 'identifiers',
|
Identifiers = 'identifiers',
|
||||||
|
Collection = 'collection',
|
||||||
}
|
}
|
||||||
|
@@ -23,7 +23,7 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="col-4">
|
<div class="col-4">
|
||||||
<p class="m-1">
|
<p class="m-1">
|
||||||
<a href="{{journal.url}}" target="_blank">
|
<a href="{{journal.url}}" target="_blank" rel="noopener noreferrer">
|
||||||
{{journal.url}}
|
{{journal.url}}
|
||||||
</a>
|
</a>
|
||||||
</p>
|
</p>
|
||||||
@@ -35,7 +35,7 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="col-4" *ngFor="let publisher of journal.publishers">
|
<div class="col-4" *ngFor="let publisher of journal.publishers">
|
||||||
<p class="m-1">
|
<p class="m-1">
|
||||||
<a href="{{publisher.uri}}" target="_blank">
|
<a href="{{publisher.uri}}" target="_blank" rel="noopener noreferrer">
|
||||||
{{publisher.name}}
|
{{publisher.name}}
|
||||||
</a>
|
</a>
|
||||||
</p>
|
</p>
|
||||||
|
@@ -8,7 +8,7 @@
|
|||||||
</p>
|
</p>
|
||||||
<ul>
|
<ul>
|
||||||
<li *ngFor="let url of policy.urls | keyvalue">
|
<li *ngFor="let url of policy.urls | keyvalue">
|
||||||
<a href="{{url.key}}" target="_blank">{{url.value}}</a>
|
<a href="{{url.key}}" target="_blank" rel="noopener noreferrer">{{url.value}}</a>
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
|
@@ -1,4 +1,4 @@
|
|||||||
import { ChangeDetectorRef, Component, OnInit, ViewChild } from '@angular/core';
|
import { ChangeDetectorRef, Component, OnDestroy, OnInit, ViewChild } from '@angular/core';
|
||||||
import { UntypedFormControl } from '@angular/forms';
|
import { UntypedFormControl } from '@angular/forms';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
@@ -66,7 +66,8 @@ import { DynamicDateControlValue } from '@ng-dynamic-forms/core/lib/model/dynami
|
|||||||
styleUrls: ['./section-upload-file-edit.component.scss'],
|
styleUrls: ['./section-upload-file-edit.component.scss'],
|
||||||
templateUrl: './section-upload-file-edit.component.html',
|
templateUrl: './section-upload-file-edit.component.html',
|
||||||
})
|
})
|
||||||
export class SubmissionSectionUploadFileEditComponent implements OnInit {
|
export class SubmissionSectionUploadFileEditComponent
|
||||||
|
implements OnInit, OnDestroy {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The FormComponent reference
|
* The FormComponent reference
|
||||||
@@ -435,13 +436,31 @@ export class SubmissionSectionUploadFileEditComponent implements OnInit {
|
|||||||
delete currentAccessCondition.startDate;
|
delete currentAccessCondition.startDate;
|
||||||
} else if (accessCondition.startDate) {
|
} else if (accessCondition.startDate) {
|
||||||
const startDate = this.retrieveValueFromField(accessCondition.startDate);
|
const startDate = this.retrieveValueFromField(accessCondition.startDate);
|
||||||
currentAccessCondition.startDate = dateToISOFormat(startDate);
|
// Clamp the start date to the maximum, if any, since the
|
||||||
|
// datepicker sometimes exceeds it.
|
||||||
|
let startDateDate = new Date(startDate);
|
||||||
|
if (accessConditionOpt.maxStartDate) {
|
||||||
|
const maxStartDateDate = new Date(accessConditionOpt.maxStartDate);
|
||||||
|
if (startDateDate > maxStartDateDate) {
|
||||||
|
startDateDate = maxStartDateDate;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
currentAccessCondition.startDate = dateToISOFormat(startDateDate);
|
||||||
}
|
}
|
||||||
if (!accessConditionOpt.hasEndDate) {
|
if (!accessConditionOpt.hasEndDate) {
|
||||||
delete currentAccessCondition.endDate;
|
delete currentAccessCondition.endDate;
|
||||||
} else if (accessCondition.endDate) {
|
} else if (accessCondition.endDate) {
|
||||||
const endDate = this.retrieveValueFromField(accessCondition.endDate);
|
const endDate = this.retrieveValueFromField(accessCondition.endDate);
|
||||||
currentAccessCondition.endDate = dateToISOFormat(endDate);
|
// Clamp the end date to the maximum, if any, since the
|
||||||
|
// datepicker sometimes exceeds it.
|
||||||
|
let endDateDate = new Date(endDate);
|
||||||
|
if (accessConditionOpt.maxEndDate) {
|
||||||
|
const maxEndDateDate = new Date(accessConditionOpt.maxEndDate);
|
||||||
|
if (endDateDate > maxEndDateDate) {
|
||||||
|
endDateDate = maxEndDateDate;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
currentAccessCondition.endDate = dateToISOFormat(endDateDate);
|
||||||
}
|
}
|
||||||
accessConditionsToSave.push(currentAccessCondition);
|
accessConditionsToSave.push(currentAccessCondition);
|
||||||
}
|
}
|
||||||
|
@@ -175,7 +175,7 @@ describe('SubmissionSectionUploadFileComponent test suite', () => {
|
|||||||
it('should init file data properly', () => {
|
it('should init file data properly', () => {
|
||||||
uploadService.getFileData.and.returnValue(observableOf(fileData));
|
uploadService.getFileData.and.returnValue(observableOf(fileData));
|
||||||
|
|
||||||
comp.ngOnChanges();
|
comp.ngOnChanges({});
|
||||||
|
|
||||||
expect(comp.fileData).toEqual(fileData);
|
expect(comp.fileData).toEqual(fileData);
|
||||||
});
|
});
|
||||||
|
@@ -1,4 +1,13 @@
|
|||||||
import { ChangeDetectorRef, Component, Input, OnChanges, OnInit, ViewChild } from '@angular/core';
|
import {
|
||||||
|
ChangeDetectorRef,
|
||||||
|
Component,
|
||||||
|
Input,
|
||||||
|
OnChanges,
|
||||||
|
OnDestroy,
|
||||||
|
OnInit,
|
||||||
|
SimpleChanges,
|
||||||
|
ViewChild
|
||||||
|
} from '@angular/core';
|
||||||
|
|
||||||
import { BehaviorSubject, Subscription } from 'rxjs';
|
import { BehaviorSubject, Subscription } from 'rxjs';
|
||||||
import { filter } from 'rxjs/operators';
|
import { filter } from 'rxjs/operators';
|
||||||
@@ -27,7 +36,7 @@ import { NgbModalOptions } from '@ng-bootstrap/ng-bootstrap/modal/modal-config';
|
|||||||
styleUrls: ['./section-upload-file.component.scss'],
|
styleUrls: ['./section-upload-file.component.scss'],
|
||||||
templateUrl: './section-upload-file.component.html',
|
templateUrl: './section-upload-file.component.html',
|
||||||
})
|
})
|
||||||
export class SubmissionSectionUploadFileComponent implements OnChanges, OnInit {
|
export class SubmissionSectionUploadFileComponent implements OnChanges, OnInit, OnDestroy {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The list of available access condition
|
* The list of available access condition
|
||||||
@@ -168,13 +177,13 @@ export class SubmissionSectionUploadFileComponent implements OnChanges, OnInit {
|
|||||||
/**
|
/**
|
||||||
* Retrieve bitstream's metadata
|
* Retrieve bitstream's metadata
|
||||||
*/
|
*/
|
||||||
ngOnChanges() {
|
ngOnChanges(changes: SimpleChanges): void {
|
||||||
if (this.availableAccessConditionOptions) {
|
if (this.availableAccessConditionOptions) {
|
||||||
// Retrieve file state
|
// Retrieve file state
|
||||||
this.subscriptions.push(
|
this.subscriptions.push(
|
||||||
this.uploadService
|
this.uploadService
|
||||||
.getFileData(this.submissionId, this.sectionId, this.fileId).pipe(
|
.getFileData(this.submissionId, this.sectionId, this.fileId)
|
||||||
filter((bitstream) => isNotUndefined(bitstream)))
|
.pipe(filter((bitstream) => isNotUndefined(bitstream)))
|
||||||
.subscribe((bitstream) => {
|
.subscribe((bitstream) => {
|
||||||
this.fileData = bitstream;
|
this.fileData = bitstream;
|
||||||
}
|
}
|
||||||
|
@@ -0,0 +1,93 @@
|
|||||||
|
import { Component, Input } from '@angular/core';
|
||||||
|
import { SubmissionFormsModel } from 'src/app/core/config/models/config-submission-forms.model';
|
||||||
|
import { ThemedComponent } from 'src/app/shared/theme-support/themed.component';
|
||||||
|
import { SubmissionSectionUploadFileComponent } from './section-upload-file.component';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'ds-themed-submission-upload-section-file',
|
||||||
|
styleUrls: [],
|
||||||
|
templateUrl: '../../../../shared/theme-support/themed.component.html'
|
||||||
|
})
|
||||||
|
export class ThemedSubmissionSectionUploadFileComponent
|
||||||
|
extends ThemedComponent<SubmissionSectionUploadFileComponent> {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The list of available access condition
|
||||||
|
* @type {Array}
|
||||||
|
*/
|
||||||
|
@Input() availableAccessConditionOptions: any[];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The submission id
|
||||||
|
* @type {string}
|
||||||
|
*/
|
||||||
|
@Input() collectionId: string;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Define if collection access conditions policy type :
|
||||||
|
* POLICY_DEFAULT_NO_LIST : is not possible to define additional access group/s for the single file
|
||||||
|
* POLICY_DEFAULT_WITH_LIST : is possible to define additional access group/s for the single file
|
||||||
|
* @type {number}
|
||||||
|
*/
|
||||||
|
@Input() collectionPolicyType: number;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The configuration for the bitstream's metadata form
|
||||||
|
* @type {SubmissionFormsModel}
|
||||||
|
*/
|
||||||
|
@Input() configMetadataForm: SubmissionFormsModel;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The bitstream id
|
||||||
|
* @type {string}
|
||||||
|
*/
|
||||||
|
@Input() fileId: string;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The bitstream array key
|
||||||
|
* @type {string}
|
||||||
|
*/
|
||||||
|
@Input() fileIndex: string;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The bitstream id
|
||||||
|
* @type {string}
|
||||||
|
*/
|
||||||
|
@Input() fileName: string;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The section id
|
||||||
|
* @type {string}
|
||||||
|
*/
|
||||||
|
@Input() sectionId: string;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The submission id
|
||||||
|
* @type {string}
|
||||||
|
*/
|
||||||
|
@Input() submissionId: string;
|
||||||
|
|
||||||
|
protected inAndOutputNames: (keyof SubmissionSectionUploadFileComponent & keyof this)[] = [
|
||||||
|
'availableAccessConditionOptions',
|
||||||
|
'collectionId',
|
||||||
|
'collectionPolicyType',
|
||||||
|
'configMetadataForm',
|
||||||
|
'fileId',
|
||||||
|
'fileIndex',
|
||||||
|
'fileName',
|
||||||
|
'sectionId',
|
||||||
|
'submissionId'
|
||||||
|
];
|
||||||
|
|
||||||
|
protected getComponentName(): string {
|
||||||
|
return 'SubmissionSectionUploadFileComponent';
|
||||||
|
}
|
||||||
|
|
||||||
|
protected importThemedComponent(themeName: string): Promise<any> {
|
||||||
|
return import(`../../../../../themes/${themeName}/app/submission/sections/upload/file/section-upload-file.component`);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected importUnthemedComponent(): Promise<any> {
|
||||||
|
return import(`./section-upload-file.component`);
|
||||||
|
}
|
||||||
|
}
|
@@ -28,7 +28,7 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<ng-container *ngFor="let fileEntry of fileList">
|
<ng-container *ngFor="let fileEntry of fileList">
|
||||||
<ds-submission-upload-section-file
|
<ds-themed-submission-upload-section-file
|
||||||
[availableAccessConditionOptions]="availableAccessConditionOptions"
|
[availableAccessConditionOptions]="availableAccessConditionOptions"
|
||||||
[collectionId]="collectionId"
|
[collectionId]="collectionId"
|
||||||
[collectionPolicyType]="collectionPolicyType"
|
[collectionPolicyType]="collectionPolicyType"
|
||||||
@@ -37,7 +37,7 @@
|
|||||||
[fileIndex]="fileList.indexOf(fileEntry)"
|
[fileIndex]="fileList.indexOf(fileEntry)"
|
||||||
[fileName]="fileNames[fileList.indexOf(fileEntry)]"
|
[fileName]="fileNames[fileList.indexOf(fileEntry)]"
|
||||||
[sectionId]="sectionData.id"
|
[sectionId]="sectionData.id"
|
||||||
[submissionId]="submissionId"></ds-submission-upload-section-file>
|
[submissionId]="submissionId"></ds-themed-submission-upload-section-file>
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-md-12">
|
<div class="col-md-12">
|
||||||
<hr/>
|
<hr/>
|
||||||
|
4
src/app/submission/sections/visibility-type.ts
Normal file
4
src/app/submission/sections/visibility-type.ts
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
export enum VisibilityType {
|
||||||
|
HIDDEN = 'HIDDEN',
|
||||||
|
READONLY = 'READONLY',
|
||||||
|
}
|
@@ -49,6 +49,7 @@ import { ResearchEntitiesModule } from '../entity-groups/research-entities/resea
|
|||||||
import { ThemedSubmissionEditComponent } from './edit/themed-submission-edit.component';
|
import { ThemedSubmissionEditComponent } from './edit/themed-submission-edit.component';
|
||||||
import { ThemedSubmissionSubmitComponent } from './submit/themed-submission-submit.component';
|
import { ThemedSubmissionSubmitComponent } from './submit/themed-submission-submit.component';
|
||||||
import { ThemedSubmissionImportExternalComponent } from './import-external/themed-submission-import-external.component';
|
import { ThemedSubmissionImportExternalComponent } from './import-external/themed-submission-import-external.component';
|
||||||
|
import { ThemedSubmissionSectionUploadFileComponent } from './sections/upload/file/themed-section-upload-file.component';
|
||||||
import { FormModule } from '../shared/form/form.module';
|
import { FormModule } from '../shared/form/form.module';
|
||||||
import { NgbAccordionModule, NgbCollapseModule, NgbModalModule } from '@ng-bootstrap/ng-bootstrap';
|
import { NgbAccordionModule, NgbCollapseModule, NgbModalModule } from '@ng-bootstrap/ng-bootstrap';
|
||||||
import { SubmissionSectionAccessesComponent } from './sections/accesses/section-accesses.component';
|
import { SubmissionSectionAccessesComponent } from './sections/accesses/section-accesses.component';
|
||||||
@@ -104,6 +105,7 @@ const DECLARATIONS = [
|
|||||||
PublisherPolicyComponent,
|
PublisherPolicyComponent,
|
||||||
PublicationInformationComponent,
|
PublicationInformationComponent,
|
||||||
MetadataInformationComponent,
|
MetadataInformationComponent,
|
||||||
|
ThemedSubmissionSectionUploadFileComponent,
|
||||||
];
|
];
|
||||||
|
|
||||||
@NgModule({
|
@NgModule({
|
||||||
|
@@ -851,6 +851,8 @@
|
|||||||
|
|
||||||
"browse.startsWith.type_text": "Filter results by typing the first few letters",
|
"browse.startsWith.type_text": "Filter results by typing the first few letters",
|
||||||
|
|
||||||
|
"browse.startsWith.input": "Filter",
|
||||||
|
|
||||||
"browse.taxonomy.button": "Browse",
|
"browse.taxonomy.button": "Browse",
|
||||||
|
|
||||||
"browse.title": "Browsing {{ collection }} by {{ field }}{{ startsWith }} {{ value }}",
|
"browse.title": "Browsing {{ collection }} by {{ field }}{{ startsWith }} {{ value }}",
|
||||||
@@ -4067,7 +4069,7 @@
|
|||||||
|
|
||||||
"submission.general.cancel": "Cancel",
|
"submission.general.cancel": "Cancel",
|
||||||
|
|
||||||
"submission.general.cannot_submit": "You have not the privilege to make a new submission.",
|
"submission.general.cannot_submit": "You don't have permission to make a new submission.",
|
||||||
|
|
||||||
"submission.general.deposit": "Deposit",
|
"submission.general.deposit": "Deposit",
|
||||||
|
|
||||||
|
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,13 @@
|
|||||||
|
import { EditBitstreamPageComponent as BaseComponent } from '../../../../../app/bitstream-page/edit-bitstream-page/edit-bitstream-page.component';
|
||||||
|
import { ChangeDetectionStrategy, Component } from '@angular/core';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'ds-edit-bitstream-page',
|
||||||
|
// styleUrls: ['./edit-bitstream-page.component.scss'],
|
||||||
|
styleUrls: ['../../../../../app/bitstream-page/edit-bitstream-page/edit-bitstream-page.component.scss'],
|
||||||
|
// templateUrl: './edit-bitstream-page.component.html',
|
||||||
|
templateUrl: '../../../../../app/bitstream-page/edit-bitstream-page/edit-bitstream-page.component.html',
|
||||||
|
changeDetection: ChangeDetectionStrategy.OnPush
|
||||||
|
})
|
||||||
|
export class EditBitstreamPageComponent extends BaseComponent {
|
||||||
|
}
|
@@ -0,0 +1,16 @@
|
|||||||
|
import { ChangeDetectionStrategy, Component } from '@angular/core';
|
||||||
|
import { fadeIn, fadeInOut } from '../../../../../../app/shared/animations/fade';
|
||||||
|
import { ItemStatusComponent as BaseComponent } from '../../../../../../app/item-page/edit-item-page/item-status/item-status.component';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'ds-item-status',
|
||||||
|
// templateUrl: './item-status.component.html',
|
||||||
|
templateUrl: '../../../../../../app/item-page/edit-item-page/item-status/item-status.component.html',
|
||||||
|
changeDetection: ChangeDetectionStrategy.Default,
|
||||||
|
animations: [
|
||||||
|
fadeIn,
|
||||||
|
fadeInOut
|
||||||
|
]
|
||||||
|
})
|
||||||
|
export class ItemStatusComponent extends BaseComponent {
|
||||||
|
}
|
@@ -0,0 +1,18 @@
|
|||||||
|
import { Component } from '@angular/core';
|
||||||
|
import {
|
||||||
|
SubmissionSectionUploadFileComponent as BaseComponent
|
||||||
|
} from 'src/app/submission/sections/upload/file/section-upload-file.component';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This component represents a single bitstream contained in the submission
|
||||||
|
*/
|
||||||
|
@Component({
|
||||||
|
selector: 'ds-submission-upload-section-file',
|
||||||
|
// styleUrls: ['./section-upload-file.component.scss'],
|
||||||
|
styleUrls: ['../../../../../../../app/submission/sections/upload/file/section-upload-file.component.scss'],
|
||||||
|
// templateUrl: './section-upload-file.component.html'
|
||||||
|
templateUrl: '../../../../../../../app/submission/sections/upload/file/section-upload-file.component.html'
|
||||||
|
})
|
||||||
|
export class SubmissionSectionUploadFileComponent
|
||||||
|
extends BaseComponent {
|
||||||
|
}
|
@@ -142,6 +142,10 @@ import {
|
|||||||
import { NgxGalleryModule } from '@kolkov/ngx-gallery';
|
import { NgxGalleryModule } from '@kolkov/ngx-gallery';
|
||||||
import { WorkspaceItemsDeletePageComponent } from './app/workspace-items-delete-page/workspace-items-delete/workspace-items-delete.component';
|
import { WorkspaceItemsDeletePageComponent } from './app/workspace-items-delete-page/workspace-items-delete/workspace-items-delete.component';
|
||||||
import { ThumbnailComponent } from './app/thumbnail/thumbnail.component';
|
import { ThumbnailComponent } from './app/thumbnail/thumbnail.component';
|
||||||
|
import { SubmissionSectionUploadFileComponent } from './app/submission/sections/upload/file/section-upload-file.component';
|
||||||
|
import { ItemStatusComponent } from './app/item-page/edit-item-page/item-status/item-status.component';
|
||||||
|
import { EditBitstreamPageComponent } from './app/bitstream-page/edit-bitstream-page/edit-bitstream-page.component';
|
||||||
|
import { FormModule } from '../../app/shared/form/form.module';
|
||||||
|
|
||||||
const DECLARATIONS = [
|
const DECLARATIONS = [
|
||||||
FileSectionComponent,
|
FileSectionComponent,
|
||||||
@@ -219,6 +223,9 @@ const DECLARATIONS = [
|
|||||||
MediaViewerVideoComponent,
|
MediaViewerVideoComponent,
|
||||||
WorkspaceItemsDeletePageComponent,
|
WorkspaceItemsDeletePageComponent,
|
||||||
ThumbnailComponent,
|
ThumbnailComponent,
|
||||||
|
SubmissionSectionUploadFileComponent,
|
||||||
|
ItemStatusComponent,
|
||||||
|
EditBitstreamPageComponent,
|
||||||
];
|
];
|
||||||
|
|
||||||
@NgModule({
|
@NgModule({
|
||||||
@@ -277,6 +284,7 @@ const DECLARATIONS = [
|
|||||||
DsoSharedModule,
|
DsoSharedModule,
|
||||||
SystemWideAlertModule,
|
SystemWideAlertModule,
|
||||||
NgxGalleryModule,
|
NgxGalleryModule,
|
||||||
|
FormModule,
|
||||||
],
|
],
|
||||||
declarations: DECLARATIONS,
|
declarations: DECLARATIONS,
|
||||||
exports: [
|
exports: [
|
||||||
|
@@ -10377,9 +10377,9 @@ socket.io-client@^4.4.1:
|
|||||||
socket.io-parser "~4.2.1"
|
socket.io-parser "~4.2.1"
|
||||||
|
|
||||||
socket.io-parser@~4.2.1:
|
socket.io-parser@~4.2.1:
|
||||||
version "4.2.2"
|
version "4.2.3"
|
||||||
resolved "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-4.2.2.tgz"
|
resolved "https://registry.yarnpkg.com/socket.io-parser/-/socket.io-parser-4.2.3.tgz#926bcc6658e2ae0883dc9dee69acbdc76e4e3667"
|
||||||
integrity sha512-DJtziuKypFkMMHCm2uIshOYC7QaylbtzQwiMYDuCKy3OPkjLzu4B2vAhTlqipRHHzrI0NJeBAizTK7X+6m1jVw==
|
integrity sha512-JMafRntWVO2DCJimKsRTh/wnqVvO4hrfwOqtO7f+uzwsQMuxO6VwImtYxaQ+ieoyshWOTJyV0fA21lccEXRPpQ==
|
||||||
dependencies:
|
dependencies:
|
||||||
"@socket.io/component-emitter" "~3.1.0"
|
"@socket.io/component-emitter" "~3.1.0"
|
||||||
debug "~4.3.1"
|
debug "~4.3.1"
|
||||||
|
Reference in New Issue
Block a user