Inheritance: IBookStorage
Exemple #1
0
        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);
 }
Exemple #3
0
        /// <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);
        }
Exemple #4
0
        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
//	        {
//
//	        }
        }
Exemple #5
0
        public BookServer(Book.Factory bookFactory, BookStorage.Factory storageFactory,
						  BookStarter.Factory bookStarterFactory)
        {
            _bookFactory = bookFactory;
            _storageFactory = storageFactory;
            _bookStarterFactory = bookStarterFactory;
        }
Exemple #6
0
 //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;
        }
Exemple #11
0
 // 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");
        }
Exemple #14
0
        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;
            }
        }
Exemple #15
0
        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));
            }
        }
Exemple #16
0
        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;
            }
        }
Exemple #17
0
        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);
        }
Exemple #18
0
        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);
            }
        }
Exemple #20
0
        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);
            }
        }
Exemple #21
0
        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);
            }
        }
Exemple #22
0
        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
        }
Exemple #23
0
        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;
 }
Exemple #25
0
        /// <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;
        }
Exemple #28
0
        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);
            }
        }
Exemple #32
0
        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);
        }
Exemple #33
0
        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;
 }
Exemple #36
0
        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;
 }
Exemple #38
0
        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
        }
Exemple #39
0
        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;
 }