public SourceCollectionsList(Book.Book.Factory bookFactory, BookStorage.Factory storageFactory, BookCollection.Factory bookCollectionFactory, string editableCollectionDirectory)
 {
     _bookFactory = bookFactory;
     _storageFactory = storageFactory;
     _bookCollectionFactory = bookCollectionFactory;
     _editableCollectionDirectory = editableCollectionDirectory;
 }
 public void InsertBook_NotPresent_InsertsInEmptyList()
 {
     var infoNew = new BookInfo("book11", true);
     var state = new List<BookInfo>();
     var collection = new BookCollection(state);
     collection.InsertBookInfo(infoNew);
     Assert.That(state[0], Is.EqualTo(infoNew), "book info should be inserted between book10 and book20");
 }
Example #3
0
        public BloomServer(CollectionSettings collectionSettings, BookCollection booksInProjectLibrary,
						   SourceCollectionsList sourceCollectionsesList, HtmlThumbNailer thumbNailer)
        {
            _collectionSettings = collectionSettings;
            _booksInProjectLibrary = booksInProjectLibrary;
            _sourceCollectionsesList = sourceCollectionsesList;
            _thumbNailer = thumbNailer;
        }
Example #4
0
        public BloomServer(CollectionSettings collectionSettings, BookCollection booksInProjectLibrary,
						   SourceCollectionsList sourceCollectionsesList, BookThumbNailer thumbNailer)
            : base(new RuntimeImageProcessor(new BookRenamedEvent()), thumbNailer)
        {
            _collectionSettings = collectionSettings;
            _booksInProjectLibrary = booksInProjectLibrary;
            _sourceCollectionsesList = sourceCollectionsesList;
        }
        public void Setup()
        {
            Palaso.Reporting.ErrorReport.IsOkToInteractWithUser = false;
            _folder  =new TemporaryFolder("BookCollectionTests");
            //			_fileLocator = new BloomFileLocator(new CollectionSettings(), new XMatterPackFinder(new string[]{}), new string[] { FileLocator.GetDirectoryDistributedWithApplication("root"), FileLocator.GetDirectoryDistributedWithApplication("factoryCollections") });
            _fileLocator = new FileLocator(new string[] { FileLocator.GetDirectoryDistributedWithApplication("BloomBrowserUI"), FileLocator.GetDirectoryDistributedWithApplication("browserui/bookCss"), FileLocator.GetDirectoryDistributedWithApplication("factoryCollections") });

            _collection = new BookCollection(_folder.Path, BookCollection.CollectionType.TheOneEditableCollection, new BookSelection());
        }
        public void SelectCollection(BookCollection collection)
        {
            if (_currentSelection == collection)
                return;

            //enhance... send out cancellable pre-change event

            _currentSelection = collection;

            InvokeSelectionChanged();
        }
        public void InsertBook_NotPresent_InsertsInCorrectOrder()
        {
            var info1 = new BookInfo("book1", true);
            var info2 = new BookInfo("book2", true);
            var info3 = new BookInfo("book10", true);
            var info4 = new BookInfo("book20", true);
            var infoNew = new BookInfo("book11", true);
            var state = new List<BookInfo>(new[] {info1, info2, info3, info4});
            var collection = new BookCollection(state);
            collection.InsertBookInfo(infoNew);
            Assert.That(state[3], Is.EqualTo(infoNew), "book info should be inserted between book10 and book20");

            var infoLast = new BookInfo("book30", true);
            collection.InsertBookInfo(infoLast);
            Assert.That(state[5], Is.EqualTo(infoLast), "book info should be inserted at end");

            var infoFirst = new BookInfo("abc", true);
            collection.InsertBookInfo(infoFirst);
            Assert.That(state[0], Is.EqualTo(infoFirst), "book info should be inserted at start");
        }
