SafeSelectNodes() public method

public SafeSelectNodes ( string xpath ) : XmlNodeList
xpath string
return System.Xml.XmlNodeList
Example #1
0
        public static void CopyImageMetadataToWholeBook(string folderPath, HtmlDom dom, Metadata metadata, IProgress progress)
        {
            progress.WriteStatus("Starting...");

            //First update the images themselves

            int completed = 0;
            var imgElements = GetImagePaths(folderPath);
            foreach (string path in imgElements)
            {
                progress.ProgressIndicator.PercentCompleted = (int)(100.0 * (float)completed / imgElements.Count());
                progress.WriteStatus("Copying to " + Path.GetFileName(path));
                using (var image = PalasoImage.FromFile(path))
                {
                    image.Metadata = metadata;
                    image.SaveUpdatedMetadataIfItMakesSense();
                }
                ++completed;
            }

            //Now update the html attributes which echo some of it, and is used by javascript to overlay displays related to
            //whether the info is there or missing or whatever.

            foreach (XmlElement img in dom.SafeSelectNodes("//img"))
            {
                UpdateImgMetdataAttributesToMatchImage(folderPath, img, progress, metadata);
            }
        }
        /// <summary>
        ///
        /// </summary>
        /// <remarks>The image-replacement feature is perhaps a one-off for a project where an advisor replaced the folders
        /// with a version that lacked most of the images (perhaps because dropbox copies small files first and didn't complete the sync)</remarks>
        /// <param name="progress"></param>
        /// <param name="pathToFolderOfReplacementImages">We'll find any matches in the entire folder, regardless of sub-folder name</param>
        public void CheckBook(IProgress progress, string pathToFolderOfReplacementImages = null)
        {
            var error = GetValidateErrors();

            if (!string.IsNullOrEmpty(error))
            {
                progress.WriteError(error);
            }

            //check for missing images

            foreach (XmlElement imgNode in Dom.SafeSelectNodes("//img"))
            {
                var imageFileName = imgNode.GetAttribute("src");
                if (string.IsNullOrEmpty(imageFileName))
                {
                    var classNames = imgNode.GetAttribute("class");
                    if (classNames == null || !classNames.Contains("licenseImage"))                    //bit of hack... it's ok for licenseImages to be blank
                    {
                        progress.WriteWarning("image src is missing");
                        //review: this, we could fix with a new placeholder... maybe in the javascript edit stuff?
                    }
                    continue;
                }
                // Certain .svg files (cogGrey.svg, FontSizeLetter.svg) aren't really part of the book and are stored elsewhere.
                // Also, at present the user can't insert them into a book. Don't report them.
                // TODO: if we ever allow the user to add .svg files, we'll need to change this
                if (Path.HasExtension(imageFileName) && Path.GetExtension(imageFileName).ToLowerInvariant() == ".svg")
                {
                    continue;
                }

                //trim off the end of "license.png?123243"
                var startOfDontCacheHack = imageFileName.IndexOf('?');
                if (startOfDontCacheHack > -1)
                {
                    imageFileName = imageFileName.Substring(0, startOfDontCacheHack);
                }

                while (Uri.UnescapeDataString(imageFileName) != imageFileName)
                {
                    imageFileName = Uri.UnescapeDataString(imageFileName);
                }

                if (!File.Exists(Path.Combine(_folderPath, imageFileName)))
                {
                    if (!string.IsNullOrEmpty(pathToFolderOfReplacementImages))
                    {
                        if (!AttemptToReplaceMissingImage(imageFileName, pathToFolderOfReplacementImages, progress))
                        {
                            progress.WriteWarning(string.Format("Could not find replacement for image {0} in {1}", imageFileName, _folderPath));
                        }
                    }
                    else
                    {
                        progress.WriteWarning(string.Format("Image {0} is missing from the folder {1}", imageFileName, _folderPath));
                    }
                }
            }
        }
Example #3
0
        public static void CopyImageMetadataToWholeBook(string folderPath, HtmlDom dom, Metadata metadata, IProgress progress)
        {
            progress.WriteStatus("Starting...");

            //First update the images themselves

            int completed = 0;
            var imgElements = GetImagePaths(folderPath);
            foreach (string path in imgElements)
            {
                progress.ProgressIndicator.PercentCompleted = (int)(100.0 * (float)completed / imgElements.Count());
                progress.WriteStatus("Copying to " + Path.GetFileName(path));

                try
                {
                    metadata.WriteIntellectualPropertyOnly(path);
                }
                catch (TagLib.CorruptFileException e)
                {
                    NonFatalProblem.Report(ModalIf.Beta, PassiveIf.All,"Image metadata problem", "Bloom had a problem accessing the metadata portion of this image " + path+ "  ref(BL-3214)", e);
                }

                ++completed;
            }

            //Now update the html attributes which echo some of it, and is used by javascript to overlay displays related to
            //whether the info is there or missing or whatever.

            foreach (XmlElement img in dom.SafeSelectNodes("//img"))
            {
                UpdateImgMetdataAttributesToMatchImage(folderPath, img, progress, metadata);
            }
        }
