Merge remote-tracking branch 'origin/main' into w2p-96252_Reduce-main-bundle-size_Reorganize-shared-module_REBASE-ON-7.4

This commit is contained in:
Yury Bondarenko
2022-12-08 16:19:41 +01:00
240 changed files with 8193 additions and 2095 deletions

View File

@@ -1,12 +1,8 @@
import { Location } from '@angular/common';
import { Component, OnInit } from '@angular/core';
import { Component } from '@angular/core';
import { Router } from '@angular/router';
import { TranslateService } from '@ngx-translate/core';
import { Observable } from 'rxjs';
import { map, switchMap } from 'rxjs/operators';
import { AuthService } from '../../core/auth/auth.service';
import { METADATA_IMPORT_SCRIPT_NAME, ScriptDataService } from '../../core/data/processes/script-data.service';
import { EPerson } from '../../core/eperson/models/eperson.model';
import { ProcessParameter } from '../../process-page/processes/process-parameter.model';
import { isNotEmpty } from '../../shared/empty.util';
import { NotificationsService } from '../../shared/notifications/notifications.service';

View File

@@ -13,32 +13,34 @@
[paginationOptions]="pageConfig"
[pageInfoState]="(bitstreamFormats | async)?.payload"
[collectionSize]="(bitstreamFormats | async)?.payload?.totalElements"
[hideGear]="true"
[hideGear]="false"
[hidePagerWhenSinglePage]="true">
<div class="table-responsive">
<table id="formats" class="table table-striped table-hover">
<thead>
<tr>
<th scope="col"></th>
<th scope="col">{{'admin.registries.bitstream-formats.table.name' | translate}}</th>
<th scope="col">{{'admin.registries.bitstream-formats.table.mimetype' | translate}}</th>
<th scope="col">{{'admin.registries.bitstream-formats.table.supportLevel.head' | translate}}</th>
</tr>
<tr>
<th scope="col"></th>
<th scope="col">{{'admin.registries.bitstream-formats.table.id' | translate}}</th>
<th scope="col">{{'admin.registries.bitstream-formats.table.name' | translate}}</th>
<th scope="col">{{'admin.registries.bitstream-formats.table.mimetype' | translate}}</th>
<th scope="col">{{'admin.registries.bitstream-formats.table.supportLevel.head' | translate}}</th>
</tr>
</thead>
<tbody>
<tr *ngFor="let bitstreamFormat of (bitstreamFormats | async)?.payload?.page">
<td>
<label>
<input type="checkbox"
[checked]="isSelected(bitstreamFormat) | async"
(change)="selectBitStreamFormat(bitstreamFormat, $event)"
>
</label>
</td>
<td><a [routerLink]="['/admin/registries/bitstream-formats', bitstreamFormat.id, 'edit']">{{bitstreamFormat.shortDescription}}</a></td>
<td><a [routerLink]="['/admin/registries/bitstream-formats', bitstreamFormat.id, 'edit']">{{bitstreamFormat.mimetype}} <span *ngIf="bitstreamFormat.internal">({{'admin.registries.bitstream-formats.table.internal' | translate}})</span></a></td>
<td><a [routerLink]="['/admin/registries/bitstream-formats', bitstreamFormat.id, 'edit']">{{'admin.registries.bitstream-formats.table.supportLevel.'+bitstreamFormat.supportLevel | translate}}</a></td>
</tr>
<tr *ngFor="let bitstreamFormat of (bitstreamFormats | async)?.payload?.page">
<td>
<label>
<input type="checkbox"
[checked]="isSelected(bitstreamFormat) | async"
(change)="selectBitStreamFormat(bitstreamFormat, $event)"
>
</label>
</td>
<td><a [routerLink]="['/admin/registries/bitstream-formats', bitstreamFormat.id, 'edit']">{{bitstreamFormat.id}}</a></td>
<td><a [routerLink]="['/admin/registries/bitstream-formats', bitstreamFormat.id, 'edit']">{{bitstreamFormat.shortDescription}}</a></td>
<td><a [routerLink]="['/admin/registries/bitstream-formats', bitstreamFormat.id, 'edit']">{{bitstreamFormat.mimetype}} <span *ngIf="bitstreamFormat.internal">({{'admin.registries.bitstream-formats.table.internal' | translate}})</span></a></td>
<td><a [routerLink]="['/admin/registries/bitstream-formats', bitstreamFormat.id, 'edit']">{{'admin.registries.bitstream-formats.table.supportLevel.'+bitstreamFormat.supportLevel | translate}}</a></td>
</tr>
</tbody>
</table>
</div>

View File

@@ -129,16 +129,19 @@ describe('BitstreamFormatsComponent', () => {
});
it('should contain the correct formats', () => {
const unknownName: HTMLElement = fixture.debugElement.query(By.css('#formats tr:nth-child(1) td:nth-child(2)')).nativeElement;
const unknownName: HTMLElement = fixture.debugElement.query(By.css('#formats tr:nth-child(1) td:nth-child(3)')).nativeElement;
expect(unknownName.textContent).toBe('Unknown');
const licenseName: HTMLElement = fixture.debugElement.query(By.css('#formats tr:nth-child(2) td:nth-child(2)')).nativeElement;
const UUID: HTMLElement = fixture.debugElement.query(By.css('#formats tr:nth-child(1) td:nth-child(2)')).nativeElement;
expect(UUID.textContent).toBe('test-uuid-1');
const licenseName: HTMLElement = fixture.debugElement.query(By.css('#formats tr:nth-child(2) td:nth-child(3)')).nativeElement;
expect(licenseName.textContent).toBe('License');
const ccLicenseName: HTMLElement = fixture.debugElement.query(By.css('#formats tr:nth-child(3) td:nth-child(2)')).nativeElement;
const ccLicenseName: HTMLElement = fixture.debugElement.query(By.css('#formats tr:nth-child(3) td:nth-child(3)')).nativeElement;
expect(ccLicenseName.textContent).toBe('CC License');
const adobeName: HTMLElement = fixture.debugElement.query(By.css('#formats tr:nth-child(4) td:nth-child(2)')).nativeElement;
const adobeName: HTMLElement = fixture.debugElement.query(By.css('#formats tr:nth-child(4) td:nth-child(3)')).nativeElement;
expect(adobeName.textContent).toBe('Adobe PDF');
});
});

View File

