Skip to content

Commit e08630e

Browse files
committed
Allow branding to be preserved in download-for-edit (BL-13110)
1 parent 08eb0d9 commit e08630e

File tree

11 files changed

+261
-31
lines changed

11 files changed

+261
-31
lines changed

DistFiles/localization/en/Bloom.xlf

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4634,6 +4634,10 @@ Do you want to go ahead?</note>
46344634
<note>ID: PublishTab.UploadCollisionDialog.AlreadyIn</note>
46354635
<note>This is the header for the book that is in bloomlibrary.org already.</note>
46364636
</trans-unit>
4637+
<trans-unit id="PublishTab.UploadCollisionDialog.ChangeBranding" translate="no">
4638+
<source xml:lang="en">The branding was "{0}" but is now "{1}". This may change logos and other material. Check this box if this is what you want.</source>
4639+
<note>ID: PublishTab.UploadCollisionDialog.ChangeBranding</note>
4640+
</trans-unit>
46374641
<trans-unit id="PublishTab.UploadCollisionDialog.ChangeUploader" translate="no">
46384642
<source xml:lang="en">Change the official uploader to {0}. (Bloom Library will hide part of your email address)</source>
46394643
<note>ID: PublishTab.UploadCollisionDialog.ChangeUploader</note>

src/BloomBrowserUI/publish/LibraryPublish/LibraryPublishSteps.tsx

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,6 @@ import { BloomSplitButton } from "../../react_components/bloomSplitButton";
3939
import { ErrorBox, WaitBox } from "../../react_components/boxes";
4040
import {
4141
IUploadCollisionDlgData,
42-
IUploadCollisionDlgProps,
4342
showUploadCollisionDialog,
4443
UploadCollisionDlg
4544
} from "./uploadCollisionDlg";

src/BloomBrowserUI/publish/LibraryPublish/uploadCollisionDlg.tsx

Lines changed: 65 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,8 @@ export interface IUploadCollisionDlgData {
5151
existingBookUrl: string;
5252
existingThumbUrl?: string;
5353
uploader?: string;
54+
oldBranding?: string;
55+
newBranding?: string;
5456
onCancel?: () => void;
5557
dialogEnvironment?: IBloomDialogEnvironmentParams;
5658
permissions?: IPermissions;
@@ -88,6 +90,7 @@ export const UploadCollisionDlg: React.FunctionComponent<IUploadCollisionDlgProp
8890
);
8991

9092
const [doChangeUploader, setDoChangeUploader] = useState(false);
93+
const [doChangeBranding, setDoChangeBranding] = useState(false);
9194

9295
const kAskForHelpColor = "#D65649";
9396
const kDarkerSecondaryTextColor = "#555555";
@@ -137,6 +140,14 @@ export const UploadCollisionDlg: React.FunctionComponent<IUploadCollisionDlgProp
137140
"This is explanatory commentary on a radio button."
138141
);
139142

143+
const changeBrandingMessage = useL10n(
144+
'The branding was "{0}" but is now "{1}". This may change logos and other material. Check this box if this is what you want.',
145+
"PublishTab.UploadCollisionDialog.ChangeBranding",
146+
"Thi is the label of a checkbox",
147+
props.oldBranding,
148+
props.newBranding
149+
);
150+
140151
const sameBookRadioLabel = useL10n(
141152
"Yes, I want to update this book",
142153
"PublishTab.UploadCollisionDialog.Radio.SameBook2",
@@ -247,6 +258,9 @@ export const UploadCollisionDlg: React.FunctionComponent<IUploadCollisionDlgProp
247258
</div>
248259
);
249260

