@@ -11,10 +11,18 @@ import {
1111import { SubmissionFormsModel } from '@dspace/core/config/models/config-submission-forms.model' ;
1212import { AuthorizationDataService } from '@dspace/core/data/feature-authorization/authorization-data.service' ;
1313import { FeatureID } from '@dspace/core/data/feature-authorization/feature-id' ;
14+ import { ItemDataService } from '@dspace/core/data/item-data.service' ;
1415import { JsonPatchOperationPathCombiner } from '@dspace/core/json-patch/builder/json-patch-operation-path-combiner' ;
1516import { JsonPatchOperationsBuilder } from '@dspace/core/json-patch/builder/json-patch-operations-builder' ;
1617import { Bitstream } from '@dspace/core/shared/bitstream.model' ;
18+ import { followLink } from '@dspace/core/shared/follow-link-config.model' ;
1719import { HALEndpointService } from '@dspace/core/shared/hal-endpoint.service' ;
20+ import { Item } from '@dspace/core/shared/item.model' ;
21+ import {
22+ getAllSucceededRemoteData ,
23+ getFirstSucceededRemoteDataPayload ,
24+ getRemoteDataPayload ,
25+ } from '@dspace/core/shared/operators' ;
1826import { WorkspaceitemSectionUploadFileObject } from '@dspace/core/submission/models/workspaceitem-section-upload-file.model' ;
1927import { SubmissionJsonPatchOperationsService } from '@dspace/core/submission/submission-json-patch-operations.service' ;
2028import { SubmissionScopeType } from '@dspace/core/submission/submission-scope-type' ;
@@ -30,7 +38,9 @@ import { DynamicFormControlModel } from '@ng-dynamic-forms/core';
3038import { TranslateModule } from '@ngx-translate/core' ;
3139import {
3240 BehaviorSubject ,
41+ combineLatest ,
3342 Observable ,
43+ of ,
3444 Subscription ,
3545} from 'rxjs' ;
3646import {
@@ -210,6 +220,7 @@ export class SubmissionSectionUploadFileComponent implements OnChanges, OnInit,
210220 * @param {SectionUploadService } uploadService
211221 * @param {AuthorizationDataService } authorizationService
212222 * @param {HALEndpointService } halService
223+ * @param {ItemDataService } itemDataService
213224 */
214225 constructor (
215226 private formService : FormService ,
@@ -220,6 +231,7 @@ export class SubmissionSectionUploadFileComponent implements OnChanges, OnInit,
220231 private uploadService : SectionUploadService ,
221232 private authorizationService : AuthorizationDataService ,
222233 private halService : HALEndpointService ,
234+ private itemDataService : ItemDataService ,
223235 ) {
224236 this . readMode = true ;
225237 }
@@ -250,23 +262,66 @@ export class SubmissionSectionUploadFileComponent implements OnChanges, OnInit,
250262 this . processingSaveStatus$ = this . submissionService . getSubmissionSaveProcessingStatus ( this . submissionId ) ;
251263 this . pathCombiner = new JsonPatchOperationPathCombiner ( 'sections' , this . sectionId ) ;
252264 this . loadFormMetadata ( ) ;
265+ this . initReplaceButtonVisibility ( ) ;
266+ }
267+
268+ /**
269+ * Sets up the subscription that drives {@link showReplaceButton$}. The version check
270+ * (`isVersionedSubmission`) is kept outside the per-file `switchMap` via `combineLatest`
271+ * so that repeated emissions from `getFileData` during form initialisation do not cancel
272+ * the in-flight version request before it has had a chance to resolve.
273+ */
274+ private initReplaceButtonVisibility ( ) : void {
275+ const scope = this . submissionService . getSubmissionScope ( ) ;
276+ if ( scope === SubmissionScopeType . WorkflowItem ) {
277+ return ;
278+ }
253279 this . subscriptions . push (
254- this . uploadService . getFileData ( this . submissionId , this . sectionId , this . fileId ) . pipe (
255- filter ( isNotUndefined ) ,
256- switchMap ( ( fileData ) => {
257- // Replace is only meaningful for archived items being edited, not for fresh submissions.
258- if ( this . submissionService . getSubmissionScope ( ) !== SubmissionScopeType . EditItem ) {
259- return [ false ] ;
260- }
261- return this . halService . getEndpoint ( 'bitstreams' ) . pipe (
262- map ( endpoint => `${ endpoint } /${ fileData . uuid } ` ) ,
263- switchMap ( bitstreamUrl => this . authorizationService . isAuthorized ( FeatureID . CanReplaceBitstreamSubmitter , bitstreamUrl ) ) ,
264- ) ;
265- } ) ,
280+ combineLatest ( [
281+ this . isVersionedSubmission ( scope ) ,
282+ this . uploadService . getFileData ( this . submissionId , this . sectionId , this . fileId ) . pipe (
283+ filter ( isNotUndefined ) ,
284+ switchMap ( ( fileData ) => this . isAuthorizedToReplace ( fileData . uuid ) ) ,
285+ ) ,
286+ ] ) . pipe (
287+ map ( ( [ isVersioned , isAuthorized ] ) => isVersioned && isAuthorized ) ,
266288 ) . subscribe ( ( canReplace ) => this . showReplaceButton$ . next ( canReplace ) ) ,
267289 ) ;
268290 }
269291
292+ /**
293+ * Returns `true` when the current submission is a new-version workspace item (i.e. its item
294+ * already belongs to a version history), or unconditionally `true` for EditItem scope where
295+ * versioning does not apply.
296+ */
297+ private isVersionedSubmission ( scope : SubmissionScopeType ) : Observable < boolean > {
298+ if ( scope !== SubmissionScopeType . WorkspaceItem ) {
299+ return of ( true ) ;
300+ }
301+ return this . submissionService . retrieveSubmission ( this . submissionId ) . pipe (
302+ getAllSucceededRemoteData ( ) ,
303+ getRemoteDataPayload ( ) ,
304+ switchMap ( ( submissionObject ) =>
305+ this . itemDataService . findByHref ( submissionObject . _links . item . href , true , true , followLink ( 'version' ) ) . pipe (
306+ getFirstSucceededRemoteDataPayload ( ) ,
307+ switchMap ( ( item : Item ) => item . version ) ,
308+ getFirstSucceededRemoteDataPayload ( ) ,
309+ map ( ( version ) => hasValue ( version ) ) ,
310+ ) ,
311+ ) ,
312+ ) ;
313+ }
314+
315+ /**
316+ * Returns whether the current user is authorized to replace the given bitstream.
317+ */
318+ private isAuthorizedToReplace ( bitstreamUuid : string ) : Observable < boolean > {
319+ return this . halService . getEndpoint ( 'bitstreams' ) . pipe (
320+ map ( ( endpoint ) => `${ endpoint } /${ bitstreamUuid } ` ) ,
321+ switchMap ( ( bitstreamUrl ) => this . authorizationService . isAuthorized ( FeatureID . CanReplaceBitstreamSubmitter , bitstreamUrl ) ) ,
322+ ) ;
323+ }
324+
270325 /**
271326 * Show confirmation dialog for delete
272327 */
0 commit comments