diff --git a/apps/metadata-editor-e2e/src/e2e/edit.cy.ts b/apps/metadata-editor-e2e/src/e2e/edit.cy.ts index 70ec2fef0a..abed248ec3 100644 --- a/apps/metadata-editor-e2e/src/e2e/edit.cy.ts +++ b/apps/metadata-editor-e2e/src/e2e/edit.cy.ts @@ -404,6 +404,88 @@ describe('editor form', () => { }) }) }) + describe('distribution resources', () => { + beforeEach(() => { + cy.get('@resourcePageBtn').click() + }) + it('adds a resource', () => { + // item count before adding + cy.get( + 'gn-ui-form-field-online-resources gn-ui-online-resource-card' + ).should('have.length', 0) + cy.editor_wrapPreviousDraft() + // add a service distribution + cy.get('[data-cy="online-resources-type"] button').eq(1).click() + cy.get('gn-ui-online-service-resource-input mat-radio-button') + .contains('WMS') + .click() + cy.get('gn-ui-online-service-resource-input') + .find('[data-cy="identifier-in-service"]') + .type('A layer name as identifier in service') + cy.get('gn-ui-form-field-online-resources') + .find('gn-ui-url-input') + .find('input') + .type('http://example.com/wms') + cy.get('gn-ui-form-field-online-resources') + .find('gn-ui-url-input') + .find('button') + .click() + cy.editor_publishAndReload() + cy.get('@saveStatus').should('eq', 'record_up_to_date') + cy.get('@resourcePageBtn').click() + cy.get( + 'gn-ui-form-field-online-resources gn-ui-online-resource-card' + ).should('have.length', 1) + }) + it('modifies a resource', () => { + cy.get('gn-ui-form-field-online-resources gn-ui-online-resource-card') + .eq(0) + .as('wmsService') + cy.get('@wmsService') + .find('[data-test=card-title]') + .invoke('text') + .invoke('trim') + .should('eql', 'A layer name as identifier in service') + cy.editor_wrapPreviousDraft() + // open modify dialog + cy.get('@wmsService').find('button[data-test=card-modify]').click() + cy.get( + 'gn-ui-modal-dialog [data-cy="identifier-in-service"] input' + ).clear() + cy.get( + 'gn-ui-modal-dialog [data-cy="identifier-in-service"] input' + ).type('{selectAll}{backspace}new identifier') + cy.get('gn-ui-modal-dialog [data-cy=confirm-button]').click() + cy.editor_publishAndReload() + cy.get('@resourcePageBtn').click() + cy.get('@wmsService') + .find('[data-test=card-title]') + .invoke('text') + .invoke('trim') + .should('eql', 'new identifier') + cy.get('@wmsService').scrollIntoView() + cy.screenshot({ capture: 'viewport' }) + }) + it('deletes a resource', () => { + // item count before deleting + cy.get( + 'gn-ui-form-field-online-resources gn-ui-online-resource-card' + ).should('have.length', 1) + cy.editor_wrapPreviousDraft() + // delete the first item + cy.get( + 'gn-ui-form-field-online-resources gn-ui-sortable-list [data-test=remove-item]' + ) + .eq(0) + .click() + cy.editor_publishAndReload() + cy.get('@saveStatus').should('eq', 'record_up_to_date') + cy.get('@resourcePageBtn').click() + cy.get( + 'gn-ui-form-field-online-resources gn-ui-online-resource-card' + ).should('have.length', 0) + }) + }) describe('attached resources', () => { beforeEach(() => { cy.get('@resourcePageBtn').click() diff --git a/libs/feature/editor/src/lib/components/online-service-resource-input/online-service-resource-input.component.css b/libs/feature/editor/src/lib/components/online-service-resource-input/online-service-resource-input.component.css new file mode 100644 index 0000000000..e69de29bb2 diff --git a/libs/feature/editor/src/lib/components/online-service-resource-input/online-service-resource-input.component.html b/libs/feature/editor/src/lib/components/online-service-resource-input/online-service-resource-input.component.html new file mode 100644 index 0000000000..4ee656c480 --- /dev/null +++ b/libs/feature/editor/src/lib/components/online-service-resource-input/online-service-resource-input.component.html @@ -0,0 +1,31 @@ +
+

+ editor.record.form.field.onlineResource.edit.protocol +

+ + help + +
+
+ + + {{ protocolOption.label | translate }} + + +
+ diff --git a/libs/feature/editor/src/lib/components/online-service-resource-input/online-service-resource-input.component.spec.ts b/libs/feature/editor/src/lib/components/online-service-resource-input/online-service-resource-input.component.spec.ts new file mode 100644 index 0000000000..d158c7e40a --- /dev/null +++ b/libs/feature/editor/src/lib/components/online-service-resource-input/online-service-resource-input.component.spec.ts @@ -0,0 +1,21 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing' +import { TranslateModule } from '@ngx-translate/core' +import { OnlineServiceResourceInputComponent } from './online-service-resource-input.component' + +describe('OnlineServiceResourceInputComponent', () => { + let component: OnlineServiceResourceInputComponent + let fixture: ComponentFixture + + beforeEach(async () => { + await TestBed.configureTestingModule({ + imports: [OnlineServiceResourceInputComponent, TranslateModule.forRoot()], + }).compileComponents() + + fixture = TestBed.createComponent(OnlineServiceResourceInputComponent) + component = fixture.componentInstance + }) + + it('should create', () => { + expect(component).toBeTruthy() + }) +}) diff --git a/libs/feature/editor/src/lib/components/online-service-resource-input/online-service-resource-input.component.ts b/libs/feature/editor/src/lib/components/online-service-resource-input/online-service-resource-input.component.ts new file mode 100644 index 0000000000..a7eab8ec5b --- /dev/null +++ b/libs/feature/editor/src/lib/components/online-service-resource-input/online-service-resource-input.component.ts @@ -0,0 +1,82 @@ +import { CommonModule } from '@angular/common' +import { + ChangeDetectionStrategy, + Component, + Input, + OnChanges, +} from '@angular/core' +import { FormsModule } from '@angular/forms' +import { MatIconModule } from '@angular/material/icon' +import { MatRadioModule } from '@angular/material/radio' +import { MatTooltipModule } from '@angular/material/tooltip' +import { marker } from '@biesbjerg/ngx-translate-extract-marker' +import { + DatasetServiceDistribution, + ServiceProtocol, +} from '@geonetwork-ui/common/domain/model/record' +import { TextInputComponent } from '@geonetwork-ui/ui/inputs' +import { TranslateModule } from '@ngx-translate/core' + +@Component({ + selector: 'gn-ui-online-service-resource-input', + templateUrl: './online-service-resource-input.component.html', + styleUrls: ['./online-service-resource-input.component.css'], + changeDetection: ChangeDetectionStrategy.OnPush, + standalone: true, + imports: [ + CommonModule, + MatIconModule, + MatTooltipModule, + MatRadioModule, + FormsModule, + TextInputComponent, + TranslateModule, + ], +}) +export class OnlineServiceResourceInputComponent implements OnChanges { + @Input() service: Omit + @Input() protocolHint?: string + + selectedProtocol: ServiceProtocol + + protocolOptions: { + label: string + value: ServiceProtocol + }[] = [ + { + label: 'OGC API', + value: 'ogcFeatures', + }, + { + label: 'WFS', + value: 'wfs', + }, + { + label: 'WMS', + value: 'wms', + }, + { + label: 'WMTS', + value: 'wmts', + }, + { + label: 'WPS', + value: 'wps', + }, + { + label: 'ESRI REST', + value: 'esriRest', + }, + { + label: marker('editor.record.onlineResource.protocol.other'), + value: 'other', + }, + ] + + ngOnChanges() { + this.selectedProtocol = + this.protocolOptions.find( + (option) => option.value === this.service.accessServiceProtocol + )?.value ?? 'other' + } +} diff --git a/libs/feature/editor/src/lib/components/record-form/form-field/form-field-online-link-resources/form-field-online-link-resources.component.html b/libs/feature/editor/src/lib/components/record-form/form-field/form-field-online-link-resources/form-field-online-link-resources.component.html index d8df0ccf3c..6bd1486741 100644 --- a/libs/feature/editor/src/lib/components/record-form/form-field/form-field-online-link-resources/form-field-online-link-resources.component.html +++ b/libs/feature/editor/src/lib/components/record-form/form-field/form-field-online-link-resources/form-field-online-link-resources.component.html @@ -20,15 +20,24 @@ -

- editor.record.form.field.onlineResource.edit.title -

- -

- editor.record.form.field.onlineResource.edit.description -

- +
+
+

+ editor.record.form.field.onlineResource.edit.title +

+ +
+
+

+ editor.record.form.field.onlineResource.edit.description +

+ +
+ + +
diff --git a/libs/feature/editor/src/lib/components/record-form/form-field/form-field-online-link-resources/form-field-online-link-resources.component.ts b/libs/feature/editor/src/lib/components/record-form/form-field/form-field-online-link-resources/form-field-online-link-resources.component.ts index a64b4aca96..a274dbc852 100644 --- a/libs/feature/editor/src/lib/components/record-form/form-field/form-field-online-link-resources/form-field-online-link-resources.component.ts +++ b/libs/feature/editor/src/lib/components/record-form/form-field/form-field-online-link-resources/form-field-online-link-resources.component.ts @@ -16,6 +16,7 @@ import { FileInputComponent, TextAreaComponent, TextInputComponent, + UrlInputComponent, } from '@geonetwork-ui/ui/inputs' import { CommonModule } from '@angular/common' import { OnlineResourceCardComponent } from '../../../online-resource-card/online-resource-card.component' @@ -43,6 +44,7 @@ import { MAX_UPLOAD_SIZE_MB } from '../../../../fields.config' OnlineResourceCardComponent, TextInputComponent, TextAreaComponent, + UrlInputComponent, TranslateModule, ], }) @@ -154,6 +156,7 @@ export class FormFieldOnlineLinkResourcesComponent { } this.dialog .open(ModalDialogComponent, { + width: '800px', data: { title: this.translateService.instant( 'editor.record.form.field.onlineResource.dialogTitle' diff --git a/libs/feature/editor/src/lib/components/record-form/form-field/form-field-online-resources/form-field-online-resources.component.css b/libs/feature/editor/src/lib/components/record-form/form-field/form-field-online-resources/form-field-online-resources.component.css new file mode 100644 index 0000000000..e69de29bb2 diff --git a/libs/feature/editor/src/lib/components/record-form/form-field/form-field-online-resources/form-field-online-resources.component.html b/libs/feature/editor/src/lib/components/record-form/form-field/form-field-online-resources/form-field-online-resources.component.html new file mode 100644 index 0000000000..8f40a8fc0f --- /dev/null +++ b/libs/feature/editor/src/lib/components/record-form/form-field/form-field-online-resources/form-field-online-resources.component.html @@ -0,0 +1,71 @@ + +
+ +
+ + + +
+
+ + + + + + + +
+
+

+ editor.record.form.field.onlineResource.edit.title +

+ +
+
+

+ editor.record.form.field.onlineResource.edit.description +

+ +
+ + + + + + +
+
diff --git a/libs/feature/editor/src/lib/components/record-form/form-field/form-field-online-resources/form-field-online-resources.component.spec.ts b/libs/feature/editor/src/lib/components/record-form/form-field/form-field-online-resources/form-field-online-resources.component.spec.ts new file mode 100644 index 0000000000..307a0d5a62 --- /dev/null +++ b/libs/feature/editor/src/lib/components/record-form/form-field/form-field-online-resources/form-field-online-resources.component.spec.ts @@ -0,0 +1,55 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing' +import { TranslateModule } from '@ngx-translate/core' +import { FormFieldOnlineResourcesComponent } from './form-field-online-resources.component' +import { MockBuilder, MockProvider } from 'ng-mocks' +import { Subject } from 'rxjs' +import { PlatformServiceInterface } from '@geonetwork-ui/common/domain/platform.service.interface' +import { NotificationsService } from '@geonetwork-ui/feature/notifications' +import { MatDialog, MatDialogRef } from '@angular/material/dialog' + +let uploadSubject: Subject +class PlatformServiceInterfaceMock { + attachFileToRecord = jest.fn(() => { + uploadSubject = new Subject() + return uploadSubject + }) +} +export class MatDialogMock { + _subject = new Subject() + _closeWithValue = (v) => this._subject.next(v) + open = jest.fn(() => ({ + afterClosed: () => this._subject, + })) +} + +describe('FormFieldOnlineResourcesComponent', () => { + let component: FormFieldOnlineResourcesComponent + let fixture: ComponentFixture + + beforeEach(() => { + return MockBuilder(FormFieldOnlineResourcesComponent) + }) + + beforeEach(async () => { + await TestBed.configureTestingModule({ + imports: [TranslateModule.forRoot()], + providers: [ + MockProvider( + PlatformServiceInterface, + PlatformServiceInterfaceMock, + 'useClass' + ), + MockProvider(NotificationsService), + MockProvider(MatDialogRef), + MockProvider(MatDialog, MatDialogMock, 'useClass'), + ], + }).compileComponents() + + fixture = TestBed.createComponent(FormFieldOnlineResourcesComponent) + component = fixture.componentInstance + }) + + it('should create', () => { + expect(component).toBeTruthy() + }) +}) diff --git a/libs/feature/editor/src/lib/components/record-form/form-field/form-field-online-resources/form-field-online-resources.component.ts b/libs/feature/editor/src/lib/components/record-form/form-field/form-field-online-resources/form-field-online-resources.component.ts new file mode 100644 index 0000000000..afec5556fb --- /dev/null +++ b/libs/feature/editor/src/lib/components/record-form/form-field/form-field-online-resources/form-field-online-resources.component.ts @@ -0,0 +1,241 @@ +import { CommonModule } from '@angular/common' +import { + ChangeDetectionStrategy, + ChangeDetectorRef, + Component, + EventEmitter, + Input, + Output, + TemplateRef, + ViewChild, +} from '@angular/core' +import { MatDialog } from '@angular/material/dialog' +import { marker } from '@biesbjerg/ngx-translate-extract-marker' +import { + DatasetDownloadDistribution, + DatasetServiceDistribution, + OnlineResource, + ServiceEndpoint, +} from '@geonetwork-ui/common/domain/model/record' +import { PlatformServiceInterface } from '@geonetwork-ui/common/domain/platform.service.interface' +import { NotificationsService } from '@geonetwork-ui/feature/notifications' +import { + FileInputComponent, + SwitchToggleComponent, + SwitchToggleOption, + TextAreaComponent, + TextInputComponent, + UrlInputComponent, +} from '@geonetwork-ui/ui/inputs' +import { + ModalDialogComponent, + SortableListComponent, +} from '@geonetwork-ui/ui/layout' +import { TranslateModule, TranslateService } from '@ngx-translate/core' +import { Subscription } from 'rxjs' +import { MAX_UPLOAD_SIZE_MB } from '../../../../fields.config' +import { OnlineResourceCardComponent } from '../../../online-resource-card/online-resource-card.component' +import { OnlineServiceResourceInputComponent } from '../../../online-service-resource-input/online-service-resource-input.component' + +type OnlineNotLinkResource = + | DatasetDownloadDistribution + | DatasetServiceDistribution + | ServiceEndpoint + +@Component({ + selector: 'gn-ui-form-field-online-resources', + templateUrl: './form-field-online-resources.component.html', + styleUrls: ['./form-field-online-resources.component.css'], + changeDetection: ChangeDetectionStrategy.OnPush, + standalone: true, + imports: [ + CommonModule, + SwitchToggleComponent, + FileInputComponent, + OnlineServiceResourceInputComponent, + UrlInputComponent, + SortableListComponent, + OnlineResourceCardComponent, + TextInputComponent, + TextAreaComponent, + TranslateModule, + ], +}) +export class FormFieldOnlineResourcesComponent { + @Input() metadataUuid: string + @Input() set value(onlineResources: Array) { + this.allResources = onlineResources + this.notLinkResources = onlineResources.filter( + (res): res is OnlineNotLinkResource => res.type !== 'link' + ) + } + @Output() valueChange: EventEmitter> = + new EventEmitter() + + @ViewChild('dialogTemplate') dialogTemplate: TemplateRef + + typeOptions: SwitchToggleOption[] = [ + { + label: marker('editor.record.form.field.onlineResource.toggle.dataset'), + value: 'download', + checked: true, + }, + { + label: marker('editor.record.form.field.onlineResource.toggle.service'), + value: 'service', + checked: false, + }, + ] + selectedType: 'download' | 'service' = 'download' + + private allResources: OnlineResource[] = [] + notLinkResources: OnlineNotLinkResource[] = [] + uploadProgress = undefined + uploadSubscription: Subscription = null + newService = { + type: 'service', + accessServiceProtocol: 'ogcFeatures', + identifierInService: '', + } as Omit + + protected MAX_UPLOAD_SIZE_MB = MAX_UPLOAD_SIZE_MB + + constructor( + private notificationsService: NotificationsService, + private translateService: TranslateService, + private platformService: PlatformServiceInterface, + private cd: ChangeDetectorRef, + private dialog: MatDialog + ) {} + + onSelectedTypeChange(selectedType: unknown) { + this.selectedType = selectedType as 'download' | 'service' + } + + handleFileChange(file: File) { + this.uploadProgress = 0 + this.uploadSubscription = this.platformService + .attachFileToRecord(this.metadataUuid, file) + .subscribe({ + next: (event) => { + if (event.type === 'progress') { + this.uploadProgress = event.progress + this.cd.detectChanges() + } else if (event.type === 'success') { + this.uploadProgress = undefined + this.cd.detectChanges() + const newResource: DatasetDownloadDistribution = { + type: 'download', + url: new URL(event.attachment.url), + name: event.attachment.fileName, + sizeBytes: event.sizeBytes, // WARNING: this is the only time that sizeBytes is set + } + this.valueChange.emit([...this.allResources, newResource]) + } + }, + error: (error: Error) => this.handleError(error.message), + }) + } + + handleUploadCancel() { + if (this.uploadSubscription) { + this.uploadProgress = undefined + this.uploadSubscription.unsubscribe() + } + } + + handleDownloadUrlChange(url: string) { + try { + const name = url.split('/').pop() + const newLink: DatasetDownloadDistribution = { + type: 'download', + url: new URL(url), + name, + } + this.valueChange.emit([...this.allResources, newLink]) + } catch (e) { + this.handleError((e as Error).message) + } + } + + handleServiceUrlChange(url: string) { + this.valueChange.emit([ + ...this.allResources, + { + ...this.newService, + url: new URL(url), + }, + ]) + } + + handleServiceModify( + oldService: DatasetServiceDistribution, + newService: DatasetServiceDistribution + ) { + oldService.accessServiceProtocol = newService.accessServiceProtocol + oldService.identifierInService = newService.identifierInService + oldService.url = newService.url + } + + handleResourcesChange(items: unknown[]) { + const notLinks = items as OnlineNotLinkResource[] + const newResources = [ + ...this.allResources.filter((r) => r.type === 'link'), + ...notLinks, + ] + this.valueChange.emit(newResources) + } + + handleResourceModify(resource: OnlineNotLinkResource, index: number) { + this.openEditDialog(resource, index) + } + + private handleError(error: string) { + this.uploadProgress = undefined + this.notificationsService.showNotification({ + type: 'error', + title: this.translateService.instant( + 'editor.record.onlineResourceError.title' + ), + text: `${this.translateService.instant( + 'editor.record.onlineResourceError.body' + )} ${error}`, + closeMessage: this.translateService.instant( + 'editor.record.onlineResourceError.closeMessage' + ), + }) + } + + private openEditDialog(resource: OnlineNotLinkResource, index: number) { + const resourceCopy = { + ...resource, + } + this.dialog + .open(ModalDialogComponent, { + width: '800px', + data: { + title: this.translateService.instant( + 'editor.record.form.field.onlineResource.dialogTitle' + ), + body: this.dialogTemplate, + bodyContext: resourceCopy, + confirmText: this.translateService.instant( + 'editor.record.form.field.onlineResource.confirm' + ), + cancelText: this.translateService.instant( + 'editor.record.form.field.onlineResource.cancel' + ), + }, + }) + .afterClosed() + .subscribe((confirmed: boolean) => { + if (!confirmed) return + const newNotLinks = [...this.notLinkResources] + newNotLinks.splice(index, 1, resourceCopy) + this.valueChange.emit([ + ...this.allResources.filter((r) => r.type === 'link'), + ...newNotLinks, + ]) + }) + } +} diff --git a/libs/feature/editor/src/lib/components/record-form/form-field/form-field.component.html b/libs/feature/editor/src/lib/components/record-form/form-field/form-field.component.html index d5ef07d21a..01ea5566af 100644 --- a/libs/feature/editor/src/lib/components/record-form/form-field/form-field.component.html +++ b/libs/feature/editor/src/lib/components/record-form/form-field/form-field.component.html @@ -121,6 +121,14 @@ (valueChange)="valueChange.emit($event)" > + + + + +

- - {{ option.label }}{{ option.label | translate }} diff --git a/libs/ui/inputs/src/lib/switch-toggle/switch-toggle.component.ts b/libs/ui/inputs/src/lib/switch-toggle/switch-toggle.component.ts index 72d8f9e161..4a0df6ba02 100644 --- a/libs/ui/inputs/src/lib/switch-toggle/switch-toggle.component.ts +++ b/libs/ui/inputs/src/lib/switch-toggle/switch-toggle.component.ts @@ -7,9 +7,11 @@ import { Output, } from '@angular/core' import { MatButtonToggleModule } from '@angular/material/button-toggle' +import { TranslateModule } from '@ngx-translate/core' export type SwitchToggleOption = { label: string + value?: unknown checked: boolean } @@ -19,7 +21,7 @@ export type SwitchToggleOption = { styleUrls: ['./switch-toggle.component.css'], changeDetection: ChangeDetectionStrategy.OnPush, standalone: true, - imports: [MatButtonToggleModule, CommonModule], + imports: [MatButtonToggleModule, CommonModule, TranslateModule], }) export class SwitchToggleComponent { @Input() options: SwitchToggleOption[] diff --git a/libs/ui/inputs/src/lib/url-input/url-input.component.html b/libs/ui/inputs/src/lib/url-input/url-input.component.html index 2f964788dc..9944a4b428 100644 --- a/libs/ui/inputs/src/lib/url-input/url-input.component.html +++ b/libs/ui/inputs/src/lib/url-input/url-input.component.html @@ -27,7 +27,11 @@ diff --git a/libs/ui/inputs/src/lib/url-input/url-input.component.spec.ts b/libs/ui/inputs/src/lib/url-input/url-input.component.spec.ts index d90106b83c..ba96445896 100644 --- a/libs/ui/inputs/src/lib/url-input/url-input.component.spec.ts +++ b/libs/ui/inputs/src/lib/url-input/url-input.component.spec.ts @@ -78,15 +78,27 @@ describe('UrlInputComponent', () => { }) describe('button', () => { - it('is disabled if value is input empty', () => { + it('is disabled if parent set it as disabled', () => { + component.disabled = true inputEl.value = '' fixture.detectChanges() expect(button.componentInstance.disabled).toBe(true) }) + it('is disabled if value is empty', () => { + inputEl.value = '' + fixture.detectChanges() + expect(button.componentInstance.disabled).toBe(true) + }) + it('is disabled if asking for parseable URL and value is not an URL', () => { + component.urlCanParse = true + inputEl.value = 'hello' + fixture.detectChanges() + expect(button.componentInstance.disabled).toBe(true) + }) it('is not disabled otherwise', () => { inputEl.value = 'hello' fixture.detectChanges() - expect(button.componentInstance.disabled).toBe(false) + expect(button.componentInstance.disabled).toBeFalsy() }) }) }) diff --git a/libs/ui/inputs/src/lib/url-input/url-input.component.ts b/libs/ui/inputs/src/lib/url-input/url-input.component.ts index 2cca056e24..9bafd36ad0 100644 --- a/libs/ui/inputs/src/lib/url-input/url-input.component.ts +++ b/libs/ui/inputs/src/lib/url-input/url-input.component.ts @@ -1,4 +1,11 @@ -import { ChangeDetectorRef, Component, Input, Output } from '@angular/core' +import { + ChangeDetectorRef, + Component, + Input, + OnChanges, + Output, + SimpleChanges, +} from '@angular/core' import { CommonModule } from '@angular/common' import { ButtonComponent } from '../button/button.component' import { MatIconModule } from '@angular/material/icon' @@ -12,16 +19,23 @@ import { Subject } from 'rxjs' standalone: true, imports: [CommonModule, ButtonComponent, MatIconModule], }) -export class UrlInputComponent { +export class UrlInputComponent implements OnChanges { @Input() value = '' @Input() extraClass = '' @Input() placeholder = 'https://' @Input() disabled: boolean + @Input() urlCanParse?: boolean rawChange = new Subject() @Output() valueChange = this.rawChange.pipe(filter((v) => !!v)) constructor(private cd: ChangeDetectorRef) {} + ngOnChanges(changes: SimpleChanges): void { + if (changes.value) { + console.log('changes.value', changes.value) + } + } + handleInput() { this.cd.markForCheck() } @@ -30,4 +44,13 @@ export class UrlInputComponent { const value = element.value this.rawChange.next(value) } + + URLcanParse(url: string): boolean { + try { + new URL(url) + return true + } catch (e) { + return false + } + } } diff --git a/translations/de.json b/translations/de.json index 4ba2803a1d..0095630ab3 100644 --- a/translations/de.json +++ b/translations/de.json @@ -1,4 +1,5 @@ { + "": "", "Add Layer As": "", "button.login": "", "catalog.figures.datasets": "{count, plural, =0{Datensätze} one{Datensatz} other{Datensätze}}", @@ -217,9 +218,13 @@ "editor.record.form.field.onlineResource.confirm": "", "editor.record.form.field.onlineResource.dialogTitle": "", "editor.record.form.field.onlineResource.edit.description": "", + "editor.record.form.field.onlineResource.edit.protocol": "", "editor.record.form.field.onlineResource.edit.title": "", "editor.record.form.field.onlineResource.fileSize": "", "editor.record.form.field.onlineResource.modify": "", + "editor.record.form.field.onlineResource.toggle.dataset": "", + "editor.record.form.field.onlineResource.toggle.service": "", + "editor.record.form.field.onlineResources": "", "editor.record.form.field.overviews": "", "editor.record.form.field.recordUpdated": "Datensatz zuletzt aktualisiert", "editor.record.form.field.resourceUpdated": "Letztes Aktualisierungsdatum", @@ -242,6 +247,7 @@ "editor.record.form.page.ressources": "", "editor.record.form.section.about.description": "", "editor.record.form.section.about.label": "", + "editor.record.form.section.annexes.description": "", "editor.record.form.section.annexes.label": "", "editor.record.form.section.associatedResources.description": "", "editor.record.form.section.associatedResources.label": "", @@ -265,6 +271,7 @@ "editor.record.loadError.body": "Der Datensatz konnte nicht geladen werden:", "editor.record.loadError.closeMessage": "Verstanden", "editor.record.loadError.title": "Fehler beim Laden des Datensatzes", + "editor.record.onlineResource.protocol.other": "", "editor.record.onlineResourceError.body": "", "editor.record.onlineResourceError.closeMessage": "", "editor.record.onlineResourceError.title": "", diff --git a/translations/en.json b/translations/en.json index 4ad061ef09..1c0638f91d 100644 --- a/translations/en.json +++ b/translations/en.json @@ -1,4 +1,5 @@ { + "": "", "Add Layer As": "", "button.login": "Log in", "catalog.figures.datasets": "{count, plural, =0{datasets} one{dataset} other{datasets}}", @@ -217,9 +218,13 @@ "editor.record.form.field.onlineResource.confirm": "Confirm", "editor.record.form.field.onlineResource.dialogTitle": "Modify the resource preview", "editor.record.form.field.onlineResource.edit.description": "Description", + "editor.record.form.field.onlineResource.edit.protocol": "Protocol", "editor.record.form.field.onlineResource.edit.title": "Title", "editor.record.form.field.onlineResource.fileSize": "{sizeMB}MB", "editor.record.form.field.onlineResource.modify": "Modify", + "editor.record.form.field.onlineResource.toggle.dataset": "Link to a dataset", + "editor.record.form.field.onlineResource.toggle.service": "Link to a service", + "editor.record.form.field.onlineResources": "Distribution", "editor.record.form.field.overviews": "Overviews", "editor.record.form.field.recordUpdated": "Record Updated", "editor.record.form.field.resourceUpdated": "Resource Updated", @@ -242,6 +247,7 @@ "editor.record.form.page.ressources": "Resources", "editor.record.form.section.about.description": "This section describes the resource.", "editor.record.form.section.about.label": "About the resource", + "editor.record.form.section.annexes.description": "", "editor.record.form.section.annexes.label": "Annexes", "editor.record.form.section.associatedResources.description": "Drop files here to associate them with the resource.", "editor.record.form.section.associatedResources.label": "Associated resources", @@ -265,6 +271,7 @@ "editor.record.loadError.body": "The record could not be loaded:", "editor.record.loadError.closeMessage": "Understood", "editor.record.loadError.title": "Error loading record", + "editor.record.onlineResource.protocol.other": "Other", "editor.record.onlineResourceError.body": "An error occurred while adding the resource:", "editor.record.onlineResourceError.closeMessage": "Understood", "editor.record.onlineResourceError.title": "Error adding resource", diff --git a/translations/es.json b/translations/es.json index 49a8c44dbc..c0a6c50667 100644 --- a/translations/es.json +++ b/translations/es.json @@ -1,4 +1,5 @@ { + "": "", "Add Layer As": "", "button.login": "", "catalog.figures.datasets": "conjuntos de datos", @@ -217,9 +218,13 @@ "editor.record.form.field.onlineResource.confirm": "", "editor.record.form.field.onlineResource.dialogTitle": "", "editor.record.form.field.onlineResource.edit.description": "", + "editor.record.form.field.onlineResource.edit.protocol": "", "editor.record.form.field.onlineResource.edit.title": "", "editor.record.form.field.onlineResource.fileSize": "", "editor.record.form.field.onlineResource.modify": "", + "editor.record.form.field.onlineResource.toggle.dataset": "", + "editor.record.form.field.onlineResource.toggle.service": "", + "editor.record.form.field.onlineResources": "", "editor.record.form.field.overviews": "", "editor.record.form.field.recordUpdated": "", "editor.record.form.field.resourceUpdated": "", @@ -242,6 +247,7 @@ "editor.record.form.page.ressources": "", "editor.record.form.section.about.description": "", "editor.record.form.section.about.label": "", + "editor.record.form.section.annexes.description": "", "editor.record.form.section.annexes.label": "", "editor.record.form.section.associatedResources.description": "", "editor.record.form.section.associatedResources.label": "", @@ -265,6 +271,7 @@ "editor.record.loadError.body": "", "editor.record.loadError.closeMessage": "", "editor.record.loadError.title": "", + "editor.record.onlineResource.protocol.other": "", "editor.record.onlineResourceError.body": "", "editor.record.onlineResourceError.closeMessage": "", "editor.record.onlineResourceError.title": "", diff --git a/translations/fr.json b/translations/fr.json index 29b8281384..1198b1c383 100644 --- a/translations/fr.json +++ b/translations/fr.json @@ -1,4 +1,5 @@ { + "": "", "Add Layer As": "", "button.login": "Se connecter", "catalog.figures.datasets": "{count, plural, =0{données} one{donnée} other{données}}", @@ -217,9 +218,13 @@ "editor.record.form.field.onlineResource.confirm": "Valider", "editor.record.form.field.onlineResource.dialogTitle": "Modifier l'aperçu de la ressource", "editor.record.form.field.onlineResource.edit.description": "Description", + "editor.record.form.field.onlineResource.edit.protocol": "Protocole", "editor.record.form.field.onlineResource.edit.title": "Titre", "editor.record.form.field.onlineResource.fileSize": "{sizeMB} Mo", "editor.record.form.field.onlineResource.modify": "Modifier", + "editor.record.form.field.onlineResource.toggle.dataset": "Lier un jeu de données", + "editor.record.form.field.onlineResource.toggle.service": "Lier un service", + "editor.record.form.field.onlineResources": "Distribution", "editor.record.form.field.overviews": "Aperçus", "editor.record.form.field.recordUpdated": "Date de dernière révision", "editor.record.form.field.resourceUpdated": "Date de dernière révision", @@ -242,8 +247,9 @@ "editor.record.form.page.ressources": "Ressources", "editor.record.form.section.about.description": "Ces informations concernent la donnée.", "editor.record.form.section.about.label": "À propos de la ressource", + "editor.record.form.section.annexes.description": "Les annexes sont optionnels. Ce sont des pièces jointes de la fiche de métadonnées qui peuvent aider à mieux comprendre la donnée (notice, etc.)", "editor.record.form.section.annexes.label": "Annexes", - "editor.record.form.section.associatedResources.description": "Déposez les jeux de données associées à cette fiche de métadonnées.", + "editor.record.form.section.associatedResources.description": "Liez des jeux de données ou des services associés à cette fiche de métadonnée.", "editor.record.form.section.associatedResources.label": "Ressources associées", "editor.record.form.section.classification.description": "La classification a un impact sur la recherche du jeu de données.", "editor.record.form.section.classification.label": "Classification", @@ -265,6 +271,7 @@ "editor.record.loadError.body": "La fiche n'a pas pu être chargée :", "editor.record.loadError.closeMessage": "Compris", "editor.record.loadError.title": "Erreur lors du chargement", + "editor.record.onlineResource.protocol.other": "Autre", "editor.record.onlineResourceError.body": "Une erreur est survenue lors de l'ajout de la ressource :", "editor.record.onlineResourceError.closeMessage": "Compris", "editor.record.onlineResourceError.title": "Erreur lors de l'ajout d'une ressource", diff --git a/translations/it.json b/translations/it.json index 55080f29f2..a5c3b93f39 100644 --- a/translations/it.json +++ b/translations/it.json @@ -1,4 +1,5 @@ { + "": "", "Add Layer As": "", "button.login": "", "catalog.figures.datasets": "{count, plural, =0{datasets} one{dataset} other{datasets}}", @@ -217,9 +218,13 @@ "editor.record.form.field.onlineResource.confirm": "", "editor.record.form.field.onlineResource.dialogTitle": "", "editor.record.form.field.onlineResource.edit.description": "", + "editor.record.form.field.onlineResource.edit.protocol": "", "editor.record.form.field.onlineResource.edit.title": "", "editor.record.form.field.onlineResource.fileSize": "", "editor.record.form.field.onlineResource.modify": "", + "editor.record.form.field.onlineResource.toggle.dataset": "", + "editor.record.form.field.onlineResource.toggle.service": "", + "editor.record.form.field.onlineResources": "", "editor.record.form.field.overviews": "", "editor.record.form.field.recordUpdated": "", "editor.record.form.field.resourceUpdated": "", @@ -242,6 +247,7 @@ "editor.record.form.page.ressources": "", "editor.record.form.section.about.description": "", "editor.record.form.section.about.label": "", + "editor.record.form.section.annexes.description": "", "editor.record.form.section.annexes.label": "", "editor.record.form.section.associatedResources.description": "", "editor.record.form.section.associatedResources.label": "", @@ -265,6 +271,7 @@ "editor.record.loadError.body": "", "editor.record.loadError.closeMessage": "", "editor.record.loadError.title": "", + "editor.record.onlineResource.protocol.other": "", "editor.record.onlineResourceError.body": "", "editor.record.onlineResourceError.closeMessage": "", "editor.record.onlineResourceError.title": "", diff --git a/translations/nl.json b/translations/nl.json index 1174781c78..3f8cf3fb7c 100644 --- a/translations/nl.json +++ b/translations/nl.json @@ -1,4 +1,5 @@ { + "": "", "Add Layer As": "", "button.login": "", "catalog.figures.datasets": "datasets", @@ -217,9 +218,13 @@ "editor.record.form.field.onlineResource.confirm": "", "editor.record.form.field.onlineResource.dialogTitle": "", "editor.record.form.field.onlineResource.edit.description": "", + "editor.record.form.field.onlineResource.edit.protocol": "", "editor.record.form.field.onlineResource.edit.title": "", "editor.record.form.field.onlineResource.fileSize": "", "editor.record.form.field.onlineResource.modify": "", + "editor.record.form.field.onlineResource.toggle.dataset": "", + "editor.record.form.field.onlineResource.toggle.service": "", + "editor.record.form.field.onlineResources": "", "editor.record.form.field.overviews": "", "editor.record.form.field.recordUpdated": "", "editor.record.form.field.resourceUpdated": "", @@ -242,6 +247,7 @@ "editor.record.form.page.ressources": "", "editor.record.form.section.about.description": "", "editor.record.form.section.about.label": "", + "editor.record.form.section.annexes.description": "", "editor.record.form.section.annexes.label": "", "editor.record.form.section.associatedResources.description": "", "editor.record.form.section.associatedResources.label": "", @@ -265,6 +271,7 @@ "editor.record.loadError.body": "", "editor.record.loadError.closeMessage": "", "editor.record.loadError.title": "", + "editor.record.onlineResource.protocol.other": "", "editor.record.onlineResourceError.body": "", "editor.record.onlineResourceError.closeMessage": "", "editor.record.onlineResourceError.title": "", diff --git a/translations/pt.json b/translations/pt.json index bb817d78a3..89d4ddc0c3 100644 --- a/translations/pt.json +++ b/translations/pt.json @@ -1,4 +1,5 @@ { + "": "", "Add Layer As": "", "button.login": "", "catalog.figures.datasets": "conjuntos de dados", @@ -217,9 +218,13 @@ "editor.record.form.field.onlineResource.confirm": "", "editor.record.form.field.onlineResource.dialogTitle": "", "editor.record.form.field.onlineResource.edit.description": "", + "editor.record.form.field.onlineResource.edit.protocol": "", "editor.record.form.field.onlineResource.edit.title": "", "editor.record.form.field.onlineResource.fileSize": "", "editor.record.form.field.onlineResource.modify": "", + "editor.record.form.field.onlineResource.toggle.dataset": "", + "editor.record.form.field.onlineResource.toggle.service": "", + "editor.record.form.field.onlineResources": "", "editor.record.form.field.overviews": "", "editor.record.form.field.recordUpdated": "", "editor.record.form.field.resourceUpdated": "", @@ -242,6 +247,7 @@ "editor.record.form.page.ressources": "", "editor.record.form.section.about.description": "", "editor.record.form.section.about.label": "", + "editor.record.form.section.annexes.description": "", "editor.record.form.section.annexes.label": "", "editor.record.form.section.associatedResources.description": "", "editor.record.form.section.associatedResources.label": "", @@ -265,6 +271,7 @@ "editor.record.loadError.body": "", "editor.record.loadError.closeMessage": "", "editor.record.loadError.title": "", + "editor.record.onlineResource.protocol.other": "", "editor.record.onlineResourceError.body": "", "editor.record.onlineResourceError.closeMessage": "", "editor.record.onlineResourceError.title": "", diff --git a/translations/sk.json b/translations/sk.json index 51f07aff9d..7af8363d38 100644 --- a/translations/sk.json +++ b/translations/sk.json @@ -1,4 +1,5 @@ { + "": "", "Add Layer As": "", "button.login": "", "catalog.figures.datasets": "{count, plural, =0{datasety} one{dataset} other{datasety}}", @@ -217,9 +218,13 @@ "editor.record.form.field.onlineResource.confirm": "", "editor.record.form.field.onlineResource.dialogTitle": "", "editor.record.form.field.onlineResource.edit.description": "", + "editor.record.form.field.onlineResource.edit.protocol": "", "editor.record.form.field.onlineResource.edit.title": "", "editor.record.form.field.onlineResource.fileSize": "", "editor.record.form.field.onlineResource.modify": "", + "editor.record.form.field.onlineResource.toggle.dataset": "", + "editor.record.form.field.onlineResource.toggle.service": "", + "editor.record.form.field.onlineResources": "", "editor.record.form.field.overviews": "", "editor.record.form.field.recordUpdated": "", "editor.record.form.field.resourceUpdated": "", @@ -242,6 +247,7 @@ "editor.record.form.page.ressources": "", "editor.record.form.section.about.description": "", "editor.record.form.section.about.label": "", + "editor.record.form.section.annexes.description": "", "editor.record.form.section.annexes.label": "", "editor.record.form.section.associatedResources.description": "", "editor.record.form.section.associatedResources.label": "", @@ -265,6 +271,7 @@ "editor.record.loadError.body": "", "editor.record.loadError.closeMessage": "", "editor.record.loadError.title": "", + "editor.record.onlineResource.protocol.other": "", "editor.record.onlineResourceError.body": "", "editor.record.onlineResourceError.closeMessage": "", "editor.record.onlineResourceError.title": "",