GetBookSetting() public method

public GetBookSetting ( string key ) : MultiTextBase
key string
return MultiTextBase
Example #1
0
        private static bool ShouldSetToDefaultCopyrightAndLicense(HtmlDom dom)
        {
            var hasCopyright    = !dom.GetBookSetting("copyright").Empty;
            var hasLicenseUrl   = !dom.GetBookSetting("licenseUrl").Empty;
            var hasLicenseNotes = !dom.GetBookSetting("licenseNotes").Empty;

            //Enhance: this logic is perhaps overly restrictive?
            return(!hasCopyright && !hasLicenseUrl && !hasLicenseNotes);
        }
Example #2
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 (& < 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.
                    }
                }
            }
        }
        /// <summary>
        /// Create a Clearshare.Metadata object by reading values out of the dom's bloomDataDiv
        /// </summary>
        /// <param name="brandingNameOrFolderPath"> Normally, the branding is just a name, which we look up in the official branding folder
        //but unit tests can instead provide a path to the folder.
        /// </param>
        public static Metadata GetMetadata(HtmlDom dom, string brandingNameOrFolderPath = "")
        {
            if (ShouldSetToDefaultCopyrightAndLicense(dom))
            {
                return GetMetadataWithDefaultCopyrightAndLicense(brandingNameOrFolderPath);
            }
            var metadata = new Metadata();
            var copyright = dom.GetBookSetting("copyright");
            if (!copyright.Empty)
            {
                metadata.CopyrightNotice = WebUtility.HtmlDecode(copyright.GetFirstAlternative());
            }

            var licenseUrl = dom.GetBookSetting("licenseUrl").GetBestAlternativeString(new[] { "*", "en" });

            if (string.IsNullOrWhiteSpace(licenseUrl))
            {
                //NB: we are mapping "RightsStatement" (which comes from XMP-dc:Rights) to "LicenseNotes" in the html.
                //custom licenses live in this field, so if we have notes (and no URL) it is a custom one.
                var licenseNotes = dom.GetBookSetting("licenseNotes");
                if (!licenseNotes.Empty)
                {
                    metadata.License = new CustomLicense { RightsStatement = WebUtility.HtmlDecode(licenseNotes.GetFirstAlternative()) };
                }
                else
                {
                    // The only remaining current option is a NullLicense
                    metadata.License = new NullLicense(); //"contact the copyright owner
                }
            }
            else // there is a licenseUrl, which means it is a CC license
            {
                try
                {
                    metadata.License = CreativeCommonsLicense.FromLicenseUrl(licenseUrl);
                }
                catch (Exception e)
                {
                    throw new ApplicationException("Bloom had trouble parsing this license url: '" + licenseUrl + "'. (ref BL-4108)", e);
                }
                //are there notes that go along with that?
                var licenseNotes = dom.GetBookSetting("licenseNotes");
                if(!licenseNotes.Empty)
                {
                    var s = WebUtility.HtmlDecode(licenseNotes.GetFirstAlternative());
                    metadata.License.RightsStatement = HtmlDom.ConvertHtmlBreaksToNewLines(s);
                }
            }
            return metadata;
        }
Example #4
0
        /// <summary>
        /// Copy the copyright & license info to the originalCopyrightAndLicense,
        /// then remove the copyright so the translator can put in their own if they
        /// want. We retain the license, but the translator is allowed to change that.
        /// If the source is already a translation (already has original copyright or license)
        /// we keep them unchanged.
        /// </summary>
        public static void SetOriginalCopyrightAndLicense(HtmlDom dom, BookData bookData, CollectionSettings collectionSettings)
        {
            // At least one of these should exist if the source was a derivative, since we don't allow a
            // book to have no license, nor to be uploaded without copyright...unless of course it was derived
            // before 3.9, when we started doing this. In that case the best we can do is record the earliest
            // information we have for this and later adaptations.
            if (bookData.GetMultiTextVariableOrEmpty("originalLicenseUrl").Count > 0 ||
                bookData.GetMultiTextVariableOrEmpty("originalLicenseNotes").Count > 0 ||
                bookData.GetMultiTextVariableOrEmpty("originalCopyright").Count > 0)
            {
                return;                 //leave the original there.
            }
            // If there's no copyright information in a source-collection book, we're presumably making
            // a new original book, and shouldn't try to record any original copyright and license information.
            // This is somewhat redundant with the check in BookStarter.SetupNewDocumentContents(), the one
            // non-unit-test current caller of this method, that doesn't call this at all if the source is
            // a template book. I was trying for a minimal reasonable change for BL-5131, and therefore
            // put in this extra check, since previously this method was simply NEVER called in a source
            // collection.
            var copyrightNotice = GetMetadata(dom).CopyrightNotice;

            if (string.IsNullOrEmpty(copyrightNotice) && collectionSettings.IsSourceCollection)
            {
                return;
            }
            bookData.Set("originalLicenseUrl", GetLicenseUrl(dom), "*");
            bookData.Set("originalCopyright", System.Web.HttpUtility.HtmlEncode(copyrightNotice), "*");
            bookData.Set("originalLicenseNotes", dom.GetBookSetting("licenseNotes").GetFirstAlternative(), "*");
            bookData.RemoveAllForms("copyright");              // RemoveAllForms does modify the dom
        }
