public void UploadBook() { _progressBox.WriteMessage("Starting..."); _uploadWorker = new BackgroundWorker(); _uploadWorker.DoWork += BackgroundUpload; _uploadWorker.WorkerReportsProgress = true; _uploadWorker.RunWorkerCompleted += (theWorker, completedEvent) => { // Return all controls to normal state. (Do this first, just in case we get some further exception somehow.) // I believe the event is guaranteed to be raised, even if something in the worker thread throws, // so there should be no way to get stuck in the state where the tabs etc. are disabled. SetStateOfNonUploadControls(true); // Don't call UpdateDisplay, it will wipe out the progress messages. if (_progressBox.CancelRequested) { _progressBox.WriteMessageWithColor(Color.Red, LocalizationManager.GetString("PublishTab.Upload.Cancelled", "Upload was cancelled")); } else { if (completedEvent.Error != null) { string errorMessage = GetBasicErrorUploadingMessage(); _progressBox.WriteError(errorMessage, _model.Title); _progressBox.WriteException(completedEvent.Error); } else if ((string)completedEvent.Result == "quiet") { // no more reporting, sufficient message already given. } else if (string.IsNullOrEmpty((string)completedEvent.Result)) { // Something went wrong, possibly already reported. if (!_model.PdfGenerationSucceeded) { ReportPdfGenerationFailed(); } else { ReportTryAgainDuringUpload(); } } else { var url = BloomLibraryUrlPrefix + "/my-books/book/" + _parseId; string congratsMessage = LocalizationManager.GetString("PublishTab.Upload.UploadCompleteNotice", "Congratulations, \"{0}\" is now available on BloomLibrary.org ({1})", "{0} is the book title; {1} is a clickable url which will display the book on the website"); _progressBox.WriteMessageWithColor(Color.Blue, congratsMessage, _model.Title, url); BookHistory.AddEvent(_model.Book, BookHistoryEventType.Uploaded, "Book uploaded to Bloom Library"); } } _uploadWorker = null; }; SetStateOfNonUploadControls(false); // Last thing we do before launching the worker, so we can't get stuck in this state. _uploadWorker.RunWorkerAsync(_model); }
/// <summary> /// This is our primary entry point for importing from a spreadsheet. There is also a CLI command, /// but we shouldn't need that one much, now that we have this on our book thumb context menus. /// </summary> public void HandleImportContentFromSpreadsheet(Book.Book book) { if (!_collectionModel.CollectionSettings.HaveEnterpriseFeatures) { Enterprise.ShowRequiresEnterpriseNotice(Form.ActiveForm, "Import to Spreadsheet"); return; } var bookPath = book.GetPathHtmlFile(); try { string inputFilepath; using (var dlg = new DialogAdapters.OpenFileDialogAdapter()) { dlg.Filter = "xlsx|*.xlsx"; dlg.RestoreDirectory = true; dlg.InitialDirectory = GetSpreadsheetFolderFor(book, false); if (DialogResult.Cancel == dlg.ShowDialog()) { return; } inputFilepath = dlg.FileName; var spreadsheetFolder = Path.GetDirectoryName(inputFilepath); SetSpreadsheetFolder(book, spreadsheetFolder); } var importer = new SpreadsheetImporter(_webSocketServer, book, Path.GetDirectoryName(inputFilepath)); importer.ImportWithProgress(inputFilepath); // The importer now does BringBookUpToDate() which accomplishes everything this did, // plus it may have actually changed the folder (and subsequent 'bookPath') // due to a newly imported title. That would cause this call to fail. //XmlHtmlConverter.SaveDOMAsHtml5(book.OurHtmlDom.RawDom, bookPath); book.ReloadFromDisk(null); BookHistory.AddEvent(book, TeamCollection.BookHistoryEventType.ImportSpreadsheet); _bookSelection.InvokeSelectionChanged(false); } catch (Exception ex) { var msg = LocalizationManager.GetString("Spreadsheet:ImportFailed", "Import failed: "); NonFatalProblem.Report(ModalIf.All, PassiveIf.None, msg + ex.Message, exception: ex, showSendReport: false); } }
public void DuplicateBook(Book.Book book) { var newBookDir = book.Storage.Duplicate(); // Get rid of any TC status we copied from the original, so Bloom treats it correctly as a new book. BookStorage.RemoveLocalOnlyFiles(newBookDir); ReloadEditableCollection(); var dupInfo = TheOneEditableCollection.GetBookInfos() .FirstOrDefault(info => info.FolderPath == newBookDir); if (dupInfo != null) { var newBook = GetBookFromBookInfo(dupInfo); SelectBook(newBook); BookHistory.AddEvent(newBook, BookHistoryEventType.Created, $"Duplicated from existing book \"{book.Title}\""); } }
private void HandleForceUnlock(ApiRequest request) { if (!_tcManager.CheckConnection()) { request.Failed(); return; } try { var bookStatus = _tcManager.CurrentCollection.GetStatus(BookFolderName); var lockedBy = bookStatus.lockedByFirstName; if (string.IsNullOrEmpty(lockedBy)) { lockedBy = bookStatus.lockedBy; } // Could be a problem if there's no current book or it's not in the collection folder. // But in that case, we don't show the UI that leads to this being called. _tcManager.CurrentCollection.ForceUnlock(BookFolderName); BookHistory.AddEvent(_bookSelection.CurrentSelection, BookHistoryEventType.ForcedUnlock, $"Admin force-unlocked while checked out to {lockedBy}."); UpdateUiForBook(); Analytics.Track("TeamCollectionRevertOtherCheckout", new Dictionary <string, string>() { { "CollectionId", _settings?.CollectionId }, { "CollectionName", _settings?.CollectionName }, { "Backend", _tcManager?.CurrentCollection?.GetBackendType() }, { "User", CurrentUser }, { "BookId", _bookSelection?.CurrentSelection?.ID }, { "BookName", _bookSelection?.CurrentSelection?.Title } }); request.PostSucceeded(); } catch (Exception e) { NonFatalProblem.Report(ModalIf.All, PassiveIf.All, "Could not force unlock", null, e, true); request.Failed("could not unlock"); } }
public void HandleCheckInCurrentBook(ApiRequest request) { Action <float> reportCheckinProgress = (fraction) => { dynamic messageBundle = new DynamicJson(); messageBundle.fraction = fraction; _socketServer.SendBundle("checkinProgress", "progress", messageBundle); // The status panel is supposed to be showing a progress bar in response to getting the bundle, // but since we're doing the checkin on the UI thread, it doesn't get painted without this. Application.DoEvents(); }; try { // Right before calling this API, the status panel makes a change that // should make the progress bar visible. But this method is running on // the UI thread so without this call it won't appear until later, when // we have Application.DoEvents() as part of reporting progress. We do // quite a bit on large books before the first file is written to the // zip, so one more DoEvents() here lets the bar appear at once. Application.DoEvents(); _bookSelection.CurrentSelection.Save(); if (!_tcManager.CheckConnection()) { request.Failed(); return; } var bookName = Path.GetFileName(_bookSelection.CurrentSelection.FolderPath); if (_tcManager.CurrentCollection.OkToCheckIn(bookName)) { _tcManager.CurrentCollection.PutBook(_bookSelection.CurrentSelection.FolderPath, true, false, reportCheckinProgress); reportCheckinProgress(0); // cleans up panel for next time // review: not super happy about this being here in the api. Was stymied by // PutBook not knowing about the actual book object, but maybe that could be passed in. BookHistory.AddEvent(_bookSelection.CurrentSelection, BookHistoryEventType.CheckIn); _tcManager.CurrentCollection.PutBook(_bookSelection.CurrentSelection.FolderPath, checkin: true); Analytics.Track("TeamCollectionCheckinBook", new Dictionary <string, string>() { { "CollectionId", _settings?.CollectionId }, { "CollectionName", _settings?.CollectionName }, { "Backend", _tcManager?.CurrentCollection?.GetBackendType() }, { "User", CurrentUser }, { "BookId", _bookSelection?.CurrentSelection.ID }, { "BookName", _bookSelection?.CurrentSelection.Title } }); } else { // We can't check in! The system has broken down...perhaps conflicting checkouts while offline. // Save our version in Lost-and-Found _tcManager.CurrentCollection.PutBook(_bookSelection.CurrentSelection.FolderPath, false, true, reportCheckinProgress); reportCheckinProgress(0); // cleans up panel for next time // overwrite it with the current repo version. _tcManager.CurrentCollection.CopyBookFromRepoToLocal(bookName, dialogOnError: true); // Force a full reload of the book from disk and update the UI to match. _bookSelection.SelectBook(_bookServer.GetBookFromBookInfo(_bookSelection.CurrentSelection.BookInfo, true)); var msg = LocalizationManager.GetString("TeamCollection.ConflictingEditOrCheckout", "Someone else has edited this book or checked it out even though you were editing it! Your changes have been saved to Lost and Found"); ErrorReport.NotifyUserOfProblem(msg); Analytics.Track("TeamCollectionConflictingEditOrCheckout", new Dictionary <string, string>() { { "CollectionId", _settings?.CollectionId }, { "CollectionName", _settings?.CollectionName }, { "Backend", _tcManager?.CurrentCollection?.GetBackendType() }, { "User", CurrentUser }, { "BookId", _bookSelection?.CurrentSelection?.ID }, { "BookName", _bookSelection?.CurrentSelection?.Title } }); } UpdateUiForBook(); request.PostSucceeded(); } catch (Exception e) { reportCheckinProgress(0); // cleans up panel progress indicator var msgId = "TeamCollection.ErrorCheckingBookIn"; var msgEnglish = "Error checking in {0}: {1}"; var log = _tcManager?.CurrentCollection?.MessageLog; // Pushing an error into the log will show the Reload Collection button. It's not obvious this // is useful here, since we don't know exactly what went wrong. However, it at least gives the user // the option to try it. if (log != null) { log.WriteMessage(MessageAndMilestoneType.Error, msgId, msgEnglish, _bookSelection?.CurrentSelection?.FolderPath, e.Message); } Logger.WriteError(String.Format(msgEnglish, _bookSelection?.CurrentSelection?.FolderPath, e.Message), e); NonFatalProblem.ReportSentryOnly(e, $"Something went wrong for {request.LocalPath()} ({_bookSelection?.CurrentSelection?.FolderPath})"); request.Failed("checkin failed"); } }