@@ -1,12 +1,11 @@
import { Component, OnDestroy, OnInit } from '@angular/core';
import { combineLatest as observableCombineLatest, Observable, zip } from 'rxjs';
import { combineLatest as observableCombineLatest, Observable} from 'rxjs';
import { RemoteData } from '../../../core/data/remote-data';
import { PaginatedList } from '../../../core/data/paginated-list.model';
import { PaginationComponentOptions } from '../../../shared/pagination/pagination-component-options.model';
import { BitstreamFormat } from '../../../core/shared/bitstream-format.model';
import { BitstreamFormatDataService } from '../../../core/data/bitstream-format-data.service';
import { map, mergeMap, switchMap, take, toArray } from 'rxjs/operators';
import { hasValue } from '../../../shared/empty.util';
import { NotificationsService } from '../../../shared/notifications/notifications.service';
import { Router } from '@angular/router';
import { TranslateService } from '@ngx-translate/core';
@@ -29,21 +28,14 @@ export class BitstreamFormatsComponent implements OnInit, OnDestroy {
*/
bitstreamFormats: Observable<RemoteData<PaginatedList<BitstreamFormat>>>;
/**
* The current pagination configuration for the page used by the FindAll method
* Currently simply renders all bitstream formats
*/
config: FindListOptions = Object.assign(new FindListOptions(), {
elementsPerPage: 20
});
/**
* The current pagination configuration for the page
* Currently simply renders all bitstream formats
*/
pageConfig: PaginationComponentOptions = Object.assign(new PaginationComponentOptions(), {
id: 'rbp',
pageSize: 20
pageSize: 20,
pageSizeOptions: [20, 40, 60, 80, 100]
});
constructor(private notificationsService: NotificationsService,
@@ -51,7 +43,7 @@ export class BitstreamFormatsComponent implements OnInit, OnDestroy {
private translateService: TranslateService,
private bitstreamFormatService: BitstreamFormatDataService,
private paginationService: PaginationService,
) {
) {
}
@@ -149,7 +141,7 @@ export class BitstreamFormatsComponent implements OnInit, OnDestroy {
ngOnInit(): void {
this.bitstreamFormats = this.paginationService.getFindListOptions(this.pageConfig.id, this.config).pipe(
this.bitstreamFormats = this.paginationService.getFindListOptions(this.pageConfig.id, this.pageConfig).pipe(
switchMap((findListOptions: FindListOptions) => {
return this.bitstreamFormatService.findAll(findListOptions);
})

View File

@@ -19,10 +19,7 @@ import { RestResponse } from '../../../core/cache/response.models';
import { MetadataSchema } from '../../../core/metadata/metadata-schema.model';
import { createSuccessfulRemoteDataObject$ } from '../../../shared/remote-data.utils';
import { PaginationService } from '../../../core/pagination/pagination.service';
import { PaginationComponentOptions } from '../../../shared/pagination/pagination-component-options.model';
import { SortDirection, SortOptions } from '../../../core/cache/models/sort-options.model';
import { PaginationServiceStub } from '../../../shared/testing/pagination-service.stub';
import { FindListOptions } from '../../../core/data/find-list-options.model';
describe('MetadataRegistryComponent', () => {
let comp: MetadataRegistryComponent;

View File

@@ -25,6 +25,7 @@
<thead>
<tr>
<th></th>
<th scope="col">{{'admin.registries.schema.fields.table.id' | translate}}</th>
<th scope="col">{{'admin.registries.schema.fields.table.field' | translate}}</th>
<th scope="col">{{'admin.registries.schema.fields.table.scopenote' | translate}}</th>
</tr>
@@ -39,6 +40,7 @@
(change)="selectMetadataField(field, $event)">
</label>
</td>
<td class="selectable-row" (click)="editField(field)">{{field.id}}</td>
<td class="selectable-row" (click)="editField(field)">{{schema?.prefix}}.{{field.element}}<label *ngIf="field.qualifier">.</label>{{field.qualifier}}</td>
<td class="selectable-row" (click)="editField(field)">{{field.scopeNote}}</td>
</tr>

View File

@@ -23,11 +23,8 @@ import { MetadataSchema } from '../../../core/metadata/metadata-schema.model';
import { MetadataField } from '../../../core/metadata/metadata-field.model';
import { createSuccessfulRemoteDataObject$ } from '../../../shared/remote-data.utils';
import { VarDirective } from '../../../shared/utils/var.directive';
import { PaginationComponentOptions } from '../../../shared/pagination/pagination-component-options.model';
import { SortDirection, SortOptions } from '../../../core/cache/models/sort-options.model';
import { PaginationService } from '../../../core/pagination/pagination.service';
import { PaginationServiceStub } from '../../../shared/testing/pagination-service.stub';
import { FindListOptions } from '../../../core/data/find-list-options.model';
describe('MetadataSchemaComponent', () => {
let comp: MetadataSchemaComponent;
@@ -169,10 +166,10 @@ describe('MetadataSchemaComponent', () => {
});
it('should contain the correct fields', () => {
const editorField: HTMLElement = fixture.debugElement.query(By.css('#metadata-fields tr:nth-child(1) td:nth-child(2)')).nativeElement;
const editorField: HTMLElement = fixture.debugElement.query(By.css('#metadata-fields tr:nth-child(1) td:nth-child(3)')).nativeElement;
expect(editorField.textContent).toBe('mock.contributor.editor');
const illustratorField: HTMLElement = fixture.debugElement.query(By.css('#metadata-fields tr:nth-child(2) td:nth-child(2)')).nativeElement;
const illustratorField: HTMLElement = fixture.debugElement.query(By.css('#metadata-fields tr:nth-child(2) td:nth-child(3)')).nativeElement;
expect(illustratorField.textContent).toBe('mock.contributor.illustrator');
});

View File

@@ -2,7 +2,7 @@ import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing';
import { MenuService } from '../../../shared/menu/menu.service';
import { MenuServiceStub } from '../../../shared/testing/menu-service.stub';
import { CSSVariableService } from '../../../shared/sass-helper/sass-helper.service';
import { CSSVariableService } from '../../../shared/sass-helper/css-variable.service';
import { CSSVariableServiceStub } from '../../../shared/testing/css-variable-service.stub';
import { Component } from '@angular/core';
import { NoopAnimationsModule } from '@angular/platform-browser/animations';

View File

@@ -6,7 +6,7 @@ import { ScriptDataService } from '../../core/data/processes/script-data.service
import { AdminSidebarComponent } from './admin-sidebar.component';
import { MenuService } from '../../shared/menu/menu.service';
import { MenuServiceStub } from '../../shared/testing/menu-service.stub';
import { CSSVariableService } from '../../shared/sass-helper/sass-helper.service';
import { CSSVariableService } from '../../shared/sass-helper/css-variable.service';
import { CSSVariableServiceStub } from '../../shared/testing/css-variable-service.stub';
import { AuthServiceStub } from '../../shared/testing/auth-service.stub';
import { AuthService } from '../../core/auth/auth.service';
@@ -16,7 +16,6 @@ import { NgbModal } from '@ng-bootstrap/ng-bootstrap';
import { RouterTestingModule } from '@angular/router/testing';
import { ActivatedRoute } from '@angular/router';
import { AuthorizationDataService } from '../../core/data/feature-authorization/authorization-data.service';
import { FeatureID } from '../../core/data/feature-authorization/feature-id';
import createSpy = jasmine.createSpy;
import { createSuccessfulRemoteDataObject } from '../../shared/remote-data.utils';
import { Item } from '../../core/shared/item.model';

View File

@@ -5,7 +5,7 @@ import { AuthService } from '../../core/auth/auth.service';
import { slideSidebar } from '../../shared/animations/slide';
import { MenuComponent } from '../../shared/menu/menu.component';
import { MenuService } from '../../shared/menu/menu.service';
import { CSSVariableService } from '../../shared/sass-helper/sass-helper.service';
import { CSSVariableService } from '../../shared/sass-helper/css-variable.service';
import { AuthorizationDataService } from '../../core/data/feature-authorization/authorization-data.service';
import { MenuID } from '../../shared/menu/menu-id.model';
import { ActivatedRoute } from '@angular/router';
@@ -69,7 +69,7 @@ export class AdminSidebarComponent extends MenuComponent implements OnInit {
*/
ngOnInit(): void {
super.ngOnInit();
this.sidebarWidth = this.variableService.getVariable('sidebarItemsWidth');
this.sidebarWidth = this.variableService.getVariable('--ds-sidebar-items-width');
this.authService.isAuthenticated()
.subscribe((loggedIn: boolean) => {
if (loggedIn) {

View File

@@ -3,7 +3,7 @@ import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing';
import { ExpandableAdminSidebarSectionComponent } from './expandable-admin-sidebar-section.component';
import { MenuService } from '../../../shared/menu/menu.service';
import { MenuServiceStub } from '../../../shared/testing/menu-service.stub';
import { CSSVariableService } from '../../../shared/sass-helper/sass-helper.service';
import { CSSVariableService } from '../../../shared/sass-helper/css-variable.service';
import { CSSVariableServiceStub } from '../../../shared/testing/css-variable-service.stub';
import { of as observableOf } from 'rxjs';
import { Component } from '@angular/core';

View File

@@ -2,7 +2,7 @@ import { Component, Inject, Injector, OnInit } from '@angular/core';
import { rotate } from '../../../shared/animations/rotate';
import { AdminSidebarSectionComponent } from '../admin-sidebar-section/admin-sidebar-section.component';
import { slide } from '../../../shared/animations/slide';
import { CSSVariableService } from '../../../shared/sass-helper/sass-helper.service';
import { CSSVariableService } from '../../../shared/sass-helper/css-variable.service';
import { bgColor } from '../../../shared/animations/bgColor';
import { MenuService } from '../../../shared/menu/menu.service';
import { combineLatest as combineLatestObservable, Observable } from 'rxjs';
@@ -65,7 +65,7 @@ export class ExpandableAdminSidebarSectionComponent extends AdminSidebarSectionC
*/
ngOnInit(): void {
super.ngOnInit();
this.sidebarActiveBg = this.variableService.getVariable('adminSidebarActiveBg');
this.sidebarActiveBg = this.variableService.getVariable('--ds-admin-sidebar-active-bg');
this.sidebarCollapsed = this.menuService.isMenuCollapsed(this.menuID);
this.sidebarPreviewCollapsed = this.menuService.isMenuPreviewCollapsed(this.menuID);
this.expanded = combineLatestObservable(this.active, this.sidebarCollapsed, this.sidebarPreviewCollapsed)

View File

@@ -18,7 +18,7 @@ import { AngularticsProviderMock } from './shared/mocks/angulartics-provider.ser
import { AuthServiceMock } from './shared/mocks/auth.service.mock';
import { AuthService } from './core/auth/auth.service';
import { MenuService } from './shared/menu/menu.service';
import { CSSVariableService } from './shared/sass-helper/sass-helper.service';
import { CSSVariableService } from './shared/sass-helper/css-variable.service';
import { CSSVariableServiceStub } from './shared/testing/css-variable-service.stub';
import { MenuServiceStub } from './shared/testing/menu-service.stub';
import { HostWindowService } from './shared/host-window.service';

View File

@@ -25,13 +25,12 @@ import { HostWindowState } from './shared/search/host-window.reducer';
import { NativeWindowRef, NativeWindowService } from './core/services/window.service';
import { isAuthenticationBlocking } from './core/auth/selectors';
import { AuthService } from './core/auth/auth.service';
import { CSSVariableService } from './shared/sass-helper/sass-helper.service';
import { CSSVariableService } from './shared/sass-helper/css-variable.service';
import { environment } from '../environments/environment';
import { models } from './core/core.module';
import { ThemeService } from './shared/theme-support/theme.service';
import { IdleModalComponent } from './shared/idle-modal/idle-modal.component';
import { distinctNext } from './core/shared/distinct-next';
import { ModalBeforeDismiss } from './shared/interfaces/modal-before-dismiss.interface';
@Component({
selector: 'ds-app',
@@ -110,18 +109,8 @@ export class AppComponent implements OnInit, AfterViewInit {
}
private storeCSSVariables() {
this.cssService.addCSSVariable('xlMin', '1200px');
this.cssService.addCSSVariable('mdMin', '768px');
this.cssService.addCSSVariable('lgMin', '576px');
this.cssService.addCSSVariable('smMin', '0');
this.cssService.addCSSVariable('adminSidebarActiveBg', '#0f1b28');
this.cssService.addCSSVariable('sidebarItemsWidth', '250px');
this.cssService.addCSSVariable('collapsedSidebarWidth', '53.234px');
this.cssService.addCSSVariable('totalSidebarWidth', '303.234px');
// const vars = variables.locals || {};
// Object.keys(vars).forEach((name: string) => {
// this.cssService.addCSSVariable(name, vars[name]);
// })
this.cssService.clearCSSVariables();
this.cssService.addCSSVariables(this.cssService.getCSSVariablesFromStylesheets(this.document));
}
ngAfterViewInit() {

View File

@@ -1,4 +1,4 @@
import * as fromRouter from '@ngrx/router-store';
import { routerReducer, RouterReducerState } from '@ngrx/router-store';
import { ActionReducerMap, createSelector, MemoizedSelector } from '@ngrx/store';
import {
ePeopleRegistryReducer,
@@ -35,7 +35,7 @@ import {
ObjectSelectionListState,
objectSelectionReducer
} from './shared/object-select/object-select.reducer';
import { cssVariablesReducer, CSSVariablesState } from './shared/sass-helper/sass-helper.reducer';
import { cssVariablesReducer, CSSVariablesState } from './shared/sass-helper/css-variable.reducer';
import { hostWindowReducer, HostWindowState } from './shared/search/host-window.reducer';
import {
@@ -53,7 +53,7 @@ import { MenusState } from './shared/menu/menus-state.model';
import { correlationIdReducer } from './correlation-id/correlation-id.reducer';
export interface AppState {
router: fromRouter.RouterReducerState;
router: RouterReducerState;
hostWindow: HostWindowState;
forms: FormState;
metadataRegistry: MetadataRegistryState;
@@ -75,7 +75,7 @@ export interface AppState {
}
export const appReducers: ActionReducerMap<AppState> = {
router: fromRouter.routerReducer,
router: routerReducer,
hostWindow: hostWindowReducer,
forms: formReducer,
metadataRegistry: metadataRegistryReducer,

View File

@@ -26,7 +26,7 @@ import {
import { FormGroup } from '@angular/forms';
import { TranslateService } from '@ngx-translate/core';
import { DynamicCustomSwitchModel } from '../../shared/form/builder/ds-dynamic-form-ui/models/custom-switch/custom-switch.model';
import { cloneDeep } from 'lodash';
import cloneDeep from 'lodash/cloneDeep';
import { BitstreamDataService } from '../../core/data/bitstream-data.service';
import {
getAllSucceededRemoteDataPayload,

View File

@@ -1,5 +1,5 @@
import { LegacyBitstreamUrlResolver } from './legacy-bitstream-url.resolver';
import { of as observableOf, EMPTY } from 'rxjs';
import { EMPTY } from 'rxjs';
import { BitstreamDataService } from '../core/data/bitstream-data.service';
import { RemoteData } from '../core/data/remote-data';
import { TestScheduler } from 'rxjs/testing';

View File

@@ -1,7 +1,6 @@
import { first } from 'rxjs/operators';
import { BrowseByGuard } from './browse-by-guard';
import { of as observableOf } from 'rxjs';
import { BrowseDefinitionDataService } from '../core/browse/browse-definition-data.service';
import { createSuccessfulRemoteDataObject$ } from '../shared/remote-data.utils';
import { BrowseDefinition } from '../core/shared/browse-definition.model';
import { BrowseByDataType } from './browse-by-switcher/browse-by-decorator';

View File

@@ -16,7 +16,7 @@ import { Collection } from '../../core/shared/collection.model';
import { RemoteData } from '../../core/data/remote-data';
import { PaginationComponentOptions } from '../../shared/pagination/pagination-component-options.model';
import { SortDirection, SortOptions } from '../../core/cache/models/sort-options.model';
import { ChangeDetectionStrategy, EventEmitter } from '@angular/core';
import { EventEmitter } from '@angular/core';
import { HostWindowService } from '../../shared/host-window.service';
import { HostWindowServiceStub } from '../../shared/testing/host-window-service.stub';
import { By } from '@angular/platform-browser';
@@ -41,7 +41,7 @@ import {
} from '../../shared/remote-data.utils';
import { createPaginatedList } from '../../shared/testing/utils.test';
import { AuthorizationDataService } from '../../core/data/feature-authorization/authorization-data.service';
import { MyDSpacePageComponent, SEARCH_CONFIG_SERVICE } from '../../my-dspace-page/my-dspace-page.component';
import { SEARCH_CONFIG_SERVICE } from '../../my-dspace-page/my-dspace-page.component';
import { SearchConfigurationServiceStub } from '../../shared/testing/search-configuration-service.stub';
import { GroupDataService } from '../../core/eperson/group-data.service';
import { LinkHeadService } from '../../core/services/link-head.service';

View File

@@ -11,11 +11,11 @@
</div>
<div>
<span class="font-weight-bold">{{'collection.source.controls.harvest.last' | translate}}</span>
<span>{{contentSource?.message ? contentSource?.message : 'collection.source.controls.harvest.no-information'|translate }}</span>
<span>{{contentSource?.lastHarvested ? contentSource?.lastHarvested : 'collection.source.controls.harvest.no-information'|translate }}</span>
</div>
<div>
<span class="font-weight-bold">{{'collection.source.controls.harvest.message' | translate}}</span>
<span>{{contentSource?.lastHarvested ? contentSource?.lastHarvested : 'collection.source.controls.harvest.no-information'|translate }}</span>
<span>{{contentSource?.message ? contentSource?.message: 'collection.source.controls.harvest.no-information'|translate }}</span>
</div>
<button *ngIf="!(testConfigRunning$ |async)" class="btn btn-secondary"

View File

@@ -8,8 +8,7 @@ import {
DynamicInputModel,
DynamicOptionControlModel,
DynamicRadioGroupModel,
DynamicSelectModel,
DynamicTextAreaModel
DynamicSelectModel
} from '@ng-dynamic-forms/core';
import { Location } from '@angular/common';
import { TranslateService } from '@ngx-translate/core';
@@ -23,7 +22,7 @@ import { RemoteData } from '../../../core/data/remote-data';
import { Collection } from '../../../core/shared/collection.model';
import { first, map, switchMap, take } from 'rxjs/operators';
import { ActivatedRoute, Router } from '@angular/router';
import { cloneDeep } from 'lodash';
import cloneDeep from 'lodash/cloneDeep';
import { CollectionDataService } from '../../../core/data/collection-data.service';
import { getFirstSucceededRemoteData, getFirstCompletedRemoteData } from '../../../core/shared/operators';
import { MetadataConfig } from '../../../core/shared/metadata-config.model';

View File

@@ -17,9 +17,6 @@ import { PageInfo } from '../../core/shared/page-info.model';
import { HostWindowService } from '../../shared/host-window.service';
import { HostWindowServiceStub } from '../../shared/testing/host-window-service.stub';
import { SelectableListService } from '../../shared/object-list/selectable-list/selectable-list.service';
import { PaginationComponentOptions } from '../../shared/pagination/pagination-component-options.model';
import { SortDirection, SortOptions } from '../../core/cache/models/sort-options.model';
import { of as observableOf } from 'rxjs';
import { PaginationService } from '../../core/pagination/pagination.service';
import { getMockThemeService } from '../../shared/mocks/theme-service.mock';
import { ThemeService } from '../../shared/theme-support/theme.service';
@@ -29,7 +26,6 @@ import { GroupDataService } from '../../core/eperson/group-data.service';
import { LinkHeadService } from '../../core/services/link-head.service';
import { ConfigurationDataService } from '../../core/data/configuration-data.service';
import { SearchConfigurationService } from '../../core/shared/search/search-configuration.service';
import { SearchServiceStub } from '../../shared/testing/search-service.stub';
import { ConfigurationProperty } from '../../core/shared/configuration-property.model';
import { createPaginatedList } from '../../shared/testing/utils.test';
import { SearchConfigurationServiceStub } from '../../shared/testing/search-configuration-service.stub';

View File

@@ -17,9 +17,6 @@ import { HostWindowService } from '../../shared/host-window.service';
import { HostWindowServiceStub } from '../../shared/testing/host-window-service.stub';
import { CommunityDataService } from '../../core/data/community-data.service';
import { SelectableListService } from '../../shared/object-list/selectable-list/selectable-list.service';
import { PaginationComponentOptions } from '../../shared/pagination/pagination-component-options.model';
import { SortDirection, SortOptions } from '../../core/cache/models/sort-options.model';
import { of as observableOf } from 'rxjs';
import { PaginationService } from '../../core/pagination/pagination.service';
import { getMockThemeService } from '../../shared/mocks/theme-service.mock';
import { ThemeService } from '../../shared/theme-support/theme.service';

View File

@@ -7,7 +7,6 @@ import { createSelector } from '@ngrx/store';
* notation packages up all of the exports into a single object.
*/
import { AuthState } from './auth.reducer';
import { AppState } from '../../app.reducer';
import { CoreState } from '../core-state.model';
import { coreSelector } from '../core.selectors';

View File

@@ -1,3 +1,4 @@
// eslint-disable-next-line import/no-namespace
import * as deepFreeze from 'deep-freeze';
import { Operation } from 'fast-json-patch';
import { Item } from '../shared/item.model';

View File

@@ -1,3 +1,4 @@
// eslint-disable-next-line import/no-namespace
import * as deepFreeze from 'deep-freeze';
import { RemoveFromObjectCacheAction } from './object-cache.actions';
import { serverSyncBufferReducer } from './server-sync-buffer.reducer';

View File

@@ -20,7 +20,6 @@ import { NotificationsService } from '../shared/notifications/notifications.serv
import { SelectableListService } from '../shared/object-list/selectable-list/selectable-list.service';
import { ObjectSelectService } from '../shared/object-select/object-select.service';
import { PaginationComponentOptions } from '../shared/pagination/pagination-component-options.model';
import { CSSVariableService } from '../shared/sass-helper/sass-helper.service';
import { SidebarService } from '../shared/sidebar/sidebar.service';
import { AuthenticatedGuard } from './auth/authenticated.guard';
import { AuthStatus } from './auth/models/auth-status.model';
@@ -240,7 +239,6 @@ const PROVIDERS = [
DefaultChangeAnalyzer,
ArrayMoveChangeAnalyzer,
ObjectSelectService,
CSSVariableService,
MenuService,
ObjectUpdatesService,
SearchService,

View File

@@ -10,6 +10,7 @@ import { ResourceType } from '../../shared/resource-type';
import { BaseDataService } from './base-data.service';
import { HALDataService } from './hal-data-service.interface';
import { dataService, getDataServiceFor } from './data-service.decorator';
import { v4 as uuidv4 } from 'uuid';
class TestService extends BaseDataService<any> {
}
@@ -28,7 +29,7 @@ let testType;
describe('@dataService/getDataServiceFor', () => {
beforeEach(() => {
testType = new ResourceType('testType-' + new Date().getTime());
testType = new ResourceType(`testType-${uuidv4()}`);
});
it('should register a resourcetype for a dataservice', () => {

View File

@@ -3,7 +3,7 @@ import { ChangeAnalyzer } from './change-analyzer';
import { Injectable } from '@angular/core';
import { DSpaceObject } from '../shared/dspace-object.model';
import { MetadataMap } from '../shared/metadata.models';
import { cloneDeep } from 'lodash';
import cloneDeep from 'lodash/cloneDeep';
/**
* A class to determine what differs between two

View File

@@ -1,3 +1,4 @@
// eslint-disable-next-line import/no-namespace
import * as deepFreeze from 'deep-freeze';
import {
AddFieldUpdateAction,

View File

@@ -1,3 +1,4 @@
// eslint-disable-next-line import/no-namespace
import * as deepFreeze from 'deep-freeze';
import {
RequestConfigureAction,

View File

@@ -4,7 +4,7 @@ import { HttpHeaders } from '@angular/common/http';
import { createSelector, MemoizedSelector, select, Store } from '@ngrx/store';
import { Observable } from 'rxjs';
import { filter, map, take, tap } from 'rxjs/operators';
import { cloneDeep } from 'lodash';
import cloneDeep from 'lodash/cloneDeep';
import { hasValue, isEmpty, isNotEmpty, hasNoValue } from '../../shared/empty.util';
import { ObjectCacheEntry } from '../cache/object-cache.reducer';
import { ObjectCacheService } from '../cache/object-cache.service';

View File

@@ -1,3 +1,4 @@
// eslint-disable-next-line import/no-namespace
import * as deepFreeze from 'deep-freeze';
import { indexReducer, MetaIndexState } from './index.reducer';

View File

@@ -3,7 +3,6 @@ import { hasValue, isNotEmpty } from '../../shared/empty.util';
import { coreSelector } from '../core.selectors';
import { URLCombiner } from '../url-combiner/url-combiner';
import { IndexState, MetaIndexState } from './index.reducer';
import * as parse from 'url-parse';
import { IndexName } from './index-name.model';
import { CoreState } from '../core-state.model';
@@ -21,17 +20,21 @@ import { CoreState } from '../core-state.model';
*/
export const getUrlWithoutEmbedParams = (url: string): string => {
if (isNotEmpty(url)) {
const parsed = parse(url);
if (isNotEmpty(parsed.query)) {
const parts = parsed.query.split(/[?|&]/)
.filter((part: string) => isNotEmpty(part))
.filter((part: string) => !(part.startsWith('embed=') || part.startsWith('embed.size=')));
let args = '';
if (isNotEmpty(parts)) {
args = `?${parts.join('&')}`;
try {
const parsed = new URL(url);
if (isNotEmpty(parsed.search)) {
const parts = parsed.search.split(/[?|&]/)
.filter((part: string) => isNotEmpty(part))
.filter((part: string) => !(part.startsWith('embed=') || part.startsWith('embed.size=')));
let args = '';
if (isNotEmpty(parts)) {
args = `?${parts.join('&')}`;
}
url = new URLCombiner(parsed.origin, parsed.pathname, args).toString();
return url;
}
url = new URLCombiner(parsed.origin, parsed.pathname, args).toString();
return url;
} catch (e) {
// Ignore parsing errors. By default, we return the original string below.
}
}
@@ -44,15 +47,19 @@ export const getUrlWithoutEmbedParams = (url: string): string => {
*/
export const getEmbedSizeParams = (url: string): { name: string, size: number }[] => {
if (isNotEmpty(url)) {
const parsed = parse(url);
if (isNotEmpty(parsed.query)) {
return parsed.query.split(/[?|&]/)
.filter((part: string) => isNotEmpty(part))
.map((part: string) => part.match(/^embed.size=([^=]+)=(\d+)$/))
.filter((matches: RegExpMatchArray) => hasValue(matches) && hasValue(matches[1]) && hasValue(matches[2]))
.map((matches: RegExpMatchArray) => {
return { name: matches[1], size: Number(matches[2]) };
});
try {
const parsed = new URL(url);
if (isNotEmpty(parsed.search)) {
return parsed.search.split(/[?|&]/)
.filter((part: string) => isNotEmpty(part))
.map((part: string) => part.match(/^embed.size=([^=]+)=(\d+)$/))
.filter((matches: RegExpMatchArray) => hasValue(matches) && hasValue(matches[1]) && hasValue(matches[2]))
.map((matches: RegExpMatchArray) => {
return { name: matches[1], size: Number(matches[2]) };
});
}
} catch (e) {
// Ignore parsing errors. By default, we return an empty result below.
}
}

View File

@@ -1,3 +1,4 @@
// eslint-disable-next-line import/no-namespace
import * as deepFreeze from 'deep-freeze';
import {

View File

@@ -1,8 +1,7 @@
import { filter, map, pairwise } from 'rxjs/operators';
import { Injectable } from '@angular/core';
import { Actions, createEffect, ofType } from '@ngrx/effects';
import * as fromRouter from '@ngrx/router-store';
import { RouterNavigationAction } from '@ngrx/router-store';
import { RouterNavigationAction, ROUTER_NAVIGATION } from '@ngrx/router-store';
import { Router } from '@angular/router';
import { RouteUpdateAction } from './router.actions';
@@ -14,7 +13,7 @@ export class RouterEffects {
*/
routeChange$ = createEffect(() => this.actions$
.pipe(
ofType(fromRouter.ROUTER_NAVIGATION),
ofType(ROUTER_NAVIGATION),
pairwise(),
map((actions: RouterNavigationAction[]) =>
actions.map((navigateAction) => {

View File

@@ -4,7 +4,7 @@ import { ActivatedRoute, NavigationEnd, Params, Router, RouterStateSnapshot, } f
import { combineLatest, Observable } from 'rxjs';
import { createSelector, MemoizedSelector, select, Store } from '@ngrx/store';
import { isEqual } from 'lodash';
import isEqual from 'lodash/isEqual';
import { AddParameterAction, SetParameterAction, SetParametersAction, SetQueryParameterAction, SetQueryParametersAction } from './route.actions';
import { coreSelector } from '../core.selectors';

View File

@@ -3,7 +3,7 @@ import { getMockRequestService } from '../../shared/mocks/request.service.mock';
import { RequestService } from '../data/request.service';
import { HALEndpointService } from './hal-endpoint.service';
import { EndpointMapRequest } from '../data/request.models';
import { combineLatest as observableCombineLatest, Observable, of as observableOf } from 'rxjs';
import { combineLatest as observableCombineLatest, of as observableOf } from 'rxjs';
import { environment } from '../../../environments/environment';
import { RemoteDataBuildService } from '../cache/builders/remote-data-build.service';
import { createSuccessfulRemoteDataObject$ } from '../../shared/remote-data.utils';

View File

@@ -5,7 +5,8 @@ import {
MetadataValueFilter,
MetadatumViewModel
} from './metadata.models';
import { groupBy, sortBy } from 'lodash';
import groupBy from 'lodash/groupBy';
import sortBy from 'lodash/sortBy';
/**
* Utility class for working with DSpace object metadata.

View File

@@ -26,6 +26,8 @@ import { SearchConfigurationService } from './search-configuration.service';
import { PaginationServiceStub } from '../../../shared/testing/pagination-service.stub';
import { RequestEntry } from '../../data/request-entry.model';
import { Angulartics2 } from 'angulartics2';
import { SearchFilterConfig } from '../../../shared/search/models/search-filter-config.model';
import anything = jasmine.anything;
@Component({ template: '' })
class DummyComponent {
@@ -36,7 +38,7 @@ describe('SearchService', () => {
let searchService: SearchService;
const router = new RouterStub();
const route = new ActivatedRouteStub();
const searchConfigService = {paginationID: 'page-id'};
const searchConfigService = { paginationID: 'page-id' };
beforeEach(() => {
TestBed.configureTestingModule({
imports: [
@@ -103,7 +105,8 @@ describe('SearchService', () => {
};
const paginationService = new PaginationServiceStub();
const searchConfigService = {paginationID: 'page-id'};
const searchConfigService = { paginationID: 'page-id' };
const requestService = getMockRequestService();
beforeEach(() => {
TestBed.configureTestingModule({
@@ -119,7 +122,7 @@ describe('SearchService', () => {
providers: [
{ provide: Router, useValue: router },
{ provide: RouteService, useValue: routeServiceStub },
{ provide: RequestService, useValue: getMockRequestService() },
{ provide: RequestService, useValue: requestService },
{ provide: RemoteDataBuildService, useValue: remoteDataBuildService },
{ provide: HALEndpointService, useValue: halService },
{ provide: CommunityDataService, useValue: {} },
@@ -138,13 +141,13 @@ describe('SearchService', () => {
it('should call the navigate method on the Router with view mode list parameter as a parameter when setViewMode is called', () => {
searchService.setViewMode(ViewMode.ListElement);
expect(paginationService.updateRouteWithUrl).toHaveBeenCalledWith('page-id', ['/search'], {page: 1}, { view: ViewMode.ListElement }
expect(paginationService.updateRouteWithUrl).toHaveBeenCalledWith('page-id', ['/search'], { page: 1 }, { view: ViewMode.ListElement }
);
});
it('should call the navigate method on the Router with view mode grid parameter as a parameter when setViewMode is called', () => {
searchService.setViewMode(ViewMode.GridElement);
expect(paginationService.updateRouteWithUrl).toHaveBeenCalledWith('page-id', ['/search'], {page: 1}, { view: ViewMode.GridElement }
expect(paginationService.updateRouteWithUrl).toHaveBeenCalledWith('page-id', ['/search'], { page: 1 }, { view: ViewMode.GridElement }
);
});
@@ -191,5 +194,23 @@ describe('SearchService', () => {
expect((searchService as any).rdb.buildFromHref).toHaveBeenCalledWith(endPoint);
});
});
describe('when getFacetValuesFor is called with a filterQuery', () => {
it('should add the encoded filterQuery to the args list', () => {
jasmine.getEnv().allowRespy(true);
const spyRequest = spyOn((searchService as any), 'request').and.stub();
spyOn(requestService, 'send').and.returnValue(true);
const searchFilterConfig = new SearchFilterConfig();
searchFilterConfig._links = {
self: {
href: 'https://demo.dspace.org/',
},
};
searchService.getFacetValuesFor(searchFilterConfig, 1, undefined, 'filter&Query');
expect(spyRequest).toHaveBeenCalledWith(anything(), 'https://demo.dspace.org?page=0&size=5&prefix=filter%26Query');
});
});
});
});

View File

@@ -3,7 +3,6 @@ import { combineLatest as observableCombineLatest, Observable } from 'rxjs';
import { Injectable, OnDestroy } from '@angular/core';
import { map, switchMap, take } from 'rxjs/operators';
import { FollowLinkConfig } from '../../../shared/utils/follow-link-config.model';
import { PaginatedList } from '../../data/paginated-list.model';
import { ResponseParsingService } from '../../data/parsing.service';
import { RemoteData } from '../../data/remote-data';
import { GetRequest } from '../../data/request.models';
@@ -271,7 +270,7 @@ export class SearchService implements OnDestroy {
let href;
let args: string[] = [];
if (hasValue(filterQuery)) {
args.push(`prefix=${filterQuery}`);
args.push(`prefix=${encodeURIComponent(filterQuery)}`);
}
if (hasValue(searchOptions)) {
searchOptions = Object.assign(new PaginatedSearchOptions({}), searchOptions, {

View File

@@ -1,6 +1,6 @@
/* eslint-disable max-classes-per-file */
import { EquatableObject, excludeFromEquals, fieldsForEquals } from './equals.decorators';
import { cloneDeep } from 'lodash';
import cloneDeep from 'lodash/cloneDeep';
class Dog extends EquatableObject<Dog> {
public name: string;

View File

@@ -1,7 +1,7 @@
import { CorrelationIdService } from './correlation-id.service';
import { CookieServiceMock } from '../shared/mocks/cookie.service.mock';
import { UUIDService } from '../core/shared/uuid.service';
import { MockStore, provideMockStore } from '@ngrx/store/testing';
import { MockStore } from '@ngrx/store/testing';
import { TestBed } from '@angular/core/testing';
import { Store, StoreModule } from '@ngrx/store';
import { appReducers, AppState, storeModuleConfig } from '../app.reducer';

View File

@@ -5,7 +5,7 @@ import { getFirstCompletedRemoteData } from '../core/shared/operators';
import { find, map } from 'rxjs/operators';
import { NotificationsService } from '../shared/notifications/notifications.service';
import { TranslateService } from '@ngx-translate/core';
import { hasValue, isEmpty, isNotEmpty, hasNoValue } from '../shared/empty.util';
import { hasValue, isEmpty, isNotEmpty } from '../shared/empty.util';
import { RemoteData } from '../core/data/remote-data';
import { Router } from '@angular/router';
import { ProcessDataService } from '../core/data/processes/process-data.service';

View File

@@ -6,8 +6,7 @@
<ds-listable-object-component-loader [object]="item" [viewMode]="viewMode" class="pb-4">
</ds-listable-object-component-loader>
</div>
<button (click)="onLoadMore()" class="btn btn-primary search-button mt-4 float-left ng-tns-c290-40">Load
more...</button>
<button (click)="onLoadMore()" class="btn btn-primary search-button mt-4 float-left ng-tns-c290-40"> {{'vocabulary-treeview.load-more' | translate }} ...</button>
</div>
<ds-error *ngIf="itemRD?.hasFailed" message="{{'error.recent-submissions' | translate}}"></ds-error>
<ds-loading *ngIf="!itemRD || itemRD.isLoading" message="{{'loading.recent-submissions' | translate}}">

View File

@@ -17,9 +17,6 @@ import { HostWindowService } from '../../shared/host-window.service';
import { HostWindowServiceStub } from '../../shared/testing/host-window-service.stub';
import { CommunityDataService } from '../../core/data/community-data.service';
import { SelectableListService } from '../../shared/object-list/selectable-list/selectable-list.service';
import { of as observableOf } from 'rxjs';
import { PaginationComponentOptions } from '../../shared/pagination/pagination-component-options.model';
import { SortDirection, SortOptions } from '../../core/cache/models/sort-options.model';
import { PaginationService } from '../../core/pagination/pagination.service';
import { getMockThemeService } from '../../shared/mocks/theme-service.mock';
import { ThemeService } from '../../shared/theme-support/theme.service';

View File

@@ -13,7 +13,7 @@ import { makeStateKey, TransferState } from '@angular/platform-browser';
import { APP_CONFIG, AppConfig } from '../config/app-config.interface';
import { environment } from '../environments/environment';
import { AppState } from './app.reducer';
import { isEqual } from 'lodash';
import isEqual from 'lodash/isEqual';
import { TranslateService } from '@ngx-translate/core';
import { LocaleService } from './core/locale/locale.service';
import { Angulartics2DSpace } from './statistics/angulartics/dspace-provider';

View File

@@ -1,12 +1,11 @@
import { Observable } from 'rxjs/internal/Observable';
import { waitForAsync, ComponentFixture, inject, TestBed } from '@angular/core/testing';
import { Component, NO_ERRORS_SCHEMA } from '@angular/core';
import { ActivatedRoute } from '@angular/router';
import { NoopAnimationsModule } from '@angular/platform-browser/animations';
import { of as observableOf, of } from 'rxjs';
import { of as observableOf } from 'rxjs';
import { TranslateModule } from '@ngx-translate/core';
import { cold } from 'jasmine-marbles';
import { ItemAuthorizationsComponent, BitstreamMapValue } from './item-authorizations.component';
import { ItemAuthorizationsComponent } from './item-authorizations.component';
import { Bitstream } from '../../../core/shared/bitstream.model';
import { Bundle } from '../../../core/shared/bundle.model';
import { Item } from '../../../core/shared/item.model';
@@ -14,8 +13,6 @@ import { LinkService } from '../../../core/cache/builders/link.service';
import { getMockLinkService } from '../../../shared/mocks/link-service.mock';
import { createSuccessfulRemoteDataObject, createSuccessfulRemoteDataObject$ } from '../../../shared/remote-data.utils';
import { createPaginatedList, createTestComponent } from '../../../shared/testing/utils.test';
import { PaginatedList, buildPaginatedList } from '../../../core/data/paginated-list.model';
import { PageInfo } from '../../../core/shared/page-info.model';
describe('ItemAuthorizationsComponent test suite', () => {
let comp: ItemAuthorizationsComponent;

View File

@@ -1,4 +1,4 @@
import { isEqual } from 'lodash';
import isEqual from 'lodash/isEqual';
import { DSONameService } from '../../../core/breadcrumbs/dso-name.service';
import { Component, OnDestroy, OnInit } from '@angular/core';
import { ActivatedRoute } from '@angular/router';

View File

@@ -17,10 +17,7 @@ import { createSuccessfulRemoteDataObject$ } from '../../../../../shared/remote-
import { createPaginatedList } from '../../../../../shared/testing/utils.test';
import { RequestService } from '../../../../../core/data/request.service';
import { PaginationService } from '../../../../../core/pagination/pagination.service';
import { PaginationComponentOptions } from '../../../../../shared/pagination/pagination-component-options.model';
import { SortDirection, SortOptions } from '../../../../../core/cache/models/sort-options.model';
import { PaginationServiceStub } from '../../../../../shared/testing/pagination-service.stub';
import { FindListOptions } from '../../../../../core/data/find-list-options.model';
describe('PaginatedDragAndDropBitstreamListComponent', () => {
let comp: PaginatedDragAndDropBitstreamListComponent;

View File

@@ -1,6 +1,6 @@
import { Component, Input, OnChanges, OnInit, SimpleChanges, ViewChild, ViewContainerRef } from '@angular/core';
import { Bitstream } from '../../../../core/shared/bitstream.model';
import { cloneDeep } from 'lodash';
import cloneDeep from 'lodash/cloneDeep';
import { ObjectUpdatesService } from '../../../../core/data/object-updates/object-updates.service';
import { Observable } from 'rxjs';
import { BitstreamFormat } from '../../../../core/shared/bitstream-format.model';

View File

@@ -5,7 +5,7 @@ import {
} from '../../../../core/shared/operators';
import { hasValue, isNotEmpty } from '../../../../shared/empty.util';
import { RegistryService } from '../../../../core/registry/registry.service';
import { cloneDeep } from 'lodash';
import cloneDeep from 'lodash/cloneDeep';
import { BehaviorSubject, Observable, of as observableOf } from 'rxjs';
import { map } from 'rxjs/operators';
import { ObjectUpdatesService } from '../../../../core/data/object-updates/object-updates.service';

View File

@@ -3,7 +3,7 @@ import { Item } from '../../../core/shared/item.model';
import { ItemDataService } from '../../../core/data/item-data.service';
import { ObjectUpdatesService } from '../../../core/data/object-updates/object-updates.service';
import { ActivatedRoute, Router } from '@angular/router';
import { cloneDeep } from 'lodash';
import cloneDeep from 'lodash/cloneDeep';
import { first, switchMap } from 'rxjs/operators';
import { getFirstCompletedRemoteData } from '../../../core/shared/operators';
import { RemoteData } from '../../../core/data/remote-data';

View File

@@ -1,18 +1,16 @@
<ds-metadata-field-wrapper [label]="label | translate">
<ng-container *ngFor="let mdValue of mdValues; let last=last;">
<ng-container *ngTemplateOutlet="(renderMarkdown ? markdown : simple); context: {value: mdValue.value, classes: 'dont-break-out preserve-line-breaks'}">
<ng-container *ngTemplateOutlet="(renderMarkdown ? markdown : simple); context: {value: mdValue.value}">
</ng-container>
<span class="separator" *ngIf="!last" [innerHTML]="separator"></span>
</ng-container>
</ds-metadata-field-wrapper>
<ng-template #markdown let-value="value" let-classes="classes">
<span class="{{classes}}" [innerHTML]="value | dsMarkdown | async">
<ng-template #markdown let-value="value">
<span class="dont-break-out" [innerHTML]="value | dsMarkdown | async">
</span>
</ng-template>
<ng-template #simple let-value="value" let-classes="classes">
<span class="{{classes}}">
{{value}}
</span>
<ng-template #simple let-value="value">
<span class="dont-break-out preserve-line-breaks">{{value}}</span>
</ng-template>

View File

@@ -16,11 +16,10 @@ import { MockBitstreamFormat1 } from '../../../../shared/mocks/item.mock';
import { By } from '@angular/platform-browser';
import { NotificationsService } from '../../../../shared/notifications/notifications.service';
import { NotificationsServiceStub } from '../../../../shared/testing/notifications-service.stub';
import { PaginationComponentOptions } from '../../../../shared/pagination/pagination-component-options.model';
import { SortDirection, SortOptions } from '../../../../core/cache/models/sort-options.model';
import { PaginationService } from '../../../../core/pagination/pagination.service';
import { PaginationServiceStub } from '../../../../shared/testing/pagination-service.stub';
import { FindListOptions } from '../../../../core/data/find-list-options.model';
import { APP_CONFIG } from 'src/config/app-config.interface';
import { environment } from 'src/environments/environment';
describe('FullFileSectionComponent', () => {
let comp: FullFileSectionComponent;
@@ -72,7 +71,8 @@ describe('FullFileSectionComponent', () => {
providers: [
{ provide: BitstreamDataService, useValue: bitstreamDataService },
{ provide: NotificationsService, useValue: new NotificationsServiceStub() },
{ provide: PaginationService, useValue: paginationService }
{ provide: PaginationService, useValue: paginationService },
{ provide: APP_CONFIG, useValue: environment },
],
schemas: [NO_ERRORS_SCHEMA]

View File

@@ -1,4 +1,4 @@
import { Component, Input, OnInit } from '@angular/core';
import { Component, Inject, Input, OnInit } from '@angular/core';
import { Observable } from 'rxjs';
import { BitstreamDataService } from '../../../../core/data/bitstream-data.service';
@@ -14,6 +14,7 @@ import { NotificationsService } from '../../../../shared/notifications/notificat
import { TranslateService } from '@ngx-translate/core';
import { hasValue, isEmpty } from '../../../../shared/empty.util';
import { PaginationService } from '../../../../core/pagination/pagination.service';
import { AppConfig, APP_CONFIG } from 'src/config/app-config.interface';
/**
* This component renders the file section of the item
@@ -34,26 +35,26 @@ export class FullFileSectionComponent extends FileSectionComponent implements On
originals$: Observable<RemoteData<PaginatedList<Bitstream>>>;
licenses$: Observable<RemoteData<PaginatedList<Bitstream>>>;
pageSize = 5;
originalOptions = Object.assign(new PaginationComponentOptions(), {
id: 'obo',
currentPage: 1,
pageSize: this.pageSize
pageSize: this.appConfig.item.bitstream.pageSize
});
licenseOptions = Object.assign(new PaginationComponentOptions(), {
id: 'lbo',
currentPage: 1,
pageSize: this.pageSize
pageSize: this.appConfig.item.bitstream.pageSize
});
constructor(
bitstreamDataService: BitstreamDataService,
protected notificationsService: NotificationsService,
protected translateService: TranslateService,
protected paginationService: PaginationService
protected paginationService: PaginationService,
@Inject(APP_CONFIG) protected appConfig: AppConfig
) {
super(bitstreamDataService, notificationsService, translateService);
super(bitstreamDataService, notificationsService, translateService, appConfig);
}
ngOnInit(): void {

View File

@@ -17,6 +17,8 @@ import { MetadataFieldWrapperComponent } from '../../../field-components/metadat
import { createPaginatedList } from '../../../../shared/testing/utils.test';
import { NotificationsService } from '../../../../shared/notifications/notifications.service';
import { NotificationsServiceStub } from '../../../../shared/testing/notifications-service.stub';
import { APP_CONFIG } from 'src/config/app-config.interface';
import { environment } from 'src/environments/environment';
describe('FileSectionComponent', () => {
let comp: FileSectionComponent;
@@ -65,7 +67,8 @@ describe('FileSectionComponent', () => {
declarations: [FileSectionComponent, VarDirective, FileSizePipe, MetadataFieldWrapperComponent],
providers: [
{ provide: BitstreamDataService, useValue: bitstreamDataService },
{ provide: NotificationsService, useValue: new NotificationsServiceStub() }
{ provide: NotificationsService, useValue: new NotificationsServiceStub() },
{ provide: APP_CONFIG, useValue: environment }
],
schemas: [NO_ERRORS_SCHEMA]

View File

@@ -1,4 +1,4 @@
import { Component, Input, OnInit } from '@angular/core';
import { Component, Inject, Input, OnInit } from '@angular/core';
import { BehaviorSubject } from 'rxjs';
import { BitstreamDataService } from '../../../../core/data/bitstream-data.service';
@@ -10,6 +10,7 @@ import { PaginatedList } from '../../../../core/data/paginated-list.model';
import { NotificationsService } from '../../../../shared/notifications/notifications.service';
import { TranslateService } from '@ngx-translate/core';
import { getFirstCompletedRemoteData } from '../../../../core/shared/operators';
import { AppConfig, APP_CONFIG } from 'src/config/app-config.interface';
/**
* This component renders the file section of the item
@@ -35,13 +36,15 @@ export class FileSectionComponent implements OnInit {
isLastPage: boolean;
pageSize = 5;
pageSize: number;
constructor(
protected bitstreamDataService: BitstreamDataService,
protected notificationsService: NotificationsService,
protected translateService: TranslateService
protected translateService: TranslateService,
@Inject(APP_CONFIG) protected appConfig: AppConfig
) {
this.pageSize = this.appConfig.item.bitstream.pageSize;
}
ngOnInit(): void {

View File

@@ -35,4 +35,10 @@ export class GenericItemPageFieldComponent extends ItemPageFieldComponent {
*/
@Input() label: string;
/**
* Whether the {@link MarkdownPipe} should be used to render this metadata.
*/
@Input() enableMarkdown = false;
}

View File

@@ -76,7 +76,7 @@
<ds-generic-item-page-field [item]="object"
[fields]="['dc.subject']"
[separator]="','"
[separator]="', '"
[label]="'item.page.subject'">
</ds-generic-item-page-field>
<ds-generic-item-page-field [item]="object"

View File

@@ -28,7 +28,6 @@ import { TruncatableService } from '../../../../shared/truncatable/truncatable.s
import { TruncatePipe } from '../../../../shared/utils/truncate.pipe';
import { GenericItemPageFieldComponent } from '../../field-components/specific-field/generic/generic-item-page-field.component';
import { compareArraysUsing, compareArraysUsingIds } from './item-relationships-utils';
import { ItemComponent } from './item.component';
import { createPaginatedList } from '../../../../shared/testing/utils.test';
import { RouteService } from '../../../../core/services/route.service';
import { MetadataValue } from '../../../../core/shared/metadata.models';

View File

@@ -61,7 +61,7 @@
<ds-generic-item-page-field [item]="object"
[fields]="['dc.subject']"
[separator]="','"
[separator]="', '"
[label]="'item.page.subject'">
</ds-generic-item-page-field>
<ds-generic-item-page-field [item]="object"

View File

@@ -11,10 +11,6 @@ import { cold, hot } from 'jasmine-marbles';
import { MyDSpaceConfigurationValueType } from './my-dspace-configuration-value-type';
import { PaginationServiceStub } from '../shared/testing/pagination-service.stub';
import { Context } from '../core/shared/context.model';
import { LinkService } from '../core/cache/builders/link.service';
import { HALEndpointService } from '../core/shared/hal-endpoint.service';
import { RequestService } from '../core/data/request.service';
import { RemoteDataBuildService } from '../core/cache/builders/remote-data-build.service';
import { HALEndpointServiceStub } from '../shared/testing/hal-endpoint-service.stub';
import { getMockRemoteDataBuildService } from '../shared/mocks/remote-data-build.service.mock';

View File

@@ -1,4 +1,4 @@
<div class="nav-item dropdown expandable-navbar-section"
<div class="nav-item dropdown expandable-navbar-section text-md-center"
*ngVar="(active | async) as isActive"
(keyup.enter)="isActive ? deactivateSection($event) : activateSection($event)"
(keyup.space)="isActive ? deactivateSection($event) : activateSection($event)"

View File

@@ -1,3 +1,10 @@
.expandable-navbar-section {
display: flex;
height: 100%;
flex-direction: column;
justify-content: center;
}
.dropdown-menu {
overflow: hidden;
min-width: 100%;

View File

@@ -1,3 +1,3 @@
<div class="nav-item navbar-section">
<div class="nav-item navbar-section text-md-center">
<ng-container *ngComponentOutlet="(sectionMap$ | async).get(section.id).component; injector: (sectionMap$ | async).get(section.id).injector;"></ng-container>
</div>
</div>

View File

@@ -0,0 +1,5 @@
.navbar-section {
display: flex;
align-items: center;
height: 100%;
}

View File

@@ -1,10 +1,13 @@
<nav [ngClass]="{'open': !(menuCollapsed | async)}" [@slideMobileNav]="!(windowService.isXsOrSm() | async) ? 'default' : ((menuCollapsed | async) ? 'collapsed' : 'expanded')"
class="navbar navbar-light navbar-expand-md p-md-0 navbar-container" role="navigation" [attr.aria-label]="'nav.main.description' | translate">
<!-- TODO remove navbar-container class when https://github.com/twbs/bootstrap/issues/24726 is fixed -->
<div class="container">
<div class="navbar-inner-container w-100" [class.container]="!(isXsOrSm$ | async)">
<div class="reset-padding-md w-100">
<div id="collapsingNav">
<ul class="navbar-nav navbar-navigation mr-auto shadow-none">
<li *ngIf="(isXsOrSm$ | async) && (isAuthenticated$ | async)">
<ds-user-menu [inExpandableNavbar]="true"></ds-user-menu>
</li>
<ng-container *ngFor="let section of (sections | async)">
<ng-container *ngComponentOutlet="(sectionMap$ | async).get(section.id)?.component; injector: (sectionMap$ | async).get(section.id)?.injector;"></ng-container>
</ng-container>

View File

@@ -6,13 +6,14 @@ nav.navbar {
/** Mobile menu styling **/
@media screen and (max-width: map-get($grid-breakpoints, md)-0.02) {
.navbar {
width: 100vw;
width: 100%;
background-color: var(--bs-white);
position: absolute;
overflow: hidden;
height: 0;
&.open {
height: 100vh; //doesn't matter because wrapper is sticky
height: auto;
min-height: 100vh; //doesn't matter because wrapper is sticky
}
}
}
@@ -27,7 +28,7 @@ nav.navbar {
/* TODO remove when https://github.com/twbs/bootstrap/issues/24726 is fixed */
.navbar-expand-md.navbar-container {
@media screen and (max-width: map-get($grid-breakpoints, md)-0.02) {
> .container {
> .navbar-inner-container {
padding: 0 var(--bs-spacer);
}
padding: 0;

View File

@@ -22,9 +22,17 @@ import { Item } from '../core/shared/item.model';
import { AuthorizationDataService } from '../core/data/feature-authorization/authorization-data.service';
import { ThemeService } from '../shared/theme-support/theme.service';
import { getMockThemeService } from '../shared/mocks/theme-service.mock';
import { Store, StoreModule } from '@ngrx/store';
import { AppState, storeModuleConfig } from '../app.reducer';
import { authReducer } from '../core/auth/auth.reducer';
import { provideMockStore } from '@ngrx/store/testing';
import { AuthTokenInfo } from '../core/auth/models/auth-token-info.model';
import { EPersonMock } from '../shared/testing/eperson.mock';
let comp: NavbarComponent;
let fixture: ComponentFixture<NavbarComponent>;
let store: Store<AppState>;
let initialState: any;
const authorizationService = jasmine.createSpyObj('authorizationService', {
isAuthorized: observableOf(true)
@@ -83,10 +91,24 @@ describe('NavbarComponent', () => {
}
),
];
initialState = {
core: {
auth: {
authenticated: true,
loaded: true,
blocking: false,
loading: false,
authToken: new AuthTokenInfo('test_token'),
userId: EPersonMock.id,
authMethods: []
}
}
};
TestBed.configureTestingModule({
imports: [
TranslateModule.forRoot(),
StoreModule.forRoot({ auth: authReducer }, storeModuleConfig),
NoopAnimationsModule,
ReactiveFormsModule,
RouterTestingModule],
@@ -99,6 +121,7 @@ describe('NavbarComponent', () => {
{ provide: ActivatedRoute, useValue: routeStub },
{ provide: BrowseService, useValue: { getBrowseDefinitions: createSuccessfulRemoteDataObject$(buildPaginatedList(undefined, browseDefinitions)) } },
{ provide: AuthorizationDataService, useValue: authorizationService },
provideMockStore({ initialState }),
],
schemas: [NO_ERRORS_SCHEMA]
})
@@ -107,7 +130,7 @@ describe('NavbarComponent', () => {
// synchronous beforeEach
beforeEach(() => {
store = TestBed.inject(Store);
fixture = TestBed.createComponent(NavbarComponent);
comp = fixture.componentInstance;

View File

@@ -8,6 +8,10 @@ import { ActivatedRoute } from '@angular/router';
import { AuthorizationDataService } from '../core/data/feature-authorization/authorization-data.service';
import { MenuID } from '../shared/menu/menu-id.model';
import { ThemeService } from '../shared/theme-support/theme.service';
import { Observable } from 'rxjs';
import { select, Store } from '@ngrx/store';
import { AppState } from '../app.reducer';
import { isAuthenticated } from '../core/auth/selectors';
/**
* Component representing the public navbar
@@ -25,18 +29,29 @@ export class NavbarComponent extends MenuComponent {
*/
menuID = MenuID.PUBLIC;
/**
* Whether user is authenticated.
* @type {Observable<string>}
*/
public isAuthenticated$: Observable<boolean>;
public isXsOrSm$: Observable<boolean>;
constructor(protected menuService: MenuService,
protected injector: Injector,
public windowService: HostWindowService,
public browseService: BrowseService,
public authorizationService: AuthorizationDataService,
public route: ActivatedRoute,
protected themeService: ThemeService
protected themeService: ThemeService,
private store: Store<AppState>,
) {
super(menuService, injector, authorizationService, route, themeService);
}
ngOnInit(): void {
super.ngOnInit();
this.isXsOrSm$ = this.windowService.isXsOrSm();
this.isAuthenticated$ = this.store.pipe(select(isAuthenticated));
}
}

View File

@@ -4,7 +4,7 @@ import { HostWindowResizeAction } from '../shared/host-window.actions';
import { Observable } from 'rxjs';
import { provideMockActions } from '@ngrx/effects/testing';
import { cold, hot } from 'jasmine-marbles';
import * as fromRouter from '@ngrx/router-store';
import { ROUTER_NAVIGATION } from '@ngrx/router-store';
import { CollapseMenuAction } from '../shared/menu/menu.actions';
import { MenuService } from '../shared/menu/menu.service';
import { MenuServiceStub } from '../shared/testing/menu-service.stub';
@@ -43,7 +43,7 @@ describe('NavbarEffects', () => {
describe('routeChange$', () => {
it('should return a COLLAPSE action in response to an UPDATE_LOCATION action', () => {
actions = hot('--a-', { a: { type: fromRouter.ROUTER_NAVIGATION } });
actions = hot('--a-', { a: { type: ROUTER_NAVIGATION } });
const expected = cold('--b-', { b: new CollapseMenuAction(MenuID.PUBLIC) });

View File

@@ -1,7 +1,7 @@
import { first, map, switchMap } from 'rxjs/operators';
import { Injectable } from '@angular/core';
import { Actions, createEffect, ofType } from '@ngrx/effects';
import * as fromRouter from '@ngrx/router-store';
import { ROUTER_NAVIGATION } from '@ngrx/router-store';
import { HostWindowActionTypes } from '../shared/host-window.actions';
import {
@@ -33,7 +33,7 @@ export class NavbarEffects {
*/
routeChange$ = createEffect(() => this.actions$
.pipe(
ofType(fromRouter.ROUTER_NAVIGATION),
ofType(ROUTER_NAVIGATION),
map(() => new CollapseMenuAction(this.menuID))
));
/**

View File

@@ -21,6 +21,7 @@ const effects = [
const ENTRY_COMPONENTS = [
// put only entry components that use custom decorator
NavbarSectionComponent,
ExpandableNavbarSectionComponent,
ThemedExpandableNavbarSectionComponent,
];
@@ -34,11 +35,9 @@ const ENTRY_COMPONENTS = [
CoreModule.forRoot()
],
declarations: [
...ENTRY_COMPONENTS,
NavbarComponent,
ThemedNavbarComponent,
NavbarSectionComponent,
ExpandableNavbarSectionComponent,
ThemedExpandableNavbarSectionComponent,
],
providers: [],
exports: [

View File

@@ -15,7 +15,6 @@ import {
} from '@angular/core/testing';
import { VarDirective } from '../../shared/utils/var.directive';
import { TranslateModule } from '@ngx-translate/core';
import { RouterTestingModule } from '@angular/router/testing';
import { CUSTOM_ELEMENTS_SCHEMA } from '@angular/core';
import { ProcessDetailFieldComponent } from './process-detail-field/process-detail-field.component';
import { Process } from '../processes/process.model';

View File

@@ -8,7 +8,7 @@ import { EPerson } from '../../core/eperson/models/eperson.model';
import { FormBuilderService } from '../../shared/form/builder/form-builder.service';
import { NotificationsService } from '../../shared/notifications/notifications.service';
import { EPersonDataService } from '../../core/eperson/eperson-data.service';
import { cloneDeep } from 'lodash';
import cloneDeep from 'lodash/cloneDeep';
import { createSuccessfulRemoteDataObject$ } from '../../shared/remote-data.utils';
describe('ProfilePageMetadataFormComponent', () => {

View File

@@ -11,7 +11,7 @@ import { TranslateService } from '@ngx-translate/core';
import { hasValue, isNotEmpty } from '../../shared/empty.util';
import { LangConfig } from '../../../config/lang-config.interface';
import { EPersonDataService } from '../../core/eperson/eperson-data.service';
import { cloneDeep } from 'lodash';
import cloneDeep from 'lodash/cloneDeep';
import { getRemoteDataPayload, getFirstSucceededRemoteData } from '../../core/shared/operators';
import { FormBuilderService } from '../../shared/form/builder/form-builder.service';
import { NotificationsService } from '../../shared/notifications/notifications.service';

View File

@@ -2,7 +2,6 @@ import { RegistrationGuard } from './registration.guard';
import { EpersonRegistrationService } from '../core/data/eperson-registration.service';
import { ActivatedRouteSnapshot, Router, RouterStateSnapshot } from '@angular/router';
import { AuthService } from '../core/auth/auth.service';
import { Location } from '@angular/common';
import {
createFailedRemoteDataObject$,
createSuccessfulRemoteDataObject,

View File

@@ -1,9 +1,9 @@
<div class="outer-wrapper" [class.d-none]="shouldShowFullscreenLoader">
<ds-themed-admin-sidebar></ds-themed-admin-sidebar>
<div class="inner-wrapper" [@slideSidebarPadding]="{
<div class="outer-wrapper" [class.d-none]="shouldShowFullscreenLoader" [@slideSidebarPadding]="{
value: (!(sidebarVisible | async) ? 'hidden' : (slideSidebarOver | async) ? 'shown' : 'expanded'),
params: {collapsedSidebarWidth: (collapsedSidebarWidth | async), totalSidebarWidth: (totalSidebarWidth | async)}
}">
<ds-themed-admin-sidebar></ds-themed-admin-sidebar>
<div class="inner-wrapper">
<ds-themed-header-navbar-wrapper></ds-themed-header-navbar-wrapper>
<main class="main-content">
<ds-themed-breadcrumbs></ds-themed-breadcrumbs>

View File

@@ -17,7 +17,7 @@ import { ActivatedRoute, Router } from '@angular/router';
import { RouterMock } from '../shared/mocks/router.mock';
import { MockActivatedRoute } from '../shared/mocks/active-router.mock';
import { MenuService } from '../shared/menu/menu.service';
import { CSSVariableService } from '../shared/sass-helper/sass-helper.service';
import { CSSVariableService } from '../shared/sass-helper/css-variable.service';
import { CSSVariableServiceStub } from '../shared/testing/css-variable-service.stub';
import { HostWindowService } from '../shared/host-window.service';
import { HostWindowServiceStub } from '../shared/testing/host-window-service.stub';

View File

@@ -10,7 +10,7 @@ import { MetadataService } from '../core/metadata/metadata.service';
import { HostWindowState } from '../shared/search/host-window.reducer';
import { NativeWindowRef, NativeWindowService } from '../core/services/window.service';
import { AuthService } from '../core/auth/auth.service';
import { CSSVariableService } from '../shared/sass-helper/sass-helper.service';
import { CSSVariableService } from '../shared/sass-helper/css-variable.service';
import { MenuService } from '../shared/menu/menu.service';
import { HostWindowService } from '../shared/host-window.service';
import { ThemeConfig } from '../../config/theme.model';
@@ -63,8 +63,8 @@ export class RootComponent implements OnInit {
ngOnInit() {
this.sidebarVisible = this.menuService.isMenuVisible(MenuID.ADMIN);
this.collapsedSidebarWidth = this.cssService.getVariable('collapsedSidebarWidth');
this.totalSidebarWidth = this.cssService.getVariable('totalSidebarWidth');
this.collapsedSidebarWidth = this.cssService.getVariable('--ds-collapsed-sidebar-width');
this.totalSidebarWidth = this.cssService.getVariable('--ds-total-sidebar-width');
const sidebarCollapsed = this.menuService.isMenuCollapsed(MenuID.ADMIN);
this.slideSidebarOver = combineLatestObservable([sidebarCollapsed, this.windowService.isXsOrSm()])

View File

@@ -12,7 +12,7 @@ export const slide = trigger('slide', [
export const slideMobileNav = trigger('slideMobileNav', [
state('expanded', style({ height: '100vh' })),
state('expanded', style({ height: 'auto', 'min-height': '100vh' })),
state('collapsed', style({ height: 0 })),

View File

@@ -2,11 +2,11 @@
<li *ngIf="!(isAuthenticated | async) && !(isXsOrSm$ | async) && (showAuth | async)" class="nav-item"
(click)="$event.stopPropagation();">
<div ngbDropdown #loginDrop display="dynamic" placement="bottom-right" class="d-inline-block" @fadeInOut>
<a href="javascript:void(0);" class="dropdownLogin px-1 " [attr.aria-label]="'nav.login' |translate" (click)="$event.preventDefault()" [attr.data-test]="'login-menu' | dsBrowserOnly" ngbDropdownToggle>
{{ 'nav.login' | translate }}
</a>
<a href="javascript:void(0);" class="dropdownLogin px-1" [attr.aria-label]="'nav.login' |translate"
(click)="$event.preventDefault()" [attr.data-test]="'login-menu' | dsBrowserOnly"
ngbDropdownToggle>{{ 'nav.login' | translate }}</a>
<div class="loginDropdownMenu" [ngClass]="{'pl-3 pr-3': (loading | async)}" ngbDropdownMenu
[attr.aria-label]="'nav.login' |translate">
[attr.aria-label]="'nav.login' | translate">
<ds-log-in
[isStandalonePage]="false"></ds-log-in>
</div>
@@ -19,16 +19,16 @@
</li>
<li *ngIf="(isAuthenticated | async) && !(isXsOrSm$ | async) && (showAuth | async)" class="nav-item">
<div ngbDropdown display="dynamic" placement="bottom-right" class="d-inline-block" @fadeInOut>
<a href="javascript:void(0);" role="button" [attr.aria-label]="'nav.logout' |translate" (click)="$event.preventDefault()" [title]="'nav.logout' | translate" class="px-1" [attr.data-test]="'user-menu' | dsBrowserOnly" ngbDropdownToggle>
<a href="javascript:void(0);" role="button" [attr.aria-label]="'nav.user-profile-menu-and-logout' |translate" (click)="$event.preventDefault()" [title]="'nav.user-profile-menu-and-logout' | translate" class="px-1" [attr.data-test]="'user-menu' | dsBrowserOnly" ngbDropdownToggle>
<i class="fas fa-user-circle fa-lg fa-fw"></i></a>
<div class="logoutDropdownMenu" ngbDropdownMenu [attr.aria-label]="'nav.logout' |translate">
<div class="logoutDropdownMenu" ngbDropdownMenu [attr.aria-label]="'nav.user-profile-menu-and-logout' |translate">
<ds-user-menu></ds-user-menu>
</div>
</div>
</li>
<li *ngIf="(isAuthenticated | async) && (isXsOrSm$ | async)" class="nav-item">
<a id="logoutLink" role="button" [attr.aria-label]="'nav.logout' |translate" [title]="'nav.logout' | translate" routerLink="/logout" routerLinkActive="active" class="px-1">
<i class="fas fa-user-circle fa-lg fa-fw"></i>
<i class="fas fa-sign-out-alt fa-lg fa-fw"></i>
<span class="sr-only">(current)</span>
</a>
</li>

View File

@@ -1,10 +1,13 @@
<ds-themed-loading *ngIf="(loading$ | async)"></ds-themed-loading>
<div *ngIf="!(loading$ | async)">
<span class="dropdown-item-text">{{(user$ | async)?.name}} ({{(user$ | async)?.email}})</span>
<a class="dropdown-item" [routerLink]="[profileRoute]" routerLinkActive="active">{{'nav.profile' | translate}}</a>
<a class="dropdown-item" [routerLink]="[mydspaceRoute]" routerLinkActive="active">{{'nav.mydspace' | translate}}</a>
<span class="dropdown-item-text" [class.pl-0]="inExpandableNavbar">
{{(user$ | async)?.name}}<br>
<span class="text-muted">{{(user$ | async)?.email}}</span>
</span>
<a [ngClass]="inExpandableNavbar ? 'nav-item nav-link' : 'dropdown-item'" [routerLink]="[profileRoute]" routerLinkActive="active">{{'nav.profile' | translate}}</a>
<a [ngClass]="inExpandableNavbar ? 'nav-item nav-link' : 'dropdown-item'" [routerLink]="[mydspaceRoute]" routerLinkActive="active">{{'nav.mydspace' | translate}}</a>
<div class="dropdown-divider"></div>
<ds-log-out></ds-log-out>
<ds-log-out *ngIf="!inExpandableNavbar" data-test="log-out-component"></ds-log-out>
</div>

View File

@@ -162,10 +162,24 @@ describe('UserMenuComponent', () => {
});
it('should display user name and email', () => {
const user = 'User Test (test@test.com)';
const username = 'User Test';
const email = 'test@test.com';
const span = deUserMenu.query(By.css('.dropdown-item-text'));
expect(span).toBeDefined();
expect(span.nativeElement.innerHTML).toBe(user);
expect(span.nativeElement.innerHTML).toContain(username);
expect(span.nativeElement.innerHTML).toContain(email);
});
it('should create logout component', () => {
const components = fixture.debugElement.query(By.css('[data-test="log-out-component"]'));
expect(components).toBeTruthy();
});
it('should not create logout component', () => {
component.inExpandableNavbar = true;
fixture.detectChanges();
const components = fixture.debugElement.query(By.css('[data-test="log-out-component"]'));
expect(components).toBeFalsy();
});
});

View File

@@ -1,4 +1,4 @@
import { Component, OnInit } from '@angular/core';
import { Component, Input, OnInit } from '@angular/core';
import { Observable } from 'rxjs';
import { select, Store } from '@ngrx/store';
@@ -20,6 +20,11 @@ import { getProfileModuleRoute } from '../../../app-routing-paths';
})
export class UserMenuComponent implements OnInit {
/**
* The input flag to show user details in navbar expandable menu
*/
@Input() inExpandableNavbar = false;
/**
* True if the authentication is loading.
* @type {Observable<boolean>}

View File

@@ -11,7 +11,6 @@ import { NO_ERRORS_SCHEMA } from '@angular/core';
import { DeleteComColPageComponent } from './delete-comcol-page.component';
import { NotificationsService } from '../../../notifications/notifications.service';
import { NotificationsServiceStub } from '../../../testing/notifications-service.stub';
import { RequestService } from '../../../../core/data/request.service';
import { getTestScheduler } from 'jasmine-marbles';
import { ComColDataService } from '../../../../core/data/comcol-data.service';
import { createFailedRemoteDataObject$, createNoContentRemoteDataObject$ } from '../../../remote-data.utils';

View File

@@ -10,7 +10,8 @@ import { AuthService } from '../../core/auth/auth.service';
import { CookieService } from '../../core/services/cookie.service';
import { getTestScheduler } from 'jasmine-marbles';
import { MetadataValue } from '../../core/shared/metadata.models';
import { clone, cloneDeep } from 'lodash';
import clone from 'lodash/clone';
import cloneDeep from 'lodash/cloneDeep';
import { ConfigurationDataService } from '../../core/data/configuration-data.service';
import { createFailedRemoteDataObject$, createSuccessfulRemoteDataObject$ } from '../remote-data.utils';
import { ConfigurationProperty } from '../../core/shared/configuration-property.model';
@@ -100,7 +101,7 @@ describe('BrowserKlaroService', () => {
mockConfig = {
translations: {
en: {
zz: {
purposes: {},
test: {
testeritis: testKey
@@ -158,8 +159,8 @@ describe('BrowserKlaroService', () => {
it('addAppMessages', () => {
service.addAppMessages();
expect(mockConfig.translations.en[appName]).toBeDefined();
expect(mockConfig.translations.en.purposes[purpose]).toBeDefined();
expect(mockConfig.translations.zz[appName]).toBeDefined();
expect(mockConfig.translations.zz.purposes[purpose]).toBeDefined();
});
it('translateConfiguration', () => {

View File

@@ -9,7 +9,8 @@ import { KlaroService } from './klaro.service';
import { hasValue, isEmpty, isNotEmpty } from '../empty.util';
import { CookieService } from '../../core/services/cookie.service';
import { EPersonDataService } from '../../core/eperson/eperson-data.service';
import { cloneDeep, debounce } from 'lodash';
import cloneDeep from 'lodash/cloneDeep';
import debounce from 'lodash/debounce';
import { ANONYMOUS_STORAGE_NAME_KLARO, klaroConfiguration } from './klaro-configuration';
import { Operation } from 'fast-json-patch';
import { getFirstCompletedRemoteData } from '../../core/shared/operators';
@@ -90,7 +91,7 @@ export class BrowserKlaroService extends KlaroService {
initialize() {
if (!environment.info.enablePrivacyStatement) {
delete this.klaroConfig.privacyPolicy;
this.klaroConfig.translations.en.consentNotice.description = 'cookies.consent.content-notice.description.no-privacy';
this.klaroConfig.translations.zz.consentNotice.description = 'cookies.consent.content-notice.description.no-privacy';
}
const hideGoogleAnalytics$ = this.configService.findByPropertyName(this.GOOGLE_ANALYTICS_KEY).pipe(
@@ -238,12 +239,12 @@ export class BrowserKlaroService extends KlaroService {
*/
addAppMessages() {
this.klaroConfig.services.forEach((app) => {
this.klaroConfig.translations.en[app.name] = {
this.klaroConfig.translations.zz[app.name] = {
title: this.getTitleTranslation(app.name),
description: this.getDescriptionTranslation(app.name)
};
app.purposes.forEach((purpose) => {
this.klaroConfig.translations.en.purposes[purpose] = this.getPurposeTranslation(purpose);
this.klaroConfig.translations.zz.purposes[purpose] = this.getPurposeTranslation(purpose);
});
});
}
@@ -257,7 +258,7 @@ export class BrowserKlaroService extends KlaroService {
*/
this.translateService.setDefaultLang(environment.defaultLanguage);
this.translate(this.klaroConfig.translations.en);
this.translate(this.klaroConfig.translations.zz);
}
/**

View File

@@ -54,10 +54,46 @@ export const klaroConfiguration: any = {
https://github.com/KIProtect/klaro/tree/master/src/translations
*/
translations: {
en: {
/*
The `zz` key contains default translations that will be used as fallback values.
This can e.g. be useful for defining a fallback privacy policy URL.
FOR DSPACE: We use 'zz' to map to our own i18n translations for klaro, see
translateConfiguration() in browser-klaro.service.ts. All the below i18n keys are specified
in your /src/assets/i18n/*.json5 translation pack.
*/
zz: {
acceptAll: 'cookies.consent.accept-all',
acceptSelected: 'cookies.consent.accept-selected',
app: {
close: 'cookies.consent.close',
consentModal: {
title: 'cookies.consent.content-modal.title',
description: 'cookies.consent.content-modal.description'
},
consentNotice: {
changeDescription: 'cookies.consent.update',
title: 'cookies.consent.content-notice.title',
description: 'cookies.consent.content-notice.description',
learnMore: 'cookies.consent.content-notice.learnMore',
},
decline: 'cookies.consent.decline',
ok: 'cookies.consent.ok',
poweredBy: 'Powered by Klaro!',
privacyPolicy: {
name: 'cookies.consent.content-modal.privacy-policy.name',
text: 'cookies.consent.content-modal.privacy-policy.text'
},
purposeItem: {
service: 'cookies.consent.content-modal.service',
services: 'cookies.consent.content-modal.services'
},
purposes: {
},
save: 'cookies.consent.save',
service: {
disableAll: {
description: 'cookies.consent.app.disable-all.description',
title: 'cookies.consent.app.disable-all.title'
},
optOut: {
description: 'cookies.consent.app.opt-out.description',
title: 'cookies.consent.app.opt-out.title'
@@ -65,26 +101,10 @@ export const klaroConfiguration: any = {
purpose: 'cookies.consent.app.purpose',
purposes: 'cookies.consent.app.purposes',
required: {
description: 'cookies.consent.app.required.description',
title: 'cookies.consent.app.required.title'
title: 'cookies.consent.app.required.title',
description: 'cookies.consent.app.required.description'
}
},
close: 'cookies.consent.close',
decline: 'cookies.consent.decline',
changeDescription: 'cookies.consent.update',
consentNotice: {
description: 'cookies.consent.content-notice.description',
learnMore: 'cookies.consent.content-notice.learnMore'
},
consentModal: {
description: 'cookies.consent.content-modal.description',
privacyPolicy: {
name: 'cookies.consent.content-modal.privacy-policy.name',
text: 'cookies.consent.content-modal.privacy-policy.text'
},
title: 'cookies.consent.content-modal.title'
},
purposes: {}
}
}
},
services: [

View File

@@ -0,0 +1,107 @@
import { dateToString, dateToNgbDateStruct, dateToISOFormat, isValidDate, yearFromString } from './date.util';
describe('Date Utils', () => {
describe('dateToISOFormat', () => {
it('should convert Date to YYYY-MM-DDThh:mm:ssZ string', () => {
// NOTE: month is zero indexed which is why it increases by one
expect(dateToISOFormat(new Date(Date.UTC(2022, 5, 3)))).toEqual('2022-06-03T00:00:00Z');
});
it('should convert Date string to YYYY-MM-DDThh:mm:ssZ string', () => {
expect(dateToISOFormat('2022-06-03')).toEqual('2022-06-03T00:00:00Z');
});
it('should convert Month string to YYYY-MM-DDThh:mm:ssZ string', () => {
expect(dateToISOFormat('2022-06')).toEqual('2022-06-01T00:00:00Z');
});
it('should convert Year string to YYYY-MM-DDThh:mm:ssZ string', () => {
expect(dateToISOFormat('2022')).toEqual('2022-01-01T00:00:00Z');
});
it('should convert ISO Date string to YYYY-MM-DDThh:mm:ssZ string', () => {
// NOTE: Time is always zeroed out as proven by this test.
expect(dateToISOFormat('2022-06-03T03:24:04Z')).toEqual('2022-06-03T00:00:00Z');
});
it('should convert NgbDateStruct to YYYY-MM-DDThh:mm:ssZ string', () => {
// NOTE: month is zero indexed which is why it increases by one
const date = new Date(Date.UTC(2022, 5, 3));
expect(dateToISOFormat(dateToNgbDateStruct(date))).toEqual('2022-06-03T00:00:00Z');
});
});
describe('dateToString', () => {
it('should convert Date to YYYY-MM-DD string', () => {
// NOTE: month is zero indexed which is why it increases by one
expect(dateToString(new Date(Date.UTC(2022, 5, 3)))).toEqual('2022-06-03');
});
it('should convert Date with time to YYYY-MM-DD string', () => {
// NOTE: month is zero indexed which is why it increases by one
expect(dateToString(new Date(Date.UTC(2022, 5, 3, 3, 24, 0)))).toEqual('2022-06-03');
});
it('should convert Month only to YYYY-MM-DD string', () => {
// NOTE: month is zero indexed which is why it increases by one
expect(dateToString(new Date(Date.UTC(2022, 5)))).toEqual('2022-06-01');
});
it('should convert ISO Date to YYYY-MM-DD string', () => {
expect(dateToString(new Date('2022-06-03T03:24:00Z'))).toEqual('2022-06-03');
});
it('should convert NgbDateStruct to YYYY-MM-DD string', () => {
// NOTE: month is zero indexed which is why it increases by one
const date = new Date(Date.UTC(2022, 5, 3));
expect(dateToString(dateToNgbDateStruct(date))).toEqual('2022-06-03');
});
});
describe('isValidDate', () => {
it('should return false for null', () => {
expect(isValidDate(null)).toBe(false);
});
it('should return false for empty string', () => {
expect(isValidDate('')).toBe(false);
});
it('should return false for text', () => {
expect(isValidDate('test')).toBe(false);
});
it('should return true for YYYY', () => {
expect(isValidDate('2022')).toBe(true);
});
it('should return true for YYYY-MM', () => {
expect(isValidDate('2022-12')).toBe(true);
});
it('should return true for YYYY-MM-DD', () => {
expect(isValidDate('2022-06-03')).toBe(true);
});
it('should return true for YYYY-MM-DDTHH:MM:SS', () => {
expect(isValidDate('2022-06-03T10:20:30')).toBe(true);
});
it('should return true for YYYY-MM-DDTHH:MM:SSZ', () => {
expect(isValidDate('2022-06-03T10:20:30Z')).toBe(true);
});
it('should return false for a month that does not exist', () => {
expect(isValidDate('2022-13')).toBe(false);
});
it('should return false for a day that does not exist', () => {
expect(isValidDate('2022-02-60')).toBe(false);
});
it('should return false for a time that does not exist', () => {
expect(isValidDate('2022-02-60T10:60:20')).toBe(false);
});
});
describe('yearFromString', () => {
it('should return year from YYYY string', () => {
expect(yearFromString('2022')).toEqual(2022);
});
it('should return year from YYYY-MM string', () => {
expect(yearFromString('1970-06')).toEqual(1970);
});
it('should return year from YYYY-MM-DD string', () => {
expect(yearFromString('1914-10-23')).toEqual(1914);
});
it('should return year from YYYY-MM-DDTHH:MM:SSZ string', () => {
expect(yearFromString('1914-10-23T10:20:30Z')).toEqual(1914);
});
it('should return null if invalid date', () => {
expect(yearFromString('test')).toBeNull();
});
});
});

View File

@@ -1,9 +1,8 @@
import { NgbDateStruct } from '@ng-bootstrap/ng-bootstrap';
import { isObject } from 'lodash';
import * as moment from 'moment';
import { isNull, isUndefined } from './empty.util';
import { formatInTimeZone } from 'date-fns-tz';
import { isValid } from 'date-fns';
import isObject from 'lodash/isObject';
import { hasNoValue } from './empty.util';
/**
* Returns true if the passed value is a NgbDateStruct.
@@ -31,21 +30,7 @@ export function dateToISOFormat(date: Date | NgbDateStruct | string): string {
const dateObj: Date = (date instanceof Date) ? date :
((typeof date === 'string') ? ngbDateStructToDate(stringToNgbDateStruct(date)) : ngbDateStructToDate(date));
let year = dateObj.getUTCFullYear().toString();
let month = (dateObj.getUTCMonth() + 1).toString();
let day = dateObj.getUTCDate().toString();
let hour = dateObj.getHours().toString();
let min = dateObj.getMinutes().toString();
let sec = dateObj.getSeconds().toString();
year = (year.length === 1) ? '0' + year : year;
month = (month.length === 1) ? '0' + month : month;
day = (day.length === 1) ? '0' + day : day;
hour = (hour.length === 1) ? '0' + hour : hour;
min = (min.length === 1) ? '0' + min : min;
sec = (sec.length === 1) ? '0' + sec : sec;
const dateStr = `${year}${month}${day}${hour}${min}${sec}`;
return moment.utc(dateStr, 'YYYYMMDDhhmmss').format();
return formatInTimeZone(dateObj, 'UTC', "yyyy-MM-dd'T'HH:mm:ss'Z'");
}
/**
@@ -81,7 +66,7 @@ export function stringToNgbDateStruct(date: string): NgbDateStruct {
* the NgbDateStruct object
*/
export function dateToNgbDateStruct(date?: Date): NgbDateStruct {
if (isNull(date) || isUndefined(date)) {
if (hasNoValue(date)) {
date = new Date();
}
@@ -102,16 +87,7 @@ export function dateToNgbDateStruct(date?: Date): NgbDateStruct {
*/
export function dateToString(date: Date | NgbDateStruct): string {
const dateObj: Date = (date instanceof Date) ? date : ngbDateStructToDate(date);
let year = dateObj.getUTCFullYear().toString();
let month = (dateObj.getUTCMonth() + 1).toString();
let day = dateObj.getUTCDate().toString();
year = (year.length === 1) ? '0' + year : year;
month = (month.length === 1) ? '0' + month : month;
day = (day.length === 1) ? '0' + day : day;
const dateStr = `${year}-${month}-${day}`;
return moment.utc(dateStr, 'YYYYMMDD').format('YYYY-MM-DD');
return formatInTimeZone(dateObj, 'UTC', 'yyyy-MM-dd');
}
/**
@@ -119,5 +95,15 @@ export function dateToString(date: Date | NgbDateStruct): string {
* @param date the string to be checked
*/
export function isValidDate(date: string) {
return moment(date).isValid();
return (hasNoValue(date)) ? false : isValid(new Date(date));
}
/**
* Parse given date string to a year number based on expected formats
* @param date the string to be parsed
* @param formats possible formats the string may align with. MUST be valid date-fns formats
*/
export function yearFromString(date: string) {
return isValidDate(date) ? new Date(date).getUTCFullYear() : null;
}

View File

@@ -53,8 +53,9 @@ export class AuthorizedCollectionSelectorComponent extends DSOSelectorComponent
* Perform a search for authorized collections with the current query and page
* @param query Query to search objects for
* @param page Page to retrieve
* @param useCache Whether or not to use the cache
*/
search(query: string, page: number): Observable<RemoteData<PaginatedList<SearchResult<DSpaceObject>>>> {
search(query: string, page: number, useCache: boolean = true): Observable<RemoteData<PaginatedList<SearchResult<DSpaceObject>>>> {
let searchListService$: Observable<RemoteData<PaginatedList<Collection>>> = null;
const findOptions: FindListOptions = {
currentPage: page,
@@ -69,7 +70,7 @@ export class AuthorizedCollectionSelectorComponent extends DSOSelectorComponent
findOptions);
} else {
searchListService$ = this.collectionDataService
.getAuthorizedCollection(query, findOptions, true, false, followLink('parentCommunity'));
.getAuthorizedCollection(query, findOptions, useCache, false, followLink('parentCommunity'));
}
return searchListService$.pipe(
getFirstCompletedRemoteData(),

View File

@@ -21,12 +21,12 @@
</button>
<button *ngFor="let listEntry of (listEntries$ | async)"
class="list-group-item list-group-item-action border-0 list-entry"
[ngClass]="{'bg-primary': listEntry.indexableObject.id === currentDSOId}"
[ngClass]="{'bg-primary': listEntry['id'] === currentDSOId}"
title="{{ getName(listEntry) }}"
dsHoverClass="ds-hover"
(click)="onSelect.emit(listEntry.indexableObject)" #listEntryElement>
(click)="onClick(listEntry)" #listEntryElement>
<ds-listable-object-component-loader [object]="listEntry" [viewMode]="viewMode"
[linkType]=linkTypes.None [context]="getContext(listEntry.indexableObject.id)"></ds-listable-object-component-loader>
[linkType]=linkTypes.None [context]="getContext(listEntry['id'])"></ds-listable-object-component-loader>
</button>
</ng-container>
<button *ngIf="loading"

View File

@@ -35,6 +35,14 @@ import { RemoteData } from '../../../core/data/remote-data';
import { NotificationsService } from '../../notifications/notifications.service';
import { TranslateService } from '@ngx-translate/core';
import { DSONameService } from '../../../core/breadcrumbs/dso-name.service';
import {
ListableNotificationObject
} from '../../object-list/listable-notification-object/listable-notification-object.model';
import { ListableObject } from '../../object-collection/shared/listable-object.model';
import { NotificationType } from '../../notifications/models/notification-type';
import {
LISTABLE_NOTIFICATION_OBJECT
} from '../../object-list/listable-notification-object/listable-notification-object.resource-type';
@Component({
selector: 'ds-dso-selector',
@@ -82,7 +90,7 @@ export class DSOSelectorComponent implements OnInit, OnDestroy {
/**
* List with search results of DSpace objects for the current query
*/
listEntries$: BehaviorSubject<SearchResult<DSpaceObject>[]> = new BehaviorSubject(null);
listEntries$: BehaviorSubject<ListableObject[]> = new BehaviorSubject(null);
/**
* The current page to load
@@ -116,11 +124,6 @@ export class DSOSelectorComponent implements OnInit, OnDestroy {
*/
linkTypes = CollectionElementLinkType;
/**
* Track whether the element has the mouse over it
*/
isMouseOver = false;
/**
* Array to track all subscriptions and unsubscribe them onDestroy
* @type {Array}
@@ -182,24 +185,28 @@ export class DSOSelectorComponent implements OnInit, OnDestroy {
})
);
})
).subscribe((rd) => {
this.loading = false;
if (rd.hasSucceeded) {
const currentEntries = this.listEntries$.getValue();
if (hasNoValue(currentEntries)) {
this.listEntries$.next(rd.payload.page);
} else {
this.listEntries$.next([...currentEntries, ...rd.payload.page]);
}
// Check if there are more pages available after the current one
this.hasNextPage = rd.payload.totalElements > this.listEntries$.getValue().length;
} else {
this.listEntries$.next(null);
this.hasNextPage = false;
}
).subscribe((rd: RemoteData<PaginatedList<SearchResult<DSpaceObject>>>) => {
this.updateList(rd);
}));
}
updateList(rd: RemoteData<PaginatedList<SearchResult<DSpaceObject>>>) {
this.loading = false;
const currentEntries = this.listEntries$.getValue();
if (rd.hasSucceeded) {
if (hasNoValue(currentEntries)) {
this.listEntries$.next(rd.payload.page);
} else {
this.listEntries$.next([...currentEntries, ...rd.payload.page]);
}
// Check if there are more pages available after the current one
this.hasNextPage = rd.payload.totalElements > this.listEntries$.getValue().length;
} else {
this.listEntries$.next([...(hasNoValue(currentEntries) ? [] : this.listEntries$.getValue()), new ListableNotificationObject(NotificationType.Error, 'dso-selector.results-could-not-be-retrieved', LISTABLE_NOTIFICATION_OBJECT.value)]);
this.hasNextPage = false;
}
}
/**
* Get a query to send for retrieving the current DSO
*/
@@ -211,8 +218,9 @@ export class DSOSelectorComponent implements OnInit, OnDestroy {
* Perform a search for the current query and page
* @param query Query to search objects for
* @param page Page to retrieve
* @param useCache Whether or not to use the cache
*/
search(query: string, page: number): Observable<RemoteData<PaginatedList<SearchResult<DSpaceObject>>>> {
search(query: string, page: number, useCache: boolean = true): Observable<RemoteData<PaginatedList<SearchResult<DSpaceObject>>>> {
return this.searchService.search(
new PaginatedSearchOptions({
query: query,
@@ -220,7 +228,9 @@ export class DSOSelectorComponent implements OnInit, OnDestroy {
pagination: Object.assign({}, this.defaultPagination, {
currentPage: page
})
})
}),
null,
useCache,
).pipe(
getFirstCompletedRemoteData()
);
@@ -262,7 +272,28 @@ export class DSOSelectorComponent implements OnInit, OnDestroy {
this.subs.filter((sub) => hasValue(sub)).forEach((sub) => sub.unsubscribe());
}
getName(searchResult: SearchResult<DSpaceObject>): string {
return this.dsoNameService.getName(searchResult.indexableObject);
/**
* Handles the user clicks on the {@link ListableObject}s. When the {@link listableObject} is a
* {@link ListableObject} it will retry the error when the user clicks it. Otherwise it will emit the {@link onSelect}.
*
* @param listableObject The {@link ListableObject} to evaluate
*/
onClick(listableObject: ListableObject): void {
if (hasValue((listableObject as SearchResult<DSpaceObject>).indexableObject)) {
this.onSelect.emit((listableObject as SearchResult<DSpaceObject>).indexableObject);
} else {
this.listEntries$.value.pop();
this.hasNextPage = true;
this.search(this.input.value ? this.input.value : '', this.currentPage$.value, false).pipe(
getFirstCompletedRemoteData(),
).subscribe((rd: RemoteData<PaginatedList<SearchResult<DSpaceObject>>>) => {
this.updateList(rd);
});
}
}
getName(listableObject: ListableObject): string {
return hasValue((listableObject as SearchResult<DSpaceObject>).indexableObject) ?
this.dsoNameService.getName((listableObject as SearchResult<DSpaceObject>).indexableObject) : null;
}
}

View File

@@ -9,7 +9,7 @@
<h3 class="position-relative py-1 my-3 font-weight-normal">
<hr>
<div id="create-community-or-separator" class="text-center position-absolute w-100">
<span class="px-4 bg-white">or</span>
<span class="px-4 bg-white">{{'dso-selector.create.community.or-divider' | translate}}</span>
</div>
</h3>

View File

@@ -10,7 +10,7 @@ import {
} from '@ng-dynamic-forms/core';
import {
mockInputWithTypeBindModel, MockRelationModel, mockDcTypeInputModel
mockInputWithTypeBindModel, MockRelationModel
} from '../../../mocks/form-models.mock';
import {DsDynamicTypeBindRelationService} from './ds-dynamic-type-bind-relation.service';
import {FormFieldMetadataValueObject} from '../models/form-field-metadata-value.model';

View File

@@ -1,4 +1,10 @@
import { DynamicFormControlLayout, DynamicFormGroupModel, DynamicFormGroupModelConfig, serializable } from '@ng-dynamic-forms/core';
import {
DynamicFormControlLayout,
DynamicFormControlRelation,
DynamicFormGroupModel,
DynamicFormGroupModelConfig,
serializable
} from '@ng-dynamic-forms/core';
import { Subject } from 'rxjs';
@@ -16,6 +22,7 @@ export interface DynamicConcatModelConfig extends DynamicFormGroupModelConfig {
separator: string;
value?: any;
hint?: string;
typeBindRelations?: DynamicFormControlRelation[];
relationship?: RelationshipOptions;
repeatable: boolean;
required: boolean;
@@ -29,6 +36,8 @@ export class DynamicConcatModel extends DynamicFormGroupModel {
@serializable() separator: string;
@serializable() hasLanguages = false;
@serializable() typeBindRelations: DynamicFormControlRelation[];
@serializable() typeBindHidden = false;
@serializable() relationship?: RelationshipOptions;
@serializable() repeatable?: boolean;
@serializable() required?: boolean;
@@ -55,6 +64,7 @@ export class DynamicConcatModel extends DynamicFormGroupModel {
this.metadataValue = config.metadataValue;
this.valueUpdates = new Subject<string>();
this.valueUpdates.subscribe((value: string) => this.value = value);
this.typeBindRelations = config.typeBindRelations ? config.typeBindRelations : [];
}
get value() {

View File

@@ -2,6 +2,7 @@ import { Subject } from 'rxjs';
import {
DynamicCheckboxGroupModel,
DynamicFormControlLayout,
DynamicFormControlRelation,
DynamicFormGroupModelConfig,
serializable
} from '@ng-dynamic-forms/core';
@@ -15,6 +16,7 @@ export interface DynamicListCheckboxGroupModelConfig extends DynamicFormGroupMod
groupLength?: number;
repeatable: boolean;
value?: any;
typeBindRelations?: DynamicFormControlRelation[];
}
export class DynamicListCheckboxGroupModel extends DynamicCheckboxGroupModel {
@@ -23,6 +25,7 @@ export class DynamicListCheckboxGroupModel extends DynamicCheckboxGroupModel {
@serializable() repeatable: boolean;
@serializable() groupLength: number;
@serializable() _value: VocabularyEntry[];
@serializable() typeBindRelations: DynamicFormControlRelation[];
isListGroup = true;
valueUpdates: Subject<any>;
@@ -37,6 +40,7 @@ export class DynamicListCheckboxGroupModel extends DynamicCheckboxGroupModel {
this.valueUpdates = new Subject<any>();
this.valueUpdates.subscribe((value: VocabularyEntry | VocabularyEntry[]) => this.value = value);
this.valueUpdates.next(config.value);
this.typeBindRelations = config.typeBindRelations ? config.typeBindRelations : [];
}
get hasAuthority(): boolean {

View File

@@ -7,7 +7,7 @@ import {
DynamicFormLayoutService,
DynamicFormValidationService
} from '@ng-dynamic-forms/core';
import { findKey } from 'lodash';
import findKey from 'lodash/findKey';
import { hasValue, isNotEmpty } from '../../../../../empty.util';
import { DynamicListCheckboxGroupModel } from './dynamic-list-checkbox-group.model';

Some files were not shown because too many files have changed in this diff Show More