Example #4
0
        public static void CopyImageMetadataToWholeBook(string folderPath, HtmlDom dom, Metadata metadata, IProgress progress)
        {
            progress.WriteStatus("Starting...");

            //First update the images themselves

            int completed   = 0;
            var imgElements = GetImagePaths(folderPath);

            foreach (string path in imgElements)
            {
                progress.ProgressIndicator.PercentCompleted = (int)(100.0 * (float)completed / imgElements.Count());
                progress.WriteStatus("Copying to " + Path.GetFileName(path));

                try
                {
                    metadata.WriteIntellectualPropertyOnly(path);
                }
                catch (TagLib.CorruptFileException e)
                {
                    NonFatalProblem.Report(ModalIf.Beta, PassiveIf.All, "Image metadata problem", "Bloom had a problem accessing the metadata portion of this image " + path + "  ref(BL-3214)", e);
                }

                ++completed;
            }

            //Now update the html attributes which echo some of it, and is used by javascript to overlay displays related to
            //whether the info is there or missing or whatever.

            foreach (XmlElement img in dom.SafeSelectNodes("//img"))
            {
                UpdateImgMetdataAttributesToMatchImage(folderPath, img, progress, metadata);
            }
        }
Example #5
0
 /// <summary>
 ///remove any x-matter in the book
 /// </summary>
 public static void RemoveExistingXMatter(HtmlDom dom)
 {
     foreach (XmlElement div in dom.SafeSelectNodes("//div[contains(@class,'bloom-frontMatter') or contains(@class,'bloom-backMatter')]"))
     {
         div.ParentNode.RemoveChild(div);
     }
 }
Example #6
0
        public static void CopyImageMetadataToWholeBook(string folderPath, HtmlDom dom, Metadata metadata, IProgress progress)
        {
            progress.WriteStatus("Starting...");

            //First update the images themselves

            int completed   = 0;
            var imgElements = GetImagePaths(folderPath);

            foreach (string path in imgElements)
            {
                progress.ProgressIndicator.PercentCompleted = (int)(100.0 * (float)completed / imgElements.Count());
                progress.WriteStatus("Copying to " + Path.GetFileName(path));
                using (var image = PalasoImage.FromFile(path))
                {
                    image.Metadata = metadata;
                    image.SaveUpdatedMetadataIfItMakesSense();
                }
                ++completed;
            }

            //Now update the html attributes which echo some of it, and is used by javascript to overlay displays related to
            //whether the info is there or missing or whatever.

            foreach (XmlElement img in dom.SafeSelectNodes("//img"))
            {
                UpdateImgMetdataAttributesToMatchImage(folderPath, img, progress, metadata);
            }
        }
        public static IEnumerable <Layout> GetLayoutChoices(HtmlDom dom, IFileLocator fileLocator)
        {
            //here we walk through all the stylesheets, looking for one with the special style which tells us which page/orientations it supports
            foreach (XmlElement link in dom.SafeSelectNodes("//link[@rel='stylesheet']"))
            {
                var fileName = link.GetStringAttribute("href");
                if (fileName.ToLowerInvariant().Contains("mode") || fileName.ToLowerInvariant().Contains("page") ||
                    fileName.ToLowerInvariant().Contains("matter") || fileName.ToLowerInvariant().Contains("languagedisplay") ||
                    fileName.ToLowerInvariant().Contains("origami"))
                {
                    continue;
                }

                fileName = fileName.Replace("file://", "").Replace("%5C", "/").Replace("%20", " ");
                var path = fileLocator.LocateFile(fileName);
                if (string.IsNullOrEmpty(path))
                {
                    // We're looking for a block of json that is typically found in Basic Book.css or a comparable place for
                    // a book based on some other template. Caling code is prepared for not finding this block.
                    // It seems safe to ignore a reference to some missing style sheet.
                    NonFatalProblem.Report(ModalIf.None, PassiveIf.Alpha, "Could not find " + fileName + " while looking for size choices");
                    continue;
                }
                var contents = RobustFile.ReadAllText(path);
                var start    = contents.IndexOf("STARTLAYOUTS");
                if (start < 0)
                {
                    continue;                      //yield break; // continue;//move on to the next stylesheet
                }
                start += "STARTLAYOUTS".Length;
                var end = contents.IndexOf("ENDLAYOUTS", start);
                var s   = contents.Substring(start, end - start);

                IEnumerable <Layout> layouts = null;

                try
                {
                    layouts = Layout.GetConfigurationsFromConfigurationOptionsString(s);
                }
                catch (Exception e)
                {
                    throw new ApplicationException("Problem parsing the 'layouts' comment of " + fileName + ". The contents were\r\n" + s, e);
                }


                foreach (var p in layouts)
                {
                    yield return(p);
                }
                yield break;
            }

            //default to A5Portrait
            yield return(new Layout {
                SizeAndOrientation = FromString("A5Portrait")
            });
        }
Example #8
0
        private static void CopyItemToFieldsInPages(HtmlDom dom, string key, string valueAttribute = null, string[] languagePreferences = null)
        {
            if (languagePreferences == null)
            {
                languagePreferences = new[] { "*", "en" }
            }
            ;

            MultiTextBase source = dom.GetBookSetting(key);

            if (key == "copyright")
            {
                // For CC0, we store the "copyright", but don't display it in the text of the book.
                var licenseUrl = dom.GetBookSetting("licenseUrl").GetExactAlternative("*");
                if (licenseUrl == CreativeCommonsLicense.CC0Url)
                {
                    source = new MultiTextBase();
                }
            }

            foreach (XmlElement target in dom.SafeSelectNodes("//*[@data-derived='" + key + "']"))
            {
                //just put value into the text of the element
                if (string.IsNullOrEmpty(valueAttribute))
                {
                    //clear out what's there now
                    target.RemoveAttribute("lang");
                    target.InnerText = "";

                    var form = source.GetBestAlternative(languagePreferences);
                    if (form != null && !string.IsNullOrWhiteSpace(form.Form))
                    {
                        // HtmlDom.GetBookSetting(key) returns the result of XmlNode.InnerXml which will be Html encoded (&amp; &lt; etc).
                        // HtmlDom.SetElementFromUserStringPreservingLineBreaks() calls XmlNode.InnerText, which Html encodes if necessary.
                        // So we need to decode here to prevent double encoding.  See http://issues.bloomlibrary.org/youtrack/issue/BL-4585.
                        // Note that HtmlDom.SetElementFromUserStringPreservingLineBreaks() handles embedded <br/> elements, but makes no
                        // effort to handle p or div elements.
                        var decoded = System.Web.HttpUtility.HtmlDecode(form.Form);
                        HtmlDom.SetElementFromUserStringSafely(target, decoded);
                        target.SetAttribute("lang", form.WritingSystemId);                         //this allows us to set the font to suit the language
                    }
                }
                else                 //Put the value into an attribute. The license image goes through this path.
                {
                    target.SetAttribute(valueAttribute, source.GetBestAlternativeString(languagePreferences));
                    if (source.Empty)
                    {
                        //if the license image is empty, make sure we don't have some alternative text
                        //about the image being missing or slow to load
                        target.SetAttribute("alt", "");
                        //over in javascript land, @alt will get set appropriately when the image url is not empty.
                    }
                }
            }
        }