Example #5
0
 /// <summary>
 /// Create a Clearshare.Metadata object by reading values out of the dom's bloomDataDiv
 /// </summary>
 /// <param name="brandingNameOrFolderPath"> Normally, the branding is just a name, which we look up in the official branding folder
 //but unit tests can instead provide a path to the folder.
 /// </param>
 public static Metadata GetMetadata(HtmlDom dom, string brandingNameOrFolderPath = "")
 {
     if (ShouldSetToDefaultCopyrightAndLicense(dom))
     {
         return(GetMetadataWithDefaultCopyrightAndLicense(brandingNameOrFolderPath));
     }
     return(CreateMetadata(dom.GetBookSetting("copyright"), GetLicenseUrl(dom), dom.GetBookSetting("licenseNotes")));
 }
Example #6
0
 /// <summary>
 /// Create a Clearshare.Metadata object by reading values out of the dom's bloomDataDiv
 /// </summary>
 public static Metadata GetMetadata(HtmlDom dom, BookData bookData)
 {
     if (ShouldSetToDefaultCopyrightAndLicense(dom))
     {
         return(GetMetadataWithDefaultCopyrightAndLicense());
     }
     return(CreateMetadata(dom.GetBookSetting("copyright"), GetLicenseUrl(dom), dom.GetBookSetting("licenseNotes"), bookData));
 }
Example #7
0
 private static bool ShouldSetToDefaultCopyrightAndLicense(HtmlDom dom)
 {
     //Enhance: this logic is perhaps overly restrictive?
     foreach (var setting in SettingsToCheckForDefaultCopyright)
     {
         if (!dom.GetBookSetting(setting).Empty)
         {
             return(false);
         }
     }
     return(true);
 }
Example #8
0
        internal static string GetOriginalCopyrightAndLicenseNotice(BookData bookData, HtmlDom dom)
        {
            var originalMetadata = GetOriginalMetadata(dom, bookData);

            // As of BL-7898, we are using the existence of an original copyright/license to determine if we are working with a derivative.
            if (!IsDerivative(originalMetadata))
            {
                return(null);
            }

            // The originalTitle strategy used here is not ideal. We would prefer to have a placeholder specifically for it
            // in both EditTab.FrontMatter.OriginalCopyrightSentence and EditTab.FrontMatter.OriginalHadNoCopyrightSentence.
            // But we don't want to require a new set of translations if we can avoid it.
            var encodedTitle  = dom.GetBookSetting("originalTitle")?.GetExactAlternative("*");
            var originalTitle = HttpUtility.HtmlDecode(encodedTitle);

            var titleCitation = "<cite data-book=\"originalTitle\"" +
                                (string.IsNullOrEmpty(originalTitle) ? " class=\"missingOriginalTitle\">" : ">") + originalTitle +
                                "</cite>";

            var languagePriorityIdsNotLang1 = bookData.GetLanguagePrioritiesForLocalizedTextOnPage(false);
            var originalLicenseSentence     = GetOriginalLicenseSentence(languagePriorityIdsNotLang1, originalMetadata.License, out string licenseOnly);

            var rawCopyright = originalMetadata.CopyrightNotice;
            // If we have all the pieces available, we want to use this one.
            // At the very least it's easier to localize into the format the language wants to use.
            var fullFormatString = LocalizationManager.GetString(
                "EditTab.FrontMatter.FullOriginalCopyrightLicenseSentence",
                "Adapted from original, {0}, {1}. Licensed under {2}.",
                "On the Credits page of a book being translated, Bloom shows the original copyright. {0} is original title, {1} is original copyright, and {2} is license information.",
                languagePriorityIdsNotLang1,
                out string langUsed);

            // The last condition here (langUsed ==...) is meant to detect if the string has been translated
            // into the current language or not.
            if (!string.IsNullOrEmpty(originalTitle) && !string.IsNullOrEmpty(rawCopyright) &&
                !string.IsNullOrEmpty(licenseOnly) && langUsed == languagePriorityIdsNotLang1.First())
            {
                return(string.Format(fullFormatString, titleCitation, rawCopyright, licenseOnly));
            }
            var copyrightNotice = GetOriginalCopyrightSentence(
                languagePriorityIdsNotLang1,
                rawCopyright,
                titleCitation).Trim();

            return((copyrightNotice + " " + originalLicenseSentence).Trim());
        }