261+
const needChangeBranding =
262+
props.oldBranding && props.oldBranding !== props.newBranding;
263+
250264
const differentBooksRadioCommentary = (): JSX.Element => (
251265
<div
252266
css={css`
@@ -286,6 +300,15 @@ export const UploadCollisionDlg: React.FunctionComponent<IUploadCollisionDlgProp
286300
closeDialog();
287301
}
288302

303+
const cssForCheckboxes = css`
304+
margin-left: 37px;
305+
.MuiFormControlLabel-root {
306+
// The default 10px margin seems to me to visually break the connection between the checkboxes and
307+
// their parent radio button.
308+
padding-top: 2px !important;
309+
}
310+
`;
311+
289312
return (
290313
<BloomDialog
291314
onCancel={() => {
@@ -482,15 +505,49 @@ export const UploadCollisionDlg: React.FunctionComponent<IUploadCollisionDlgProp
482505
ariaLabel="Same book radio button"
483506
commentaryChildren={sameBookRadioCommentary()}
484507
/>
508+
{canUpload && needChangeBranding && (
509+
<div css={cssForCheckboxes}>
510+
{/* The checkbox has an icon prop we could use instead of making it part of
511+
the label, but the mockup has it wrapping as part of the first line of the
512+
label, whereas our BloomCheckbox class puts it out to the left of all the lines
513+
of label, and does something funny with positioning so that neither the icon nor
514+
the text aligns with the text of the other checkbox when both are present. */}
515+
<BloomCheckbox
516+
label={
517+
<p>
518+
<WarningIcon
519+
color="warning"
520+
css={css`
521+
// Aligns it with the text baseline
522+
position: relative;
523+
top: 2px;
524+
// Makes it a little smaller than using 'small' as the fontSize prop.
525+
// more in line with the mockup.
526+
font-size: 1em;
527+
margin-right: 5px;
528+
`}
529+
/>
530+
{changeBrandingMessage}
531+
</p>
532+
}
533+
alreadyLocalized={true}
534+
l10nKey="ignored"
535+
checked={doChangeBranding}
536+
onCheckChanged={() => {
537+
// Enhance: it would probably be nice to select the appropriate radio button
538+
// if it isn't already, but this is a rare special case (branding is rarely
539+
// changed), we're trying to discourage doing it by accident, and it's not
540+
// easy to actually take control of the radio button embedded in the
541+
// RadioWithLabelAndCommentary from here. So for now, the user must do both.)
542+
setDoChangeBranding(!doChangeBranding);
543+
}}
544+
></BloomCheckbox>
545+
</div>
546+
)}
485547
{canUpload &&
486548
canBecomeUploader &&
487549
props.uploader !== props.userEmail && (
488-
<div
489-
css={css`
490-
margin-left: 37px;
491-
margin-top: -12px;
492-
`}
493-
>
550+
<div css={cssForCheckboxes}>
494551
<BloomCheckbox
495552
label={changeTheUploader}
496553
alreadyLocalized={true}
@@ -538,7 +595,8 @@ export const UploadCollisionDlg: React.FunctionComponent<IUploadCollisionDlgProp
538595
enabled={
539596
// If we don't have permission to overwrite, we can only upload using a new ID
540597
buttonState !== RadioState.Indeterminate &&
541-
(canUpload || buttonState === RadioState.Different)
598+
(canUpload || buttonState === RadioState.Different) &&
599+
(doChangeBranding || !needChangeBranding)
542600
}
543601
size="large"
544602
onClick={() => {

src/BloomExe/Book/BookSelection.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,8 @@ public void SelectBook(Book book, bool aboutToEdit = false)
4343
// BringUpToDate, typically only in unit tests.
4444
if (book != null && book.BookData != null && book.IsSaveable)
4545
{
46+
// Before we bring it up to date, so it updates to the right branding
47+
book.CollectionSettings.SetCurrentBook(book);
4648
book.EnsureUpToDate();
4749
}
4850

src/BloomExe/Collection/CollectionSettings.cs

Lines changed: 116 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -10,11 +10,13 @@
1010
using Bloom.Api;
1111
using Bloom.Book;
1212
using Bloom.MiscUI;
13+
using Bloom.Publish.BloomLibrary;
1314
using Bloom.Publish.BloomPub;
1415
using Bloom.Utils;
1516
using Bloom.web.controllers;
1617
using DesktopAnalytics;
1718
using L10NSharp;
19+
using Newtonsoft.Json.Linq;
1820
using SIL.Code;
1921
using SIL.Extensions;
2022
using SIL.IO;
@@ -384,6 +386,25 @@ public static string CollectionIdFromCollectionFolder(string collectionFolder)
384386
}
385387
}
386388

389+
/// <summary>
390+
/// Get the branding that the settings file specifies, without checking the subscription code
391+
/// as we would do if creating the object from the settings file.
392+
/// </summary>
393+
public static string LoadBranding(string pathToCollectionFile)
394+
{
395+
try
396+
{
397+
var settingsContent = RobustFile.ReadAllText(pathToCollectionFile, Encoding.UTF8);
398+
var xml = XElement.Parse(settingsContent);
399+
return ReadString(xml, "BrandingProjectName", "");
400+
}
401+
catch (Exception ex)
402+
{
403+
Bloom.Utils.MiscUtils.SuppressUnusedExceptionVarWarning(ex);
404+
return "";
405+
}
406+
}
407+
387408
/// ------------------------------------------------------------------------------------
388409
public void Load()
389410
{
@@ -662,7 +683,55 @@ internal IEnumerable<string> GetAllLanguageTags()
662683
}
663684

664685
// e.g. "ABC2020" or "Kyrgyzstan2020[English]"
665-
public string BrandingProjectKey { get; set; }
686+
public string BrandingProjectKey
687+
{
688+
get => _overrideBrandingForEditDownload ?? _brandingProjectKey;
689+
set
690+
{
691+
_brandingProjectKey = value;
692+
_overrideBrandingForEditDownload = null;
693+
}
694+
}
695+
696+
private string _overrideBrandingForEditDownload;
697+
698+
public void SetCurrentBook(Book.Book book)
699+
{
700+
if (book == null)
701+
return;
702+
// We allow a previous override to stand until some other book is selected.
703+
// One reason is that CollectionModel.BringBookUpToDate changes the selection to null during the update,
704+
// but we would like it to get updated to the right branding.
705+
_overrideBrandingForEditDownload = null;
706+
var downloadEditPath = Path.Combine(
707+
Path.GetDirectoryName(book.FolderPath),
708+
BloomLibraryPublishModel.kNameOfDownloadForEditFile
709+
);
710+
if (!RobustFile.Exists(downloadEditPath))
711+
return;
712+
try
713+
{
714+
var bookOfCollectionData = JObject.Parse(RobustFile.ReadAllText(downloadEditPath));
715+
//var databaseId = bookOfCollectionData["databaseId"];
716+
var instanceId = bookOfCollectionData["instanceId"]?.ToString();
717+
var bookFolder = bookOfCollectionData["bookFolder"]?.ToString();
718+
var branding = bookOfCollectionData["branding"]?.ToString();
719+
if (
720+
string.IsNullOrEmpty(branding)
721+
|| instanceId != book.ID
722+
|| bookFolder != book.FolderPath.Replace("\\", "/")
723+
)
724+
return; // not validating as the one special book we can edit without the code (or it never had one)
725+
// Now, the final question: has the user reset that branding? If not...if it's just Default in the
726+
// variable because we don't have a code...then we'll do the override.
727+
if (branding == LoadBranding(SettingsFilePath))
728+
_overrideBrandingForEditDownload = branding;
729+
}
730+
catch (Exception)
731+
{
732+
// If we can't process the file, just treat it as not the special book.
733+
}
734+
}
666735

667736
public string GetBrandingFlavor()
668737
{
@@ -680,17 +749,41 @@ out var flavor
680749
return folderName;
681750
}
682751

683-
public string SubscriptionCode { get; set; }
752+
public string SubscriptionCode
753+
{
754+
get => _subscriptionCode;
755+
set => _subscriptionCode = value;
756+
}
684757

685-
public int OneTimeCheckVersionNumber { get; set; }
758+
public int OneTimeCheckVersionNumber
759+
{
760+
get => _oneTimeCheckVersionNumber;
761+
set => _oneTimeCheckVersionNumber = value;
762+
}
686763

687-
public bool AllowNewBooks { get; set; }
764+
public bool AllowNewBooks
765+
{
766+
get => _allowNewBooks;
767+
set => _allowNewBooks = value;
768+
}
688769

689-
public TalkingBookApi.AudioRecordingMode AudioRecordingMode { get; set; }
770+
public TalkingBookApi.AudioRecordingMode AudioRecordingMode
771+
{
772+
get => _audioRecordingMode;
773+
set => _audioRecordingMode = value;
774+
}
690775

691-
public int AudioRecordingTrimEndMilliseconds { get; set; }
776+
public int AudioRecordingTrimEndMilliseconds
777+
{
778+
get => _audioRecordingTrimEndMilliseconds;
779+
set => _audioRecordingTrimEndMilliseconds = value;
780+
}
692781

693-
public int BooksOnWebGoal { get; set; }
782+
public int BooksOnWebGoal
783+
{
784+
get => _booksOnWebGoal;
785+
set => _booksOnWebGoal = value;
786+
}
694787

695788
public BulkBloomPubPublishSettings BulkPublishBloomPubSettings =
696789
new BulkBloomPubPublishSettings
@@ -847,6 +940,22 @@ public string CharactersForDigitsForPageNumbers
847940
private readonly Dictionary<string, string> ColorPalettes =
848941
new Dictionary<string, string>();
849942

943+
private string _invalidBranding;
944+
private bool _isSourceCollection;
945+
private string _collectionName;
946+
private string _settingsFilePath;
947+
private string _country;
948+
private string _province;
949+
private string _district;
950+
private string _pageNumberStyle;
951+
private string _brandingProjectKey;
952+
private string _subscriptionCode;
953+
private int _oneTimeCheckVersionNumber;
954+
private bool _allowNewBooks;
955+
private TalkingBookApi.AudioRecordingMode _audioRecordingMode;
956+
private int _audioRecordingTrimEndMilliseconds;
957+
private int _booksOnWebGoal;
958+
850959
public string GetColorPaletteAsJson(string paletteTag)
851960
{
852961
var colorElementList = new List<string>();

src/BloomExe/Program.cs

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1310,8 +1310,6 @@ private static void HandleProjectWindowActivated(object sender, EventArgs e)
13101310
// Sometimes after closing the splash screen the project window
13111311
// looses focus, so do this.
13121312
_projectContext.ProjectWindow.Activate();
1313-
1314-
(_projectContext.ProjectWindow as Shell).CheckForInvalidBranding();
13151313
}
13161314

13171315
/// ------------------------------------------------------------------------------------

src/BloomExe/Publish/BloomLibrary/BloomLibraryPublishModel.cs

Lines changed: 22 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -144,6 +144,19 @@ internal dynamic GetConflictingBookInfoFromServer(int index)
144144
return result;
145145
}
146146

147+
public static JObject GetDownloadForEditData(string pathToBookFolder)
148+
{
149+
var filePath = Path.Combine(
150+
Path.GetDirectoryName(pathToBookFolder),
151+
kNameOfDownloadForEditFile
152+
);
153+
if (RobustFile.Exists(filePath))
154+
{
155+
return JObject.Parse(RobustFile.ReadAllText(filePath));
156+
}
157+
return null;
158+
}
159+
147160
/// <summary>
148161
/// If we have multiple conflicting books, we want to sort them in a way that makes sense to the user.
149162
/// If we're in a collection that was made for editing one particular book, and this is the book,
@@ -174,13 +187,9 @@ Func<string, bool> canUpload
174187
return books;
175188
var remaining = books.ToList();
176189
var bookList = new List<dynamic>();
177-
var filePath = Path.Combine(
178-
Path.GetDirectoryName(pathToBookFolder),
179-
kNameOfDownloadForEditFile
180-
);
181-
if (File.Exists(filePath))
190+
var bookOfCollectionData = GetDownloadForEditData(pathToBookFolder);
191+
if (bookOfCollectionData != null)
182192
{
183-
var bookOfCollectionData = JObject.Parse(RobustFile.ReadAllText(filePath));
184193
var databaseId = bookOfCollectionData["databaseId"];
185194
var instanceId = bookOfCollectionData["instanceId"];
186195
var bookFolder = bookOfCollectionData["bookFolder"]?.ToString();
@@ -788,6 +797,11 @@ out string[] existingLanguages
788797
var updatedDate = updatedDateTime.ToString("d", CultureInfo.CurrentCulture);
789798
var existingThumbUrl = GetBloomLibraryThumbnailUrl(existingBookInfo);
790799

800+
// We could get it from the data about the download-for-edit book, but why limit this to those books?
801+
//var bookDownloadForEditData = GetDownloadForEditData(Book.FolderPath);
802+
//var oldBranding = bookDownloadForEditData?["branding"]?.ToString();
803+
var oldBranding = existingBookInfo.brandingProjectName?.ToString();
804+
791805
// Must match IUploadCollisionDlgProps in uploadCollisionDlg.tsx.
792806
return new
793807
{
@@ -803,6 +817,8 @@ out string[] existingLanguages
803817
existingUpdatedDate = updatedDate,
804818
existingBookUrl,
805819
existingThumbUrl,
820+
newBranding = Book.BookInfo.BrandingProjectKey,
821+
oldBranding,
806822
uploader = existingBookInfo.uploader.email,
807823
count = existingBookInfo.count,
808824
permissions = existingBookInfo.permissions

src/BloomExe/Shell.cs

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -112,11 +112,6 @@ AudioRecording audioRecording
112112
SetWindowText(null);
113113
}
114114

115-
public void CheckForInvalidBranding()
116-
{
117-
_workspaceView.CheckForInvalidBranding();
118-
}
119-
120115
protected override void OnHandleCreated(EventArgs e)
121116
{
122117
base.OnHandleCreated(e);

0 commit comments

Comments
 (0)