/// <summary> /// Create an instance. The webSocketServer may be null unless using ImportWithProgress. /// </summary> /// <remarks>The web socket server is a constructor argument as a step in the direction /// of allowing this class to be instantiated and supplied with the socket server by /// AutoFac. However, for that to work, we'd need to move the other constructor arguments, /// which AutoFac can't know, to the Import method. And for now, all callers which need /// to pass a socket server already have one.</remarks> public SpreadsheetImporter(IBloomWebSocketServer webSocketServer, HtmlDom destinationDom, string pathToSpreadsheetFolder = null, string pathToBookFolder = null, CollectionSettings collectionSettings = null) { _destinationDom = destinationDom; _dataDivElement = _destinationDom.SafeSelectNodes("//div[@id='bloomDataDiv']").Cast <XmlElement>().First(); _pathToBookFolder = pathToBookFolder; // Tests and CLI may not set one or more of these _pathToSpreadsheetFolder = pathToSpreadsheetFolder; _webSocketServer = webSocketServer; _collectionSettings = collectionSettings; }
// Firebase version of the login dialog. Uses BrowserDialog, since Firebase login is only supported in browsers. public static void ShowFirebaseLoginDialog(IBloomWebSocketServer webSocketServer) { try { var url = GetLoginDialogUrl(); // Precondition: we must be on the UI thread for Gecko to work. using (var dlg = new BrowserDialog(url)) { dlg.WebSocketServer = webSocketServer; dlg.CloseMessage = "close"; dlg.Width = 400; // This is more than we usually need. But it saves scrolling when doing an email sign-up. dlg.Height = 510; dlg.ShowDialog(); } } catch (Exception ex) { Logger.WriteError("*** FirebaseLoginDialog threw an exception", ex); } }
public WebSocketProgress(IBloomWebSocketServer bloomWebSocketServer) { _bloomWebSocketServer = bloomWebSocketServer; }
/// <summary> /// Get an object that you can use to send messages to a given clientContext /// </summary> /// <param name="bloomWebSocketServer"></param> /// <param name="clientContext">This goes out with our messages and, on the client side (typescript), messages are filtered /// down to the context (usualy a screen) that requested them.</param> public WebSocketProgress(IBloomWebSocketServer bloomWebSocketServer, string clientContext) { _bloomWebSocketServer = bloomWebSocketServer; _clientContext = clientContext; }
/// <summary> /// Used by the main SpreadsheetApi call. Tests and CLI (which don't usually have access to the book) /// use the other ctor. /// </summary> public SpreadsheetImporter(IBloomWebSocketServer webSocketServer, Book.Book book, string pathToSpreadsheetFolder) : this(webSocketServer, book.OurHtmlDom, pathToSpreadsheetFolder, book.FolderPath, book.CollectionSettings) { _book = book; }
public BloomLibraryUploadControl(PublishView parentView, BloomLibraryPublishModel model, IBloomWebSocketServer webSocketServer) { _model = model; _parentView = parentView; _webSocketServer = webSocketServer; InitializeComponent(); _originalLoginText = _loginLink.Text; // Before anything might modify it (but after InitializeComponent creates it). _titleLabel.Text = _model.Title; _uploadSource.SelectedIndex = 0; _progressBox.ShowDetailsMenuItem = true; _progressBox.ShowCopyToClipboardMenuItem = true; _progressBox.LinkClicked += _progressBox_LinkClicked; _okToUpload = _model.MetadataIsReadyToPublish; // See if saved credentials work. try { _model.LogIn(); } catch (Exception e) { LogAndInformButDontReportFailureToConnectToServer(e); } CommonApi.NotifyLogin(() => { this.Invoke((Action)(UpdateDisplay)); ; }); switch (_model.LicenseType) { case LicenseState.CreativeCommons: _creativeCommonsLink.Text = _model.LicenseToken; _usingNotesSuggestion = false; if (string.IsNullOrWhiteSpace(_model.LicenseRights)) { _licenseNotesLabel.Hide(); } else { _licenseNotesLabel.Text = LocalizationManager.GetString("PublishTab.Upload.AdditionalRequests", "Additional Requests: ") + _model.LicenseRights; } break; case LicenseState.Null: _usingCcControls = false; _licenseNotesLabel.Text = LocalizationManager.GetString("PublishTab.Upload.AllReserved", "All rights reserved (Contact the Copyright holder for any permissions.)"); if (!string.IsNullOrWhiteSpace(_model.LicenseRights)) { _licenseNotesLabel.Text += Environment.NewLine + _model.LicenseRights; } _licenseSuggestion.Text = LocalizationManager.GetString("PublishTab.Upload.SuggestAssignCC", "Suggestion: Assigning a Creative Commons License makes it easy for you to clearly grant certain permissions to everyone."); break; case LicenseState.Custom: // This must be custom a license (with non-blank rights...actually, // currently, the palaso dialog will not allow a custom license with no rights statement). _usingCcControls = false; _licenseNotesLabel.Text = _model.LicenseRights; _licenseSuggestion.Text = LocalizationManager.GetString("PublishTab.Upload.SuggestChangeCC", "Suggestion: Creative Commons Licenses make it much easier for others to use your book, even if they aren't fluent in the language of your custom license."); break; default: throw new ApplicationException("Unknown License state."); } _copyrightLabel.Text = _model.Copyright; _creditsLabel.Text = _model.Credits; _summaryBox.Text = _model.Summary; if (!TeamCollectionApi.TheOneInstance.CanEditBook()) { _summaryBox.Enabled = false; _summaryOptionalLabel.Text = LocalizationManager.GetString("TeamCollection.OptionalCheckOutEdit", "optional--check out to edit"); } UpdateFeaturesCheckBoxesDisplay(); var allLanguages = _model.AllLanguages; foreach (var lang in allLanguages.Keys) { var checkBox = new CheckBox(); checkBox.UseMnemonic = false; checkBox.Text = _model.PrettyLanguageName(lang); if (allLanguages[lang]) { checkBox.Checked = true; } else { checkBox.Text += @" " + LocalizationManager.GetString("PublishTab.Upload.IncompleteTranslation", "(incomplete translation)", "This is added after the language name, in order to indicate that some parts of the book have not been translated into this language yet."); } // Disable clicking on languages that have been selected for display in this book. // See https://issues.bloomlibrary.org/youtrack/issue/BL-7166. if (lang == _model.Book.BookData.Language1.Iso639Code || lang == _model.Book.Language2IsoCode || lang == _model.Book.Language3IsoCode) { checkBox.Checked = true; // even if partial checkBox.AutoCheck = false; } checkBox.Margin = _checkBoxMargin; checkBox.AutoSize = true; checkBox.Tag = lang; checkBox.CheckStateChanged += delegate(object sender, EventArgs args) { _langsLabel.ForeColor = LanguagesOkToUpload ? Color.Black : Color.Red; if (_okToUploadDependsOnLangsChecked) { _okToUpload = LanguagesOkToUpload; UpdateDisplay(); } }; _languagesFlow.Controls.Add(checkBox); } UpdateAudioCheckBoxDisplay(); _summaryOptionalLabel.Left = _summaryBox.Right - _summaryOptionalLabel.Width; // right-align these (even if localization changes their width) // Copyright info is not required if the book has been put in the public domain // or if we are publishing from a source collection and we have original copyright info if (!_model.IsBookPublicDomain && !_model.HasOriginalCopyrightInfoInSourceCollection) { RequireValue(_copyrightLabel); } RequireValue(_titleLabel); if (BookUpload.UseSandbox) { var oldTextWidth = TextRenderer.MeasureText(_uploadButton.Text, _uploadButton.Font).Width; // Do not localize the following string (https://issues.bloomlibrary.org/youtrack/issue/BL-7383). _uploadButton.Text = "Upload (to dev.bloomlibrary.org)"; var neededWidth = TextRenderer.MeasureText(_uploadButton.Text, _uploadButton.Font).Width; _uploadButton.Width += neededWidth - oldTextWidth; } // After considering all the factors except whether any languages are selected, // if we can upload at this point, whether we can from here on depends on whether one is checked. // This test needs to come after evaluating everything else uploading depends on (except login) _okToUploadDependsOnLangsChecked = _okToUpload; if (allLanguages.Keys.Any()) { return; } // No languages in the book have complete data const string space = " "; _langsLabel.Text += space + LocalizationManager.GetString("PublishTab.Upload.NoLangsFound", "(None found)"); if (!_model.OkToUploadWithNoLanguages) { _langsLabel.ForeColor = Color.Red; _okToUpload = false; } }
public BloomWebSocketServer() { Instance = this; }
public static void DoWorkWithProgressDialog(IBloomWebSocketServer socketServer, string socketContext, Func <Form> makeDialog, Func <IWebSocketProgress, BackgroundWorker, bool> doWhat, Action <Form> doWhenMainActionFalse = null, IWin32Window owner = null) { var progress = new WebSocketProgress(socketServer, socketContext); // NOTE: This (specifically ShowDialog) blocks the main thread until the dialog is closed. // Be careful to avoid deadlocks. using (var dlg = makeDialog()) { // For now let's not try to handle letting the user abort. dlg.ControlBox = false; var worker = new BackgroundWorker(); worker.WorkerSupportsCancellation = true; worker.DoWork += (sender, args) => { ProgressDialogApi.SetCancelHandler(() => { worker.CancelAsync(); }); // A way of waiting until the dialog is ready to receive progress messages while (!socketServer.IsSocketOpen(socketContext)) { Thread.Sleep(50); } bool waitForUserToCloseDialogOrReportProblems; try { waitForUserToCloseDialogOrReportProblems = doWhat(progress, worker); } catch (Exception ex) { // depending on the nature of the problem, we might want to do more or less than this. // But at least this lets the dialog reach one of the states where it can be closed, // and gives the user some idea things are not right. socketServer.SendEvent(socketContext, "finished"); waitForUserToCloseDialogOrReportProblems = true; progress.MessageWithoutLocalizing("Something went wrong: " + ex.Message, ex is FatalException? ProgressKind.Fatal:ProgressKind.Error); } // stop the spinner socketServer.SendEvent(socketContext, "finished"); if (waitForUserToCloseDialogOrReportProblems) { // Now the user is allowed to close the dialog or report problems. // (ProgressDialog in JS-land is watching for this message, which causes it to turn // on the buttons that allow the dialog to be manually closed (or a problem to be reported). socketServer.SendBundle(socketContext, "show-buttons", new DynamicJson()); } else { // Just close the dialog dlg.Invoke((Action)(() => { if (doWhenMainActionFalse != null) { doWhenMainActionFalse(dlg); } else { dlg.Close(); } })); } }; worker.RunWorkerAsync(); dlg.ShowDialog(owner); // returns when dialog closed if (progress.HasFatalProblemBeenReported) { Application.Exit(); } ProgressDialogApi.SetCancelHandler(null); } }