Example #9
0
        public static void CopyImageMetadataToWholeBook(string folderPath, HtmlDom dom, Metadata metadata, IProgress progress)
        {
            var       filesWithProblems = new List <string>();
            Exception lastError         = null;
            const int kMaxFilesToList   = 4;

            progress.WriteStatus("Starting...");

            //First update the images themselves

            int completed   = 0;
            var imgElements = GetImagePaths(folderPath);

            foreach (string path in imgElements)
            {
                progress.ProgressIndicator.PercentCompleted = (int)(100.0 * (float)completed / imgElements.Count());
                progress.WriteStatus("Copying to " + Path.GetFileName(path));

                try
                {
                    metadata.WriteIntellectualPropertyOnly(path);
                }
                catch (Exception e)
                {
                    lastError = e;
                    filesWithProblems.Add((Path.GetFileName(path)));
                }
                ++completed;
            }

            if (filesWithProblems.Count > 0)
            {
                // Don't overflow the screen with a needlessly long list of files if for some reason
                // there are huge numbers of failures.
                var namesToList = filesWithProblems.Take(kMaxFilesToList);
                // Purposefully not producing different messages if the list is being trimmed.
                //ErrorReport.ReportNonFatalExceptionWithMessage(lastError, "Bloom was not able to copy the metadata to {0} images: {1}. ref(BL-3214)", filesWithProblems.Count, string.Join(", ", namesToList));
                var list = string.Join(", ", namesToList);
                if (filesWithProblems.Count > kMaxFilesToList)
                {
                    list += ", ...";
                }
                var msg = LocalizationManager.GetString("Errors.CopyImageMetadata",
                                                        "Bloom was not able to copy the metadata to {0} image(s): {1}. Try restarting your computer. If that does not fix the problem, please send us a report and we will help you fix it.");
                ErrorReport.NotifyUserOfProblem(lastError, msg, filesWithProblems.Count, list);
            }

            //Now update the html attributes which echo some of it, and is used by javascript to overlay displays related to
            //whether the info is there or missing or whatever.

            foreach (XmlElement img in dom.SafeSelectNodes("//img"))
            {
                UpdateImgMetadataAttributesToMatchImage(folderPath, img, progress, metadata);
            }
        }
 private void EnsureDoesntHaveLinkToStyleSheet(HtmlDom dom, string path)
 {
     foreach (XmlElement link in dom.SafeSelectNodes("//link[@rel='stylesheet']"))
     {
         var fileName = link.GetStringAttribute("href");
         if (fileName == path)
         {
             dom.RemoveStyleSheetIfFound(path);
         }
     }
 }
Example #11
0
 private void RemoveDataDivElementIfEmptyValue(string key, string value)
 {
     if (string.IsNullOrEmpty(value))
     {
         foreach (
             XmlElement node in _dom.SafeSelectNodes("//div[@id='bloomDataDiv']//div[@data-book='" + key + "']"))
         {
             node.ParentNode.RemoveChild(node);
         }
     }
 }
        public static IEnumerable <Layout> GetLayoutChoices(HtmlDom dom, IFileLocator fileLocator)
        {
            //here we walk through all the stylesheets, looking for one with the special style which tells us which page/orientations it supports
            foreach (XmlElement link in dom.SafeSelectNodes("//link[@rel='stylesheet']"))
            {
                var fileName = link.GetStringAttribute("href");
                if (fileName.ToLower().Contains("mode") || fileName.ToLower().Contains("page") ||
                    fileName.ToLower().Contains("matter") || fileName.ToLower().Contains("languagedisplay"))
                {
                    continue;
                }

                fileName = fileName.Replace("file://", "").Replace("%5C", "/");
                fileName = fileName.Replace("file://", "").Replace("%20", " ");
                var path = fileLocator.LocateFile(fileName);
                if (string.IsNullOrEmpty(path))
                {
                    throw new ApplicationException("Could not locate " + fileName);
                }
                var contents = File.ReadAllText(path);
                var start    = contents.IndexOf("STARTLAYOUTS");
                if (start < 0)
                {
                    continue;                      //yield break; // continue;//move on to the next stylesheet
                }
                start += "STARTLAYOUTS".Length;
                var end = contents.IndexOf("ENDLAYOUTS", start);
                var s   = contents.Substring(start, end - start);

                IEnumerable <Layout> layouts = null;

                try
                {
                    layouts = Layout.GetConfigurationsFromConfigurationOptionsString(s);
                }
                catch (Exception e)
                {
                    throw new ApplicationException("Problem parsing the 'layouts' comment of " + fileName + ". The contents were\r\n" + s, e);
                }


                foreach (var p in layouts)
                {
                    yield return(p);
                }
                yield break;
            }

            //default to A5Portrait
            yield return(new Layout {
                SizeAndOrientation = FromString("A5Portrait")
            });
        }
 private void EnsureHasLinkToStyleSheet(HtmlDom dom, string path)
 {
     foreach (XmlElement link in dom.SafeSelectNodes("//link[@rel='stylesheet']"))
     {
         var fileName = link.GetStringAttribute("href");
         if (fileName == path)
         {
             return;
         }
     }
     dom.AddStyleSheet(path);
 }
