public SourceCollectionsList(Book.Book.Factory bookFactory, BookStorage.Factory storageFactory, BookCollection.Factory bookCollectionFactory, string editableCollectionDirectory) { _bookFactory = bookFactory; _storageFactory = storageFactory; _bookCollectionFactory = bookCollectionFactory; _editableCollectionDirectory = editableCollectionDirectory; }
public void InsertBook_NotPresent_InsertsInEmptyList() { var infoNew = new BookInfo("book11", true); var state = new List<BookInfo>(); var collection = new BookCollection(state); collection.InsertBookInfo(infoNew); Assert.That(state[0], Is.EqualTo(infoNew), "book info should be inserted between book10 and book20"); }
public BloomServer(CollectionSettings collectionSettings, BookCollection booksInProjectLibrary, SourceCollectionsList sourceCollectionsesList, HtmlThumbNailer thumbNailer) { _collectionSettings = collectionSettings; _booksInProjectLibrary = booksInProjectLibrary; _sourceCollectionsesList = sourceCollectionsesList; _thumbNailer = thumbNailer; }
public BloomServer(CollectionSettings collectionSettings, BookCollection booksInProjectLibrary, SourceCollectionsList sourceCollectionsesList, BookThumbNailer thumbNailer) : base(new RuntimeImageProcessor(new BookRenamedEvent()), thumbNailer) { _collectionSettings = collectionSettings; _booksInProjectLibrary = booksInProjectLibrary; _sourceCollectionsesList = sourceCollectionsesList; }
public void Setup() { Palaso.Reporting.ErrorReport.IsOkToInteractWithUser = false; _folder =new TemporaryFolder("BookCollectionTests"); // _fileLocator = new BloomFileLocator(new CollectionSettings(), new XMatterPackFinder(new string[]{}), new string[] { FileLocator.GetDirectoryDistributedWithApplication("root"), FileLocator.GetDirectoryDistributedWithApplication("factoryCollections") }); _fileLocator = new FileLocator(new string[] { FileLocator.GetDirectoryDistributedWithApplication("BloomBrowserUI"), FileLocator.GetDirectoryDistributedWithApplication("browserui/bookCss"), FileLocator.GetDirectoryDistributedWithApplication("factoryCollections") }); _collection = new BookCollection(_folder.Path, BookCollection.CollectionType.TheOneEditableCollection, new BookSelection()); }
public void SelectCollection(BookCollection collection) { if (_currentSelection == collection) return; //enhance... send out cancellable pre-change event _currentSelection = collection; InvokeSelectionChanged(); }
public void InsertBook_NotPresent_InsertsInCorrectOrder() { var info1 = new BookInfo("book1", true); var info2 = new BookInfo("book2", true); var info3 = new BookInfo("book10", true); var info4 = new BookInfo("book20", true); var infoNew = new BookInfo("book11", true); var state = new List<BookInfo>(new[] {info1, info2, info3, info4}); var collection = new BookCollection(state); collection.InsertBookInfo(infoNew); Assert.That(state[3], Is.EqualTo(infoNew), "book info should be inserted between book10 and book20"); var infoLast = new BookInfo("book30", true); collection.InsertBookInfo(infoLast); Assert.That(state[5], Is.EqualTo(infoLast), "book info should be inserted at end"); var infoFirst = new BookInfo("abc", true); collection.InsertBookInfo(infoFirst); Assert.That(state[0], Is.EqualTo(infoFirst), "book info should be inserted at start"); }
public LibraryModel(string pathToLibrary, CollectionSettings collectionSettings, SendReceiver sendReceiver, BookSelection bookSelection, SourceCollectionsList sourceCollectionsList, BookCollection.Factory bookCollectionFactory, EditBookCommand editBookCommand, CreateFromSourceBookCommand createFromSourceBookCommand, BookServer bookServer, CurrentEditableCollectionSelection currentEditableCollectionSelection) { _bookSelection = bookSelection; _pathToLibrary = pathToLibrary; _collectionSettings = collectionSettings; _sendReceiver = sendReceiver; _sourceCollectionsList = sourceCollectionsList; _bookCollectionFactory = bookCollectionFactory; _editBookCommand = editBookCommand; _bookServer = bookServer; _currentEditableCollectionSelection = currentEditableCollectionSelection; createFromSourceBookCommand.Subscribe(CreateFromSourceBook); }
public HtmlDom GetDomForPrinting(PublishModel.BookletPortions bookletPortion, BookCollection currentBookCollection, BookServer bookServer) { var printingDom = GetBookDomWithStyleSheets("previewMode.css", "origami.css"); AddCreationTypeAttribute(printingDom); if (IsFolio) { AddChildBookContentsToFolio(printingDom, currentBookCollection, bookServer); printingDom.SortStyleSheetLinks(); } //whereas the base is to our embedded server during editing, it's to the file folder //when we make a PDF, because we wan the PDF to use the original hi-res versions var pathSafeForWkHtml2Pdf = FileUtils.MakePathSafeFromEncodingProblems(FolderPath); BookStorage.SetBaseForRelativePaths(printingDom, pathSafeForWkHtml2Pdf); switch (bookletPortion) { case PublishModel.BookletPortions.AllPagesNoBooklet: break; case PublishModel.BookletPortions.BookletCover: DeletePages(printingDom.RawDom, p => !p.GetAttribute("class").ToLowerInvariant().Contains("cover")); break; case PublishModel.BookletPortions.BookletPages: DeletePages(printingDom.RawDom, p => p.GetAttribute("class").ToLowerInvariant().Contains("cover")); break; default: throw new ArgumentOutOfRangeException("bookletPortion"); } AddCoverColor(printingDom, Color.White); AddPreviewJavascript(printingDom); return printingDom; }
/// <summary> /// /// </summary> /// <returns>True if the collection should be shown</returns> private bool LoadOneCollection(BookCollection collection, FlowLayoutPanel flowLayoutPanel) { collection.CollectionChanged += OnCollectionChanged; bool loadedAtLeastOneBook = false; foreach (var bookInfo in collection.GetBookInfos()) { try { if (!bookInfo.IsExperimental || Settings.Default.ShowExperimentalBooks) { loadedAtLeastOneBook = true; AddOneBook(bookInfo, flowLayoutPanel, collection); } } catch (Exception error) { ErrorReport.NotifyUserOfProblem(error, "Could not load the book at " + bookInfo.FolderPath); } } if (collection.ContainsDownloadedBooks) { _downloadedBookCollection = collection; collection.FolderContentChanged += DownLoadedBooksChanged; collection.WatchDirectory(); // In case another instance downloads a book. var bloomLibrayLink = new LinkLabel() { Text = LocalizationManager.GetString("CollectionTab.BloomLibraryLinkLabel", "Get more source books at BloomLibrary.org", "Shown at the bottom of the list of books. User can click on it and it will attempt to open a browser to show the Bloom Library"), Width = 400, Margin = new Padding(17, 0, 0, 0), LinkColor = Palette.TextAgainstDarkBackground }; bloomLibrayLink.Click += new EventHandler(OnBloomLibrary_Click); flowLayoutPanel.Controls.Add(bloomLibrayLink); return true; } return loadedAtLeastOneBook; }
private void AddOneBook(BookInfo bookInfo, FlowLayoutPanel flowLayoutPanel, BookCollection collection) { string title = bookInfo.QuickTitleUserDisplay; if (collection.IsFactoryInstalled) title = LocalizationManager.GetDynamicString("Bloom", "TemplateBooks.BookName." + title, title); var button = new Button { Size = new Size(ButtonWidth, ButtonHeight), Font = bookInfo.IsEditable ? _editableBookFont : _collectionBookFont, TextImageRelation = TextImageRelation.ImageAboveText, ImageAlign = ContentAlignment.TopCenter, TextAlign = ContentAlignment.BottomCenter, FlatStyle = FlatStyle.Flat, ForeColor = Palette.TextAgainstDarkBackground, UseMnemonic = false, //otherwise, it tries to interpret '&' as a shortcut ContextMenuStrip = _bookContextMenu, AutoSize = false, Tag = new BookButtonInfo(bookInfo, collection, collection == _model.TheOneEditableCollection) }; button.MouseDown += OnClickBook; //we need this for right-click menu selection, which needs to 1st select the book //doesn't work: item.DoubleClick += (sender,arg)=>_model.DoubleClickedBook(); button.Text = ShortenTitleIfNeeded(title, button); button.FlatAppearance.BorderSize = 1; button.FlatAppearance.BorderColor = BackColor; toolTip1.SetToolTip(button, title); Image thumbnail = Resources.PagePlaceHolder; _bookThumbnails.Images.Add(bookInfo.Id, thumbnail); button.ImageIndex = _bookThumbnails.Images.Count - 1; flowLayoutPanel.Controls.Add(button); // important to add it before RefreshOneThumbnail; uses parent flow to decide whether primary // Can't use this test until after we add button (uses parent info) if (!IsUsableBook(button)) button.ForeColor = Palette.DisabledTextAgainstDarkBackColor; Image img; var refreshThumbnail = false; //review: we could do this at idle time, too: if (bookInfo.TryGetPremadeThumbnail(out img)) { RefreshOneThumbnail(bookInfo, img); } else { //show this one for now, in the background someone will do the slow work of getting us a better one RefreshOneThumbnail(bookInfo,Resources.placeHolderBookThumbnail); refreshThumbnail = true; } _buttonsNeedingSlowUpdate.Enqueue(new ButtonRefreshInfo(button, refreshThumbnail)); }
public BookButtonInfo(BookInfo bookInfo, BookCollection collection, bool isVernacular) { _bookInfo = bookInfo; _collection = collection; }
private bool LoadOneCollection(BookCollection collection, FlowLayoutPanel flowLayoutPanel) { collection.CollectionChanged += OnCollectionChanged; bool loadedAtLeastOneBook = false; foreach (Book.BookInfo bookInfo in collection.GetBookInfos()) { try { var isSuitableSourceForThisEditableCollection = (_model.IsShellProject && bookInfo.IsSuitableForMakingShells) || (!_model.IsShellProject && bookInfo.IsSuitableForVernacularLibrary); if (isSuitableSourceForThisEditableCollection || collection.Type == BookCollection.CollectionType.TheOneEditableCollection) { if (!bookInfo.IsExperimental || Settings.Default.ShowExperimentalBooks) { loadedAtLeastOneBook = true; AddOneBook(bookInfo, flowLayoutPanel); } } } catch (Exception error) { Palaso.Reporting.ErrorReport.NotifyUserOfProblem(error,"Could not load the book at "+bookInfo.FolderPath); } } return loadedAtLeastOneBook; }
public XmlDocument GetDomForPrinting(PublishModel.BookletPortions bookletPortion, BookCollection currentBookCollection, BookServer bookServer) { var printingDom = GetBookDomWithStyleSheets("previewMode.css"); //dom.LoadXml(OurHtmlDom.OuterXml); if (IsFolio) { AddChildBookContentsToFolio(printingDom, currentBookCollection, bookServer); } //whereas the base is to our embedded server during editing, it's to the file folder //when we make a PDF, because we wan the PDF to use the original hi-res versions BookStorage.SetBaseForRelativePaths(printingDom, FolderPath, false); switch (bookletPortion) { case PublishModel.BookletPortions.AllPagesNoBooklet: break; case PublishModel.BookletPortions.BookletCover: HidePages(printingDom.RawDom, p=>!p.GetAttribute("class").ToLower().Contains("cover")); break; case PublishModel.BookletPortions.BookletPages: HidePages(printingDom.RawDom, p => p.GetAttribute("class").ToLower().Contains("cover")); break; default: throw new ArgumentOutOfRangeException("bookletPortion"); } AddCoverColor(printingDom, Color.White); AddPreviewJScript(printingDom); return printingDom.RawDom; }
public void WatchDirectory_CausesNotification_OnAddFile() { var temp = new TemporaryFolder("BookCollectionWatch"); var collection = new BookCollection(temp.Path, BookCollection.CollectionType.SourceCollection, null); collection.WatchDirectory(); bool gotNotification = false; collection.FolderContentChanged += (sender, args) => { gotNotification = true; }; File.WriteAllText(Path.Combine(temp.Path, "somefile"), @"This is some test data"); // It takes a little time to get the notification. This tells NUnit to try every 20ms for up to 1s. Assert.That(() => gotNotification, Is.True.After(1000, 20)); }
public void InsertBook_Present_Replaces() { var info1 = new BookInfo("book1", true); var info2 = new BookInfo("book2", true); var info3 = new BookInfo("book10", true); var info4 = new BookInfo("book20", true); var infoNew = new BookInfo("book10", true); var state = new List<BookInfo>(new[] { info1, info2, info3, info4 }); var collection = new BookCollection(state); collection.InsertBookInfo(infoNew); Assert.That(state[2], Is.EqualTo(infoNew), "book info should replace existing book"); Assert.That(state, Has.Count.EqualTo(4)); }
public void Setup() { SIL.Reporting.ErrorReport.IsOkToInteractWithUser = false; _folder =new TemporaryFolder("BookCollectionTests"); _fileLocator = new BloomFileLocator(new CollectionSettings(), new XMatterPackFinder(new string[] {}), ProjectContext.GetFactoryFileLocations(), ProjectContext.GetFoundFileLocations(), ProjectContext.GetAfterXMatterFileLocations()); _collection = new BookCollection(_folder.Path, BookCollection.CollectionType.TheOneEditableCollection, new BookSelection()); }
private static int CompareBookCollections(BookCollection x, BookCollection y) { if (x.Name == y.Name) return 0; if (x.Name.ToLower().Contains("templates")) return -1; if (y.Name.ToLower().Contains("templates")) return 1; return 0; }
/// <summary> /// used when this book is a "master"/"folio" book that is used to bring together a number of other books in the collection /// </summary> /// <param name="printingDom"></param> /// <param name="currentBookCollection"></param> /// <param name="bookServer"></param> private void AddChildBookContentsToFolio(HtmlDom printingDom, BookCollection currentBookCollection, BookServer bookServer) { XmlNode currentLastContentPage = GetLastPageForInsertingNewContent(printingDom); //currently we have no way of filtering them, we just take them all foreach (var bookInfo in currentBookCollection.GetBookInfos()) { if (bookInfo.IsFolio) continue; var childBook =bookServer.GetBookFromBookInfo(bookInfo); //this will set the class bloom-content1 on the correct language //this happens anyhow if the page was ever looked at in the Edti Tab //But if we are testing a collection's folio pdf'ing ability on a newly-generated //SHRP collection, and we don't do this, we see lots of sample text because every //bloom-editable has "bloom-content1", even the "Z" language ones. childBook.UpdateEditableAreasOfElement(childBook.OurHtmlDom); //add links to the template css needed by the children. HtmlDom.AddStylesheetFromAnotherBook(childBook.OurHtmlDom, printingDom); printingDom.SortStyleSheetLinks(); foreach (XmlElement pageDiv in childBook.OurHtmlDom.RawDom.SafeSelectNodes("/html/body//div[contains(@class, 'bloom-page') and not(contains(@class,'bloom-frontMatter')) and not(contains(@class,'bloom-backMatter'))]")) { XmlElement importedPage = (XmlElement) printingDom.RawDom.ImportNode(pageDiv, true); currentLastContentPage.ParentNode.InsertAfter(importedPage, currentLastContentPage); currentLastContentPage = importedPage; foreach(XmlElement img in HtmlDom.SelectChildImgAndBackgroundImageElements(importedPage)) { var bookFolderName = Path.GetFileName(bookInfo.FolderPath); var path = HtmlDom.GetImageElementUrl(img); var pathRelativeToFolioFolder = ".../" + bookFolderName + "/" + path.NotEncoded; //NB: URLEncode would replace spaces with '+', which is ok in the parameter section, but not the URL //So we are using UrlPathEncode HtmlDom.SetImageElementUrl(new ElementProxy(img), UrlPathString.CreateFromUnencodedString(pathRelativeToFolioFolder)); } } } }
/// <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, _thumbnailer, null); publishModel.PageLayout = book.GetLayout(); var view = new PublishView(publishModel, new SelectedTabChangedEvent(), new LocalizationChangedEvent(), this, null, null); 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 (languagesToUpload.Any()) FullUpload(book, dlg.Progress, view, languagesToUpload, out dummy, dlg); return; } foreach (var sub in Directory.GetDirectories(folder)) UploadInternal(sub, dlg, container, ref context); }
/// <summary> /// used when this book is a "master"/"folio" book that is used to bring together a number of other books in the collection /// </summary> /// <param name="printingDom"></param> /// <param name="currentBookCollection"></param> /// <param name="bookServer"></param> private void AddChildBookContentsToFolio(HtmlDom printingDom, BookCollection currentBookCollection, BookServer bookServer) { XmlNode currentLastContentPage = GetLastPageForInsertingNewContent(printingDom); //currently we have no way of filtering them, we just take them all foreach (var bookInfo in currentBookCollection.GetBookInfos()) { if (bookInfo.IsFolio) continue; var childBook =bookServer.GetBookFromBookInfo(bookInfo); //add links to the template css needed by the children. //NB: at this point this code can't hand the "customBookStyles" from children, it'll ignore them (they woul conflict with each other) //NB: at this point custom styles (e.g. larger/smaller font rules) from children will be lost. var customStyleSheets = new List<string>(); foreach (string sheetName in childBook.OurHtmlDom.GetTemplateStyleSheets()) { if (!customStyleSheets.Contains(sheetName)) //nb: if two books have stylesheets with the same name, we'll only be grabbing the 1st one. { customStyleSheets.Add(sheetName); printingDom.AddStyleSheetIfMissing("file://"+Path.Combine(childBook.FolderPath,sheetName)); } } printingDom.SortStyleSheetLinks(); foreach (XmlElement pageDiv in childBook.OurHtmlDom.RawDom.SafeSelectNodes("/html/body/div[contains(@class, 'bloom-page') and not(contains(@class,'bloom-frontMatter')) and not(contains(@class,'bloom-backMatter'))]")) { XmlElement importedPage = (XmlElement) printingDom.RawDom.ImportNode(pageDiv, true); currentLastContentPage.ParentNode.InsertAfter(importedPage, currentLastContentPage); currentLastContentPage = importedPage; ImageUpdater.MakeImagePathsOfImportedPagePointToOriginalLocations(importedPage, bookInfo.FolderPath); } } }