-
+ [steps]="['files', 'metadata', 'contributors', 'projects', 'diffusion']"
+/>
+
-
+@if (deposit){
+
+}
diff --git a/projects/sonar/src/app/deposit/upload/upload.component.ts b/projects/sonar/src/app/deposit/upload/upload.component.ts
index faad6b45..7c13876a 100644
--- a/projects/sonar/src/app/deposit/upload/upload.component.ts
+++ b/projects/sonar/src/app/deposit/upload/upload.component.ts
@@ -15,35 +15,14 @@
* along with this program. If not, see
.
*/
import {
- AfterContentChecked,
- ChangeDetectorRef,
Component,
- OnDestroy,
OnInit,
+ inject
} from '@angular/core';
-import {
- AbstractControl,
- UntypedFormArray,
- UntypedFormBuilder,
- Validators,
-} from '@angular/forms';
import { ActivatedRoute, Router } from '@angular/router';
-import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker';
-import { TranslateService } from '@ngx-translate/core';
-import { DialogService } from '@rero/ng-core';
import { NgxSpinnerService } from 'ngx-spinner';
-import { ToastrService } from 'ngx-toastr';
-import { forkJoin, from, Observable, of } from 'rxjs';
import {
- concatMap,
- delay,
- first,
- map,
- mergeMap,
- reduce,
- switchMap,
- takeWhile,
- tap,
+ tap
} from 'rxjs/operators';
import { DepositService } from '../deposit.service';
@@ -53,7 +32,7 @@ const MAX_FILE_SIZE = 500; // Max file size in Mb.
selector: 'sonar-deposit-upload',
templateUrl: './upload.component.html',
})
-export class UploadComponent implements OnInit, AfterContentChecked, OnDestroy {
+export class UploadComponent implements OnInit {
/**
* Files list, an item can be a file to upload or an uploaded file.
*/
@@ -64,283 +43,64 @@ export class UploadComponent implements OnInit, AfterContentChecked, OnDestroy {
*/
deposit: any = null;
- /** Form for handling files metdata */
- filesForm: UntypedFormArray = null;
-
- /** Flag activated when component is destroyed. Is used to unsubscribe to observables with takeWhile operator. */
- destroyed = false;
-
- constructor(
- private _toastr: ToastrService,
- private _depositService: DepositService,
- private _router: Router,
- private _route: ActivatedRoute,
- private _dialogService: DialogService,
- private _spinner: NgxSpinnerService,
- private _translateService: TranslateService,
- private _fb: UntypedFormBuilder,
- private _cd: ChangeDetectorRef
- ) {}
+ // services
+ private depositService = inject(DepositService);
+ private router = inject(Router);
+ private route = inject(ActivatedRoute);
+ private spinner = inject(NgxSpinnerService);
ngOnInit(): void {
- this._spinner.show();
-
- this._route.params
- .pipe(
- first(),
- tap(() => {
- this._spinner.show();
- }),
- switchMap((params) => {
- if (params.id !== '0') {
- return forkJoin([
- this._depositService.get(params.id),
- this._depositService.getFiles(params.id),
- ]).pipe(
- tap((result) => {
- this.deposit = result[0].metadata;
-
- if (
- this._depositService.canAccessDeposit(this.deposit) === false
- ) {
- this._router.navigate([
- 'deposit',
- this.deposit.pid,
- 'confirmation',
- ]);
- }
-
- this.files = result[1];
- })
- );
- } else {
- this.deposit = null;
- this.files = [];
- return of(null);
- }
- })
- )
- .subscribe(() => {
- this._spinner.hide();
- this._initForm();
- });
- }
+ if (this.route.snapshot.routeConfig.path == 'deposit/create') {
+ this.createEmptyDeposit();
+ } else if (this.route.snapshot?.params?.id) {
- ngAfterContentChecked() {
- this._cd.detectChanges();
+ this.depositService
+ .get(this.route.snapshot.params.id)
+ .pipe(
+ tap((result) => {
+ this.spinner.show();
+ this.deposit = result.metadata;
+ if (this.depositService.canAccessDeposit(this.deposit) === false) {
+ this.router.navigate([
+ 'deposit',
+ this.deposit.pid,
+ 'confirmation',
+ ]);
+ }
+ })
+ )
+ .subscribe(() => this.spinner.hide());
+ }
}
- ngOnDestroy() {
- this.destroyed = true;
+ /**
+ * Create a deposit without associated files.
+ * @param event - Event
+ */
+ createEmptyDeposit() {
+ this.depositService.create().subscribe((deposit: any) => {
+ this.router.navigate(['deposit', deposit.id, 'files']);
+ });
}
/**
* Return link prefix
*/
get linkPrefix() {
- return `/deposit/${this.deposit ? this.deposit.pid : '0'}/`;
+ return `/deposit/${this.deposit ? this.deposit.pid : ''}/`;
}
/**
* Get max step
*/
get maxStep() {
- if (this.deposit) {
- return this.deposit.step;
- }
- return 'create';
- }
-
- /**
- * Get list of file for the given type.
- * @param type - string, type of files
- */
- getFilesByType(type: string): Array
{
- return this.files.filter((item: any) => item.category === type);
- }
-
- /**
- * Upload a file to the server and create a deposit if it's not already
- * created.
- * @param files Array of files to upload
- * @param type Type of file
- */
- uploadFiles(files: Array, type: string) {
- this._spinner.show();
-
- // Create a deposit if not existing
- let createDeposit$: Observable = this._depositService.create().pipe(
- tap((deposit) => (this.deposit = deposit.metadata)),
- delay(1000),
- map(() => {
- if (type === 'main') {
- this._router.navigate(['deposit', this.deposit.pid, 'create']);
- }
- })
- );
if (this.deposit) {
- createDeposit$ = of(this.deposit);
- }
-
- createDeposit$
- .pipe(
- switchMap(() => {
- return from(files).pipe(
- concatMap((file: File) => {
- return this._depositService.uploadFile(
- this.deposit.pid,
- file.name,
- type,
- file
- );
- })
- );
- })
- )
- .subscribe({
- next: (file: any) => {
- this._toastr.success(
- _('File uploaded successfully') + ': ' + file.key
- );
- this.files.push(file);
- this._addFormField(file);
- },
- complete: () => {
- this._spinner.hide();
- },
- error: () => {
- this._spinner.hide();
- this._toastr.error(
- _('An error occurred during file upload process, please try again.')
- );
- },
- });
- }
-
- /**
- * Removes a deposit (after confirmation) and go back to upload homepage.
- */
- cancelDeposit() {
- this._depositService
- .deleteDepositWithConfirmation(this.deposit)
- .subscribe((result: any) => {
- if (result === true) {
- this.files = [];
- this.deposit = null;
- this._router.navigate(['deposit', '0', 'create']);
- }
- });
- }
-
- /**
- * Method called when ngx-dropzone receive a file, add the selected file to
- * files list.
- * @param event - Event, contains the added files.
- * @param type Type of file to upload.
- * @param limit Limit files for the type.
- */
- onSelect(event: any, type: string, limit: number) {
- if (limit !== 0 && this.getFilesByType(type).length >= limit) {
- this._toastr.error(
- _('You cannot add more files, please remove existing files first.')
- );
- return;
- }
-
- event.addedFiles.forEach((file: any, index: number) => {
- try {
- if (file.size > MAX_FILE_SIZE * 1000 * 1000) {
- throw new Error(`The maximum size for a file is ${MAX_FILE_SIZE}Mb, ${file.name} cannot be uploaded.`);
- }
-
- if (
- this.getFilesByType(type).filter((item) => item.key === file.name)
- .length > 0
- ) {
- throw new Error(
- `File with the same name is already added to the deposit: ${file.name}`
- );
- }
- } catch (error) {
- this._toastr.error(_(error.message));
- event.addedFiles.splice(index, 1);
- }
- });
-
- if (event.addedFiles.length > 0) {
- this.uploadFiles(event.addedFiles, type);
+ return this.deposit.step;
}
+ return 'metadata';
}
- /**
- * Method called when a file is removed from dropzone. If file is already
- * uploaded, it has to be deleted from database with confirmation.
- *
- * @param Event DOM event triggered
- * @param file - File to remove
- */
- removeFile(event: Event, file: any) {
- event.preventDefault();
-
- this._dialogService
- .show({
- ignoreBackdropClick: true,
- initialState: {
- title: _('Confirmation'),
- body: _('Do you really want to remove this file?'),
- confirmButton: true,
- confirmTitleButton: _('OK'),
- cancelTitleButton: _('Cancel'),
- },
- })
- .pipe(
- switchMap((confirm: boolean) => {
- if (confirm === true) {
- return this._depositService
- .removeFile(this.deposit.pid, file.key, file.version_id)
- .pipe(map(() => true));
- }
- return of(false);
- })
- )
- .subscribe((removed: boolean) => {
- if (removed === true) {
- const index = this.files.indexOf(file);
- this.files.splice(this.files.indexOf(file), 1);
- this._toastr.success(
- this._translateService.instant(
- `File ${file.key} removed successfully.`
- )
- );
- this.filesForm.removeAt(index);
- }
- });
- }
-
- /**
- * Create a deposit without associated files.
- * @param event - Event
- */
- createEmptyDeposit(event: Event) {
- event.preventDefault();
-
- if (this.deposit) {
- this._router.navigate(['deposit', this.deposit.pid, 'metadata']);
- return;
- }
-
- this._depositService
- .create()
- .pipe(
- tap(() => this._spinner.show()),
- delay(1000)
- )
- .subscribe((deposit: any) => {
- this._spinner.hide();
- this._router.navigate(['deposit', deposit.id, 'metadata']);
- });
- }
/**
* Save files metadata and go to next step.
@@ -348,101 +108,6 @@ export class UploadComponent implements OnInit, AfterContentChecked, OnDestroy {
*/
saveAndContinue(event: Event) {
event.preventDefault();
-
- const filesToUpdate = [];
-
- if (this.filesForm.touched === true) {
- this.filesForm.value.forEach((value: any) => {
- const index = this.files.findIndex(
- (item) => item.version_id === value.id
- );
-
- if (this.filesForm.at(index).dirty) {
- // remove embargo date if embargo is not checked
- value.embargoDate = value.embargo === true ? value.embargoDate : '';
-
- this.files[index] = { ...this.files[index], ...value };
- filesToUpdate.push(this.files[index]);
- }
- });
- }
-
- if (filesToUpdate.length > 0) {
- from(filesToUpdate)
- .pipe(
- mergeMap((file: any) => {
- return this._depositService.updateFile(this.deposit.pid, file);
- }),
- reduce(() => true)
- )
- .subscribe(() => {
- this._toastr.success(
- this._translateService.instant(
- 'The files have been updated successfully.'
- )
- );
- this._router.navigate(['deposit', this.deposit.pid, 'metadata']);
- });
- } else {
- this._router.navigate(['deposit', this.deposit.pid, 'metadata']);
- }
- }
-
- /**
- * Check if the field is invalid.
- * @param field Form field to check
- */
- isFieldInvalid(field: any) {
- return field.invalid && (field.dirty || field.touched);
- }
-
- /**
- * Create form for managing files
- */
- private _initForm() {
- this.filesForm = this._fb.array([]);
-
- this.files.forEach((file) => {
- this._addFormField(file);
- });
- }
-
- /**
- * Add a new entry in the form.
- * @param file File data
- */
- private _addFormField(file: any) {
- const control = this._fb.group({
- label: [file.label, Validators.required],
- embargo: [file.embargo],
- embargoDate: [file.embargoDate, this._embargoDateValidator],
- exceptInOrganisation: [file.exceptInOrganisation],
- id: file.version_id,
- });
- this.filesForm.push(control);
- control
- .get('embargo')
- .valueChanges.pipe(takeWhile(() => this.destroyed === false))
- .subscribe((values: any) => {
- if (values === false) {
- control.get('embargoDate').setValue('');
- }
- control.get('embargoDate').updateValueAndValidity();
- });
- }
-
- /**
- * Conditional validator for embargo date.
- * @param formControl Form control to add a validator
- */
- private _embargoDateValidator(formControl: AbstractControl) {
- if (!formControl.parent) {
- return null;
- }
-
- if (formControl.parent.get('embargo').value) {
- return Validators.required(formControl);
- }
- return null;
+ this.router.navigate(['deposit', this.deposit.pid, 'metadata']);
}
}
diff --git a/projects/sonar/src/app/record/document/detail/detail.component.html b/projects/sonar/src/app/record/document/detail/detail.component.html
index 29898b8d..875010c8 100644
--- a/projects/sonar/src/app/record/document/detail/detail.component.html
+++ b/projects/sonar/src/app/record/document/detail/detail.component.html
@@ -15,233 +15,214 @@
along with this program. If not, see .
-->
@if(record) {
-
-
-
-
-
-
-
-
- @if(record.documentType){
-
- {{ 'document_type_' + record.documentType | translate }}
-
- }
+
+
+
+
+
-
-
- @if(record.masked && record.masked !== 'not_masked') {
-
- }
-
- @if(record.title[0].subtitle) {
- : {{
- record.title[0].subtitle | languageValue | async
- }}
- }
-
-
-
- @if(record.organisation) {
-
- {{ organisation.name }}
-
- }
- @if(record.subdivisions) {
-
- {{ subdivision.name | languageValue | async }}
-
- }
-
-
-
-
-
- @if(record.provisionActivity && record.provisionActivity.length > 0) {
-
- @for(statement of record.provisionActivity; track statement) {
- @if(statement.text && statement.text.default) {
- @for(item of statement.text | keyvalue; track item) {
- -
- {{ item.value }}
-
- }
- } @else {
- - {{ statement.text }}
- }
- }
-
+
+ @if(record.documentType){
+
+ {{ 'document_type_' + record.documentType | translate }}
+
+ }
+
+
+
+ @if(record.masked && record.masked !== 'not_masked') {
+
+ }
+
+ @if(record.title[0].subtitle) { : {{
+ record.title[0].subtitle | languageValue | async
+ }}
}
+
-
- @if(record.extent || record.formats) {
-
- @if(record.extent) {
- {{ record.extent }}
- }
- @if(record.extent && record.formats) { ; }
- @if(record.formats) {
- {{
- record.formats | join : ', '
- }}
- }
-
+
+ @if(record.organisation) {
+
+ {{ organisation.name }}
+
+ } @if(record.subdivisions) {
+
+ {{ subdivision.name | languageValue | async }}
+
}
+
+
+
+
+
+
+ @if(record.provisionActivity && record.provisionActivity.length > 0) {
+
+ @for(statement of record.provisionActivity; track statement) {
+ @if(statement.text && statement.text.default) { @for(item of
+ statement.text | keyvalue; track item) {
+ -
+ {{ item.value }}
+
+ } } @else {
+ - {{ statement.text }}
+ } }
+
+ }
-
- @if(record.editionStatement) {
-
- {{ record.editionStatement.editionDesignation.value }}
- @if(record.editionStatement.responsibility) {
- / {{ record.editionStatement.responsibility.value }}
- }
-
+
+ @if(record.extent || record.formats) {
+
+ @if(record.extent) {
+ {{ record.extent }}
+ } @if(record.extent && record.formats) { ; } @if(record.formats) {
+ {{ record.formats | join : ', ' }}
}
+
+ }
-
- @if(record.dissertation) {
-
- {{ record.dissertation.text }}
-
+
+ @if(record.editionStatement) {
+
+ {{ record.editionStatement.editionDesignation.value }}
+ @if(record.editionStatement.responsibility) { /
+ {{ record.editionStatement.responsibility.value }}
}
+
+ }
+
+ @if(record.dissertation) {
+
+ {{ record.dissertation.text }}
+
+ }
-
- @if (record.partOf && record.partOf.length > 0) {
-
-
-
-
-
-
- @if (filteredFiles.length > 1) {
-
- Other files
-
- @if (tabOther.active) {
-
-
-
+
+
+
+ @for (i of [1, 2, 3]; track i) {
+
}
-
- }
-
- Statistics
-
- @if (tabStats.active) {
+
+
+
+ {{ language.value | translateLanguage }}
+
+
+
+
+
+
+
+
+
+
+
+
+ {{
+ 'classification_' + classification.classificationPortion
+ | translate
+ }}
+
+
+
+
+
+
+
+
+ {{ otherEdition.publicNote }}
+
+
+
+
+
+
+
+
+
+ {{ relatedTo.publicNote }}
+
+
+
+
+
+
+
+
+ {{ serie.name }}
+ @if (serie.number) { ; {{ serie.number }}
+ }
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ usageAndAccessPolicy.license | translate }}
+ @if(usageAndAccessPolicy.label) {
+
{{ usageAndAccessPolicy.label }}
+ }
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ permalink }}
+
+
+
+
+
+
+
+
+
+
+ 1? false: true"
+ id="documents-other-files-tab"
+ [header]="'Other files' | translate"
+ cache="false"
+ >
+
+
+
+
+
+
+
+
+
- }
-
-
- Edit Files
-
- @if (tabEditFiles.active) {
+
+
+
+
+
- }
-
-
+
+
+
}
diff --git a/projects/sonar/src/app/record/document/detail/field-description/field-description.component.html b/projects/sonar/src/app/record/document/detail/field-description/field-description.component.html
new file mode 100644
index 00000000..44b92e78
--- /dev/null
+++ b/projects/sonar/src/app/record/document/detail/field-description/field-description.component.html
@@ -0,0 +1,30 @@
+
+@if (field()) {
+
+
{{ label() }}
+
+ @if(type() === 'array') {
+
+ @for (value of field(); track value; let last=$last; let index=$index) {
+ -
+ @if (template) {
+
+ } @else {
+ {{ value }}
+ }
+
+ }
+
+ }
+ @else {
+ @if (template) {
+
+ } @else {
+ {{ field() }}
+ }
+ }
+
+
+}
diff --git a/projects/sonar/src/app/record/files/file-item/file-item.component.html b/projects/sonar/src/app/record/files/file-item/file-item.component.html
index 9ac50404..caa936a4 100644
--- a/projects/sonar/src/app/record/files/file-item/file-item.component.html
+++ b/projects/sonar/src/app/record/files/file-item/file-item.component.html
@@ -91,6 +91,7 @@
Versions
+ {{file.metadata.order}}.
@if (file?.links?.self) {
{
- // main file (such as pdf)
- if (entry.type == 'file') {
+ // main file (such as pdf) and avoid the first
+ if (entry.type == 'file' && entry?.order != '1') {
const dataFile: any = {
label: entry?.label ? entry.label : entry.key,
mimetype: entry.mimetype,
diff --git a/projects/sonar/src/app/record/files/upload-files/upload-files.component.html b/projects/sonar/src/app/record/files/upload-files/upload-files.component.html
index 1f684387..314b185c 100644
--- a/projects/sonar/src/app/record/files/upload-files/upload-files.component.html
+++ b/projects/sonar/src/app/record/files/upload-files/upload-files.component.html
@@ -74,6 +74,7 @@ Upload Files
+ @if(files?.length > 0) {
Upload Files
>
+ }
} @else {
diff --git a/projects/sonar/src/app/record/files/upload-files/upload-files.component.ts b/projects/sonar/src/app/record/files/upload-files/upload-files.component.ts
index 730c4237..1e7b689e 100644
--- a/projects/sonar/src/app/record/files/upload-files/upload-files.component.ts
+++ b/projects/sonar/src/app/record/files/upload-files/upload-files.component.ts
@@ -189,14 +189,17 @@ export class UploadFilesComponent {
.pipe(
catchError((e: any) => {
let msg = this.translateService.instant('Server error');
- if (e.error.message) {
+ if (e?.error?.message) {
msg = `${msg}: ${e.error.message}`;
}
this.toastrService.error(msg);
return of([]);
}),
+ switchMap(() => this.getRecord()),
+ switchMap(() => {
+ return this._reorder();
+ }),
tap(() => {
- this.getRecord();
this.resetFilter();
this.fileUpload.clear();
this.toastrService.success(
@@ -232,10 +235,10 @@ export class UploadFilesComponent {
this.toastrService.error(msg);
return of(null);
}),
- map((file: any) => {
+ switchMap((file: any) =>
// update the record and the files
- this.getRecord();
- }),
+ this.getRecord()
+ ),
tap(() => {
this.filesChanged.emit(this.files);
this.resetFilter();
@@ -251,15 +254,14 @@ export class UploadFilesComponent {
* Get the record and the files from the backend.
*/
getRecord() {
- this.fileService
+ return this.fileService
.get(`/api/${this.recordType()}/${this.pid()}`)
.pipe(
map((rec: any) => (rec = rec.metadata)),
tap((record) => (this.record = record)),
switchMap((record) => this.getFiles(record)),
tap((files) => (this.files = files))
- )
- .subscribe();
+ );
}
/**
@@ -279,7 +281,7 @@ export class UploadFilesComponent {
map((file: any) => {
this.nUploadedFiles += 1;
this.files = this.processFiles([
- { label: file.key, ...file },
+ { label: file.key, metadata:{order: this.files.length + 1}, ...file },
...this.files,
]);
}),
@@ -352,14 +354,19 @@ export class UploadFilesComponent {
`/api/${this.recordType()}/${this.pid()}/files/${file.key}`
)
.pipe(
- map((res) => {
+ tap(() => {
this.files = this.files.filter((f) => f.key !== file.key);
+ this.record._files = this.record._files.filter(
+ (item: any) => file.key !== item.key
+ );
+ }),
+ switchMap(() => this._reorder()),
+ tap(() => {
this.resetFilter();
this.toastrService.success(
this.translateService.instant('File removed successfully.')
);
this.filesChanged.emit(this.files);
- return true;
})
);
}
@@ -452,19 +459,28 @@ export class UploadFilesComponent {
* Reorder the files.
*/
reorder() {
+
+ this._reorder().subscribe((record: any) => {
+ this.filesChanged.emit(this.files);
+ });
+ }
+
+ _reorder() {
this.files.map((file, index) => {
let recordFile = this._getFileInRecord(file.key);
recordFile.order = index + 1;
});
- this.fileService
+ return this.fileService
.put(`/api/${this.recordType()}/${this.pid()}`, this.record)
- .subscribe((record: any) => {
+ .pipe(
+ tap((record: any) => {
this.record = record.metadata;
this.files.map((file) => {
file.metadata = this._getFileInRecord(file.key);
});
- this.filesChanged.emit(this.files);
- });
+
+ })
+ );
}
/**
diff --git a/projects/sonar/src/app/record/organisation/detail/detail.component.ts b/projects/sonar/src/app/record/organisation/detail/detail.component.ts
index 8a89f78c..ae40acbc 100644
--- a/projects/sonar/src/app/record/organisation/detail/detail.component.ts
+++ b/projects/sonar/src/app/record/organisation/detail/detail.component.ts
@@ -71,10 +71,4 @@ export class DetailComponent implements OnInit {
this.collections = result[1].hits.hits;
});
}
-
- updateFiles(files) {
- this.recordService.getRecord('organisations', this.record.id, 1).pipe(
- map(doc => this.record._files = doc.metadata._files)
- ).subscribe();
- }
}
diff --git a/projects/sonar/src/styles.scss b/projects/sonar/src/styles.scss
index f7b53583..3e206f46 100644
--- a/projects/sonar/src/styles.scss
+++ b/projects/sonar/src/styles.scss
@@ -105,7 +105,7 @@ $secondary: rgb(246, 130, 17) !default;
}
}
-@import 'primeng/resources/themes/bootstrap4-light-blue/theme';
+@import 'primeng/resources/themes/lara-light-blue/theme';
@import 'primeng/resources/primeng';
@layer primengother {
@import 'primeicons/primeicons';
@@ -117,3 +117,6 @@ $secondary: rgb(246, 130, 17) !default;
font-size: 0.9rem;
}
}
+@layer ng-core {
+ @import 'node_modules/@rero/ng-core/assets/scss/ng-core';
+}
diff --git a/tsconfig.json b/tsconfig.json
index 91de5f9c..3de016a6 100644
--- a/tsconfig.json
+++ b/tsconfig.json
@@ -7,7 +7,7 @@
"declaration": false,
"downlevelIteration": true,
"experimentalDecorators": true,
- "module": "es2020",
+ "module": "es2022",
"moduleResolution": "node",
"importHelpers": true,
"target": "ES2022",