Example #14
0
//		/// <summary>
//		/// Creates a relative path from one file or folder to another.
//		/// </summary>
//		/// <param name="fromPath">Contains the directory that defines the start of the relative path.</param>
//		/// <param name="toPath">Contains the path that defines the endpoint of the relative path.</param>
//		/// <param name="dontEscape">Boolean indicating whether to add uri safe escapes to the relative path</param>
//		/// <returns>The relative path from the start directory to the end path.</returns>
//		/// <exception cref="ArgumentNullException"></exception>
//		public static String MakeRelativePath(String fromPath, String toPath)
//		{
//			if (String.IsNullOrEmpty(fromPath)) throw new ArgumentNullException("fromPath");
//			if (String.IsNullOrEmpty(toPath)) throw new ArgumentNullException("toPath");
//
//			//the stuff later on needs to see directory names trailed by a "/" or "\".
//			fromPath = fromPath.Trim();
//			if (!fromPath.EndsWith(Path.DirectorySeparatorChar.ToString()))
//			{
//				if (Directory.Exists(fromPath))
//				{
//					fromPath = fromPath + Path.DirectorySeparatorChar;
//				}
//			}
//			Uri fromUri = new Uri(fromPath);
//			Uri toUri = new Uri(toPath);
//
//			Uri relativeUri = fromUri.MakeRelativeUri(toUri);
//			String relativePath = Uri.UnescapeDataString(relativeUri.ToString());
//
//			return relativePath.Replace('/', Path.DirectorySeparatorChar);
//		}

        private void UpdateStyleSheetLinkPaths(HtmlDom dom, IFileLocator fileLocator, IProgress log)
        {
            foreach (XmlElement linkNode in dom.SafeSelectNodes("/html/head/link"))
            {
                var href = linkNode.GetAttribute("href");
                if (href == null)
                {
                    continue;
                }

                //TODO: see long comment on ProjectContextGetFileLocations() about linking to the right version of a css

                //TODO: what cause this to get encoded this way? Saw it happen when creating wall calendar
                href = href.Replace("%5C", "/");


                var fileName = Path.GetFileName(href);
                if (!fileName.StartsWith("xx"))                 //I use xx  as a convenience to temporarily turn off stylesheets during development
                {
                    var path = fileLocator.LocateOptionalFile(fileName);

                    //we want these stylesheets to come from the book folder
                    if (string.IsNullOrEmpty(path) || path.Contains("languageDisplay.css"))
                    {
                        //look in the same directory as the book
                        var local = Path.Combine(_folderPath, fileName);
                        if (File.Exists(local))
                        {
                            path = local;
                        }
                    }
                    //we want these stylesheets to come from the user's collection folder, not ones found in the templates directories
                    else if (path.Contains("CollectionStyles.css"))                     //settingsCollectionStyles & custonCollectionStyles
                    {
                        //look in the parent directory of the book
                        var pathInCollection = Path.Combine(Path.GetDirectoryName(_folderPath), fileName);
                        if (File.Exists(pathInCollection))
                        {
                            path = pathInCollection;
                        }
                    }
                    if (!string.IsNullOrEmpty(path))
                    {
                        //this is here for geckofx 11... probably can remove it when we move up to modern gecko, as FF22 doesn't like it.
                        linkNode.SetAttribute("href", "file://" + path);
                    }
                    else
                    {
                        throw new ApplicationException(string.Format("Bloom could not find the stylesheet '{0}', which is used in {1}", fileName, _folderPath));
                    }
                }
            }
        }
Example #15
0
 private static void EnsureIdsAreUnique(HtmlDom dom, string elementTag, List <string> ids, StringBuilder builder)
 {
     foreach (XmlElement element in dom.SafeSelectNodes("//" + elementTag + "[@id]"))
     {
         var id = element.GetAttribute("id");
         if (ids.Contains(id))
         {
             builder.AppendLine("The id of this " + elementTag + " must be unique, but is not: " + element.OuterXml);
         }
         else
         {
             ids.Add(id);
         }
     }
 }
Example #16
0
        /// <summary>
        /// We mirror several metadata tags in the html for quick access by the UI.
        /// This method makes sure they are all up to date.
        /// </summary>
        /// <param name="progress"> </param>
        public static void UpdateAllHtmlDataAttributesForAllImgElements(string folderPath, HtmlDom dom, IProgress progress)
        {
            //Update the html attributes which echo some of it, and is used by javascript to overlay displays related to
            //whether the info is there or missing or whatever.

            var imgElements = dom.SafeSelectNodes("//img");
            int completed   = 0;

            foreach (XmlElement img in imgElements)
            {
                progress.ProgressIndicator.PercentCompleted = (int)(100.0 * (float)completed / (float)imgElements.Count);
                UpdateImgMetdataAttributesToMatchImage(folderPath, img, progress);
                completed++;
            }
        }
        public static void UpdateBook(HtmlDom dom, string language1Iso639Code)
        {
            int page = 0;
            foreach (XmlElement pageDiv in dom.SafeSelectNodes("/html/body//div[contains(@class,'bloom-page')]"))
            {
                var term = pageDiv.SelectSingleNode("//div[contains(@data-book,'term')]").InnerText.Trim();
                XmlNode weekDataNode = pageDiv.SelectSingleNode("//div[contains(@data-book,'week')]");
                if(weekDataNode==null)
                    continue; // term intro books don't have weeks

                var week = weekDataNode.InnerText.Trim();
                // TODO: need a better way to identify thumbnails, like a class that is always there, lest  we replace some other img that we don't want to replace
                foreach (XmlElement thumbnailContainer in pageDiv.SafeSelectNodes(".//img"))
                {
                    ++page;
                    thumbnailContainer.SetAttribute("src", language1Iso639Code + "-t" + term + "-w" + week + "-p" + page + ".png");
                }
            }
        }
