public override void Setup() { base.Setup(); Program.SetUpLocalization(new ApplicationContainer()); _bookServer = CreateBookServer(); s_bookSelection.SelectBook(CreateBook()); }
protected Bloom.Book.Book CreateTestBook(string body, bool createPhysicalFile = false, string extraHeadContent = "") { Bloom.Book.Book book; string head = @"<meta charset='UTF-8'/> <link rel='stylesheet' href='defaultLangStyles.css'/> <link rel='stylesheet' href='basePage.css' type='text/css'/> <link rel='stylesheet' href='langVisibility.css' type='text/css'/> <link rel='stylesheet' href='customCollectionStyles.css' type='text/css'/> <link rel='stylesheet' href='Basic Book.css' type='text/css'></link> <link rel='stylesheet' href='Device-XMatter.css' type='text/css'></link> <link rel='stylesheet' href='customBookStyles.css'/>" + extraHeadContent; if (createPhysicalFile) { book = CreateBookWithPhysicalFile(MakeBookHtml(body, head)); } else { SetDom(body, head); book = CreateBook(); } CreateCommonCssFiles(book); s_bookSelection.SelectBook(book); // Set up the visibility classes correctly book.UpdateEditableAreasOfElement(book.OurHtmlDom); book.CollectionSettings.XMatterPackName = "Device"; // give us predictable xmatter with content on page 2 return(book); }
private void OnBookCollectionCreated(object collection, EventArgs args) { var c = collection as BookCollection; if (c.ContainsDownloadedBooks) { c.FolderContentChanged += (sender, eventArgs) => { if (IsDisposed) { Debug.Fail("FolderContentChanged handler invoked from a CollectionTabView that has already been disposed. Did the collection have cleanup such as StopWatchingDirectory() occur?"); return; } if (_tabSelection.ActiveTab == WorkspaceTab.collection) { // We got a new or modified book in the downloaded books collection. // If this (collection) tab is active, we want to select it. // (If we're in the middle of editing or publishing some book, we // don't want to change that.) // One day we may enhance it so that we switch tabs and show it, // but there are states where that would be dangerous. var newBook = new BookInfo(eventArgs.Path, false); var book = _model.GetBookFromBookInfo(newBook, true); _bookSelection.SelectBook(book, false); } }; } }
private EnhancedImageServer CreateImageServer() { var bookSelection = new BookSelection(); bookSelection.SelectBook(new Bloom.Book.Book()); return(new EnhancedImageServer(new RuntimeImageProcessor(new BookRenamedEvent()), null, bookSelection, _fileLocator)); }
private void SelectBook(BookInfo bookInfo) { try { _bookSelection.SelectBook(_model.GetBookFromBookInfo(bookInfo)); _bookContextMenu.Enabled = true; //Debug.WriteLine("before selecting " + SelectedBook.Title); _model.SelectBook(SelectedBook); //Debug.WriteLine("after selecting " + SelectedBook.Title); //didn't help: _listView.Focus();//hack we were losing clicks SelectedBook.ContentsChanged -= new EventHandler(OnContentsOfSelectedBookChanged); //in case we're already subscribed SelectedBook.ContentsChanged += new EventHandler(OnContentsOfSelectedBookChanged); deleteMenuItem.Enabled = _model.CanDeleteSelection; _updateThumbnailMenu.Visible = _model.CanUpdateSelection; _updateFrontMatterToolStripMenu.Visible = _model.CanUpdateSelection; } catch (Exception error) { //skip over the dependency injection layer if (error.Source == "Autofac" && error.InnerException != null) { error = error.InnerException; } Palaso.Reporting.ErrorReport.NotifyUserOfProblem(error, "Bloom cannot display that book."); } }
/// <summary> /// Handles the recursion through directories: if a folder looks like a Bloom book upload it; otherwise, try its children. /// Invisible folders like .hg are ignored. /// </summary> /// <param name="folder"></param> /// <param name="dlg"></param> /// <param name="container"></param> /// <param name="context"></param> private void UploadInternal(string folder, BulkUploadProgressDlg dlg, ApplicationContainer container, ref ProjectContext context) { if (Path.GetFileName(folder).StartsWith(".")) { return; // secret folder, probably .hg } if (Directory.GetFiles(folder, "*.htm").Count() == 1) { // Exactly one htm file, assume this is a bloom book folder. dlg.Progress.WriteMessage("Starting to upload " + folder); // Make sure the files we want to upload are up to date. // Unfortunately this requires making a book object, which requires making a ProjectContext, which must be created with the // proper parent book collection if possible. var parent = Path.GetDirectoryName(folder); var collectionPath = Directory.GetFiles(parent, "*.bloomCollection").FirstOrDefault(); if (collectionPath == null && context == null) { collectionPath = Settings.Default.MruProjects.Latest; } if (context == null || context.SettingsPath != collectionPath) { if (context != null) { context.Dispose(); } // optimise: creating a context seems to be quite expensive. Probably the only thing we need to change is // the collection. If we could update that in place...despite autofac being told it has lifetime scope...we would save some time. // Note however that it's not good enough to just store it in the project context. The one that is actually in // the autofac object (_scope in the ProjectContext) is used by autofac to create various objects, in particular, books. context = container.CreateProjectContext(collectionPath); } var server = context.BookServer; var book = server.GetBookFromBookInfo(new BookInfo(folder, true)); book.BringBookUpToDate(new NullProgress()); // Assemble the various arguments needed to make the objects normally involved in an upload. // We leave some constructor arguments not actually needed for this purpose null. var bookSelection = new BookSelection(); bookSelection.SelectBook(book); var currentEditableCollectionSelection = new CurrentEditableCollectionSelection(); if (collectionPath != null) { var collection = new BookCollection(collectionPath, BookCollection.CollectionType.SourceCollection, bookSelection); currentEditableCollectionSelection.SelectCollection(collection); } var publishModel = new PublishModel(bookSelection, new PdfMaker(), currentEditableCollectionSelection, null, server, _htmlThumbnailer); publishModel.PageLayout = book.GetLayout(); var view = new PublishView(publishModel, new SelectedTabChangedEvent(), new LocalizationChangedEvent(), this, null); string dummy; FullUpload(book, dlg.Progress, view, out dummy, dlg); return; } foreach (var sub in Directory.GetDirectories(folder)) { UploadInternal(sub, dlg, container, ref context); } }
private BloomServer CreateBloomServer(BookInfo info = null) { var bookSelection = new BookSelection(); var collectionSettings = new CollectionSettings(); bookSelection.SelectBook(new Bloom.Book.Book(info)); return(new BloomServer(new RuntimeImageProcessor(new BookRenamedEvent()), bookSelection, collectionSettings, _fileLocator)); }
public void Setup() { _bookSelection = new BookSelection(); _bookSelection.SelectBook(new Bloom.Book.Book()); _server = new BloomServer(_bookSelection); var controller = new ReadersApi(_bookSelection, null); controller.RegisterWithApiHandler(_server.ApiHandler); }
public void InitialSetup() { var bookSelection = new BookSelection(); bookSelection.SelectBook(new Bloom.Book.Book()); _server = new BloomServer(bookSelection); var controller = new FileIOApi(bookSelection); controller.RegisterWithApiHandler(_server.ApiHandler); }
private BookSelection GetDefaultBookSelection() { var bookSelection = new BookSelection(); var mockBook = new Mock <Bloom.Book.Book>(); mockBook.Setup(x => x.TitleBestForUserDisplay).Returns("Fake Book Title"); bookSelection.SelectBook(mockBook.Object); return(bookSelection); }
public void Setup() { var bookSelection = new BookSelection(); bookSelection.SelectBook(new Bloom.Book.Book()); _server = new EnhancedImageServer(bookSelection); //needed to avoid a check in the server _server.CurrentCollectionSettings = new CollectionSettings(); var controller = new ReadersApi(bookSelection); controller.RegisterWithServer(_server); }
private void OnClickBook(object sender, EventArgs e) { try { BookInfo bookInfo = ((Button)sender).Tag as BookInfo; if (bookInfo == null) { return; } if (SelectedBook != null && bookInfo == SelectedBook.BookInfo) { //I couldn't get the DoubleClick event to work, so I rolled my own if (Control.MouseButtons == MouseButtons.Left && DateTime.Now.Subtract(_lastClickTime).TotalMilliseconds < SystemInformation.DoubleClickTime) { _model.DoubleClickedBook(); return; } } else { _bookSelection.SelectBook(_model.GetBookFromBookInfo(bookInfo)); } _lastClickTime = DateTime.Now; _bookContextMenu.Enabled = true; //Debug.WriteLine("before selecting " + SelectedBook.Title); _model.SelectBook(SelectedBook); //Debug.WriteLine("after selecting " + SelectedBook.Title); //didn't help: _listView.Focus();//hack we were losing clicks SelectedBook.ContentsChanged -= new EventHandler(OnContentsOfSelectedBookChanged); //in case we're already subscribed SelectedBook.ContentsChanged += new EventHandler(OnContentsOfSelectedBookChanged); deleteMenuItem.Enabled = _model.CanDeleteSelection; _updateThumbnailMenu.Visible = _model.CanUpdateSelection; _updateFrontMatterToolStripMenu.Visible = _model.CanUpdateSelection; } catch (Exception error) { //skip over the dependency injection layer if (error.Source == "Autofac" && error.InnerException != null) { error = error.InnerException; } Palaso.Reporting.ErrorReport.NotifyUserOfProblem(error, "Bloom cannot display that book."); } }
public void DeleteBook(Book.BookInfo bookInfo) { var didDelete = ConfirmRecycleDialog.Recycle(bookInfo.FolderPath); if (!didDelete) { return; } Logger.WriteEvent("After BookStorage.DeleteBook({0})", bookInfo.FolderPath); HandleBookDeletedFromCollection(bookInfo.FolderPath); if (_bookSelection != null) { _bookSelection.SelectBook(null); } }
public void DeleteBook(Book.BookInfo bookInfo) { var didDelete = ConfirmRecycleDialog.Recycle(bookInfo.FolderPath); if (!didDelete) { return; } Logger.WriteEvent("After BookStorage.DeleteBook({0})", bookInfo.FolderPath); //Debug.Assert(_bookInfos.Contains(bookInfo)); this will occur if we delete a book from the BloomLibrary section _bookInfos.Remove(bookInfo); if (CollectionChanged != null) { CollectionChanged.Invoke(this, null); } if (_bookSelection != null) { _bookSelection.SelectBook(null); } }
public void DeleteBook(Book.BookInfo bookInfo) { var didDelete = ConfirmRecycleDialog.Recycle(bookInfo.FolderPath); if (!didDelete) { return; } Logger.WriteEvent("After BookStorage.DeleteBook({0})", bookInfo.FolderPath); //ListOfBooksIsOutOfDate(); Debug.Assert(_bookInfos.Contains(bookInfo)); _bookInfos.Remove(bookInfo); if (CollectionChanged != null) { CollectionChanged.Invoke(this, null); } if (_bookSelection != null) { _bookSelection.SelectBook(null); } }
/// <summary> /// Handles the recursion through directories: if a folder looks like a Bloom book upload it; otherwise, try its children. /// Invisible folders like .hg are ignored. /// </summary> /// <param name="folder"></param> /// <param name="dlg"></param> /// <param name="container"></param> /// <param name="excludeAudio"></param> /// <param name="alreadyUploaded"></param> /// <param name="context"></param> private void UploadInternal(string folder, BulkUploadProgressDlg dlg, ApplicationContainer container, bool excludeAudio, string[] alreadyUploaded, ref ProjectContext context) { var lastFolderPart = Path.GetFileName(folder); if (lastFolderPart != null && lastFolderPart.StartsWith(".")) { return; // secret folder, probably .hg } if (Directory.GetFiles(folder, "*.htm").Length == 1) { if (alreadyUploaded.Contains(folder)) { return; // skip this one; we already successfully uploaded it at some point } // Exactly one htm file, assume this is a bloom book folder. dlg.Progress.WriteMessage("Starting to upload " + folder); // Make sure the files we want to upload are up to date. // Unfortunately this requires making a book object, which requires making a ProjectContext, which must be created with the // proper parent book collection if possible. var parent = Path.GetDirectoryName(folder); var collectionPath = Directory.GetFiles(parent, "*.bloomCollection").FirstOrDefault(); if (collectionPath == null) { collectionPath = Settings.Default.MruProjects.Latest; } if (collectionPath == null) { throw new ApplicationException("Collection not found in this or parent directory."); } if (context == null || context.SettingsPath != collectionPath) { context?.Dispose(); // optimise: creating a context seems to be quite expensive. Probably the only thing we need to change is // the collection. If we could update that in place...despite autofac being told it has lifetime scope...we would save some time. // Note however that it's not good enough to just store it in the project context. The one that is actually in // the autofac object (_scope in the ProjectContext) is used by autofac to create various objects, in particular, books. context = container.CreateProjectContext(collectionPath); Program.SetProjectContext(context); } var server = context.BookServer; var bookInfo = new BookInfo(folder, true); bookInfo.BookshelfList = GetBookshelfName(folder); var book = server.GetBookFromBookInfo(bookInfo); book.BringBookUpToDate(new NullProgress()); // Assemble the various arguments needed to make the objects normally involved in an upload. // We leave some constructor arguments not actually needed for this purpose null. var bookSelection = new BookSelection(); bookSelection.SelectBook(book); var currentEditableCollectionSelection = new CurrentEditableCollectionSelection(); var collection = new BookCollection(collectionPath, BookCollection.CollectionType.SourceCollection, bookSelection); currentEditableCollectionSelection.SelectCollection(collection); var publishModel = new PublishModel(bookSelection, new PdfMaker(), currentEditableCollectionSelection, context.Settings, server, _thumbnailer, null); publishModel.PageLayout = book.GetLayout(); var view = new PublishView(publishModel, new SelectedTabChangedEvent(), new LocalizationChangedEvent(), this, null, null, null); var blPublishModel = new BloomLibraryPublishModel(this, book); string dummy; // Normally we let the user choose which languages to upload. Here, just the ones that have complete information. var langDict = book.AllLanguages; var languagesToUpload = langDict.Keys.Where(l => langDict[l]).ToArray(); if (blPublishModel.MetadataIsReadyToPublish && (languagesToUpload.Any() || blPublishModel.OkToUploadWithNoLanguages)) { if (blPublishModel.BookIsAlreadyOnServer) { var msg = "Apparently this book is already on the server. Overwriting..."; ReportToLogBoxAndLogger(dlg.Progress, folder, msg); } FullUpload(book, dlg.Progress, view, languagesToUpload, out dummy, excludeAudio); AppendBookToUploadLogFile(folder); } else { // report to the user why we are not uploading their book ReportToLogBoxAndLogger(dlg.Progress, folder, blPublishModel.GetReasonForNotUploadingBook()); } return; } foreach (var sub in Directory.GetDirectories(folder)) { UploadInternal(sub, dlg, container, excludeAudio, alreadyUploaded, ref context); } }
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"); } }
public void SelectBook(Book.Book book) { _bookSelection.SelectBook(book); }
public void GetTextOfContentPagesAsJson_OmitsUnwantedPages() { var htmlLeveledReader = $@"<html><head><meta charset='UTF-8'></meta></head> <body class='leveled-reader' data-l1='en' data-l2='fr' data-l3='es'> <!-- ignore page with bloom-frontMatter class --> <div class='bloom-page bloom-frontMatter' id='b8408838-e9ed-4d76-bb8a-24ad0708b329' lang='fr'> <div class='marginBox'> <div class='bloom-translationGroup bookTitle' data-default-languages='V,N1'> <div class='bloom-editable bloom-content1 bloom-visibility-code-on' lang='en'> <p>Level Two</p> </div> </div> <div class='bloom-imageContainer' title='Name: aor_IN16.png'> <img src='aor_IN16.png'></img> <div class='bloom-translationGroup bloom-imageDescription'> <div class='bloom-editable bloom-content1 bloom-visibility-code-on' lang='en'>cat</div> </div> </div> </div> </div> <!-- ignore page with bloom-nonprinting class --> <div class='bloom-page bloom-noreader bloom-nonprinting screen-only' id='33b7e4c5-6cd1-4611-a50f-85837143cbf6' lang=''> <div class='pageLabel' data-i18n='TemplateBooks.PageLabel.Translation Instructions' lang='en'>Translation Instructions</div> <div class='marginBox'> <div class='split-pane-component-inner'> <div class='bloom-translationGroup' data-default-languages='*'> <div class='bloom-editable bloom-content1 bloom-visibility-code-on' data-book='' lang='en' contenteditable='true'> <p>Instructions in vernacular language ...</p> </div> </div> </div> </div> </div> <!-- skip over page with no bloom-content1 content apart from image descriptions --> <div class='bloom-page numberedPage' id='a2ecb8be-5c7f-440d-9ef5-d503476211cd' data-page-number='1' lang=''> <div class='pageLabel' data-i18n='TemplateBooks.PageLabel.Just a Picture' lang='en'>Just a Picture</div> <div class='marginBox'> <div class='split-pane-component-inner'> <div class='bloom-imageContainer' title='Name: aor_acc034m.png'> <img src='aor_acc034m.png' alt='cat staring at something outside the picture'></img> <div class='bloom-translationGroup bloom-imageDescription bloom-trailingElement'> <div class='bloom-editable bloom-content1 bloom-visibility-code-on' lang='en'> <p>cat staring at something outside the picture</p> </div> </div> </div> </div> </div> </div> <!-- include content page with bloom-content1 content, ignoring image description --> <div class='bloom-page numberedPage' id='85a320a4-b73f-4149-87a1-9a1297ef04b0' data-page-number='2' lang=''> <div class='pageLabel' data-i18n='TemplateBooks.PageLabel.Basic Text & Picture' lang='en'>Basic Text & Picture</div> <div class='marginBox'> <div class='split-pane horizontal-percent'> <div class='split-pane-component position-top'> <div class='split-pane-component-inner'> <div class='bloom-imageContainer bloom-leadingElement' title='Name: aor_Cat3.png'> <img src='aor_Cat3.png' alt='cat lying down and staring at you through slitted eyes'></img> <div class='bloom-translationGroup bloom-imageDescription'> <div class='bloom-editable bloom-content1 bloom-visibility-code-on' lang='en'> <p>cat lying down and staring at you through slitted eyes</p> </div> </div> </div> </div> </div> <div class='split-pane-divider horizontal-divider'></div> <div class='split-pane-component position-bottom'> <div class='split-pane-component-inner'> <div class='bloom-translationGroup' data-default-languages='auto'> <div class='bloom-editable bloom-content1 bloom-visibility-code-on' id='i57437cd1-c55c-499e-b0c3-d7195fba5f11' lang='en'> <p>A cat stares at you.</p> </div> </div> </div> </div> </div> </div> </div> <!-- include content page with empty bloom-content1 content, ignoring image description --> <div class='bloom-page numberedPage' id='d46e4259-2a99-4197-b21d-bf97a992b7d0' data-page-number='3' lang=''> <div class='pageLabel' data-i18n='TemplateBooks.PageLabel.Basic Text & Picture' lang='en'>Basic Text & Picture</div> <div class='marginBox'> <div class='split-pane horizontal-percent'> <div class='split-pane-component position-top'> <div class='split-pane-component-inner'> <div class='bloom-imageContainer' title='Name: aor_ACC029M.png'> <img src='aor_ACC029M.png' alt='cat sleeping in a box that is just large enough'></img> <div class='bloom-translationGroup bloom-imageDescription'> <div class='bloom-editable bloom-content1 bloom-visibility-code-on' lang='en'> <p>cat sleeping in a box that is just large enough</p> </div> </div> </div> </div> </div> <div class='split-pane-divider horizontal-divider'></div> <div class='split-pane-component position-bottom'> <div class='split-pane-component-inner'> <div class='bloom-translationGroup' data-default-languages='auto'> <div class='bloom-editable bloom-content1 bloom-visibility-code-on' lang='en'> <p></p> </div> </div> </div> </div> </div> </div> </div> <!-- include content page with bloom-content1 content and no image --> <div class='bloom-page numberedPage' id='5a424678-ec70-4c97-a547-015ff38dfd11' data-page-number='4' lang=''> <div class='marginBox'> <div class='bloom-translationGroup bloom-trailingElement' data-default-languages='auto'> <div class='bloom-editable normal-style bloom-content1 bloom-visibility-code-on' lang='en'> <p>See the two kittens.</p> </div> </div> </div> </div> <!-- include page with non-vernacular text if it also has vernacular text --> <div class='bloom-page numberedPage' id='ebbd7f47-05fa-4e6d-a7a1-cb526bb5efb8' data-page-number='5' lang=''> <div class='pageLabel' data-i18n='TemplateBooks.PageLabel.Custom' lang='en'>Custom</div> <div class='marginBox'> <div class='split-pane horizontal-percent'> <div class='split-pane-component position-top'> <div class='split-pane-component-inner' min-width='60px 150px 250px' min-height='60px 150px'> <div class='bloom-translationGroup bloom-trailingElement' data-default-languages='N1'> <div class='bloom-editable bloom-contentNational1 bloom-visibility-code-on' lang='fr'> <p>national language text</p> </div> </div> </div> </div> <div class='split-pane-divider horizontal-divider' title='33.2%'></div> <div class='split-pane-component position-bottom'> <div class='split-pane-component position-top'> <div class='split-pane-component-inner' min-width='60px 150px 250px' min-height='60px 150px'> <div class='bloom-translationGroup bloom-trailingElement' data-default-languages='V'> <div class='bloom-editable bloom-content1 bloom-visibility-code-on' lang='en'> <p>local language text</p> </div> </div> </div> </div> </div> </div> </div> </div> <!-- ignore page with bloom-ignoreForReaderStats class --> <div class='bloom-page numberedPage bloom-ignoreForReaderStats' id='ebad7a47-05fa-4e6d-a7a1-cb526bb5efb8' data-page-number='6' lang=''> <div class='pageLabel' data-i18n='TemplateBooks.PageLabel.Custom' lang='en'>Custom</div> <div class='marginBox'> <div class='split-pane horizontal-percent'> <div class='split-pane-component position-top'> <div class='split-pane-component-inner' min-width='60px 150px 250px' min-height='60px 150px'> <div class='bloom-translationGroup bloom-trailingElement' data-default-languages='N1'> <div class='bloom-editable normal-style bloom-contentNational1 bloom-visibility-code-on' lang='fr'> <p>This is in the national language.</p> </div> </div> </div> </div> <div class='split-pane-divider horizontal-divider' title='33.2%'></div> <div class='split-pane-component position-bottom'> <div class='split-pane-component position-top'> <div class='split-pane-component-inner' min-width='60px 150px 250px' min-height='60px 150px'> <div class='bloom-translationGroup bloom-trailingElement' data-default-languages='V'> <div class='bloom-editable normal-style bloom-content1 bloom-visibility-code-on' lang='en'> <p>This is in the local language.</p> </div> </div> </div> </div> </div> </div> </div> </div> <!-- ignore page with bloom-backMatter class --> <div class='bloom-page titlePage bloom-backMatter' id='0f8d4d80-0519-42b6-bcde-6362b99ed1ff' lang='fr'> <div class='pageLabel' lang='en' data-i18n='TemplateBooks.PageLabel.Title Page'>Title Page</div> <div class='marginBox'> <div class='bloom-translationGroup' data-default-languages='V,N1' id='titlePageTitleBlock'> <div class='bloom-editable bloom-content1 bloom-visibility-code-on' lang='en'> <p>Level Two</p> </div> </div> </div> </div> </body></html> "; var doc = new XmlDocument(); doc.LoadXml(htmlLeveledReader); var dom = new HtmlDom(doc); var storage = CreateMockStorage(dom, "GetPagesForReader"); var book = new Bloom.Book.Book(storage.Object.BookInfo, storage.Object); _bookSelection.SelectBook(book); var result = ApiTest.GetString(_server, "readers/io/textOfContentPages"); Assert.That(result, Is.EqualTo("{\"85a320a4-b73f-4149-87a1-9a1297ef04b0\":\"<p>A cat stares at you.</p>\",\"d46e4259-2a99-4197-b21d-bf97a992b7d0\":\"<p />\",\"5a424678-ec70-4c97-a547-015ff38dfd11\":\"<p>See the two kittens.</p>\",\"ebbd7f47-05fa-4e6d-a7a1-cb526bb5efb8\":\"<p>local language text</p>\"}")); }
public override void Setup() { base.Setup(); _bookServer = CreateBookServer(); s_bookSelection.SelectBook(CreateBook()); }
private void UploadBookInternal(IProgress progress, ApplicationContainer container, BookUploadParameters uploadParams, ref ProjectContext context) { progress.WriteMessageWithColor("Cyan", "Starting to upload " + uploadParams.Folder); // Make sure the files we want to upload are up to date. // Unfortunately this requires making a book object, which requires making a ProjectContext, which must be created with the // proper parent book collection if possible. var parent = Path.GetDirectoryName(uploadParams.Folder); var collectionPath = Directory.GetFiles(parent, "*.bloomCollection").FirstOrDefault(); if (collectionPath == null || !RobustFile.Exists(collectionPath)) { progress.WriteError("Skipping book because no collection file was found in its parent directory."); return; } _collectionFoldersUploaded.Add(collectionPath); // Compute the book hash file and compare it to the existing one for bulk upload. var currentHashes = BookUpload.HashBookFolder(uploadParams.Folder); progress.WriteMessage(currentHashes); var pathToLocalHashInfoFromLastUpload = Path.Combine(uploadParams.Folder, HashInfoFromLastUpload); if (!uploadParams.ForceUpload) { var canSkip = false; if (Program.RunningUnitTests) { canSkip = _singleBookUploader.CheckAgainstLocalHashfile(currentHashes, pathToLocalHashInfoFromLastUpload); } else { canSkip = _singleBookUploader.CheckAgainstHashFileOnS3(currentHashes, uploadParams.Folder, progress); RobustFile.WriteAllText(pathToLocalHashInfoFromLastUpload, currentHashes); // ensure local copy is saved } if (canSkip) { // local copy of hashes file is identical or has been saved progress.WriteMessageWithColor("green", $"Skipping '{Path.GetFileName(uploadParams.Folder)}' because it has not changed since being uploaded."); ++_booksSkipped; return; // skip this one; we already uploaded it earlier. } } // save local copy of hashes file: it will be uploaded with the other book files RobustFile.WriteAllText(pathToLocalHashInfoFromLastUpload, currentHashes); if (context == null || context.SettingsPath != collectionPath) { context?.Dispose(); // optimise: creating a context seems to be quite expensive. Probably the only thing we need to change is // the collection. If we could update that in place...despite autofac being told it has lifetime scope...we would save some time. // Note however that it's not good enough to just store it in the project context. The one that is actually in // the autofac object (_scope in the ProjectContext) is used by autofac to create various objects, in particular, books. context = container.CreateProjectContext(collectionPath); Program.SetProjectContext(context); } var server = context.BookServer; var bookInfo = new BookInfo(uploadParams.Folder, true); var book = server.GetBookFromBookInfo(bookInfo); book.BringBookUpToDate(new NullProgress()); bookInfo.Bookshelf = book.CollectionSettings.DefaultBookshelf; var bookshelfName = String.IsNullOrWhiteSpace(book.CollectionSettings.DefaultBookshelf) ? "(none)" : book.CollectionSettings.DefaultBookshelf; progress.WriteMessage($"Bookshelf is '{bookshelfName}'"); // Assemble the various arguments needed to make the objects normally involved in an upload. // We leave some constructor arguments not actually needed for this purpose null. var bookSelection = new BookSelection(); bookSelection.SelectBook(book); var currentEditableCollectionSelection = new CurrentEditableCollectionSelection(); var collection = new BookCollection(collectionPath, BookCollection.CollectionType.SourceCollection, bookSelection); currentEditableCollectionSelection.SelectCollection(collection); var publishModel = new PublishModel(bookSelection, new PdfMaker(), currentEditableCollectionSelection, context.Settings, server, _thumbnailer); publishModel.PageLayout = book.GetLayout(); var view = new PublishView(publishModel, new SelectedTabChangedEvent(), new LocalizationChangedEvent(), _singleBookUploader, null, null, null, null); var blPublishModel = new BloomLibraryPublishModel(_singleBookUploader, book, publishModel); string dummy; // Normally we let the user choose which languages to upload. Here, just the ones that have complete information. var langDict = book.AllPublishableLanguages(); var languagesToUpload = langDict.Keys.Where(l => langDict[l]).ToList(); if (!string.IsNullOrEmpty(book.CollectionSettings.SignLanguageIso639Code) && BookUpload.GetVideoFilesToInclude(book).Any()) { languagesToUpload.Insert(0, book.CollectionSettings.SignLanguageIso639Code); } if (blPublishModel.MetadataIsReadyToPublish && (languagesToUpload.Any() || blPublishModel.OkToUploadWithNoLanguages)) { if (blPublishModel.BookIsAlreadyOnServer) { var msg = $"Overwriting the copy of {uploadParams.Folder} on the server..."; progress.WriteWarning(msg); } using (var tempFolder = new TemporaryFolder(Path.Combine("BloomUpload", Path.GetFileName(book.FolderPath)))) { BookUpload.PrepareBookForUpload(ref book, server, tempFolder.FolderPath, progress); uploadParams.LanguagesToUpload = languagesToUpload.ToArray(); _singleBookUploader.FullUpload(book, progress, view, uploadParams, out dummy); } progress.WriteMessageWithColor("Green", "{0} has been uploaded", uploadParams.Folder); if (blPublishModel.BookIsAlreadyOnServer) { ++_booksUpdated; } else { ++_newBooksUploaded; } } else { // report to the user why we are not uploading their book var reason = blPublishModel.GetReasonForNotUploadingBook(); progress.WriteError("{0} was not uploaded. {1}", uploadParams.Folder, reason); ++_booksWithErrors; } }