diff --git a/src/app/core/json-patch/builder/json-patch-operation-path-combiner.ts b/src/app/core/json-patch/builder/json-patch-operation-path-combiner.ts index 1650f57d63..c99cd94966 100644 --- a/src/app/core/json-patch/builder/json-patch-operation-path-combiner.ts +++ b/src/app/core/json-patch/builder/json-patch-operation-path-combiner.ts @@ -18,6 +18,8 @@ export class JsonPatchOperationPathCombiner { constructor(rootElement, ...subRootElements: string[]) { this._rootElement = rootElement; this._subRootElement = subRootElements.join('/'); + console.log(subRootElements); + console.log(this._subRootElement); } get rootElement(): string { diff --git a/src/app/core/submission/models/workspaceitem-section-deduplication.model.ts b/src/app/core/submission/models/workspaceitem-section-deduplication.model.ts index 9233780be8..a80ca257ca 100644 --- a/src/app/core/submission/models/workspaceitem-section-deduplication.model.ts +++ b/src/app/core/submission/models/workspaceitem-section-deduplication.model.ts @@ -1,6 +1,6 @@ import { Item } from '../../shared/item.model'; -export interface WorkspaceitemSectionDetectDuplicateObject { +export interface WorkspaceitemSectionDeduplicationObject { matches: { [itemId: string]: DetectDuplicateMatch; }; diff --git a/src/app/core/submission/submission-response-parsing.service.ts b/src/app/core/submission/submission-response-parsing.service.ts index f1879cdeb0..7f02cfa2ef 100644 --- a/src/app/core/submission/submission-response-parsing.service.ts +++ b/src/app/core/submission/submission-response-parsing.service.ts @@ -33,7 +33,7 @@ export class SubmissionResponseParsingService extends BaseResponseParsingService parse(request: RestRequest, data: DSpaceRESTV2Response): RestResponse { if (isNotEmpty(data.payload) && isNotEmpty(data.payload._links) - && (data.statusCode === '201' || data.statusCode === '200')) { + && (data.statusCode === '201' || data.statusCode === '200' || data.statusCode === 'OK')) { const dataDefinition = this.processResponse(data.payload, request.href); return new SubmissionSuccessResponse(dataDefinition[Object.keys(dataDefinition)[0]], data.statusCode, this.processPageInfo(data.payload)); } else if (isEmpty(data.payload) && data.statusCode === '204') { diff --git a/src/app/shared/shared.module.ts b/src/app/shared/shared.module.ts index 9c23440dbf..43f8cc16df 100644 --- a/src/app/shared/shared.module.ts +++ b/src/app/shared/shared.module.ts @@ -71,6 +71,7 @@ import { DsDatePickerComponent } from './form/builder/ds-dynamic-form-ui/models/ import { DsDynamicLookupComponent } from './form/builder/ds-dynamic-form-ui/models/lookup/dynamic-lookup.component'; import { MockAdminGuard } from './mocks/mock-admin-guard.service'; import { AlertsComponent } from './alerts/alerts.component'; +import { ObjNgFor } from './utils/object-ngfor.pipe'; const MODULES = [ // Do NOT include UniversalModule, HttpModule, or JsonpModule here @@ -98,7 +99,8 @@ const PIPES = [ FileSizePipe, SafeUrlPipe, TruncatePipe, - ConsolePipe + ConsolePipe, + ObjNgFor ]; const COMPONENTS = [ diff --git a/src/app/submission/sections/deduplication/match/deduplication-match.component.html b/src/app/submission/sections/deduplication/match/deduplication-match.component.html deleted file mode 100644 index 28546ed9d3..0000000000 --- a/src/app/submission/sections/deduplication/match/deduplication-match.component.html +++ /dev/null @@ -1,81 +0,0 @@ - - -
-
-
-
- - {{submitterDecision$ | async}} - -
-
- - -
-
-
- -
- - - - -
- -
- -
- - - - - - - diff --git a/src/app/submission/sections/detect-duplicate/deduplication.service.ts b/src/app/submission/sections/detect-duplicate/deduplication.service.ts new file mode 100644 index 0000000000..5b0a029f2a --- /dev/null +++ b/src/app/submission/sections/detect-duplicate/deduplication.service.ts @@ -0,0 +1,51 @@ +import { Store } from '@ngrx/store'; +import { SubmissionState } from '../../submission.reducers'; +import { Injectable } from '@angular/core'; +import { Observable } from 'rxjs/Observable'; +import { HttpHeaders } from '@angular/common/http'; +import { HttpOptions } from '../../../core/dspace-rest-v2/dspace-rest-v2.service'; + +@Injectable() +export class DeduplicationService { + + constructor(private store: Store) { + } + + // setWorkspaceDuplicated(payload: any): Observable { + // const options: HttpOptions = Object.create({}); + // let headers = new HttpHeaders(); + // headers = headers.append('Content-Type', 'application/json'); + // options.headers = headers; + // // TODO REST CALL + // // return this.restService.postToEndpoint('workspace/deduplication', payload, null, options); + // return Observable.of(payload); + // } + + setWorkspaceDuplicationSuccess(payload: any): void { + // TODO + + } + + setWorkspaceDuplicationError(payload: any): void { + // TODO + } + + // setWorkflowDuplicated(payload: any): Observable { + // const options: HttpOptions = Object.create({}); + // let headers = new HttpHeaders(); + // headers = headers.append('Content-Type', 'application/json'); + // options.headers = headers; + // // TODO REST CALL + // // return this.restService.postToEndpoint('workflow/deduplication', payload, null, options); + // return Observable.of(payload); + // } + + setWorkflowDuplicationSuccess(payload: any): void { + // TODO Update the redux store + + } + + setWorkflowDuplicationError(payload: any): void { + // TODO Update the redux store + } +} diff --git a/src/app/submission/sections/detect-duplicate/duplicate-match/duplicate-match.component.html b/src/app/submission/sections/detect-duplicate/duplicate-match/duplicate-match.component.html index 28546ed9d3..ace30f77c4 100644 --- a/src/app/submission/sections/detect-duplicate/duplicate-match/duplicate-match.component.html +++ b/src/app/submission/sections/detect-duplicate/duplicate-match/duplicate-match.component.html @@ -3,48 +3,55 @@ [object]="object" > -
-
-
-
- - {{submitterDecision$ | async}} - -
-
- - -
-
-
-
+ + + + + + + +
+ +
+ + + + + + + + +
+ {{'submission.sections.deduplication.not_duplicated' | translate}} +
-
+
@@ -60,19 +67,18 @@
diff --git a/src/app/submission/sections/deduplication/match/deduplication-match.component.ts b/src/app/submission/sections/detect-duplicate/duplicate-match/duplicate-match.component.ts similarity index 50% rename from src/app/submission/sections/deduplication/match/deduplication-match.component.ts rename to src/app/submission/sections/detect-duplicate/duplicate-match/duplicate-match.component.ts index bc8e4b7ce0..585c961e61 100644 --- a/src/app/submission/sections/deduplication/match/deduplication-match.component.ts +++ b/src/app/submission/sections/detect-duplicate/duplicate-match/duplicate-match.component.ts @@ -4,7 +4,9 @@ import { DetectDuplicateMatch } from '../../../../core/submission/models/workspa import { SubmissionService } from '../../../submission.service'; import { NgbModal, NgbModalRef } from '@ng-bootstrap/ng-bootstrap'; import { FormBuilder, FormGroup, Validators } from '@angular/forms'; -import { DetectDuplicateService } from '../detect-duplicate.service'; +import { Store } from '@ngrx/store'; +import { SubmissionState } from '../../../submission.reducers'; +import { DeduplicationService } from '../deduplication.service'; import { JsonPatchOperationsBuilder } from '../../../../core/json-patch/builder/json-patch-operations-builder'; import { JsonPatchOperationPathCombiner } from '../../../../core/json-patch/builder/json-patch-operation-path-combiner'; import { TranslateService } from '@ngx-translate/core'; @@ -13,8 +15,9 @@ import { SubmissionScopeType } from '../../../../core/submission/submission-scop import { DuplicateDecisionValue } from '../models/duplicate-decision-value'; import { DuplicateDecision } from '../models/duplicate-decision.model'; import { DuplicateDecisionType } from '../models/duplicate-decision-type'; +import { SaveSubmissionSectionFormAction } from '../../../objects/submission-objects.actions'; +import { match } from 'minimatch'; import { isNotEmpty } from '../../../../shared/empty.util'; -import { SectionsService } from '../../sections.service'; @Component({ selector: 'ds-duplicate-match', @@ -33,8 +36,7 @@ export class DuplicateMatchComponent implements OnInit { isWorkFlow = false; showSubmitterDecision = false; decisionType: DuplicateDecisionType; - submitterDecision$: Observable; - submitterNote: string; + submitterDecisionTxt: string; hasDecision: boolean; @@ -42,18 +44,16 @@ export class DuplicateMatchComponent implements OnInit { rejectForm: FormGroup; modalRef: NgbModalRef; pathCombiner: JsonPatchOperationPathCombiner; - public processingVerify: Observable = Observable.of(false); - public processingReject: Observable = Observable.of(false); - decisionLabelClass: string; - duplicateBtnLabel$: Observable; - notDuplicateBtnLabel$: Observable; - constructor(private detectDuplicateService: DetectDuplicateService, - private formBuilder: FormBuilder, - private modalService: NgbModal, - private operationsBuilder: JsonPatchOperationsBuilder, - private sectionService: SectionsService, + duplicatedBtnLabel: Observable; + submitterDecisionLabel: Observable; + + constructor(private deduplicationService: DeduplicationService, private submissionService: SubmissionService, + private modalService: NgbModal, + private formBuilder: FormBuilder, + private store: Store, + protected operationsBuilder: JsonPatchOperationsBuilder, private translate: TranslateService) { } @@ -72,55 +72,51 @@ export class DuplicateMatchComponent implements OnInit { : this.match.submitterDecision !== null; if (this.match.submitterDecision) { - this.submitterDecision$ = (this.match.submitterDecision === DuplicateDecisionValue.Reject) ? - this.translate.get('submission.sections.detect-duplicate.not-duplicate') : - this.translate.get('submission.sections.detect-duplicate.duplicate'); - this.decisionLabelClass = (this.match.submitterDecision === DuplicateDecisionValue.Reject) ? 'badge-success' : 'badge-warning'; - this.submitterNote = this.match.submitterNote; + if (this.match.submitterDecision === 'verify') { + this.submitterDecisionTxt = 'It\'s a duplicate'; + } else { + this.submitterDecisionTxt = 'It\'s not a duplicate'; + } } else { - this.submitterDecision$ = this.translate.get('submission.sections.detect-duplicate.no-decision'); - this.decisionLabelClass = 'badge-light'; + this.submitterDecisionTxt = 'Not decided'; } this.pathCombiner = new JsonPatchOperationPathCombiner('sections', this.sectionId); - this.duplicateBtnLabel$ = this.isWorkFlow ? - ((this.match.submitterDecision === DuplicateDecisionValue.Verify) ? - this.translate.get('submission.sections.detect-duplicate.confirm-duplicate') : - this.translate.get('submission.sections.detect-duplicate.duplicate-ctrl')) - : this.translate.get('submission.sections.detect-duplicate.duplicate'); + this.duplicatedBtnLabel = this.isWorkFlow ? + this.translate.get('submission.sections.deduplication.duplicated_ctrl') + : this.translate.get('submission.sections.deduplication.duplicated'); - this.notDuplicateBtnLabel$ = (this.isWorkFlow && this.match.submitterDecision === DuplicateDecisionValue.Reject) ? - this.translate.get('submission.sections.detect-duplicate.confirm-not-duplicate') : - this.translate.get('submission.sections.detect-duplicate.not-duplicate'); + this.submitterDecisionLabel = this.isWorkFlow ? + this.translate.get('submission.sections.deduplication.submitter_decision') + : this.translate.get('submission.sections.deduplication.your_decision'); } setAsDuplicate() { - this.processingVerify = Observable.of(true); const decision = new DuplicateDecision( DuplicateDecisionValue.Verify, this.decisionType, this.rejectForm.get('reason').value); - + console.log('Setting item #' + this.item.uuid + ' as duplicated...'); this.dispatchAction(decision); this.modalRef.dismiss(); } setAsNotDuplicate() { - this.processingReject = Observable.of(true); const decision = new DuplicateDecision( DuplicateDecisionValue.Reject, this.decisionType); - + console.log('Setting item #' + this.item.uuid + ' as not duplicated...'); this.dispatchAction(decision); } clearDecision() { + console.log('Clearing item #' + this.item.uuid + ' from previous decision...'); const decision = new DuplicateDecision( DuplicateDecisionValue.Undo, this.decisionType); - + console.log('Setting item #' + this.item.uuid + ' as not duplicated...'); this.dispatchAction(decision); } @@ -132,15 +128,50 @@ export class DuplicateMatchComponent implements OnInit { note: isNotEmpty(decision.note) ? decision.note : null }; - // dispatch patch operation only when section is active - this.sectionService.isSectionActive(this.submissionId, this.sectionId) - .filter((isActive: boolean) => isActive) - .take(1) - .subscribe(() => { - this.operationsBuilder.add(this.pathCombiner.getPath(pathDecision), payload, false, true); - this.detectDuplicateService.saveDuplicateDecision(this.submissionId, this.sectionId) - }); + this.operationsBuilder.add(this.pathCombiner.getPath(pathDecision), payload, false, true); + this.store.dispatch(new SaveSubmissionSectionFormAction(this.submissionId, this.sectionId)); + // Call workflow action + // const decision = clear ? null : duplicate ? 'verify' : 'reject'; + // const pathDecision = this.isWorkFlow ? 'workflowDecision' : 'submitterDecision'; + // this.operationsBuilder.add(this.pathCombiner.getPath(pathDecision), decision, false, true); + // + // if (!clear && duplicate) { + // const note = this.rejectForm.get('reason').value; + // const pathNote = this.isWorkFlow ? 'workflowNote' : 'submitterNote'; + // this.operationsBuilder.add(this.pathCombiner.getPath(pathNote), note, false, true); + // } + // const now = new Date(); + // const time = now.getUTCFullYear() + '/' + now.getUTCMonth() + 1 + '/' + now.getDay(); + + // if (this.isWorkFlow) { + // // Call workflow action + // payload.data.workflowDecision = clear ? null : duplicated ? 'verify' : 'reject'; + // // payload.data.workflowTime = time; + // if (!clear && duplicated) { + // const note = this.rejectForm.get('reason').value; + // payload.data.workflowNote = note; + // } + // // Dispatch WorkFLOW action + // // this.store.dispatch(new SetWorkflowDuplicatedAction(payload)); + // const path = 'workflowDecision' + // this.operationsBuilder.add(this.pathCombiner.getPath(path), payload.data.workflowDecision, false, true); + // + // } else { + // // Call workspace action + // payload.data.submitterDecision = clear ? null : duplicated ? 'verify' : 'reject'; + // // payload.data.submitterTime = time; + // if (!clear && duplicated) { + // const note = this.rejectForm.get('reason').value; + // payload.data.submitterNote = note; + // } + // // Dispatch workSPACE action + // this.store.dispatch(new SetWorkspaceDuplicatedAction(payload)); + // } + } + + toggleSubmitterDecision() { + this.showSubmitterDecision = !this.showSubmitterDecision; } openModal(modal) { diff --git a/src/app/submission/sections/detect-duplicate/models/duplicate-decision-type.ts b/src/app/submission/sections/detect-duplicate/models/duplicate-decision-type.ts new file mode 100644 index 0000000000..57a30a13b3 --- /dev/null +++ b/src/app/submission/sections/detect-duplicate/models/duplicate-decision-type.ts @@ -0,0 +1,5 @@ +export enum DuplicateDecisionType { + WORKSPACE = 'WORKSPACE', + WORKFLOW = 'WORKFLOW', + ADMIN = 'ADMIN' +} diff --git a/src/app/submission/sections/detect-duplicate/models/duplicate-decision-value.ts b/src/app/submission/sections/detect-duplicate/models/duplicate-decision-value.ts new file mode 100644 index 0000000000..298e33ca39 --- /dev/null +++ b/src/app/submission/sections/detect-duplicate/models/duplicate-decision-value.ts @@ -0,0 +1,5 @@ +export enum DuplicateDecisionValue { + Reject = 'reject', + Verify = 'verify', + Undo = '' +} diff --git a/src/app/submission/sections/detect-duplicate/models/duplicate-decision.model.ts b/src/app/submission/sections/detect-duplicate/models/duplicate-decision.model.ts new file mode 100644 index 0000000000..e18053e134 --- /dev/null +++ b/src/app/submission/sections/detect-duplicate/models/duplicate-decision.model.ts @@ -0,0 +1,57 @@ +import { DuplicateDecisionType } from './duplicate-decision-type'; +import { DuplicateDecisionValue } from './duplicate-decision-value'; +import { isNotNull } from '../../../../shared/empty.util'; + +export class DuplicateDecision { + + private _value: DuplicateDecisionValue; + private _type: DuplicateDecisionType; + private _note: string; + private _date: any; + + public constructor(value = null, type = null, note = null) { + if (isNotNull(value)) { + this.value = value; + } + + if (isNotNull(type)) { + this.type = type; + } + + if (isNotNull(note)) { + this.note = note; + } + } + + get value(): DuplicateDecisionValue { + return this._value; + } + + set value(value: DuplicateDecisionValue) { + this._value = value; + } + + get type(): DuplicateDecisionType { + return this._type; + } + + set type(value: DuplicateDecisionType) { + this._type = value; + } + + get date(): any { + return this._date; + } + + set date(value: any) { + this._date = value; + } + + get note(): string { + return this._note; + } + + set note(value: string) { + this._note = value; + } +} diff --git a/src/app/submission/sections/deduplication/section-deduplication.component.html b/src/app/submission/sections/detect-duplicate/section-detect-duplicate.component.html similarity index 72% rename from src/app/submission/sections/deduplication/section-deduplication.component.html rename to src/app/submission/sections/detect-duplicate/section-detect-duplicate.component.html index 95ed64e5a7..5ce0fd85c1 100644 --- a/src/app/submission/sections/deduplication/section-deduplication.component.html +++ b/src/app/submission/sections/detect-duplicate/section-detect-duplicate.component.html @@ -1,11 +1,18 @@ - - + +
+
+

No duplicated yet.

+
+
- - + + ; public matches = {}; @@ -35,11 +33,9 @@ export class DetectDuplicateSectionComponent extends SectionModelComponent imple isWorkFlow = false; disclaimer: Observable; - sub: Subscription; - constructor(private detectDuplicateService: DetectDuplicateService, + constructor(protected store: Store, private translate: TranslateService, - private sectionService: SectionsService, private submissionService: SubmissionService, @Inject('collectionIdProvider') public injectedCollectionId: string, @Inject('sectionDataProvider') public injectedSectionData: SectionDataObject, @@ -53,34 +49,28 @@ export class DetectDuplicateSectionComponent extends SectionModelComponent imple this.config.pageSize = 2; this.sortConfig = new SortOptions('dc.title', SortDirection.ASC); - this.sectionData$ = this.detectDuplicateService.getDuplicateMatches(this.submissionId, this.sectionData.id); + this.sectionData$ = this.store.select(submissionSectionDataFromIdSelector(this.submissionId, this.sectionData.id)) + .filter((sd) => isNotEmpty(sd)) + .startWith({matches: {}}) + .distinctUntilChanged(); - this.totalMatch$ = this.detectDuplicateService.getDuplicateTotalMatches(this.submissionId, this.sectionData.id); + this.totalMatch$ = this.store.select(submissionSectionDataFromIdSelector(this.submissionId, this.sectionData.id)) + .filter((sd) => isNotEmpty(sd)) + .startWith({matches: {}}) + .map((sd) => Object.keys(sd.matches).length) + .distinctUntilChanged(); this.isWorkFlow = this.submissionService.getSubmissionScope() === SubmissionScopeType.WorkflowItem; this.disclaimer = this.isWorkFlow ? - this.translate.get('submission.sections.detect-duplicate.disclaimer-ctrl') - : this.translate.get('submission.sections.detect-duplicate.disclaimer'); + this.translate.get('submission.sections.deduplication.disclaimer_ctrl') + : this.translate.get('submission.sections.deduplication.disclaimer'); this.isLoading = false; - - this.sub = this.totalMatch$ - .map((totalMatches: number) => totalMatches === 0) - .distinctUntilChanged() - .subscribe((status: boolean) => { - this.sectionService.setSectionStatus(this.submissionId, this.sectionData.id, status); - }) } setPage(page) { this.config.currentPage = page; } - ngOnDestroy(): void { - if (hasValue(this.sub)) { - this.sub.unsubscribe(); - } - } - } diff --git a/src/app/submission/sections/sections-type.ts b/src/app/submission/sections/sections-type.ts index a21bdf6cd4..c035379474 100644 --- a/src/app/submission/sections/sections-type.ts +++ b/src/app/submission/sections/sections-type.ts @@ -5,5 +5,5 @@ export enum SectionsType { CcLicense = 'cclicense', collection = 'collection', Recycle = 'recycle', - Deduplication = 'deduplication' + DetectDuplicate = 'detect-duplicate' } diff --git a/src/app/submission/sections/upload/section-upload.component.ts b/src/app/submission/sections/upload/section-upload.component.ts index 64dabecf02..f759385145 100644 --- a/src/app/submission/sections/upload/section-upload.component.ts +++ b/src/app/submission/sections/upload/section-upload.component.ts @@ -86,8 +86,19 @@ export class UploadSectionComponent extends SectionModelComponent implements OnI .subscribe((collectionData) => { this.collectionName = collectionData.payload.name; + console.log(collectionData.payload.defaultAccessConditions); + const defaultAccessConditions$ = collectionData.payload.defaultAccessConditions + || Observable.of( + new RemoteData( + false, + false, + true, + undefined, + undefined + )); + // Default Access Conditions - this.subs.push(collectionData.payload.defaultAccessConditions + this.subs.push(defaultAccessConditions$ .filter((accessConditions) => isNotUndefined((accessConditions.payload))) .take(1) .subscribe((defaultAccessConditions) => {