private void UpdateEditabilityMetadata(BookStorage storage) { //Here's the logic: If we're in a shell-making library, then it's safe to say that a newly- //created book is going to be a shell. Any derivatives will then act as shells. But it won't //prevent us from editing it while in a shell-making collections, since we don't honor this //tag in shell-making collections. //The problem is, if you make a book in some vernacular library, then share it so that others //can use it as a shell, then (as of version 2) Bloom doesn't have a way of realizing that it's //being used as a shell. So everything is editable (not a big deal) but you're also locked out // of editing the acknowledgements for translated version. //It seems to me at the moment (May 2014) that the time to mark something as locked down should //be when the they create a book based on a source-with-content book. So the current approach //below, of pre-locking it, would go away. if (_isSourceCollection) { storage.Dom.UpdateMetaElement("lockedDownAsShell", "true"); } #if maybe //hard to pin down when a story primer, dictionary, etc. also becomes a new "source for new shells" //things like picture dictionaries could be used repeatedly //but things from Basic Book are normally not. var x = GetMetaValue(storage.Dom, "DerivativesAreSuitableForMakingShells", "false"); #else var x = "false"; #endif storage.Dom.UpdateMetaElement("SuitableForMakingShells", x); }
public void Save_BookHadOnlyPaperSizeStyleSheet_StillHasIt() { File.WriteAllText(_bookPath, "<html><head><link rel='stylesheet' href='Basic Book.css' type='text/css' /></head><body><div class='bloom-page'></div></body></html>"); var storage = new BookStorage(_folder.FolderPath, _fileLocator, new BookRenamedEvent(), new CollectionSettings()); storage.Save(); AssertThatXmlIn.HtmlFile(_bookPath).HasSpecifiedNumberOfMatchesForXpath("//link[contains(@href, 'Basic Book')]", 1); }
/// <summary> /// Given a template, make a new book /// </summary> /// <param name="sourceBookFolder"></param> /// <param name="parentCollectionPath"></param> /// <returns>path to the new book folder</returns> public string CreateBookOnDiskFromTemplate(string sourceBookFolder, string parentCollectionPath) { Logger.WriteEvent("BookStarter.CreateBookOnDiskFromTemplate({0}, {1})", sourceBookFolder, parentCollectionPath); // We use the "initial name" to make the initial copy, and it gives us something //to name the folder and file until such time as the user enters a title in for the book. string initialBookName = GetInitialName(parentCollectionPath); var newBookFolder = Path.Combine(parentCollectionPath, initialBookName); CopyFolder(sourceBookFolder, newBookFolder); BookStorage.RemoveLocalOnlyFiles(newBookFolder); //if something bad happens from here on out, we need to delete that folder we just made try { var oldNamedFile = Path.Combine(newBookFolder, Path.GetFileName(GetPathToHtmlFile(sourceBookFolder))); var newNamedFile = Path.Combine(newBookFolder, initialBookName + ".htm"); RobustFile.Move(oldNamedFile, newNamedFile); //the destination may change here... newBookFolder = SetupNewDocumentContents(sourceBookFolder, newBookFolder); if (OnNextRunSimulateFailureMakingBook) { throw new ApplicationException("Simulated failure for unit test"); } } catch (Exception) { SIL.IO.RobustIO.DeleteDirectory(newBookFolder, true); throw; } return(newBookFolder); }
private static void SetBookTitle(BookStorage storage, BookData bookData) { //NB: no multi-lingual name suggestion ability yet //otherwise, the case where there is no defaultNameForDerivedBooks, we just want to use the names //that the shell used, e.g. "Vaccinations". //We don't have to do anything special to get that. string kdefaultName = null; var nameSuggestion = storage.Dom.GetMetaValue("defaultNameForDerivedBooks", kdefaultName); // var nameSuggestion = storage.Dom.SafeSelectNodes("//head/meta[@name='defaultNameForDerivedBooks']"); if (nameSuggestion != null) { bookData.Set("bookTitle", nameSuggestion, "en"); storage.MetaData.Title = nameSuggestion; } storage.Dom.RemoveMetaElement("defaultNameForDerivedBooks"); // //var name = "New Book"; //shouldn't rarel show up, because it will be overriden by the meta tag // if (nameSuggestion.Count > 0) // { // var metaTag = (XmlElement) nameSuggestion[0]; // var name = metaTag.GetAttribute("content"); // bookData.SetDataDivBookVariable("bookTitle", name, "en"); // metaTag.ParentNode.RemoveChild(metaTag); // } // else // { // // } }
public BookServer(Book.Factory bookFactory, BookStorage.Factory storageFactory, BookStarter.Factory bookStarterFactory) { _bookFactory = bookFactory; _storageFactory = storageFactory; _bookStarterFactory = bookStarterFactory; }
//autofac uses this public BookStarter(IChangeableFileLocator fileLocator, BookStorage.Factory bookStorageFactory, CollectionSettings collectionSettings) { _fileLocator = fileLocator; _bookStorageFactory = bookStorageFactory; _collectionSettings = collectionSettings; _isSourceCollection = collectionSettings.IsSourceCollection; }
public SourceCollectionsList(Book.Book.Factory bookFactory, BookStorage.Factory storageFactory, BookCollection.Factory bookCollectionFactory, string editableCollectionDirectory) { _bookFactory = bookFactory; _storageFactory = storageFactory; _bookCollectionFactory = bookCollectionFactory; _editableCollectionDirectory = editableCollectionDirectory; }
public BookServer(Book.Factory bookFactory, BookStorage.Factory storageFactory, BookStarter.Factory bookStarterFactory, Configurator.Factory configuratorFactory) { _bookFactory = bookFactory; _storageFactory = storageFactory; _bookStarterFactory = bookStarterFactory; _configuratorFactory = configuratorFactory; }
public void Save_BookHadEditStyleSheet_NowHasPreviewAndBase() { File.WriteAllText(_bookPath, "<html><head> href='file://blahblah\\editMode.css' type='text/css' /></head><body><div class='bloom-page'></div></body></html>"); var storage = new BookStorage(_folder.FolderPath, _fileLocator, new BookRenamedEvent(), new CollectionSettings()); storage.Save(); AssertThatXmlIn.HtmlFile(_bookPath).HasSpecifiedNumberOfMatchesForXpath("//link[contains(@href, 'basePage')]", 1); AssertThatXmlIn.HtmlFile(_bookPath).HasSpecifiedNumberOfMatchesForXpath("//link[contains(@href, 'preview')]", 1); }
public SourceCollectionsList(Book.Book.Factory bookFactory, BookStorage.Factory storageFactory, string editableCollectionDirectory, IEnumerable<string> sourceRootFolders) { _bookFactory = bookFactory; _storageFactory = storageFactory; _editableCollectionDirectory = editableCollectionDirectory; _sourceRootFolders = sourceRootFolders; }
// At publish time, we strip out pages with the bloom-enterprise class. // But we don't want to do that for comic pages in derivatives. See BL-10586. private void StripBloomEnterpriseClassFromComicPages(BookStorage storage) { foreach (XmlElement pageDiv in storage.Dom.RawDom.SafeSelectNodes("//div[contains(@class,'bloom-page')]")) { if (HtmlDom.HasClass(pageDiv, "comic")) { HtmlDom.RemoveClass(pageDiv, "enterprise-only"); } } }
public PageTemplatesApi(SourceCollectionsList sourceCollectionsList,BookSelection bookSelection, PageSelection pageSelection, TemplateInsertionCommand templateInsertionCommand, BookThumbNailer thumbNailer, Book.Book.Factory bookFactory, BookStorage.Factory storageFactory) { _sourceCollectionsList = sourceCollectionsList; _bookSelection = bookSelection; _pageSelection = pageSelection; _templateInsertionCommand = templateInsertionCommand; _thumbNailer = thumbNailer; _bookFactory = bookFactory; _storageFactory = storageFactory; }
/// <summary> /// TemplateStarter intentionally makes its children (user's custom templates) have a special xmatter. /// But books creates with those custom templates should just use whatever xmatter normal books use, /// at least until we allow users to choose different ones, or allow template makers to specify which /// xmatter children should use. /// </summary> private static void ProcessXMatterMetaTags(BookStorage storage) { // Don't copy the parent's xmatter if they specify it storage.Dom.RemoveMetaElement("xmatter"); // But if the parent says what children should use, then use that. if (storage.Dom.HasMetaElement("xmatter-for-children")) { storage.Dom.UpdateMetaElement("xmatter", storage.Dom.GetMetaValue("xmatter-for-children", "")); } // Children, but not grand-children. So we remove this so the next generation doesn't see it. storage.Dom.RemoveMetaElement("xmatter-for-children"); }
public Book CreateFromSourceBook(string sourceBookFolder, string containingDestinationFolder) { string pathToFolderOfNewBook = null; Logger.WriteMinorEvent("Starting CreateFromSourceBook({0})", sourceBookFolder); try { var starter = _bookStarterFactory(); pathToFolderOfNewBook = starter.CreateBookOnDiskFromTemplate(sourceBookFolder, containingDestinationFolder); if (Configurator.IsConfigurable(pathToFolderOfNewBook)) { var c = _configuratorFactory(containingDestinationFolder); if (DialogResult.Cancel == c.ShowConfigurationDialog(pathToFolderOfNewBook)) { SIL.IO.RobustIO.DeleteDirectory(pathToFolderOfNewBook, true); return(null); // the template had a configuration page and they clicked "cancel" } c.ConfigureBook(BookStorage.FindBookHtmlInFolder(pathToFolderOfNewBook)); } // We're creating a new book, so for now, it can certainly be saved. However, this bookInfo might // survive past where it gets checked in, so hook up the proper SaveContext. var sc = _tcManager?.CurrentCollectionEvenIfDisconnected ?? new AlwaysEditSaveContext() as ISaveContext; var newBookInfo = new BookInfo(pathToFolderOfNewBook, true, sc); // _bookInfos.Find(b => b.FolderPath == pathToFolderOfNewBook); if (newBookInfo is ErrorBookInfo) { throw ((ErrorBookInfo)newBookInfo).Exception; } Book newBook = GetBookFromBookInfo(newBookInfo); //Hack: this is a bit of a hack, to handle problems where we make the book with the suggested initial name, but the title is still something else var name = Path.GetFileName(newBookInfo.FolderPath); // this way, we get "my book 1", "my book 2", etc. newBook.SetTitle(name); Logger.WriteMinorEvent("Finished CreateFromnewBook({0})", newBook.FolderPath); Logger.WriteEvent("CreateFromSourceBook({0})", newBook.FolderPath); return(newBook); } catch (Exception) { Logger.WriteEvent("Cleaning up after error CreateFromSourceBook({0})", pathToFolderOfNewBook); //clean up this ill-fated book folder up if (!string.IsNullOrEmpty(pathToFolderOfNewBook) && Directory.Exists(pathToFolderOfNewBook)) { SIL.IO.RobustIO.DeleteDirectory(pathToFolderOfNewBook, true); } throw; } }
public static void CompressBookForDevice(string outputPath, Book book, BookServer bookServer, Color backColor, IWebSocketProgress progress) { using (var temp = new TemporaryFolder()) { var modifiedBook = BloomReaderFileMaker.PrepareBookForBloomReader(book, bookServer, temp, backColor, progress); // We want at least 256 for Bloom Reader, because the screens have a high pixel density. And (at the moment) we are asking for // 64dp in Bloom Reader. MakeSizedThumbnail(modifiedBook, backColor, temp.FolderPath, 256); CompressDirectory(outputPath, modifiedBook.FolderPath, "", reduceImages: true, omitMetaJson: false, wrapWithFolder: false, pathToFileForSha: BookStorage.FindBookHtmlInFolder(book.FolderPath)); } }
public Book CreateFromSourceBook(Book sourceBook, string containingDestinationFolder) { string pathToFolderOfNewBook = null; Logger.WriteMinorEvent("Starting CreateFromSourceBook({0})", sourceBook.FolderPath); try { var starter = _bookStarterFactory(); pathToFolderOfNewBook = starter.CreateBookOnDiskFromTemplate(sourceBook.FolderPath, containingDestinationFolder); if (Configurator.IsConfigurable(pathToFolderOfNewBook)) { var c = _configuratorFactory(containingDestinationFolder); if (DialogResult.Cancel == c.ShowConfigurationDialog(pathToFolderOfNewBook)) { Directory.Delete(pathToFolderOfNewBook, true); return(null); // the template had a configuration page and they clicked "cancel" } c.ConfigureBook(BookStorage.FindBookHtmlInFolder(pathToFolderOfNewBook)); } var newBookInfo = new BookInfo(pathToFolderOfNewBook, true); // _bookInfos.Find(b => b.FolderPath == pathToFolderOfNewBook); if (newBookInfo is ErrorBookInfo) { throw ((ErrorBookInfo)newBookInfo).Exception; } Book newBook = GetBookFromBookInfo(newBookInfo); //Hack: this is a bit of a hack, to handle problems where we make the book with the suggested initial name, but the title is still something else var name = Path.GetFileName(newBookInfo.FolderPath); // this way, we get "my book 1", "my book 2", etc. newBook.SetTitle(name); Logger.WriteMinorEvent("Finished CreateFromnewBook({0})", newBook.FolderPath); Logger.WriteEvent("CreateFromSourceBook({0})", newBook.FolderPath); return(newBook); } catch (Exception) { Logger.WriteEvent("Cleaning up after error CreateFromSourceBook({0})", pathToFolderOfNewBook); //clean up this ill-fated book folder up if (!string.IsNullOrEmpty(pathToFolderOfNewBook) && Directory.Exists(pathToFolderOfNewBook)) { Directory.Delete(pathToFolderOfNewBook, true); } throw; } }
public static Book MakeDeviceXmatterTempBook(Book book, BookServer bookServer, string tempFolderPath) { BookStorage.CopyDirectory(book.FolderPath, tempFolderPath); var bookInfo = new BookInfo(tempFolderPath, true); bookInfo.XMatterNameOverride = "Device"; var modifiedBook = bookServer.GetBookFromBookInfo(bookInfo); modifiedBook.BringBookUpToDate(new NullProgress()); modifiedBook.AdjustCollectionStylesToBookFolder(); modifiedBook.Save(); modifiedBook.Storage.UpdateSupportFiles(); // Copy the possibly modified stylesheets after UpdateSupportFiles so that they don't // get replaced by the factory versions. BookStorage.CopyCollectionStyles(book.FolderPath, tempFolderPath); return(modifiedBook); }
private void UpdateEditabilityMetadata(BookStorage storage) { //Here's the logic: If we're in a shell-making library, then it's safe to say that a newly- //created book is going to be a shell. Any derivatives will then act as shells. But it won't //prevent us from editing it while in a shell-making collections, since we don't honor this //tag in shell-making collections. if (_isSourceCollection) { storage.Dom.UpdateMetaElement("lockedDownAsShell", "true"); } #if maybe //hard to pin down when a story primer, dictionary, etc. also becomes a new "source for new shells" //things like picture dictionaries could be used repeatedly //but things from Basic Book are normally not. var x = GetMetaValue(storage.Dom, "DerivativesAreSuitableForMakingShells", "false"); #else var x = "false"; #endif storage.Dom.UpdateMetaElement("SuitableForMakingShells", x); }
public void SetBookName_FolderWithNameAlreadyExists_AddsANumberToName() { using (var original = new TemporaryFolder(_folder, "original")) using (var x = new TemporaryFolder(_folder, "foo")) using (var y = new TemporaryFolder(_folder, "foo1")) using (var z = new TemporaryFolder(_folder, "foo2")) { File.WriteAllText(Path.Combine(original.Path, "original.htm"), "<html><head> href='file://blahblah\\editMode.css' type='text/css' /></head><body><div class='bloom-page'></div></body></html>"); var storage = new BookStorage(original.Path, _fileLocator, new BookRenamedEvent(), new CollectionSettings()); storage.Save(); Directory.Delete(z.Path); //so, we ask for "foo", but should get "foo2", because there is already a foo and foo1 var newBookName = Path.GetFileName(x.Path); storage.SetBookName(newBookName); var newPath = z.Combine("foo2.htm"); Assert.IsTrue(Directory.Exists(z.Path), "Expected folder:" + z.Path); Assert.IsTrue(File.Exists(newPath), "Expected file:" + newPath); } }
private void InjectXMatter(string initialPath, BookStorage storage, Layout sizeAndOrientation) { //now add in the xmatter from the currently selected xmatter pack if (!TestingSoSkipAddingXMatter) { var data = new Dictionary <string, string>(); Debug.Assert(!string.IsNullOrEmpty(_collectionSettings.Language1.Iso639Code)); Debug.Assert(!string.IsNullOrEmpty(_collectionSettings.Language2.Iso639Code)); // Review: this sort of duplicates the knowledge in BookData.WritingSystemAliases // Is it worth creating a BookData here? Since we're just starting the new book, it can't // yet have any language settings different from the collection. data.Add("V", _collectionSettings.Language1.Iso639Code); data.Add("N1", _collectionSettings.Language2.Iso639Code); data.Add("N2", _collectionSettings.Language3.Iso639Code); var helper = new XMatterHelper(storage.Dom, _collectionSettings.XMatterPackName, _fileLocator); helper.FolderPathForCopyingXMatterFiles = storage.FolderPath; helper.InjectXMatter(data, sizeAndOrientation, false, _collectionSettings.Language2.Iso639Code); //TranslationGroupManager.PrepareDataBookTranslationGroups(storage.Dom,languages); } }
private void InjectXMatter(string initialPath, BookStorage storage, Layout sizeAndOrientation) { //now add in the xmatter from the currently selected xmatter pack if (!TestingSoSkipAddingXMatter) { var data = new DataSet(); Debug.Assert(!string.IsNullOrEmpty(_collectionSettings.Language1Iso639Code)); Debug.Assert(!string.IsNullOrEmpty(_collectionSettings.Language2Iso639Code)); data.WritingSystemAliases.Add("V", _collectionSettings.Language1Iso639Code); data.WritingSystemAliases.Add("N1", _collectionSettings.Language2Iso639Code); data.WritingSystemAliases.Add("N2", _collectionSettings.Language3Iso639Code); //by default, this comes from the collection, but the book can select one, inlucing "null" to select the factory-supplied empty xmatter var xmatterName = storage.Dom.GetMetaValue("xmatter", _collectionSettings.XMatterPackName); var helper = new XMatterHelper(storage.Dom, xmatterName, _fileLocator); helper.FolderPathForCopyingXMatterFiles = storage.FolderPath; helper.InjectXMatter(data.WritingSystemAliases, sizeAndOrientation); } }
private void SetLineageAndId(BookStorage storage) { var parentId = GetMetaValue(storage.Dom.RawDom, "bloomBookId", ""); var lineage = GetMetaValue(storage.Dom.RawDom, "bloomBookLineage", ""); if (string.IsNullOrEmpty(lineage)) { lineage = GetMetaValue(storage.Dom.RawDom, "bookLineage", ""); //try the old name for this value } if (!string.IsNullOrEmpty(lineage)) { lineage += ","; } if (!string.IsNullOrEmpty(parentId)) { storage.Dom.UpdateMetaElement("bloomBookLineage", lineage + parentId); } storage.Dom.UpdateMetaElement("bloomBookId", Guid.NewGuid().ToString()); storage.Dom.RemoveMetaElement("bookLineage"); //old name }
private static Book MakeBook(CollectionSettings collectionSettings, string sourceBookFolderPath) { var xmatterLocations = new List<string>(); xmatterLocations.Add(ProjectContext.XMatterAppDataFolder); xmatterLocations.Add(FileLocator.GetDirectoryDistributedWithApplication( kpathToSHRPTemplates)); xmatterLocations.Add(FileLocator.GetDirectoryDistributedWithApplication("xMatter")); var locator = new BloomFileLocator(collectionSettings, new XMatterPackFinder(xmatterLocations), new string[] {}); var starter = new BookStarter(locator, path => new BookStorage(path, locator, new BookRenamedEvent(), collectionSettings), collectionSettings); var pathToFolderOfNewBook = starter.CreateBookOnDiskFromTemplate(sourceBookFolderPath, collectionSettings.FolderPath); var newBookInfo = new BookInfo(pathToFolderOfNewBook, false /*saying not editable works us around a langdisplay issue*/); BookStorage bookStorage = new BookStorage(pathToFolderOfNewBook, locator, new BookRenamedEvent(), collectionSettings); var book = new Book(newBookInfo, bookStorage, null, collectionSettings, null, null, null, null); return book; }
private BookStorage GetInitialStorageWithCustomHtml(string html) { RobustFile.WriteAllText(_bookPath, html); var projectFolder = new TemporaryFolder("BookStorageTests_ProjectCollection"); var collectionSettings = new CollectionSettings(Path.Combine(projectFolder.Path, "test.bloomCollection")); var storage = new BookStorage(_folder.Path, _fileLocator, new BookRenamedEvent(), collectionSettings); storage.Save(); return storage; }
/// <summary> /// Adds a directory, along with all files and subdirectories, to the ZipStream. /// </summary> /// <param name="directoryToCompress">The directory to add recursively</param> /// <param name="zipStream">The ZipStream to which the files and directories will be added</param> /// <param name="dirNameOffset">This number of characters will be removed from the full directory or file name /// before creating the zip entry name</param> /// <param name="dirNamePrefix">string to prefix to the zip entry name</param> /// <param name="forReaderTools">If True, then some pre-processing will be done to the contents of decodable /// and leveled readers before they are added to the ZipStream</param> /// <param name="excludeAudio">If true, the contents of the audio directory will not be included</param> /// <param name="reduceImages">If true, images will be compressed before being added to the zip file</param> /// <param name="omitMetaJson">If true, meta.json is excluded (typically for HTML readers).</param> /// <para> name="reduceImages">If true, image files are reduced in size to no larger than 300x300 before saving</para> /// <remarks>Protected for testing purposes</remarks> private static void CompressDirectory(string directoryToCompress, ZipOutputStream zipStream, int dirNameOffset, string dirNamePrefix, bool forReaderTools, bool excludeAudio, bool reduceImages, bool omitMetaJson = false, string pathToFileForSha = null) { if (excludeAudio && Path.GetFileName(directoryToCompress).ToLowerInvariant() == "audio") { return; } var files = Directory.GetFiles(directoryToCompress); var bookFile = BookStorage.FindBookHtmlInFolder(directoryToCompress); foreach (var filePath in files) { if (ExcludedFileExtensionsLowerCase.Contains(Path.GetExtension(filePath.ToLowerInvariant()))) { continue; // BL-2246: skip putting this one into the BloomPack } var fileName = Path.GetFileName(filePath).ToLowerInvariant(); if (fileName.StartsWith(BookStorage.PrefixForCorruptHtmFiles)) { continue; } // Various stuff we keep in the book folder that is useful for editing or bloom library // or displaying collections but not needed by the reader. The most important is probably // eliminating the pdf, which can be very large. Note that we do NOT eliminate the // basic thumbnail.png, as we want eventually to extract that to use in the Reader UI. if (fileName == "thumbnail-70.png" || fileName == "thumbnail-256.png") { continue; } if (fileName == "meta.json" && omitMetaJson) { continue; } FileInfo fi = new FileInfo(filePath); var entryName = dirNamePrefix + filePath.Substring(dirNameOffset); // Makes the name in zip based on the folder entryName = ZipEntry.CleanName(entryName); // Removes drive from name and fixes slash direction ZipEntry newEntry = new ZipEntry(entryName) { DateTime = fi.LastWriteTime, IsUnicodeText = true }; // encode filename and comment in UTF8 byte[] modifiedContent = {}; // if this is a ReaderTools book, call GetBookReplacedWithTemplate() to get the contents if (forReaderTools && (bookFile == filePath)) { modifiedContent = Encoding.UTF8.GetBytes(GetBookReplacedWithTemplate(filePath)); newEntry.Size = modifiedContent.Length; } else if (forReaderTools && (Path.GetFileName(filePath) == "meta.json")) { modifiedContent = Encoding.UTF8.GetBytes(GetMetaJsonModfiedForTemplate(filePath)); newEntry.Size = modifiedContent.Length; } else if (reduceImages && ImageFileExtensions.Contains(Path.GetExtension(filePath.ToLowerInvariant()))) { modifiedContent = GetBytesOfReducedImage(filePath); newEntry.Size = modifiedContent.Length; } else if (reduceImages && (bookFile == filePath)) { var originalContent = File.ReadAllText(bookFile, Encoding.UTF8); var dom = XmlHtmlConverter.GetXmlDomFromHtml(originalContent); StripImagesWithMissingSrc(dom, bookFile); StripContentEditable(dom); InsertReaderStylesheet(dom); ConvertImagesToBackground(dom); var newContent = XmlHtmlConverter.ConvertDomToHtml5(dom); modifiedContent = Encoding.UTF8.GetBytes(newContent); newEntry.Size = modifiedContent.Length; if (pathToFileForSha != null) { // Make an extra entry containing the sha var sha = Book.MakeVersionCode(File.ReadAllText(pathToFileForSha, Encoding.UTF8), pathToFileForSha); var name = "version.txt"; // must match what BloomReader is looking for in NewBookListenerService.IsBookUpToDate() MakeExtraEntry(zipStream, name, sha); } MakeExtraEntry(zipStream, "readerStyles.css", File.ReadAllText(FileLocator.GetFileDistributedWithApplication(Path.Combine(BloomFileLocator.BrowserRoot, "publish", "android", "readerStyles.css")))); } else { newEntry.Size = fi.Length; } zipStream.PutNextEntry(newEntry); if (modifiedContent.Length > 0) { using (var memStream = new MemoryStream(modifiedContent)) { // There is some minimum buffer size (44 was too small); I don't know exactly what it is, // but 1024 makes it happy. StreamUtils.Copy(memStream, zipStream, new byte[Math.Max(modifiedContent.Length, 1024)]); } } else { // Zip the file in buffered chunks byte[] buffer = new byte[4096]; using (var streamReader = RobustFile.OpenRead(filePath)) { StreamUtils.Copy(streamReader, zipStream, buffer); } } zipStream.CloseEntry(); } var folders = Directory.GetDirectories(directoryToCompress); foreach (var folder in folders) { var dirName = Path.GetFileName(folder); if ((dirName == null) || (dirName.ToLowerInvariant() == "sample texts")) { continue; // Don't want to bundle these up } CompressDirectory(folder, zipStream, dirNameOffset, dirNamePrefix, forReaderTools, excludeAudio, reduceImages); } }
private static void SetBookTitle(BookStorage storage, BookData bookData, bool usingTemplate) { //This is what we were trying to do: there was a defaultNameForDerivedBooks meta tag in the html //which had no language code. It worked fine for English, e.g., naming new English books //"My Book" or "My Dicionary" or whatever. //But in other cases, it actually hurt becuase that English name would be hidden down in the new //book, where the author wouldn't see it. But some consumer, using English, would see it, and //"My Book" is a pretty dumb name for tha carefully prepared book to be listed under. // //Now, if we are making this book from a shell book, we can keep whatever (title,language) pairs it has. //Those will be just fine, for example, if we have English as one of our national langauges and so get // "vaccinations" for free wihtout having to type that in again. // //But if we are making this from a *template*, then we *don't* want to keep the various ways to say the //name of the template. Seeing "Basic Book" as the name of a resulting shell is not helpful. //We just don't have a use for this at all anymore: nice idea, doesn't really work: storage.Dom.RemoveMetaElement("defaultNameForDerivedBooks"); // Clear these out let other code set again when there is a real title. storage.MetaData.Title = ""; storage.Dom.Title = ""; //If we're making a book from a template, remove all the titles in all languages if(usingTemplate) { bookData.RemoveAllForms("bookTitle"); } }
private BookStorage Get_NotYetConfigured_CalendardBookStorage() { var source = FileLocator.GetDirectoryDistributedWithApplication("factoryCollections", "Templates", "Wall Calendar"); var path = GetPathToHtml(_starter.CreateBookOnDiskFromTemplate(source, _libraryFolder.Path)); var projectFolder = new TemporaryFolder("ConfiguratorTests_ProjectCollection"); //review var collectionSettings = new CollectionSettings(Path.Combine(projectFolder.Path, "test.bloomCollection")); var bs = new BookStorage(Path.GetDirectoryName(path), _fileLocator, new BookRenamedEvent(), collectionSettings); return bs; }
private void SetLineageAndId(BookStorage storage, string sourceFolderPath) { string parentId = null; string lineage = null; if (File.Exists(Path.Combine(sourceFolderPath, BookInfo.MetaDataFileName))) { var sourceMetaData = new BookInfo(sourceFolderPath, false); parentId = sourceMetaData.Id; lineage = sourceMetaData.BookLineage; } else { // No parent meta.json, try for legacy embedded metadata in html parentId = GetMetaValue(storage.Dom.RawDom, "bloomBookId", ""); lineage = GetMetaValue(storage.Dom.RawDom, "bloomBookLineage", ""); if (string.IsNullOrEmpty(lineage)) { lineage = GetMetaValue(storage.Dom.RawDom, "bookLineage", ""); //try the old name for this value } } if (!string.IsNullOrEmpty(lineage)) lineage += ","; if (!string.IsNullOrEmpty(parentId)) { storage.MetaData.BookLineage = lineage + parentId; } storage.MetaData.Id = Guid.NewGuid().ToString(); storage.Dom.RemoveMetaElement("bloomBookLineage"); //old metadata storage.Dom.RemoveMetaElement("bookLineage"); // even older name }
private void UpdateEditabilityMetadata(BookStorage storage) { //Here's the logic: If we're in a shell-making library, then it's safe to say that a newly- //created book is going to be a shell. Any derivatives will then act as shells. But it won't //prevent us from editing it while in a shell-making collections, since we don't honor this //tag in shell-making collections. //The problem is, if you make a book in some vernacular library, then share it so that others //can use it as a shell, then (as of version 2) Bloom doesn't have a way of realizing that it's //being used as a shell. So everything is editable (not a big deal) but you're also locked out // of editing the acknowledgements for translated version. //It seems to me at the moment (May 2014) that the time to mark something as locked down should //be when the they create a book based on a source-with-content book. So the current approach //below, of pre-locking it, would go away. if(_isSourceCollection) { storage.Dom.UpdateMetaElement("lockedDownAsShell", "true"); } #if maybe //hard to pin down when a story primer, dictionary, etc. also becomes a new "source for new shells" //things like picture dictionaries could be used repeatedly //but things from Basic Book are normally not. var x = GetMetaValue(storage.Dom, "DerivativesAreSuitableForMakingShells", "false"); #else var x = false; #endif storage.MetaData.IsSuitableForMakingShells = x; }
Bloom.Book.Book BookFactory(BookStorage storage, bool editable) { return new Bloom.Book.Book(new BookInfo(storage.FolderPath, true), storage, null, new CollectionSettings(new NewCollectionSettings() { PathToSettingsFile = CollectionSettings.GetPathForNewSettings(_folder.Path, "test"), Language1Iso639Code = "xyz" }), null, new PageSelection(), new PageListChangedEvent(), new BookRefreshEvent()); }
/// <summary> /// Adds a directory, along with all files and subdirectories, to the ZipStream. /// </summary> /// <param name="directoryToCompress">The directory to add recursively</param> /// <param name="zipStream">The ZipStream to which the files and directories will be added</param> /// <param name="dirNameOffset">This number of characters will be removed from the full directory or file name /// before creating the zip entry name</param> /// <param name="dirNamePrefix">string to prefix to the zip entry name</param> /// <param name="depthFromCollection">int with the number of folders away it is from the collection folder. The collection folder itself is 0, /// a book is 1, a subfolder of the book is 2, etc.</param> /// <param name="forReaderTools">If True, then some pre-processing will be done to the contents of decodable /// and leveled readers before they are added to the ZipStream</param> /// <param name="excludeAudio">If true, the contents of the audio directory will not be included</param> /// <param name="reduceImages">If true, image files are reduced in size to no larger than the max size before saving</para> /// <param name="omitMetaJson">If true, meta.json is excluded (typically for HTML readers).</param> private static void CompressDirectory(string directoryToCompress, ZipOutputStream zipStream, int dirNameOffset, string dirNamePrefix, int depthFromCollection, bool forReaderTools, bool excludeAudio, bool reduceImages, bool omitMetaJson = false, string pathToFileForSha = null) { if (excludeAudio && Path.GetFileName(directoryToCompress).ToLowerInvariant() == "audio") { return; } var files = Directory.GetFiles(directoryToCompress); // Don't get distracted by HTML files in any folder other than the book folder. // These HTML files in other locations aren't generated by Bloom. They may not have the format Bloom expects, // causing needless parsing errors to be thrown if we attempt to read them using Bloom code. bool shouldScanHtml = depthFromCollection == 1; // 1 means 1 level below the collection level, i.e. this is the book level var bookFile = shouldScanHtml ? BookStorage.FindBookHtmlInFolder(directoryToCompress) : null; XmlDocument dom = null; List <string> imagesToGiveTransparentBackgrounds = null; List <string> imagesToPreserveResolution = null; // Tests can also result in bookFile being null. if (!String.IsNullOrEmpty(bookFile)) { var originalContent = File.ReadAllText(bookFile, Encoding.UTF8); dom = XmlHtmlConverter.GetXmlDomFromHtml(originalContent); var fullScreenAttr = dom.GetElementsByTagName("body").Cast <XmlElement>().First().Attributes["data-bffullscreenpicture"]?.Value; if (fullScreenAttr != null && fullScreenAttr.IndexOf("bloomReader", StringComparison.InvariantCulture) >= 0) { // This feature (currently used for motion books in landscape mode) triggers an all-black background, // due to a rule in bookFeatures.less. // Making white pixels transparent on an all-black background makes line-art disappear, // which is bad (BL-6564), so just make an empty list in this case. imagesToGiveTransparentBackgrounds = new List <string>(); } else { imagesToGiveTransparentBackgrounds = FindCoverImages(dom); } imagesToPreserveResolution = FindImagesToPreserveResolution(dom); FindBackgroundAudioFiles(dom); } else { imagesToGiveTransparentBackgrounds = new List <string>(); imagesToPreserveResolution = new List <string>(); } // Some of the knowledge about ExcludedFileExtensions might one day move into this method. // But we'd have to check carefully the other places it is used. var localOnlyFiles = BookStorage.LocalOnlyFiles(directoryToCompress); foreach (var filePath in files) { if (ExcludedFileExtensionsLowerCase.Contains(Path.GetExtension(filePath.ToLowerInvariant()))) { continue; // BL-2246: skip putting this one into the BloomPack } if (IsUnneededWaveFile(filePath, depthFromCollection)) { continue; } if (localOnlyFiles.Contains(filePath)) { continue; } var fileName = Path.GetFileName(filePath).ToLowerInvariant(); if (fileName.StartsWith(BookStorage.PrefixForCorruptHtmFiles)) { continue; } // Various stuff we keep in the book folder that is useful for editing or bloom library // or displaying collections but not needed by the reader. The most important is probably // eliminating the pdf, which can be very large. Note that we do NOT eliminate the // basic thumbnail.png, as we want eventually to extract that to use in the Reader UI. if (fileName == "thumbnail-70.png" || fileName == "thumbnail-256.png") { continue; } if (fileName == "meta.json" && omitMetaJson) { continue; } FileInfo fi = new FileInfo(filePath); var entryName = dirNamePrefix + filePath.Substring(dirNameOffset); // Makes the name in zip based on the folder entryName = ZipEntry.CleanName(entryName); // Removes drive from name and fixes slash direction ZipEntry newEntry = new ZipEntry(entryName) { DateTime = fi.LastWriteTime, IsUnicodeText = true }; // encode filename and comment in UTF8 byte[] modifiedContent = {}; // if this is a ReaderTools book, call GetBookReplacedWithTemplate() to get the contents if (forReaderTools && (bookFile == filePath)) { modifiedContent = Encoding.UTF8.GetBytes(GetBookReplacedWithTemplate(filePath)); newEntry.Size = modifiedContent.Length; } else if (forReaderTools && (Path.GetFileName(filePath) == "meta.json")) { modifiedContent = Encoding.UTF8.GetBytes(GetMetaJsonModfiedForTemplate(filePath)); newEntry.Size = modifiedContent.Length; } else if (reduceImages && ImageFileExtensions.Contains(Path.GetExtension(filePath.ToLowerInvariant()))) { fileName = Path.GetFileName(filePath); // restore original capitalization if (imagesToPreserveResolution.Contains(fileName)) { modifiedContent = RobustFile.ReadAllBytes(filePath); } else { // Cover images should be transparent if possible. Others don't need to be. var makeBackgroundTransparent = imagesToGiveTransparentBackgrounds.Contains(fileName); modifiedContent = GetImageBytesForElectronicPub(filePath, makeBackgroundTransparent); } newEntry.Size = modifiedContent.Length; } else if (Path.GetExtension(filePath).ToLowerInvariant() == ".bloomcollection") { modifiedContent = Encoding.UTF8.GetBytes(GetBloomCollectionModifiedForTemplate(filePath)); newEntry.Size = modifiedContent.Length; } // CompressBookForDevice is always called with reduceImages set. else if (reduceImages && bookFile == filePath) { SignLanguageApi.ProcessVideos(HtmlDom.SelectChildVideoElements(dom.DocumentElement).Cast <XmlElement>(), directoryToCompress); var newContent = XmlHtmlConverter.ConvertDomToHtml5(dom); modifiedContent = Encoding.UTF8.GetBytes(newContent); newEntry.Size = modifiedContent.Length; if (pathToFileForSha != null) { // Make an extra entry containing the sha var sha = Book.ComputeHashForAllBookRelatedFiles(pathToFileForSha); var name = "version.txt"; // must match what BloomReader is looking for in NewBookListenerService.IsBookUpToDate() MakeExtraEntry(zipStream, name, sha); LastVersionCode = sha; } } else { newEntry.Size = fi.Length; } zipStream.PutNextEntry(newEntry); if (modifiedContent.Length > 0) { using (var memStream = new MemoryStream(modifiedContent)) { // There is some minimum buffer size (44 was too small); I don't know exactly what it is, // but 1024 makes it happy. StreamUtils.Copy(memStream, zipStream, new byte[Math.Max(modifiedContent.Length, 1024)]); } } else { // Zip the file in buffered chunks byte[] buffer = new byte[4096]; using (var streamReader = RobustFile.OpenRead(filePath)) { StreamUtils.Copy(streamReader, zipStream, buffer); } } zipStream.CloseEntry(); } var folders = Directory.GetDirectories(directoryToCompress); foreach (var folder in folders) { var dirName = Path.GetFileName(folder); if ((dirName == null) || (dirName.ToLowerInvariant() == "sample texts")) { continue; // Don't want to bundle these up } CompressDirectory(folder, zipStream, dirNameOffset, dirNamePrefix, depthFromCollection + 1, forReaderTools, excludeAudio, reduceImages); } }
private void UpdateEditabilityMetadata(BookStorage storage) { //Here's the logic: If we're in a shell-making library, then it's safe to say that a newly- //created book is going to be a shell. Any derivatives will then act as shells. But it won't //prevent us from editing it while in a shell-making collections, since we don't honor this //tag in shell-making collections. if(_isSourceCollection) { storage.Dom.UpdateMetaElement("lockedDownAsShell", "true"); } #if maybe //hard to pin down when a story primer, dictionary, etc. also becomes a new "source for new shells" //things like picture dictionaries could be used repeatedly //but things from Basic Book are normally not. var x = GetMetaValue(storage.Dom, "DerivativesAreSuitableForMakingShells", "false"); #else var x = "false"; #endif storage.Dom.UpdateMetaElement("SuitableForMakingShells", x); }
private void InjectXMatter(string initialPath, BookStorage storage, Layout sizeAndOrientation) { //now add in the xmatter from the currently selected xmatter pack if (!TestingSoSkipAddingXMatter) { var data = new DataSet(); Debug.Assert(!string.IsNullOrEmpty(_collectionSettings.Language1Iso639Code)); Debug.Assert(!string.IsNullOrEmpty(_collectionSettings.Language2Iso639Code)); data.WritingSystemCodes.Add("V", _collectionSettings.Language1Iso639Code); data.WritingSystemCodes.Add("N1", _collectionSettings.Language2Iso639Code); data.WritingSystemCodes.Add("N2", _collectionSettings.Language3Iso639Code); //by default, this comes from the collection, but the book can select one, inlucing "null" to select the factory-supplied empty xmatter var xmatterName = storage.Dom.GetMetaValue("xmatter", _collectionSettings.XMatterPackName); var helper = new XMatterHelper(storage.Dom, xmatterName, _fileLocator); helper.FolderPathForCopyingXMatterFiles = storage.FolderPath; helper.InjectXMatter(data.WritingSystemCodes, sizeAndOrientation); } }
private BookStorage GetInitialStorageUsingUNCPath() { var testFolder = new TemporaryFolder(); var bookPath = testFolder.Combine("theBook.htm"); File.WriteAllText(bookPath, "<html><head> href='file://blahblah\\editMode.css' type='text/css' /></head><body><div class='bloom-page'></div></body></html>"); var collectionSettings = new CollectionSettings(Path.Combine(testFolder.Path, "test.bloomCollection")); var folderPath = ConvertToNetworkPath(testFolder.Path); Debug.WriteLine(Path.GetPathRoot(folderPath)); var storage = new BookStorage(folderPath, _fileLocator, new BookRenamedEvent(), collectionSettings); return storage; }
private BookStorage GetInitialStorageWithCustomHead(string head) { File.WriteAllText(_bookPath, "<html><head>" + head + " </head></body></html>"); var storage = new BookStorage(_folder.Path, _fileLocator, new BookRenamedEvent(), new CollectionSettings()); storage.Save(); return storage; }
private static void SetBookTitle(BookStorage storage, BookData bookData) { //NB: no multi-lingual name suggestion ability yet //otherwise, the case where there is no defaultNameForDerivedBooks, we just want to use the names //that the shell used, e.g. "Vaccinations". //We don't have to do anything special to get that. string kdefaultName = null; var nameSuggestion = storage.Dom.GetMetaValue("defaultNameForDerivedBooks", kdefaultName); // var nameSuggestion = storage.Dom.SafeSelectNodes("//head/meta[@name='defaultNameForDerivedBooks']"); if(nameSuggestion!=null) bookData.Set("bookTitle",nameSuggestion,"en"); storage.Dom.RemoveMetaElement("defaultNameForDerivedBooks"); // //var name = "New Book"; //shouldn't rarel show up, because it will be overriden by the meta tag // if (nameSuggestion.Count > 0) // { // var metaTag = (XmlElement) nameSuggestion[0]; // var name = metaTag.GetAttribute("content"); // bookData.SetDataDivBookVariable("bookTitle", name, "en"); // metaTag.ParentNode.RemoveChild(metaTag); // } // else // { // // } }
private BookStorage GetInitialStorageWithDifferentFileName(string bookName) { var bookPath = _folder.Combine(bookName + ".htm"); File.WriteAllText(bookPath, "<html><head> href='file://blahblah\\editMode.css' type='text/css' /></head><body><div class='bloom-page'></div></body></html>"); var projectFolder = new TemporaryFolder("BookStorageTests_ProjectCollection"); var collectionSettings = new CollectionSettings(Path.Combine(projectFolder.Path, "test.bloomCollection")); var storage = new BookStorage(_folder.Path, _fileLocator, new BookRenamedEvent(), collectionSettings); storage.Save(); return storage; }
private void SetLineageAndId(BookStorage storage) { var parentId = GetMetaValue(storage.Dom.RawDom, "bloomBookId", ""); var lineage = GetMetaValue(storage.Dom.RawDom, "bloomBookLineage", ""); if (string.IsNullOrEmpty(lineage)) { lineage = GetMetaValue(storage.Dom.RawDom, "bookLineage", ""); //try the old name for this value } if (!string.IsNullOrEmpty(lineage)) lineage += ","; if (!string.IsNullOrEmpty(parentId)) { storage.Dom.UpdateMetaElement("bloomBookLineage", lineage + parentId); } storage.Dom.UpdateMetaElement("bloomBookId",Guid.NewGuid().ToString()); storage.Dom.RemoveMetaElement("bookLineage");//old name }
private string SetupNewDocumentContents(string sourceFolderPath, string initialPath) { var storage = _bookStorageFactory(initialPath); bool usingTemplate = storage.BookInfo.IsSuitableForMakingShells; bool makingTemplate = storage.BookInfo.IsSuitableForMakingTemplates; // If we're not making it from a template or making a template, we're deriving a translation from an existing book var makingTranslation = !usingTemplate && !makingTemplate; var bookData = new BookData(storage.Dom, _collectionSettings, null); UpdateEditabilityMetadata(storage); //Path.GetFileName(initialPath).ToLower().Contains("template")); // BL-7614 We don't want a derivative of a book downloaded from a "bookshelf" to have the same bookshelf storage.BookInfo.ClearBookshelf(); // NB: For a new book based on a page template, I think this should remove *everything*, // because the rest is in the xmatter. // For shells, we'll still have pages. // BL-6108: But if this is a template and we remove all the pages and xmatter, // there won't be anything left to tell us what the template's preferred layout was, // so we'll save that first. Layout templateLayout = null; if (usingTemplate) { templateLayout = Layout.FromDom(storage.Dom, Layout.A5Portrait); } // Remove from the new book any pages labeled as "extra". // Typically pages in a template are marked "extra" to indicate that they are options to insert with "Add Page" // but not pages (which a few templates have) that should be automatically inserted into every book // made from the template. // Originally we removed 'extra' pages in all cases, but we haven't actually used the capability // of deleting 'extra' pages from translations of shell books, and on the other hand, we did briefly release // a version of Bloom that incorrectly left shell book pages so marked. Something like 73 books in our library // may have this problem (BL-6392). Since we don't actually need the capability for making translations // of shell books, we decided to simply disable it: all pages in a shell book, even those marked // 'extra', will be kept in the translation. if (!makingTranslation) { for (var initialPageDivs = storage.Dom.SafeSelectNodes("/html/body/div[contains(@data-page,'extra')]"); initialPageDivs.Count > 0; initialPageDivs = storage.Dom.SafeSelectNodes("/html/body/div[contains(@data-page,'extra')]")) { initialPageDivs[0].ParentNode.RemoveChild(initialPageDivs[0]); } } else { // When making a translation of an original move the 'publisher' (if there is one) to 'originalPublisher'. storage.BookInfo.MovePublisherToOriginalPublisher(); } XMatterHelper.RemoveExistingXMatter(storage.Dom); // BL-4586 Some old books ended up with background-image urls containing XML img tags // in the HTML-encoded string. This happened because the coverImage data-book element // contained an img tag instead of a bare filename. // Normally such a thing would get fixed on loading the book, but if the "old book" in question // is a shell downloaded from BloomLibrary.org, Bloom is not allowed to modify the book, // so if such a thing exists in this copied book here we will strip it out and replace it with the // filename in the img src attribute. Book.RemoveImgTagInDataDiv(storage.Dom); bookData.RemoveAllForms("ISBN"); //ISBN number of the original doesn't apply to derivatives var sizeAndOrientation = Layout.FromDomAndChoices(storage.Dom, templateLayout ?? Layout.A5Portrait, _fileLocator); //Note that we do this *before* injecting frontmatter, which is more likely to have a good reason for having English //Useful for things like Primers. Note that Lorem Ipsum and prefixing all text with "_" also work. // if ("true"==GetMetaValue(storage.Dom.RawDom, "removeTranslationsWhenMakingNewBook", "false")) // { // ClearAwayAllTranslations(storage.Dom.RawDom); // } ProcessXMatterMetaTags(storage); // If we are making a shell (from a template, as opposed to making a translation of a shell), // it should not have a pre-determined license. A default will be filled in later. // (But, if we're MAKING a template, we want to keep the CC0 from Template Starter.) if (usingTemplate && !makingTemplate) { BookCopyrightAndLicense.RemoveLicense(storage); } InjectXMatter(initialPath, storage, sizeAndOrientation); SetLineageAndId(storage, sourceFolderPath); if (makingTranslation) { storage.EnsureOriginalTitle(); // Before SetBookTitle, so we definitely won't use this book's new empty title } SetBookTitle(storage, bookData, usingTemplate); TransformCreditPageData(storage.Dom, bookData, _collectionSettings, storage, makingTranslation); //Few sources will have this set at all. A template picture dictionary is one place where we might expect it to call for, say, bilingual int multilingualLevel = int.Parse(GetMetaValue(storage.Dom.RawDom, "defaultMultilingualLevel", "1")); TranslationGroupManager.SetInitialMultilingualSetting(bookData, multilingualLevel); var sourceDom = XmlHtmlConverter.GetXmlDomFromHtmlFile(sourceFolderPath.CombineForPath(Path.GetFileName(GetPathToHtmlFile(sourceFolderPath))), false); //If this is a shell book, make elements to hold the vernacular foreach (XmlElement div in storage.Dom.RawDom.SafeSelectNodes("//div[contains(@class,'bloom-page')]")) { XmlElement sourceDiv = sourceDom.SelectSingleNode("//div[@id='" + div.GetAttribute("id") + "']") as XmlElement; SetupIdAndLineage(sourceDiv, div); SetupPage(div, bookData); } ClearAwayDraftText(storage.Dom.RawDom); storage.UpdateSupportFiles(); // Copy branding files etc. storage.PerformNecessaryMaintenanceOnBook(); // Fix image files as needed etc. try { storage.Save(); } catch (UnauthorizedAccessException e) { BookStorage.ShowAccessDeniedErrorReport(e); // Well, not sure what else to return here, so I guess just let it continue and return storage.FolderPath } //REVIEW this actually undoes the setting of the initial files name: // storage.UpdateBookFileAndFolderName(_librarySettings); return(storage.FolderPath); }
private BookStorage Get_NotYetConfigured_CalendardBookStorage() { var source = FileLocator.GetDirectoryDistributedWithApplication("factoryCollections", "Templates", "Wall Calendar"); var path = GetPathToHtml(_starter.CreateBookOnDiskFromTemplate(source, _libraryFolder.Path)); var bs = new BookStorage(Path.GetDirectoryName(path), _fileLocator, new BookRenamedEvent(), new CollectionSettings()); return bs; }
private void CleanupBrandingImages(XmlElement newPageDiv, string branding, string bookFolderPath, Layout layout) { if (branding == null) { return; // in testing. } if (BookStorage.IsStaticContent(bookFolderPath)) { return; } var prefix = BrandingApi.kApiBrandingImage + "?id="; foreach (XmlElement imageElt in newPageDiv.SafeSelectNodes("//img").Cast <XmlElement>().ToArray()) { var src = imageElt.Attributes["src"]?.Value; if (src == null || !src.StartsWith(prefix)) { continue; } var fileName = src.Substring(prefix.Length); var pathToRealImage = BrandingApi.FindBrandingImageFileIfPossible(branding, fileName, layout); if (string.IsNullOrEmpty(pathToRealImage)) { if (!TemporaryDom) { // If the book folder contains this file already, it's obsolete, from some previous branding choice. // Get rid of it to save space. We might also have an obsolete file with a png extension; get rid of // that too. Of course, if this is just for a temporary DOM, we don't want to mess with the real folder. var destFileName = Path.Combine(bookFolderPath, fileName); RobustFile.Delete(destFileName); RobustFile.Delete(Path.ChangeExtension(destFileName, ".png")); } // At this point the <img> element has src api/branding/something not in the current branding. // So it's not actually going to produce an image in Bloom itself. We could change it, as // in the following block, so that it points to a non-existent file in the book folder, // and do various tricks to try to prevent warnings about missing files. // But such an element does no good. Even if someone manually inserted the missing file // (presumably an attempt to circumvent the current branding?) the next bring-book-up-to-date // will remove it as not part of the current branding. // Meanwhile, we need tricks to prevent missing-image errors in Bloom itself, and more tricks // to prevent them in epubs, and .bloomd, and however else we might publish. // All this is made unnecessary by just deleting the <img> element if it's not used in the // current branding. // If we later switch to a different branding which does need it, // the next cycle of updating xmatter will start again with the original xmatter files // which contain all the api/branding urls, and this time we will find the file and not // delete the <img>. imageElt.ParentNode.RemoveChild(imageElt); } else { if (TemporaryDom) { // for a temporary DOM we don't care if the path is absolute and to something outside the book folder. // It just has to work. And we don't want to modify the book folder. It does need to go through the // image server, otherwise we'll get cross-domain problems. And we need the marker that prevents // creating screen-only quality images. imageElt.SetAttribute("src", (EnhancedImageServer.OriginalImageMarker + "/" + pathToRealImage).ToLocalhost()); } else { // We want to actually copy it into the book folder, where things will work right // even if someone just opens the HTML file in a context where our image server isn't // running at all. // We want to use the original name in the book folder...for one thing, works with // deletion code above...but the correct extension for the file we actually found. var destFileName = Path.ChangeExtension(Path.GetFileName(fileName), Path.GetExtension(pathToRealImage)); RobustFile.Copy(pathToRealImage, Path.Combine(bookFolderPath, destFileName), true); // Point the <img> element at the local file...which should be available since we // just copied it there. imageElt.SetAttribute("src", destFileName); } } } }
private void ChangeNameAndCheck(TemporaryFolder newFolder, BookStorage storage) { var newBookName = Path.GetFileName(newFolder.Path); storage.SetBookName(newBookName); var newPath = newFolder.Combine(newBookName + ".htm"); Assert.IsTrue(Directory.Exists(newFolder.Path), "Expected folder:" + newFolder.Path); Assert.IsTrue(File.Exists(newPath), "Expected file:" + newPath); }
// // [Test] // public void Delete_IsDeleted() // { // BookStorage storage = GetInitialStorage(); // Assert.IsTrue(Directory.Exists(_folder.Path)); // Assert.IsTrue(storage.DeleteBook()); // Thread.Sleep(2000); // Assert.IsFalse(Directory.Exists(_folder.Path)); // } private BookStorage GetInitialStorage() { File.WriteAllText(_bookPath, "<html><head> href='file://blahblah\\editMode.css' type='text/css' /></head><body><div class='bloom-page'></div></body></html>"); var storage = new BookStorage(_folder.Path, _fileLocator, new BookRenamedEvent(), new CollectionSettings()); storage.Save(); return storage; }