@@ -157,11 +157,14 @@ public async Task<string> CreateProjectAsync(string curUserId, SFProjectCreateSe
157157 resources = await _paratextService . GetResourcesAsync ( curUserId ) ;
158158 }
159159
160+ // The project will be synced later, and permissions updated on that subsequent sync
160161 TranslateSource source = await GetTranslateSourceAsync (
162+ conn ,
161163 curUserId ,
162164 projectDoc . Id ,
163165 settings . SourceParatextId ,
164166 syncIfCreated : false ,
167+ updatePermissions : false ,
165168 ptProjects ,
166169 resources
167170 ) ;
@@ -222,6 +225,7 @@ public async Task<string> CreateResourceProjectAsync(string curUserId, string pa
222225 }
223226 }
224227
228+ await using IConnection conn = await RealtimeService . ConnectAsync ( curUserId ) ;
225229 if ( addUser )
226230 {
227231 // See if the project exists to add the user to it
@@ -233,18 +237,18 @@ public async Task<string> CreateResourceProjectAsync(string curUserId, string pa
233237 // Add the user, if they are not already on the project
234238 if ( ! project . UserRoles . ContainsKey ( curUserId ) )
235239 {
236- await AddUserAsync ( curUserId , project . Id , projectRole : null ) ;
240+ await AddUserAsync ( conn , curUserId , project . Id , projectRole : null ) ;
237241 }
238242
239243 return project . Id ;
240244 }
241245 }
242246
243247 // Create the project, as it does not already exist, and add the user if we should
244- string projectId = await CreateResourceProjectInternalAsync ( curUserId , ptProject ) ;
248+ string projectId = await CreateResourceProjectInternalAsync ( conn , ptProject ) ;
245249 if ( addUser )
246250 {
247- await AddUserAsync ( curUserId , projectId , projectRole : null ) ;
251+ await AddUserAsync ( conn , curUserId , projectId , projectRole : null ) ;
248252 }
249253
250254 return projectId ;
@@ -401,10 +405,12 @@ public async Task UpdateSettingsAsync(string curUserId, string projectId, SFProj
401405 if ( settings . SourceParatextId != null && ! unsetSourceProject )
402406 {
403407 source = await GetTranslateSourceAsync (
408+ conn ,
404409 curUserId ,
405410 projectId ,
406411 settings . SourceParatextId ,
407412 syncIfCreated : false ,
413+ updatePermissions : true ,
408414 ptProjects ,
409415 resources ,
410416 projectDoc . Data . UserRoles
@@ -421,10 +427,13 @@ public async Task UpdateSettingsAsync(string curUserId, string projectId, SFProj
421427 if ( settings . AlternateSourceParatextId != null && ! unsetAlternateSourceProject )
422428 {
423429 alternateSource = await GetTranslateSourceAsync (
430+ conn ,
424431 curUserId ,
425432 projectId ,
426433 settings . AlternateSourceParatextId ,
427434 syncIfCreated : true ,
435+ // Only update permissions if this project is different to the preceding project
436+ updatePermissions : settings . SourceParatextId != settings . AlternateSourceParatextId ,
428437 ptProjects ,
429438 resources ,
430439 projectDoc . Data . UserRoles
@@ -441,10 +450,14 @@ public async Task UpdateSettingsAsync(string curUserId, string projectId, SFProj
441450 if ( settings . AlternateTrainingSourceParatextId != null && ! unsetAlternateTrainingSourceProject )
442451 {
443452 alternateTrainingSource = await GetTranslateSourceAsync (
453+ conn ,
444454 curUserId ,
445455 projectId ,
446456 settings . AlternateTrainingSourceParatextId ,
447457 syncIfCreated : true ,
458+ // Only update permissions if this project is different to the preceding projects
459+ updatePermissions : settings . SourceParatextId != settings . AlternateTrainingSourceParatextId
460+ && settings . AlternateSourceParatextId != settings . AlternateTrainingSourceParatextId ,
448461 ptProjects ,
449462 resources ,
450463 projectDoc . Data . UserRoles
@@ -461,10 +474,15 @@ public async Task UpdateSettingsAsync(string curUserId, string projectId, SFProj
461474 if ( settings . AdditionalTrainingSourceParatextId != null && ! unsetAdditionalTrainingSourceProject )
462475 {
463476 additionalTrainingSource = await GetTranslateSourceAsync (
477+ conn ,
464478 curUserId ,
465479 projectId ,
466480 settings . AdditionalTrainingSourceParatextId ,
467481 syncIfCreated : true ,
482+ // Only update permissions if this project is different to the preceding projects
483+ updatePermissions : settings . SourceParatextId != settings . AdditionalTrainingSourceParatextId
484+ && settings . AlternateSourceParatextId != settings . AdditionalTrainingSourceParatextId
485+ && settings . AlternateTrainingSourceParatextId != settings . AdditionalTrainingSourceParatextId ,
468486 ptProjects ,
469487 resources ,
470488 projectDoc . Data . UserRoles
@@ -1839,14 +1857,14 @@ private static void UpdateSetting<T>(
18391857 /// <summary>
18401858 /// Asynchronously creates a Scripture Forge project from Paratext resource/project.
18411859 /// </summary>
1842- /// <param name="curUserId ">The current user identifier .</param>
1860+ /// <param name="conn ">The connection to the realtime server .</param>
18431861 /// <param name="ptProject">The paratext project.</param>
18441862 /// <returns>SF project id of created project</returns>
18451863 /// <remarks>
18461864 /// This method will also work for a source project that has been deleted for some reason.
18471865 /// </remarks>
18481866 /// <exception cref="InvalidOperationException"></exception>
1849- private async Task < string > CreateResourceProjectInternalAsync ( string curUserId , ParatextProject ptProject )
1867+ private async Task < string > CreateResourceProjectInternalAsync ( IConnection conn , ParatextProject ptProject )
18501868 {
18511869 var project = new SFProject
18521870 {
@@ -1866,7 +1884,6 @@ private async Task<string> CreateResourceProjectInternalAsync(string curUserId,
18661884
18671885 // Create the new project using the realtime service
18681886 string projectId = ObjectId . GenerateNewId ( ) . ToString ( ) ;
1869- await using IConnection conn = await RealtimeService . ConnectAsync ( curUserId ) ;
18701887 if ( RealtimeService . QuerySnapshots < SFProject > ( ) . Any ( sfProject => sfProject . ParatextId == project . ParatextId ) )
18711888 {
18721889 throw new InvalidOperationException ( ErrorAlreadyConnectedKey ) ;
@@ -1882,20 +1899,24 @@ private async Task<string> CreateResourceProjectInternalAsync(string curUserId,
18821899 /// <summary>
18831900 /// Gets the translation source asynchronously.
18841901 /// </summary>
1902+ /// <param name="conn">The connection to the realtime server.</param>
18851903 /// <param name="curUserId">The current user identifier.</param>
18861904 /// <param name="sfProjectId">The Scripture Forge project identifier.</param>
18871905 /// <param name="paratextId">The paratext identifier.</param>
18881906 /// <param name="syncIfCreated">If <c>true</c> sync the project if it is created.</param>
1907+ /// <param name="updatePermissions">If <c>true</c> update the project's permissions.</param>
18891908 /// <param name="ptProjects">The paratext projects.</param>
18901909 /// <param name="resources">The paratext resources.</param>
18911910 /// <param name="userRoles">The ids and roles of the users who will need to access the source.</param>
18921911 /// <returns>The <see cref="TranslateSource"/> object for the specified resource.</returns>
18931912 /// <exception cref="DataNotFoundException">The source paratext project does not exist.</exception>
18941913 private async Task < TranslateSource > GetTranslateSourceAsync (
1914+ IConnection conn ,
18951915 string curUserId ,
18961916 string sfProjectId ,
18971917 string paratextId ,
18981918 bool syncIfCreated ,
1919+ bool updatePermissions ,
18991920 IEnumerable < ParatextProject > ptProjects ,
19001921 IEnumerable < ParatextResource > resources ,
19011922 IReadOnlyDictionary < string , string > ? userRoles = null
@@ -1928,12 +1949,10 @@ private async Task<TranslateSource> GetTranslateSourceAsync(
19281949 }
19291950 else
19301951 {
1931- sourceProjectRef = await CreateResourceProjectInternalAsync ( curUserId , sourcePTProject ) ;
1952+ sourceProjectRef = await CreateResourceProjectInternalAsync ( conn , sourcePTProject ) ;
19321953 projectCreated = true ;
19331954 }
19341955
1935- await using IConnection conn = await RealtimeService . ConnectAsync ( curUserId ) ;
1936- IDocument < SFProject > projectDoc = projectCreated ? null : await GetProjectDocAsync ( sourceProjectRef , conn ) ;
19371956 // Add each user in the target project to the source project so they can access it
19381957 foreach ( string userId in userIds )
19391958 {
@@ -1942,15 +1961,7 @@ private async Task<TranslateSource> GetTranslateSourceAsync(
19421961 // Add the user to the project, if the user does not have a role in it
19431962 if ( sourceProject == null || ! sourceProject . UserRoles . ContainsKey ( userId ) )
19441963 {
1945- await AddUserAsync ( userId , sourceProjectRef , null ) ;
1946- }
1947- else if ( projectDoc != null )
1948- {
1949- Attempt < string > attempt = await TryGetProjectRoleAsync ( projectDoc . Data , userId ) ;
1950- if ( attempt . Success )
1951- {
1952- await UpdatePermissionsAsync ( userId , projectDoc ) ;
1953- }
1964+ await AddUserAsync ( conn , userId , sourceProjectRef , null ) ;
19541965 }
19551966 }
19561967 catch ( ForbiddenException )
@@ -1974,6 +1985,12 @@ private async Task<TranslateSource> GetTranslateSourceAsync(
19741985 r => r . UpdateTranslationSourcesAsync ( curUserId , sfProjectId )
19751986 ) ;
19761987 }
1988+ else if ( updatePermissions )
1989+ {
1990+ // Update all the permissions for all the users on this project or resource
1991+ IDocument < SFProject > projectDoc = await GetProjectDocAsync ( sourceProjectRef , conn ) ;
1992+ await UpdatePermissionsAsync ( curUserId , projectDoc ) ;
1993+ }
19771994
19781995 return new TranslateSource
19791996 {
0 commit comments