Example #8
0
        public LibraryModel(string pathToLibrary, CollectionSettings collectionSettings,
			SendReceiver sendReceiver,
			BookSelection bookSelection,
			SourceCollectionsList sourceCollectionsList,
			BookCollection.Factory bookCollectionFactory,
			EditBookCommand editBookCommand,
			CreateFromSourceBookCommand createFromSourceBookCommand,
			BookServer bookServer,
			CurrentEditableCollectionSelection currentEditableCollectionSelection)
        {
            _bookSelection = bookSelection;
            _pathToLibrary = pathToLibrary;
            _collectionSettings = collectionSettings;
            _sendReceiver = sendReceiver;
            _sourceCollectionsList = sourceCollectionsList;
            _bookCollectionFactory = bookCollectionFactory;
            _editBookCommand = editBookCommand;
            _bookServer = bookServer;
            _currentEditableCollectionSelection = currentEditableCollectionSelection;

            createFromSourceBookCommand.Subscribe(CreateFromSourceBook);
        }
Example #9
0
        public HtmlDom GetDomForPrinting(PublishModel.BookletPortions bookletPortion, BookCollection currentBookCollection, BookServer bookServer)
        {
            var printingDom = GetBookDomWithStyleSheets("previewMode.css", "origami.css");
            AddCreationTypeAttribute(printingDom);

            if (IsFolio)
            {
                AddChildBookContentsToFolio(printingDom, currentBookCollection, bookServer);
                printingDom.SortStyleSheetLinks();
            }

            //whereas the base is to our embedded server during editing, it's to the file folder
            //when we make a PDF, because we wan the PDF to use the original hi-res versions

            var pathSafeForWkHtml2Pdf = FileUtils.MakePathSafeFromEncodingProblems(FolderPath);
            BookStorage.SetBaseForRelativePaths(printingDom, pathSafeForWkHtml2Pdf);

            switch (bookletPortion)
            {
                case PublishModel.BookletPortions.AllPagesNoBooklet:
                    break;
                case PublishModel.BookletPortions.BookletCover:
                    DeletePages(printingDom.RawDom, p => !p.GetAttribute("class").ToLowerInvariant().Contains("cover"));
                    break;
                 case PublishModel.BookletPortions.BookletPages:
                    DeletePages(printingDom.RawDom, p => p.GetAttribute("class").ToLowerInvariant().Contains("cover"));
                    break;
                 default:
                    throw new ArgumentOutOfRangeException("bookletPortion");
            }
            AddCoverColor(printingDom, Color.White);
            AddPreviewJavascript(printingDom);
            return printingDom;
        }
 /// <summary>
 ///
 /// </summary>
 /// <returns>True if the collection should be shown</returns>
 private bool LoadOneCollection(BookCollection collection, FlowLayoutPanel flowLayoutPanel)
 {
     collection.CollectionChanged += OnCollectionChanged;
     bool loadedAtLeastOneBook = false;
     foreach (var bookInfo in collection.GetBookInfos())
     {
         try
         {
             if (!bookInfo.IsExperimental || Settings.Default.ShowExperimentalBooks)
             {
                 loadedAtLeastOneBook = true;
                 AddOneBook(bookInfo, flowLayoutPanel, collection);
             }
         }
         catch (Exception error)
         {
             ErrorReport.NotifyUserOfProblem(error, "Could not load the book at " + bookInfo.FolderPath);
         }
     }
     if (collection.ContainsDownloadedBooks)
     {
         _downloadedBookCollection = collection;
         collection.FolderContentChanged += DownLoadedBooksChanged;
         collection.WatchDirectory(); // In case another instance downloads a book.
         var bloomLibrayLink = new LinkLabel()
         {
             Text =
                 LocalizationManager.GetString("CollectionTab.BloomLibraryLinkLabel",
                                                         "Get more source books at BloomLibrary.org",
                                                         "Shown at the bottom of the list of books. User can click on it and it will attempt to open a browser to show the Bloom Library"),
             Width = 400,
             Margin = new Padding(17, 0, 0, 0),
             LinkColor = Palette.TextAgainstDarkBackground
         };
         bloomLibrayLink.Click += new EventHandler(OnBloomLibrary_Click);
         flowLayoutPanel.Controls.Add(bloomLibrayLink);
         return true;
     }
     return loadedAtLeastOneBook;
 }
        private void AddOneBook(BookInfo bookInfo, FlowLayoutPanel flowLayoutPanel, BookCollection collection)
        {
            string title = bookInfo.QuickTitleUserDisplay;
            if (collection.IsFactoryInstalled)
                title = LocalizationManager.GetDynamicString("Bloom", "TemplateBooks.BookName." + title, title);

            var button = new Button
            {
                Size = new Size(ButtonWidth, ButtonHeight),
                Font = bookInfo.IsEditable ? _editableBookFont : _collectionBookFont,
                TextImageRelation = TextImageRelation.ImageAboveText,
                ImageAlign = ContentAlignment.TopCenter,
                TextAlign = ContentAlignment.BottomCenter,
                FlatStyle = FlatStyle.Flat,
                ForeColor = Palette.TextAgainstDarkBackground,
                UseMnemonic = false, //otherwise, it tries to interpret '&' as a shortcut
                ContextMenuStrip = _bookContextMenu,
                AutoSize = false,

                Tag = new BookButtonInfo(bookInfo, collection, collection == _model.TheOneEditableCollection)
            };

            button.MouseDown += OnClickBook; //we need this for right-click menu selection, which needs to 1st select the book
            //doesn't work: item.DoubleClick += (sender,arg)=>_model.DoubleClickedBook();

            button.Text = ShortenTitleIfNeeded(title, button);
            button.FlatAppearance.BorderSize = 1;
            button.FlatAppearance.BorderColor = BackColor;

            toolTip1.SetToolTip(button, title);

            Image thumbnail = Resources.PagePlaceHolder;
            _bookThumbnails.Images.Add(bookInfo.Id, thumbnail);
            button.ImageIndex = _bookThumbnails.Images.Count - 1;
            flowLayoutPanel.Controls.Add(button); // important to add it before RefreshOneThumbnail; uses parent flow to decide whether primary

            // Can't use this test until after we add button (uses parent info)
            if (!IsUsableBook(button))
                button.ForeColor = Palette.DisabledTextAgainstDarkBackColor;

            Image img;
            var refreshThumbnail = false;
            //review: we could do this at idle time, too:
            if (bookInfo.TryGetPremadeThumbnail(out img))
            {
                RefreshOneThumbnail(bookInfo, img);
            }
            else
            {
                //show this one for now, in the background someone will do the slow work of getting us a better one
                RefreshOneThumbnail(bookInfo,Resources.placeHolderBookThumbnail);
                refreshThumbnail = true;
            }
            _buttonsNeedingSlowUpdate.Enqueue(new ButtonRefreshInfo(button, refreshThumbnail));
        }
 public BookButtonInfo(BookInfo bookInfo, BookCollection collection, bool isVernacular)
 {
     _bookInfo = bookInfo;
     _collection = collection;
 }