Example #18
0
 private static void CopyStringToFieldsInPages(HtmlDom dom, string key, string val, string lang)
 {
     foreach (XmlElement target in dom.SafeSelectNodes("//*[@data-derived='" + key + "']"))
     {
         if (target == null)                 // don't think this can happen, but something like it seemed to in one test...
         {
             continue;
         }
         if (string.IsNullOrEmpty(val))
         {
             target.RemoveAttribute("lang");
             target.InnerText = "";
         }
         else
         {
             HtmlDom.SetElementFromUserStringSafely(target, val);
             target.SetAttribute("lang", lang);
         }
     }
 }
        //TODO: make this be a real extension
        public static void UpdateBook(HtmlDom dom, string language1Iso639Code)
        {
            int day = 0;
            foreach (XmlElement pageDiv in dom.SafeSelectNodes("/html/body/div[contains(@class,'bloom-page')]"))
            {
                var term = pageDiv.SelectSingleNode("//div[contains(@data-book,'term')]").InnerText.Trim();
                var week = pageDiv.SelectSingleNode("//div[contains(@data-book,'week')]").InnerText.Trim();

                var thumbnailHolders = pageDiv.SafeSelectNodes(".//img");
                if (thumbnailHolders.Count == 2)
                {
                    ++day;
                    ((XmlElement)thumbnailHolders[0]).SetAttribute("src", language1Iso639Code+"-t"+term + "-w" + week + "-d" + day + ".png");
                    ++day;
                    ((XmlElement)thumbnailHolders[1]).SetAttribute("src", language1Iso639Code + "-t" + term + "-w" + week + "-d" + day + ".png");
                }
                //day1Thumbnail day2Thumbnail day4Thumbnail
                //unfortunately Day3 went out with  an img container just copied from day1, with erroneous "day1Thumbnail" class

            }
        }
Example #20
0
 /// <summary>
 ///remove any x-matter in the book
 /// </summary>
 public static void RemoveExistingXMatter(HtmlDom dom)
 {
     foreach (XmlElement div in dom.SafeSelectNodes("//div[contains(@class,'bloom-frontMatter') or contains(@class,'bloom-backMatter')]"))
     {
         div.ParentNode.RemoveChild(div);
     }
 }
        public static IEnumerable<Layout> GetLayoutChoices(HtmlDom dom, IFileLocator fileLocator)
        {
            //here we walk through all the stylesheets, looking for one with the special style which tells us which page/orientations it supports
            foreach (XmlElement link in dom.SafeSelectNodes("//link[@rel='stylesheet']"))
            {
                var fileName = link.GetStringAttribute("href");
                if (fileName.ToLower().Contains("mode") || fileName.ToLower().Contains("page") ||
                    fileName.ToLower().Contains("matter") || fileName.ToLower().Contains("languagedisplay"))
                    continue;

                fileName = fileName.Replace("file://", "").Replace("%5C", "/");
                var path = fileLocator.LocateFile(fileName);
                if(string.IsNullOrEmpty(path))
                {
                    throw new ApplicationException("Could not locate "+fileName);
                }
                var contents = File.ReadAllText(path);
                var start = contents.IndexOf("STARTLAYOUTS");
                if (start < 0)
                     continue; //yield break; // continue;//move on to the next stylesheet
                start += "STARTLAYOUTS".Length;
                var end = contents.IndexOf("ENDLAYOUTS",start);
                var s = contents.Substring(start, end - start);

                IEnumerable<Layout> layouts = null;

                try
                {
                    layouts = Layout.GetConfigurationsFromConfigurationOptionsString(s);
                }
                catch (Exception e)
                {
                    throw new ApplicationException("Problem parsing the 'layouts' comment of " + fileName + ". The contents were\r\n" + s, e);
                }

                foreach (var p in layouts)
                {
                    yield return p;
                }
                yield break;

            }

            //default to A5Portrait
            yield return new Layout {SizeAndOrientation = FromString("A5Portrait")};
        }
        public static IEnumerable<Layout> GetLayoutChoices(HtmlDom dom, IFileLocator fileLocator)
        {
            //here we walk through all the stylesheets, looking for one with the special style which tells us which page/orientations it supports
            foreach (XmlElement link in dom.SafeSelectNodes("//link[@rel='stylesheet']"))
            {
                var fileName = link.GetStringAttribute("href");
                if (fileName.ToLowerInvariant().Contains("mode") || fileName.ToLowerInvariant().Contains("page") ||
                    fileName.ToLowerInvariant().Contains("matter") || fileName.ToLowerInvariant().Contains("languagedisplay"))
                    continue;

                fileName = fileName.Replace("file://", "").Replace("%5C", "/").Replace("%20", " ");
                var path = fileLocator.LocateFile(fileName);
                if(string.IsNullOrEmpty(path))
                {
                    // We're looking for a block of json that is typically found in Basic Book.css or a comparable place for
                    // a book based on some other template. Caling code is prepared for not finding this block.
                    // It seems safe to ignore a reference to some missing style sheet.
                    NonFatalProblem.Report(ModalIf.None, PassiveIf.Alpha, "Could not find " + fileName+" while looking for size choices");
                    continue;
                }
                var contents = RobustFile.ReadAllText(path);
                var start = contents.IndexOf("STARTLAYOUTS");
                if (start < 0)
                     continue; //yield break; // continue;//move on to the next stylesheet
                start += "STARTLAYOUTS".Length;
                var end = contents.IndexOf("ENDLAYOUTS",start);
                var s = contents.Substring(start, end - start);

                IEnumerable<Layout> layouts = null;

                try
                {
                    layouts = Layout.GetConfigurationsFromConfigurationOptionsString(s);
                }
                catch (Exception e)
                {
                    throw new ApplicationException("Problem parsing the 'layouts' comment of " + fileName + ". The contents were\r\n" + s, e);
                }

                foreach (var p in layouts)
                {
                    yield return p;
                }
                yield break;

            }

            //default to A5Portrait
            yield return new Layout {SizeAndOrientation = FromString("A5Portrait")};
        }