Example #9
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);

            var target = dom.SelectSingleNode("//*[@data-derived='" + key + "']");

            if (target == null)
            {
                return;
            }


            //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.SetElementFromUserStringPreservingLineBreaks(target, form.Form);
                    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 #10
0
        /// <summary>
        /// Copy the copyright & license info to the originalCopyrightAndLicense,
        /// then remove the copyright so the translator can put in their own if they
        /// want. We retain the license, but the translator is allowed to change that.
        /// If the source is already a translation (already has original copyright or license)
        /// we keep them unchanged.
        /// </summary>
        public static void SetOriginalCopyrightAndLicense(HtmlDom dom, BookData bookData, CollectionSettings collectionSettings)
        {
            // If it already has some of this information, just keep it.
            if (bookData.BookIsDerivative())
            {
                return;                 //leave the original there.
            }
            // If there's no copyright information in a source-collection book, we're presumably making
            // a new original book, and shouldn't try to record any original copyright and license information.
            // This is somewhat redundant with the check in BookStarter.SetupNewDocumentContents(), the one
            // non-unit-test current caller of this method, that doesn't call this at all if the source is
            // a template book. I was trying for a minimal reasonable change for BL-5131, and therefore
            // put in this extra check, since previously this method was simply NEVER called in a source
            // collection.
            var copyrightNotice = BookCopyrightAndLicense.GetMetadata(dom).CopyrightNotice;

            if (String.IsNullOrEmpty(copyrightNotice) && collectionSettings.IsSourceCollection)
            {
                return;
            }
            bookData.Set("originalLicenseUrl", BookCopyrightAndLicense.GetLicenseUrl(dom), "*");
            bookData.Set("originalCopyright", System.Web.HttpUtility.HtmlEncode(copyrightNotice), "*");
            bookData.Set("originalLicenseNotes", dom.GetBookSetting("licenseNotes").GetFirstAlternative(), "*");
        }
 public static void LogMetdata(HtmlDom dom)
 {
     Logger.WriteEvent("LicenseUrl: " + dom.GetBookSetting("licenseUrl"));
     Logger.WriteEvent("LicenseNotes: " + dom.GetBookSetting("licenseNotes"));
     Logger.WriteEvent("");
 }
Example #12
0
 public static string GetLicenseUrl(HtmlDom dom)
 {
     return(dom.GetBookSetting("licenseUrl").GetBestAlternativeString(new[] { "*", "en" }));
 }