Example #13
0
        private bool LoadOneCollection(BookCollection collection, FlowLayoutPanel flowLayoutPanel)
        {
            collection.CollectionChanged += OnCollectionChanged;
            bool loadedAtLeastOneBook = false;
            foreach (Book.BookInfo bookInfo in collection.GetBookInfos())
            {
                try
                {
                    var isSuitableSourceForThisEditableCollection = (_model.IsShellProject && bookInfo.IsSuitableForMakingShells) ||
                              (!_model.IsShellProject && bookInfo.IsSuitableForVernacularLibrary);

                    if (isSuitableSourceForThisEditableCollection || collection.Type == BookCollection.CollectionType.TheOneEditableCollection)
                    {
                        if (!bookInfo.IsExperimental || Settings.Default.ShowExperimentalBooks)
                        {
                            loadedAtLeastOneBook = true;
                            AddOneBook(bookInfo, flowLayoutPanel);
                        }
                    }
                }
                catch (Exception error)
                {
                    Palaso.Reporting.ErrorReport.NotifyUserOfProblem(error,"Could not load the book at "+bookInfo.FolderPath);
                }
            }
            return loadedAtLeastOneBook;
        }
Example #14
0
        public XmlDocument GetDomForPrinting(PublishModel.BookletPortions bookletPortion, BookCollection currentBookCollection, BookServer bookServer)
        {
            var printingDom = GetBookDomWithStyleSheets("previewMode.css");
            //dom.LoadXml(OurHtmlDom.OuterXml);

            if (IsFolio)
            {
                AddChildBookContentsToFolio(printingDom, currentBookCollection, bookServer);
            }

            //whereas the base is to our embedded server during editing, it's to the file folder
            //when we make a PDF, because we wan the PDF to use the original hi-res versions
            BookStorage.SetBaseForRelativePaths(printingDom, FolderPath, false);

            switch (bookletPortion)
            {
                case PublishModel.BookletPortions.AllPagesNoBooklet:
                    break;
                case PublishModel.BookletPortions.BookletCover:
                    HidePages(printingDom.RawDom, p=>!p.GetAttribute("class").ToLower().Contains("cover"));
                    break;
                 case PublishModel.BookletPortions.BookletPages:
                    HidePages(printingDom.RawDom, p => p.GetAttribute("class").ToLower().Contains("cover"));
                    break;
                 default:
                    throw new ArgumentOutOfRangeException("bookletPortion");
            }
            AddCoverColor(printingDom, Color.White);
            AddPreviewJScript(printingDom);
            return printingDom.RawDom;
        }
 public void WatchDirectory_CausesNotification_OnAddFile()
 {
     var temp = new TemporaryFolder("BookCollectionWatch");
     var collection = new BookCollection(temp.Path, BookCollection.CollectionType.SourceCollection, null);
     collection.WatchDirectory();
     bool gotNotification = false;
     collection.FolderContentChanged += (sender, args) =>
     {
         gotNotification = true;
     };
     File.WriteAllText(Path.Combine(temp.Path, "somefile"), @"This is some test data");
     // It takes a little time to get the notification. This tells NUnit to try every 20ms for up to 1s.
     Assert.That(() => gotNotification, Is.True.After(1000, 20));
 }
 public void InsertBook_Present_Replaces()
 {
     var info1 = new BookInfo("book1", true);
     var info2 = new BookInfo("book2", true);
     var info3 = new BookInfo("book10", true);
     var info4 = new BookInfo("book20", true);
     var infoNew = new BookInfo("book10", true);
     var state = new List<BookInfo>(new[] { info1, info2, info3, info4 });
     var collection = new BookCollection(state);
     collection.InsertBookInfo(infoNew);
     Assert.That(state[2], Is.EqualTo(infoNew), "book info should replace existing book");
     Assert.That(state, Has.Count.EqualTo(4));
 }
 public void Setup()
 {
     SIL.Reporting.ErrorReport.IsOkToInteractWithUser = false;
     _folder  =new TemporaryFolder("BookCollectionTests");
     _fileLocator = new BloomFileLocator(new CollectionSettings(), new XMatterPackFinder(new string[] {}), ProjectContext.GetFactoryFileLocations(),
         ProjectContext.GetFoundFileLocations(), ProjectContext.GetAfterXMatterFileLocations());
     _collection = new BookCollection(_folder.Path, BookCollection.CollectionType.TheOneEditableCollection, new BookSelection());
 }