Example #23
0
 public void UpdatePageToTemplate(HtmlDom pageDom, XmlElement templatePageDiv, string pageId)
 {
     var pageDiv = pageDom.SafeSelectNodes("//body/div[@id='" + pageId + "']").Cast<XmlElement>().FirstOrDefault();
     if(pageDiv != null)
     {
         var idAttr = templatePageDiv.Attributes["id"];
         var templateId = idAttr == null ? "" : idAttr.Value;
         var oldLineage = MigrateEditableData(pageDiv, templatePageDiv, templateId);
         var props = new Dictionary<string, string>();
         props["newLayout"] = templateId;
         props["oldLineage"] = oldLineage;
         Analytics.Track("Change Page Layout", props);
     }
 }
Example #24
0
 private static void EnsureIdsAreUnique(HtmlDom dom, string elementTag, List<string> ids, StringBuilder builder)
 {
     foreach(XmlElement element in dom.SafeSelectNodes("//" + elementTag + "[@id]"))
     {
         var id = element.GetAttribute("id");
         if(ids.Contains(id))
             builder.AppendLine("The id of this " + elementTag + " must be unique, but is not: " + element.OuterXml);
         else
             ids.Add(id);
     }
 }
Example #25
0
 /// <summary>
 /// This is called both for the whole book, and for individual pages when the user uses Origami to make changes to the layout of the page.
 /// It would be nicer in the HtmlDom, but it uses knowledge about the collection and book languages that the DOM doesn't have.
 /// </summary>
 /// <param name="elementToUpdate"></param>
 public void UpdateEditableAreasOfElement(HtmlDom dom)
 {
     var language1Iso639Code = _collectionSettings.Language1Iso639Code;
     var multilingualContentLanguage2 = _bookData.MultilingualContentLanguage2;
     var multilingualContentLanguage3 = _bookData.MultilingualContentLanguage3;
     foreach (XmlElement div in dom.SafeSelectNodes("//div[contains(@class,'bloom-page')]"))
     {
         TranslationGroupManager.PrepareElementsInPageOrDocument(div, _collectionSettings);
         TranslationGroupManager.UpdateContentLanguageClasses(div, _collectionSettings, language1Iso639Code, multilingualContentLanguage2, multilingualContentLanguage3);
     }
 }
Example #26
0
        private void CopyStyleSheets(HtmlDom pageDom)
        {
            foreach (XmlElement link in pageDom.SafeSelectNodes("//link[@rel='stylesheet']"))
            {
                var href = Path.Combine(Book.FolderPath, link.GetAttribute("href"));
                var name = Path.GetFileName(href);
                if (name == "fonts.css")
                    continue; // generated file for this book, already copied to output.

                var fl = Storage.GetFileLocator();
                //var path = this.GetFileLocator().LocateFileWithThrow(name);
                var path = fl.LocateFileWithThrow(name);
                CopyFileToEpub(path);
            }
        }
Example #27
0
        private void UpdatePageFromFactoryTemplates(HtmlDom bookDom, IProgress progress)
        {
            var originalLayout = Layout.FromDom(bookDom, Layout.A5Portrait);

            var templatePath = BloomFileLocator.GetFactoryBookTemplateDirectory( "Basic Book");

            var templateDom = XmlHtmlConverter.GetXmlDomFromHtmlFile(templatePath.CombineForPath("Basic Book.html"), false);

            progress.WriteStatus("Updating pages that were based on Basic Book...");
            foreach (XmlElement templatePageDiv in templateDom.SafeSelectNodes("//body/div"))
            {
                if (templatePageDiv.GetOptionalStringAttribute("class", "").Contains("customPage"))
                    continue; // we sure don't want to revert this page to its blank custom state

                var templateId = templatePageDiv.GetStringAttribute("id");
                if (string.IsNullOrEmpty(templateId))
                    continue;

                var templatePageClasses = templatePageDiv.GetAttribute("class");
                //note, lineage is a series of guids separated by a semicolon
                foreach (XmlElement pageDiv in bookDom.SafeSelectNodes("//body/div[contains(@data-pagelineage, '" + templateId + "')]"))
                {
                    pageDiv.SetAttribute("class", templatePageClasses);

                    //now for all the editable elements within the page
                    int count = 0;
                    foreach (XmlElement templateElement in templatePageDiv.SafeSelectNodes("div/div"))
                    {
                        UpdateDivInsidePage(count, templateElement, pageDiv, progress);
                        ++count;
                    }
                }
            }

            //custom layout gets messed up when we copy classes over from, for example, Basic Book
            SetLayout(originalLayout);

            //Likewise, the multilingual settings (e.g. bloom-bilingual) get messed up, so restore those
            UpdateMultilingualSettings(bookDom);
        }
