Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -309,7 +309,7 @@ <h1>{{ t("importing_draft") }}</h1>
@if (progress.failedChapters.length > 0) {
<div class="failed-chapters">
<mat-icon class="error-icon">error</mat-icon>
<span>{{ t("failed") }} {{ getFailedChapters(progress) }}</span>
<span class="error-message">{{ t("failed") }} {{ getFailedChapters(progress) }}</span>
</div>
}
</div>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,10 @@
.error-icon {
font-size: 1.1rem;
}

.error-message {
width: 100%;
}
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -792,16 +792,30 @@ export class DraftImportWizardComponent implements OnInit {
}

getFailedChapters(progress: ImportProgress): string {
const failedChapters: number[] = progress.failedChapters.filter(f => f.chapterNum !== 0).map(f => f.chapterNum);
if (!progress.failedChapters.some(f => f.chapterNum === 0)) {
// A subset of chapters failed
return failedChapters.join(', ');
// A subset of chapters failed. Display the failure message next to each chapter, if present.
return progress.failedChapters
.filter(f => f.chapterNum !== 0)
.map(f => (f.message ? `${f.chapterNum} (${f.message})` : f.chapterNum))
.join(', ');
} else if (progress.totalChapters > 1) {
// All chapters failed, so display as a range
return `1-${progress.totalChapters}`;
// All chapters failed, so display as a range, with any failure messages
return (
`1-${progress.totalChapters}. ` +
progress.failedChapters
.filter(f => f.chapterNum === 0 && f.message != null)
.map(f => f.message)
.join(', ')
).trim();
} else {
// The only chapter in the book failed
return `${progress.totalChapters}`;
// The only chapter in the book failed, so display that chapter number with any failure messages
return (
`${progress.totalChapters}. ` +
progress.failedChapters
.filter(f => f.chapterNum === 0 && f.message != null)
.map(f => f.message)
.join(', ')
).trim();
}
}

Expand Down
2 changes: 2 additions & 0 deletions src/SIL.XForge.Scripture/Services/IParatextService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,8 @@ Task<SyncMetricInfo> UpdateParatextPermissionsForNewBooksAsync(
UserSecret userSecret,
string paratextId,
IDocument<SFProject> projectDoc,
int[] booksToUpdate,
bool currentUserOnly,
bool writeToParatext
);
string? GetLatestSharedVersion(UserSecret userSecret, string paratextId);
Expand Down
25 changes: 18 additions & 7 deletions src/SIL.XForge.Scripture/Services/MachineApiService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -501,15 +501,28 @@ await projectService.UpdatePermissionsAsync(
.ToList(),
cancellationToken
);

// When the user is a project administrator, and do not have permission to
// update the specified books, add the permissions for them to update them.
await paratextService.UpdateParatextPermissionsForNewBooksAsync(
userSecret,
targetProjectDoc.Data.ParatextId,
targetProjectDoc,
booksToUpdate: [.. updatedBooks],
currentUserOnly: true,
writeToParatext: false
);
}

if (createdBooks.Count > 0)
{
// Update permissions for new books
// Update permissions for new books, adding all users that should have access to them
await paratextService.UpdateParatextPermissionsForNewBooksAsync(
userSecret,
targetProjectDoc.Data.ParatextId,
targetProjectDoc,
booksToUpdate: [],
currentUserOnly: false,
writeToParatext: false
);
}
Expand All @@ -533,7 +546,7 @@ await draftHubContext.NotifyDraftApplyProgress(
BookNum = bookNum,
ChapterNum = 0,
Status = DraftApplyStatus.Failed,
Message = $"Could not save draft for {Canon.BookNumberToId(bookNum)}.",
Message = $"You do not have permission to write to this book.",
}
);
}
Expand Down Expand Up @@ -563,7 +576,7 @@ await draftHubContext.NotifyDraftApplyProgress(
BookNum = bookNum,
ChapterNum = 0,
Status = DraftApplyStatus.Failed,
Message = $"Could not save draft for {Canon.BookNumberToId(bookNum)}.",
Message = $"You do not have permission to write to this book.",
}
);
}
Expand All @@ -585,8 +598,7 @@ await draftHubContext.NotifyDraftApplyProgress(
BookNum = bookNum,
ChapterNum = chapterDelta.Number,
Status = DraftApplyStatus.Failed,
Message =
$"Could not save draft for {Canon.BookNumberToId(bookNum)} {chapterDelta.Number}.",
Message = $"You do not have permission to write to this chapter.",
}
);
continue;
Expand Down Expand Up @@ -619,8 +631,7 @@ await draftHubContext.NotifyDraftApplyProgress(
BookNum = bookNum,
ChapterNum = chapterDelta.Number,
Status = DraftApplyStatus.Failed,
Message =
$"Could not save draft for {Canon.BookNumberToId(bookNum)} {chapterDelta.Number}.",
Message = $"You do not have permission to write to this chapter.",
}
);
continue;
Expand Down
32 changes: 28 additions & 4 deletions src/SIL.XForge.Scripture/Services/ParatextService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1732,10 +1732,27 @@ IReadOnlyList<BiblicalTerm> biblicalTerms
return syncMetricInfo;
}