Example #18
0
 private static int CompareBookCollections(BookCollection x, BookCollection y)
 {
     if (x.Name == y.Name)
         return 0;
     if (x.Name.ToLower().Contains("templates"))
         return -1;
     if (y.Name.ToLower().Contains("templates"))
         return 1;
     return 0;
 }
Example #19
0
        /// <summary>
        /// used when this book is a "master"/"folio" book that is used to bring together a number of other books in the collection
        /// </summary>
        /// <param name="printingDom"></param>
        /// <param name="currentBookCollection"></param>
        /// <param name="bookServer"></param>
        private void AddChildBookContentsToFolio(HtmlDom printingDom, BookCollection currentBookCollection, BookServer bookServer)
        {
            XmlNode currentLastContentPage = GetLastPageForInsertingNewContent(printingDom);

            //currently we have no way of filtering them, we just take them all
            foreach (var bookInfo in currentBookCollection.GetBookInfos())
            {
                if (bookInfo.IsFolio)
                    continue;
                var childBook =bookServer.GetBookFromBookInfo(bookInfo);

                //this will set the class bloom-content1 on the correct language
                //this happens anyhow if the page was ever looked at in the Edti Tab
                //But if we are testing a collection's folio pdf'ing ability on a newly-generated
                //SHRP collection, and we don't do this, we see lots of sample text because every
                //bloom-editable has "bloom-content1", even the "Z" language ones.
                childBook.UpdateEditableAreasOfElement(childBook.OurHtmlDom);

                //add links to the template css needed by the children.

                HtmlDom.AddStylesheetFromAnotherBook(childBook.OurHtmlDom, printingDom);
                printingDom.SortStyleSheetLinks();

                foreach (XmlElement pageDiv in childBook.OurHtmlDom.RawDom.SafeSelectNodes("/html/body//div[contains(@class, 'bloom-page') and not(contains(@class,'bloom-frontMatter')) and not(contains(@class,'bloom-backMatter'))]"))
                {
                    XmlElement importedPage = (XmlElement) printingDom.RawDom.ImportNode(pageDiv, true);
                    currentLastContentPage.ParentNode.InsertAfter(importedPage, currentLastContentPage);
                    currentLastContentPage = importedPage;

                    foreach(XmlElement img in HtmlDom.SelectChildImgAndBackgroundImageElements(importedPage))
                    {
                        var bookFolderName = Path.GetFileName(bookInfo.FolderPath);
                        var path = HtmlDom.GetImageElementUrl(img);
                        var pathRelativeToFolioFolder = ".../" + bookFolderName + "/" + path.NotEncoded;
                        //NB: URLEncode would replace spaces with '+', which is ok in the parameter section, but not the URL
                        //So we are using UrlPathEncode

                        HtmlDom.SetImageElementUrl(new ElementProxy(img), UrlPathString.CreateFromUnencodedString(pathRelativeToFolioFolder));

                    }
                }
            }
        }