Example #28
0
 public static bool HtmlHasLockedDownFlag(HtmlDom dom)
 {
     var node = dom.SafeSelectNodes(String.Format("//meta[@name='lockedDownAsShell' and @content='true']"));
     return node.Count > 0;
 }
Example #29
0
        //        /// <summary>
        //        /// Creates a relative path from one file or folder to another.
        //        /// </summary>
        //        /// <param name="fromPath">Contains the directory that defines the start of the relative path.</param>
        //        /// <param name="toPath">Contains the path that defines the endpoint of the relative path.</param>
        //        /// <param name="dontEscape">Boolean indicating whether to add uri safe escapes to the relative path</param>
        //        /// <returns>The relative path from the start directory to the end path.</returns>
        //        /// <exception cref="ArgumentNullException"></exception>
        //        public static String MakeRelativePath(String fromPath, String toPath)
        //        {
        //            if (String.IsNullOrEmpty(fromPath)) throw new ArgumentNullException("fromPath");
        //            if (String.IsNullOrEmpty(toPath)) throw new ArgumentNullException("toPath");
        //
        //            //the stuff later on needs to see directory names trailed by a "/" or "\".
        //            fromPath = fromPath.Trim();
        //            if (!fromPath.EndsWith(Path.DirectorySeparatorChar.ToString()))
        //            {
        //                if (Directory.Exists(fromPath))
        //                {
        //                    fromPath = fromPath + Path.DirectorySeparatorChar;
        //                }
        //            }
        //            Uri fromUri = new Uri(fromPath);
        //            Uri toUri = new Uri(toPath);
        //
        //            Uri relativeUri = fromUri.MakeRelativeUri(toUri);
        //            String relativePath = Uri.UnescapeDataString(relativeUri.ToString());
        //
        //            return relativePath.Replace('/', Path.DirectorySeparatorChar);
        //        }
        private void UpdateStyleSheetLinkPaths(HtmlDom dom, IFileLocator fileLocator, IProgress log)
        {
            foreach (XmlElement linkNode in dom.SafeSelectNodes("/html/head/link"))
            {
                var href = linkNode.GetAttribute("href");
                if (href == null)
                {
                    continue;
                }

                //TODO: see long comment on ProjectContextGetFileLocations() about linking to the right version of a css

                //TODO: what cause this to get encoded this way? Saw it happen when creating wall calendar
                href = href.Replace("%5C", "/");

                var fileName = Path.GetFileName(href);
                if (!fileName.StartsWith("xx")) //I use xx  as a convenience to temporarily turn off stylesheets during development
                {
                    var path = fileLocator.LocateOptionalFile(fileName);

                    //we want these stylesheets to come from the book folder
                    if (string.IsNullOrEmpty(path)|| path.Contains("languageDisplay.css"))
                    {
                        //look in the same directory as the book
                        var local = Path.Combine(_folderPath, fileName);
                        if (File.Exists(local))
                            path = local;
                    }
                    //we want these stylesheets to come from the user's collection folder, not ones found in the templates directories
                    else if (path.Contains("CollectionStyles.css")) //settingsCollectionStyles & custonCollectionStyles
                    {
                        //look in the parent directory of the book
                        var pathInCollection = Path.Combine(Path.GetDirectoryName(_folderPath), fileName);
                        if (File.Exists(pathInCollection))
                            path = pathInCollection;
                    }
                    if (!string.IsNullOrEmpty(path))
                    {
                        //this is here for geckofx 11... probably can remove it when we move up to modern gecko, as FF22 doesn't like it.
                        linkNode.SetAttribute("href", "file://" + path);
                    }
                    else
                    {
                        throw new ApplicationException(string.Format("Bloom could not find the stylesheet '{0}', which is used in {1}", fileName, _folderPath));
                    }
                }
            }
        }
Example #30
0
 private void EnsureHasLinkToStyleSheet(HtmlDom dom, string path)
 {
     foreach (XmlElement link in dom.SafeSelectNodes("//link[@rel='stylesheet']"))
     {
         var fileName = link.GetStringAttribute("href");
         if (fileName == path)
             return;
     }
     dom.AddStyleSheet(path);
 }
Example #31
0
        /// <summary>
        /// We mirror several metadata tags in the html for quick access by the UI.
        /// This method makes sure they are all up to date.
        /// </summary>
        /// <param name="progress"> </param>
        public static void UpdateAllHtmlDataAttributesForAllImgElements(string folderPath, HtmlDom dom, IProgress progress)
        {
            //Update the html attributes which echo some of it, and is used by javascript to overlay displays related to
            //whether the info is there or missing or whatever.

            var imgElements = dom.SafeSelectNodes("//img");
            int completed = 0;
            foreach (XmlElement img in imgElements)
            {
                progress.ProgressIndicator.PercentCompleted = (int)(100.0 * (float)completed / (float)imgElements.Count);
                UpdateImgMetdataAttributesToMatchImage(folderPath, img, progress);
                completed++;
            }
        }