/// <summary>
/// Updates Paratext permissions for any new books, or the books specified in <paramref name="booksToUpdate"/>.
/// </summary>
/// <param name="userSecret">The user secret.</param>
/// <param name="paratextId">The Paratext project identifier</param>
/// <param name="projectDoc">The project document.</param>
/// <param name="booksToUpdate">
/// The book numbers to update.
/// If specified these books will be updated, otherwise only new books not on disk will be updated.
/// </param>
/// <param name="currentUserOnly">If <c>true</c>, only update permissions for the current user.</param>
/// <param name="writeToParatext">
/// If <c>true</c>, update the Paratext project on disk; otherwise if <c>false</c>, update the project document.
/// </param>
/// <returns></returns>
public async Task<SyncMetricInfo> UpdateParatextPermissionsForNewBooksAsync(
UserSecret userSecret,
string paratextId,
IDocument<SFProject> projectDoc,
int[] booksToUpdate,
bool currentUserOnly,
bool writeToParatext
)
{
Expand All @@ -1751,19 +1768,26 @@ bool writeToParatext
return syncMetricInfo;
}

// Get all projects that are not on disk
// Get all books that are not on disk
for (int i = 0; i < projectDoc.Data.Texts.Count; i++)
{
TextInfo text = projectDoc.Data.Texts[i];
int bookNum = text.BookNum;
if (scrText.BookPresent(bookNum))
if (booksToUpdate.Length == 0 && scrText.BookPresent(bookNum))
{
// This book is already on disk, so skip to the next book
continue;
}
else if (booksToUpdate.Length > 0 && !booksToUpdate.Contains(bookNum))
{
// Book is on disk, skip to the next book
// This book is not one of the ones we are to update, so skip to the next book
continue;
}

// Add any users to the book who would have the ability to access it
foreach (var user in projectDoc.Data.ParatextUsers)
foreach (
var user in projectDoc.Data.ParatextUsers.Where(u => !currentUserOnly || u.SFUserId == userSecret.Id)
)
{
// If there is no SF user id or PT username, ignore this user
if (string.IsNullOrEmpty(user.SFUserId) || string.IsNullOrEmpty(user.Username))
Expand Down
2 changes: 2 additions & 0 deletions src/SIL.XForge.Scripture/Services/ParatextSyncRunner.cs
Original file line number Diff line number Diff line change
Expand Up @@ -216,6 +216,8 @@ CancellationToken token
_userSecret,
targetParatextId,
_projectDoc,
booksToUpdate: [],
currentUserOnly: false,
writeToParatext: true
);
await GetAndUpdateParatextBooksAndNotes(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -240,6 +240,8 @@ public async Task ApplyPreTranslationToProjectAsync_ExceptionFromParatext()
Arg.Any<UserSecret>(),
Arg.Any<string>(),
Arg.Any<IDocument<SFProject>>(),
booksToUpdate: [],
currentUserOnly: false,
writeToParatext: false
)
.ThrowsAsync(new NotSupportedException());
Expand Down Expand Up @@ -5118,13 +5120,15 @@ int writeChapters
}
});

// Update the permissions for the user applying the draft
// Update the permissions for the user adding new books
ParatextService
.When(x =>
x.UpdateParatextPermissionsForNewBooksAsync(
Arg.Any<UserSecret>(),
Arg.Any<string>(),
Arg.Any<IDocument<SFProject>>(),
booksToUpdate: [],
currentUserOnly: false,
writeToParatext: false
)
)
Expand All @@ -5147,6 +5151,39 @@ int writeChapters
}
}
});