Example #20
0
        /// <summary>
        /// Handles the recursion through directories: if a folder looks like a Bloom book upload it; otherwise, try its children.
        /// Invisible folders like .hg are ignored.
        /// </summary>
        /// <param name="folder"></param>
        /// <param name="dlg"></param>
        /// <param name="container"></param>
        /// <param name="context"></param>
        private void UploadInternal(string folder, BulkUploadProgressDlg dlg, ApplicationContainer container, ref ProjectContext context)
        {
            if (Path.GetFileName(folder).StartsWith("."))
                return; // secret folder, probably .hg

            if (Directory.GetFiles(folder, "*.htm").Count() == 1)
            {
                // Exactly one htm file, assume this is a bloom book folder.
                dlg.Progress.WriteMessage("Starting to upload " + folder);

                // Make sure the files we want to upload are up to date.
                // Unfortunately this requires making a book object, which requires making a ProjectContext, which must be created with the
                // proper parent book collection if possible.
                var parent = Path.GetDirectoryName(folder);
                var collectionPath = Directory.GetFiles(parent, "*.bloomCollection").FirstOrDefault();
                if (collectionPath == null && context == null)
                {
                    collectionPath = Settings.Default.MruProjects.Latest;
                }
                if (context == null || context.SettingsPath != collectionPath)
                {
                    if (context != null)
                        context.Dispose();
                    // optimise: creating a context seems to be quite expensive. Probably the only thing we need to change is
                    // the collection. If we could update that in place...despite autofac being told it has lifetime scope...we would save some time.
                    // Note however that it's not good enough to just store it in the project context. The one that is actually in
                    // the autofac object (_scope in the ProjectContext) is used by autofac to create various objects, in particular, books.
                    context = container.CreateProjectContext(collectionPath);
                }
                var server = context.BookServer;
                var book = server.GetBookFromBookInfo(new BookInfo(folder, true));
                book.BringBookUpToDate(new NullProgress());

                // Assemble the various arguments needed to make the objects normally involved in an upload.
                // We leave some constructor arguments not actually needed for this purpose null.
                var bookSelection = new BookSelection();
                bookSelection.SelectBook(book);
                var currentEditableCollectionSelection = new CurrentEditableCollectionSelection();
                if (collectionPath != null)
                {
                    var collection = new BookCollection(collectionPath, BookCollection.CollectionType.SourceCollection,
                        bookSelection);
                    currentEditableCollectionSelection.SelectCollection(collection);
                }
                var publishModel = new PublishModel(bookSelection, new PdfMaker(), currentEditableCollectionSelection, null, server, _thumbnailer, null);
                publishModel.PageLayout = book.GetLayout();
                var view = new PublishView(publishModel, new SelectedTabChangedEvent(), new LocalizationChangedEvent(), this, null, null);
                string dummy;
                // Normally we let the user choose which languages to upload. Here, just the ones that have complete information.
                var langDict = book.AllLanguages;
                var languagesToUpload = langDict.Keys.Where(l => langDict[l]).ToArray();
                if (languagesToUpload.Any())
                    FullUpload(book, dlg.Progress, view, languagesToUpload, out dummy, dlg);
                return;
            }
            foreach (var sub in Directory.GetDirectories(folder))
                UploadInternal(sub, dlg, container, ref context);
        }