Example #32
0
        public static IEnumerable <Layout> GetLayoutChoices(HtmlDom dom, IFileLocator fileLocator)
        {
            //here we walk through all the stylesheets, looking for one with the special comment which tells us which page/orientations it supports
            foreach (XmlElement link in dom.SafeSelectNodes("//link[@rel='stylesheet']"))
            {
                var fileName = link.GetStringAttribute("href");
                if (fileName.ToLowerInvariant().Contains("mode") || fileName.ToLowerInvariant().Contains("page") ||
                    fileName.ToLowerInvariant().Contains("matter") || fileName.ToLowerInvariant().Contains("languagedisplay") ||
                    fileName.ToLowerInvariant().Contains("origami") || fileName.ToLowerInvariant().Contains("defaultlangstyles") ||
                    fileName.ToLowerInvariant().Contains("customcollectionstyles") ||
                    // Ignore this obsolete styles file as well.  See https://issues.bloomlibrary.org/youtrack/issue/BL-9128.
                    fileName.ToLowerInvariant().EndsWith(Book.kOldCollectionStyles.ToLowerInvariant(), StringComparison.InvariantCulture))
                {
                    continue;
                }

                fileName = fileName.Replace("file://", "").Replace("%5C", "/").Replace("%20", " ");
                fileName = fileName.Replace("\\", "/");
                var path = fileLocator.LocateFile(fileName);
                if (string.IsNullOrEmpty(path) && fileName.StartsWith("../"))
                {
                    path = fileLocator.LocateFile(fileName.Substring(3));
                }
                if (string.IsNullOrEmpty(path))
                {
                    // We're looking for a block of json that is typically found in Basic Book.css or a comparable place for
                    // a book based on some other template. Calling code is prepared for not finding this block.
                    // It seems safe to ignore a reference to some missing style sheet.
                    if (fileName.ToLowerInvariant().Contains("branding") || fileName.ToLowerInvariant().Contains("readerstyles"))
                    {
                        continue;                         // these don't contain page size info, anyhow.
                    }
                    NonFatalProblem.Report(ModalIf.None, PassiveIf.Alpha, "Could not find " + fileName + " while looking for size choices");
                    continue;
                }
                var contents = RobustFile.ReadAllText(path);
                var start    = contents.IndexOf("STARTLAYOUTS", StringComparison.InvariantCulture);
                if (start < 0)
                {
                    continue;                      //move on to the next stylesheet
                }
                start += "STARTLAYOUTS".Length;
                var end = contents.IndexOf("ENDLAYOUTS", start, StringComparison.InvariantCulture);
                var s   = contents.Substring(start, end - start);

                IEnumerable <Layout> layouts = null;

                try
                {
                    layouts = Layout.GetConfigurationsFromConfigurationOptionsString(s);
                }
                catch (Exception e)
                {
                    throw new ApplicationException("Problem parsing the 'layouts' comment of " + fileName + ". The contents were\r\n" + s, e);
                }


                foreach (var p in layouts)
                {
                    yield return(p);
                }
                yield break;
            }

            // default set of Layouts (These used to be given in 'Basic Book.less'.)
            // See https://silbloom.myjetbrains.com/youtrack/issue/BL-6125.
            yield return(new Layout {
                SizeAndOrientation = FromString("A5Portrait")
            });

            yield return(new Layout {
                SizeAndOrientation = FromString("A5Landscape")
            });

            yield return(new Layout {
                SizeAndOrientation = FromString("A6Portrait")
            });

            yield return(new Layout {
                SizeAndOrientation = FromString("A6Landscape")
            });

            yield return(new Layout {
                SizeAndOrientation = FromString("A4Portrait")
            });

            yield return(new Layout {
                SizeAndOrientation = FromString("A4Landscape")
            });

            yield return(new Layout {
                SizeAndOrientation = FromString("A4Landscape"), Style = "SideBySide"
            });

            yield return(new Layout {
                SizeAndOrientation = FromString("A4Landscape"), Style = "SplitAcrossPages"
            });                                                                                                                 // does this work anywhere?

            yield return(new Layout {
                SizeAndOrientation = FromString("A3Portrait")
            });

            yield return(new Layout {
                SizeAndOrientation = FromString("A3Landscape")
            });

            yield return(new Layout {
                SizeAndOrientation = FromString("B5Portrait")
            });

            yield return(new Layout {
                SizeAndOrientation = FromString("LetterPortrait")
            });

            yield return(new Layout {
                SizeAndOrientation = FromString("LetterLandscape")
            });

            yield return(new Layout {
                SizeAndOrientation = FromString("LegalPortrait")
            });

            yield return(new Layout {
                SizeAndOrientation = FromString("LegalLandscape")
            });

            yield return(new Layout {
                SizeAndOrientation = FromString("HalfLetterPortrait")
            });

            yield return(new Layout {
                SizeAndOrientation = FromString("HalfLetterLandscape")
            });

            yield return(new Layout {
                SizeAndOrientation = FromString("QuarterLetterPortrait")
            });

            yield return(new Layout {
                SizeAndOrientation = FromString("QuarterLetterLandscape")
            });

            yield return(new Layout {
                SizeAndOrientation = FromString("Device16x9Portrait")
            });

            yield return(new Layout {
                SizeAndOrientation = FromString("Device16x9Landscape")
            });

            yield return(new Layout {
                SizeAndOrientation = FromString("Cm13Landscape")
            });                                                                                         // actually square, but acts more like landscape than portrait

            yield return(new Layout {
                SizeAndOrientation = FromString("USComicPortrait")
            });
        }
Example #33
0
 private void EnsureDoesntHaveLinkToStyleSheet(HtmlDom dom, string path)
 {
     foreach (XmlElement link in dom.SafeSelectNodes("//link[@rel='stylesheet']"))
     {
         var fileName = link.GetStringAttribute("href");
         if (fileName == path)
             dom.RemoveStyleSheetIfFound(path);
     }
 }