// Update the permissions for the user applying the draft to existing books
ParatextService
.When(x =>
x.UpdateParatextPermissionsForNewBooksAsync(
Arg.Any<UserSecret>(),
Arg.Any<string>(),
Arg.Any<IDocument<SFProject>>(),
booksToUpdate: Arg.Any<int[]>(),
currentUserOnly: true,
writeToParatext: false
)
)
.Do(callInfo =>
{
UserSecret userSecret = callInfo.ArgAt<UserSecret>(0);
var projectDoc = callInfo.ArgAt<IDocument<SFProject>>(2);
int[] booksToUpdate = callInfo.ArgAt<int[]>(3);
foreach (var text in projectDoc.Data.Texts.Where(t => booksToUpdate.Contains(t.BookNum)))
{
text.Permissions.TryAdd(
userSecret.Id,
canWriteBook ? TextInfoPermission.Write : TextInfoPermission.Read
);
foreach (var chapter in text.Chapters)
{
chapter.Permissions.TryAdd(
userSecret.Id,
chapter.Number <= writeChapters ? TextInfoPermission.Write : TextInfoPermission.Read
);
}
}
});
}

public async Task VerifyDraftAsync(
Expand All @@ -5166,6 +5203,8 @@ await ParatextService
Arg.Any<UserSecret>(),
Arg.Any<string>(),
Arg.Any<IDocument<SFProject>>(),
booksToUpdate: [],
currentUserOnly: false,
writeToParatext: false
);
}
Expand Down
12 changes: 12 additions & 0 deletions test/SIL.XForge.Scripture.Tests/Services/ParatextServiceTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6443,6 +6443,8 @@ public async Task UpdateParatextPermissionsForNewBooksAsync_AllBooksPresentOnDis
userSecret,
paratextId,
projectDoc,
booksToUpdate: [],
currentUserOnly: false,
writeToParatext: false
);
SyncMetricInfo expected = new SyncMetricInfo();
Expand Down Expand Up @@ -6472,6 +6474,8 @@ public async Task UpdateParatextPermissionsForNewBooksAsync_MissingScrText()
userSecret,
paratextId,
projectDoc,
booksToUpdate: [],
currentUserOnly: false,
writeToParatext: false
);
SyncMetricInfo expected = new SyncMetricInfo();
Expand Down Expand Up @@ -6501,6 +6505,8 @@ public async Task UpdateParatextPermissionsForNewBooksAsync_ProjectNotEditable()
userSecret,
paratextId,
projectDoc,
booksToUpdate: [],
currentUserOnly: false,
writeToParatext: false
);
SyncMetricInfo expected = new SyncMetricInfo();
Expand Down Expand Up @@ -6530,6 +6536,8 @@ public async Task UpdateParatextPermissionsForNewBooksAsync_ProjectHasNoBooks()
userSecret,
paratextId,
projectDoc,
booksToUpdate: [],
currentUserOnly: false,
writeToParatext: false
);
SyncMetricInfo expected = new SyncMetricInfo();
Expand Down Expand Up @@ -6564,6 +6572,8 @@ public async Task UpdateParatextPermissionsForNewBooksAsync_UpdatesMongo()
userSecret,
paratextId,
projectDoc,
booksToUpdate: [],
currentUserOnly: false,
writeToParatext: false
);
SyncMetricInfo expected = new SyncMetricInfo { Updated = 1 };
Expand Down Expand Up @@ -6608,6 +6618,8 @@ public async Task UpdateParatextPermissionsForNewBooksAsync_UpdatesParatext()
userSecret,
paratextId,
projectDoc,
booksToUpdate: [],
currentUserOnly: false,
writeToParatext: true
);
SyncMetricInfo expected = new SyncMetricInfo { Updated = 1 };
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -758,6 +758,8 @@ await env
Arg.Any<UserSecret>(),
Arg.Any<string>(),
Arg.Any<IDocument<SFProject>>(),
booksToUpdate: [],
currentUserOnly: false,
writeToParatext: true
);
}
Expand Down
Loading