Example #21
0
        /// <summary>
        /// used when this book is a "master"/"folio" book that is used to bring together a number of other books in the collection
        /// </summary>
        /// <param name="printingDom"></param>
        /// <param name="currentBookCollection"></param>
        /// <param name="bookServer"></param>
        private void AddChildBookContentsToFolio(HtmlDom printingDom, BookCollection currentBookCollection, BookServer bookServer)
        {
            XmlNode currentLastContentPage = GetLastPageForInsertingNewContent(printingDom);

            //currently we have no way of filtering them, we just take them all
            foreach (var bookInfo in currentBookCollection.GetBookInfos())
            {
                if (bookInfo.IsFolio)
                    continue;
                var childBook =bookServer.GetBookFromBookInfo(bookInfo);

                //add links to the template css needed by the children.
                //NB: at this point this code can't hand the "customBookStyles" from children, it'll ignore them (they woul conflict with each other)
                //NB: at this point custom styles (e.g. larger/smaller font rules) from children will be lost.
                var customStyleSheets = new List<string>();
                foreach (string sheetName in childBook.OurHtmlDom.GetTemplateStyleSheets())
                {
                    if (!customStyleSheets.Contains(sheetName)) //nb: if two books have stylesheets with the same name, we'll only be grabbing the 1st one.
                    {
                        customStyleSheets.Add(sheetName);
                        printingDom.AddStyleSheetIfMissing("file://"+Path.Combine(childBook.FolderPath,sheetName));
                    }
                }
                printingDom.SortStyleSheetLinks();

                foreach (XmlElement pageDiv in childBook.OurHtmlDom.RawDom.SafeSelectNodes("/html/body/div[contains(@class, 'bloom-page') and not(contains(@class,'bloom-frontMatter')) and not(contains(@class,'bloom-backMatter'))]"))
                {
                    XmlElement importedPage = (XmlElement) printingDom.RawDom.ImportNode(pageDiv, true);
                    currentLastContentPage.ParentNode.InsertAfter(importedPage, currentLastContentPage);
                    currentLastContentPage = importedPage;

                    ImageUpdater.MakeImagePathsOfImportedPagePointToOriginalLocations(importedPage, bookInfo.FolderPath);
                }
            }
        }