Example #13
0
 public void GetBookSetting_TwoVariationsWereThere_ReturnsBoth()
 {
     var bookDom = new HtmlDom(@"<html ><head></head><body>
         <div id='bloomDataDiv'>
                 <div data-book='leaveMe' lang='en'>something unique</div>
                 <div data-book='getMe' lang='id'>Buku</div>
                 <div data-book='getMe' lang='tpi'>Buk</div>
         </div>
      </body></html>");
     var result = bookDom.GetBookSetting("getMe");
     Assert.AreEqual(2,result.Count);
     Assert.AreEqual("Buk", result["tpi"]);
     Assert.AreEqual("Buku", result["id"]);
 }
Example #14
0
 public void GetBookSetting_NotThere_ReturnsEmptyMultistring()
 {
     var bookDom = new HtmlDom(@"<html ><head></head><body>
         <div id='bloomDataDiv'>
         </div>
      </body></html>");
     var result = bookDom.GetBookSetting("getMe");
     Assert.AreEqual(0, result.Count);
 }
        private static bool ShouldSetToDefaultCopyrightAndLicense(HtmlDom dom)
        {
            var hasCopyright = !dom.GetBookSetting("copyright").Empty;
            var hasLicenseUrl = !dom.GetBookSetting("licenseUrl").Empty;
            var hasLicenseNotes = !dom.GetBookSetting("licenseNotes").Empty;

            //Enhance: this logic is perhaps overly restrictive?
            return !hasCopyright && !hasLicenseUrl && !hasLicenseNotes;
        }
Example #16
0
        internal static string GetOriginalCopyrightAndLicenseNotice(CollectionSettings collectionSettings, HtmlDom dom)
        {
            var originalMetadata = GetOriginalMetadata(dom);

            // As of BL-7898, we are using the existence of an original copyright/license to determine if we are working with a derivative.
            if (!IsDerivative(originalMetadata))
            {
                return(null);
            }

            string idOfLanguageUsed;
            var    languagePriorityIds = collectionSettings.LicenseDescriptionLanguagePriorities;

            var    license = originalMetadata.License.GetMinimalFormForCredits(languagePriorityIds, out idOfLanguageUsed);
            string originalLicenseSentence;
            var    preferredLanguageIds = new[] { collectionSettings.Language2Iso639Code, LocalizationManager.UILanguageId, "en" };
            // The originalTitle strategy used here is not ideal. We would prefer to have a placeholder specifically for it
            // in both EditTab.FrontMatter.OriginalCopyrightSentence and EditTab.FrontMatter.OriginalHadNoCopyrightSentence.
            // But we don't want to require a new set of translations if we can avoid it.
            string encodedTitle  = dom.GetBookSetting("originalTitle")?.GetExactAlternative("*");
            string originalTitle = HttpUtility.HtmlDecode(encodedTitle);

            // Used when we insert into the no-copyright string, typically "Adapted from an original with no copyright"
            var originalTitleBeforePeriod = ", <cite data-book=\"originalTitle\">" + originalTitle + "</cite>";
            // Used when we insert into the usual string, typicall "Adapted from original, {0}" ahead of the copyright and license
            // as part of the {0} replacement.
            var originalTitleAfterComma = "<cite data-book=\"originalTitle\">" + originalTitle + "</cite>, ";

            if (string.IsNullOrEmpty(originalTitle))
            {
                // We need to add the "missingOriginalTitle class.
                originalTitleBeforePeriod = ", <cite data-book=\"originalTitle\" class=\"missingOriginalTitle\"></cite>";
                originalTitleAfterComma   = "<cite data-book=\"originalTitle\" class=\"missingOriginalTitle\"></cite>, ";
            }
            if (originalMetadata.License is CustomLicense)
            {
                // I can imagine being more fancy... something like "Licensed under custom license:", and get localizations
                // for that... but sheesh, these are even now very rare in Bloom-land and should become more rare.
                // So for now, let's just print the custom license contents.
                originalLicenseSentence = license;
            }
            else
            {
                var licenseSentenceTemplate = LocalizationManager.GetString("EditTab.FrontMatter.OriginalLicenseSentence",
                                                                            "Licensed under {0}.",
                                                                            "On the Credits page of a book being translated, Bloom puts texts like 'Licensed under CC-BY', so that we have a record of what the license was for the original book. Put {0} in the translation, where the license should go in the sentence.",
                                                                            preferredLanguageIds, out idOfLanguageUsed);
                originalLicenseSentence = string.IsNullOrWhiteSpace(license) ? "" : string.Format(licenseSentenceTemplate, license);
                originalLicenseSentence = originalLicenseSentence.Replace("..", ".");                 // in case had notes which also had a period.
            }

            string copyrightNotice;

            if (string.IsNullOrWhiteSpace(originalMetadata.CopyrightNotice))
            {
                var noCopyrightSentence = LocalizationManager.GetString("EditTab.FrontMatter.OriginalHadNoCopyrightSentence",
                                                                        "Adapted from original without a copyright notice.",
                                                                        "On the Credits page of a book being translated, Bloom shows this if the original book did not have a copyright notice.",
                                                                        preferredLanguageIds, out idOfLanguageUsed);

                noCopyrightSentence = noCopyrightSentence.Substring(0, noCopyrightSentence.Length - 1) +
                                      originalTitleBeforePeriod + ".";

                copyrightNotice = noCopyrightSentence + " " + originalLicenseSentence;
            }
            else
            {
                var originalCopyrightSentence = LocalizationManager.GetString("EditTab.FrontMatter.OriginalCopyrightSentence",
                                                                              "Adapted from original, {0}.",
                                                                              "On the Credits page of a book being translated, Bloom shows the original copyright. Put {0} in the translation where the copyright notice should go. For example in English, 'Adapted from original, {0}.' comes out like 'Adapted from original, Copyright 2011 SIL'.",
                                                                              preferredLanguageIds, out idOfLanguageUsed);
                copyrightNotice = String.Format(originalCopyrightSentence, originalTitleAfterComma + originalMetadata.CopyrightNotice.Trim()) + " " +
                                  originalLicenseSentence;
            }

            return(copyrightNotice.Trim());
        }
Example #17
0
 public static Metadata GetOriginalMetadata(HtmlDom dom, string brandingNameOrFolderPath = "")
 {
     return(CreateMetadata(dom.GetBookSetting("originalCopyright"), dom.GetBookSetting("originalLicenseUrl").GetExactAlternative("*"), dom.GetBookSetting("originalLicenseNotes")));
 }
Example #18
0
 public static void LogMetdata(HtmlDom dom)
 {
     Logger.WriteEvent("LicenseUrl: " + dom.GetBookSetting("licenseUrl"));
     Logger.WriteEvent("LicenseNotes: " + dom.GetBookSetting("licenseNotes"));
     Logger.WriteEvent("");
 }
Example #19
0
        /// <summary>
        /// Create a Clearshare.Metadata object by reading values out of the dom's bloomDataDiv
        /// </summary>
        /// <param name="brandingNameOrFolderPath"> Normally, the branding is just a name, which we look up in the official branding folder
        //but unit tests can instead provide a path to the folder.
        /// </param>
        public static Metadata GetMetadata(HtmlDom dom, string brandingNameOrFolderPath = "")
        {
            if (ShouldSetToDefaultCopyrightAndLicense(dom))
            {
                return(GetMetadataWithDefaultCopyrightAndLicense(brandingNameOrFolderPath));
            }
            var metadata  = new Metadata();
            var copyright = dom.GetBookSetting("copyright");

            if (!copyright.Empty)
            {
                metadata.CopyrightNotice = WebUtility.HtmlDecode(copyright.GetFirstAlternative());
            }

            var licenseUrl = dom.GetBookSetting("licenseUrl").GetBestAlternativeString(new[] { "*", "en" });

            if (string.IsNullOrWhiteSpace(licenseUrl))
            {
                //NB: we are mapping "RightsStatement" (which comes from XMP-dc:Rights) to "LicenseNotes" in the html.
                //custom licenses live in this field, so if we have notes (and no URL) it is a custom one.
                var licenseNotes = dom.GetBookSetting("licenseNotes");
                if (!licenseNotes.Empty)
                {
                    metadata.License = new CustomLicense {
                        RightsStatement = WebUtility.HtmlDecode(licenseNotes.GetFirstAlternative())
                    };
                }
                else
                {
                    // The only remaining current option is a NullLicense
                    metadata.License = new NullLicense();                     //"contact the copyright owner
                }
            }
            else             // there is a licenseUrl, which means it is a CC license
            {
                try
                {
                    metadata.License = CreativeCommonsLicense.FromLicenseUrl(licenseUrl);
                }
                catch (IndexOutOfRangeException)
                {
                    // Need to handle urls which do not end with the version number.
                    // Simply set it to the default version.
                    if (!licenseUrl.EndsWith("/"))
                    {
                        licenseUrl += "/";
                    }
                    licenseUrl      += CreativeCommonsLicense.kDefaultVersion;
                    metadata.License = CreativeCommonsLicense.FromLicenseUrl(licenseUrl);
                }
                catch (Exception e)
                {
                    throw new ApplicationException("Bloom had trouble parsing this license url: '" + licenseUrl + "'. (ref BL-4108)", e);
                }
                //are there notes that go along with that?
                var licenseNotes = dom.GetBookSetting("licenseNotes");
                if (!licenseNotes.Empty)
                {
                    var s = WebUtility.HtmlDecode(licenseNotes.GetFirstAlternative());
                    metadata.License.RightsStatement = HtmlDom.ConvertHtmlBreaksToNewLines(s);
                }
            }
            return(metadata);
        }
Example #20
0
 public static Metadata GetOriginalMetadata(HtmlDom dom, BookData bookData)
 {
     return(CreateMetadata(dom.GetBookSetting("originalCopyright"), dom.GetBookSetting("originalLicenseUrl").GetExactAlternative("*"), dom.GetBookSetting("originalLicenseNotes"), bookData));
 }
        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);

            var target = dom.SelectSingleNode("//*[@data-derived='" + key + "']");
            if (target == null)
            {
                return;
            }

            //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.SetElementFromUserStringPreservingLineBreaks(target, form.Form);
                    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.
                }
            }
        }