@@ -14,7 +14,13 @@ import { FeatureID } from '@dspace/core/data/feature-authorization/feature-id';
1414import { JsonPatchOperationPathCombiner } from '@dspace/core/json-patch/builder/json-patch-operation-path-combiner' ;
1515import { JsonPatchOperationsBuilder } from '@dspace/core/json-patch/builder/json-patch-operations-builder' ;
1616import { Bitstream } from '@dspace/core/shared/bitstream.model' ;
17+ import { followLink } from '@dspace/core/shared/follow-link-config.model' ;
1718import { HALEndpointService } from '@dspace/core/shared/hal-endpoint.service' ;
19+ import { Item } from '@dspace/core/shared/item.model' ;
20+ import {
21+ getAllSucceededRemoteData ,
22+ getRemoteDataPayload ,
23+ } from '@dspace/core/shared/operators' ;
1824import { WorkspaceitemSectionUploadFileObject } from '@dspace/core/submission/models/workspaceitem-section-upload-file.model' ;
1925import { SubmissionJsonPatchOperationsService } from '@dspace/core/submission/submission-json-patch-operations.service' ;
2026import { SubmissionScopeType } from '@dspace/core/submission/submission-scope-type' ;
@@ -31,6 +37,7 @@ import { TranslateModule } from '@ngx-translate/core';
3137import {
3238 BehaviorSubject ,
3339 Observable ,
40+ of ,
3441 Subscription ,
3542} from 'rxjs' ;
3643import {
@@ -39,6 +46,7 @@ import {
3946 switchMap ,
4047} from 'rxjs/operators' ;
4148
49+ import { LinkService } from '../../../../core/cache/builders/link.service' ;
4250import { BtnDisabledDirective } from '../../../../shared/btn-disabled.directive' ;
4351import { ThemedFileDownloadLinkComponent } from '../../../../shared/file-download-link/themed-file-download-link.component' ;
4452import { FormService } from '../../../../shared/form/form.service' ;
@@ -210,6 +218,7 @@ export class SubmissionSectionUploadFileComponent implements OnChanges, OnInit,
210218 * @param {SectionUploadService } uploadService
211219 * @param {AuthorizationDataService } authorizationService
212220 * @param {HALEndpointService } halService
221+ * @param {LinkService } linkService
213222 */
214223 constructor (
215224 private formService : FormService ,
@@ -220,6 +229,7 @@ export class SubmissionSectionUploadFileComponent implements OnChanges, OnInit,
220229 private uploadService : SectionUploadService ,
221230 private authorizationService : AuthorizationDataService ,
222231 private halService : HALEndpointService ,
232+ private linkService : LinkService ,
223233 ) {
224234 this . readMode = true ;
225235 }
@@ -253,20 +263,58 @@ export class SubmissionSectionUploadFileComponent implements OnChanges, OnInit,
253263 this . subscriptions . push (
254264 this . uploadService . getFileData ( this . submissionId , this . sectionId , this . fileId ) . pipe (
255265 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- } ) ,
266+ switchMap ( ( fileData ) => this . canShowReplaceButton ( fileData . uuid ) ) ,
266267 ) . subscribe ( ( canReplace ) => this . showReplaceButton$ . next ( canReplace ) ) ,
267268 ) ;
268269 }
269270
271+ /**
272+ * Returns an Observable that emits whether the replace button should be shown for the given
273+ * bitstream. WorkflowItems never show the button; WorkspaceItems only show it when the
274+ * submission is a new version of an existing item; Archived items rely solely on backend
275+ * authorization.
276+ */
277+ private canShowReplaceButton ( bitstreamUuid : string ) : Observable < boolean > {
278+ const scope = this . submissionService . getSubmissionScope ( ) ;
279+ if ( scope === SubmissionScopeType . WorkflowItem ) {
280+ return of ( false ) ;
281+ }
282+ return this . isVersionedSubmission ( scope ) . pipe (
283+ switchMap ( ( isVersioned ) => isVersioned ? this . isAuthorizedToReplace ( bitstreamUuid ) : of ( false ) ) ,
284+ ) ;
285+ }
286+
287+ /**
288+ * Returns `true` when the current submission is a new-version workspace item (i.e. its item
289+ * already belongs to a version history), or unconditionally `true` for EditItem scope where
290+ * versioning does not apply.
291+ */
292+ private isVersionedSubmission ( scope : SubmissionScopeType ) : Observable < boolean > {
293+ if ( scope !== SubmissionScopeType . WorkspaceItem ) {
294+ return of ( true ) ;
295+ }
296+ return this . submissionService . retrieveSubmission ( this . submissionId ) . pipe (
297+ getAllSucceededRemoteData ( ) ,
298+ getRemoteDataPayload ( ) ,
299+ map ( ( submissionObject ) => Object . assign ( new Item ( ) , submissionObject . item ) ) ,
300+ map ( ( item : Item ) => this . linkService . resolveLink ( item , followLink ( 'version' ) ) ) ,
301+ switchMap ( ( item : Item ) => item . version ) ,
302+ getAllSucceededRemoteData ( ) ,
303+ getRemoteDataPayload ( ) ,
304+ map ( ( version ) => hasValue ( version ) ) ,
305+ ) ;
306+ }
307+
308+ /**
309+ * Returns whether the current user is authorized to replace the given bitstream.
310+ */
311+ private isAuthorizedToReplace ( bitstreamUuid : string ) : Observable < boolean > {
312+ return this . halService . getEndpoint ( 'bitstreams' ) . pipe (
313+ map ( ( endpoint ) => `${ endpoint } /${ bitstreamUuid } ` ) ,
314+ switchMap ( ( bitstreamUrl ) => this . authorizationService . isAuthorized ( FeatureID . CanReplaceBitstreamSubmitter , bitstreamUrl ) ) ,
315+ ) ;
316+ }
317+
270318 /**
271319 * Show confirmation dialog for delete
272320 */
0 commit comments