/// <summary>
 /// Check whether the new image file is the same as the one we already have chosen.
 /// (or at least the same pathname in the filesystem)
 /// </summary>
 /// <remarks>
 /// See https://silbloom.myjetbrains.com/youtrack/issue/BL-2776.
 /// If the user goes out of his way to choose the exact same picture file from the
 /// original location again, a copy will still be created with a slightly revised
 /// name.  Cropping a picture also results in a new copy of the file with the
 /// revised name.  We still need a tool to remove unused picture files from a
 /// book's folder.  (ie, BL-2351)
 /// </remarks>
 private bool IsSameFilePath(string bookFolderPath, UrlPathString src, PalasoImage imageInfo)
 {
     if (src!=null)
     {
         var path = Path.Combine(bookFolderPath, src.NotEncoded);
         if (path == imageInfo.OriginalFilePath)
             return true;
     }
     return false;
 }
        public void Encoded_RoundTripTortureTest()
        {
            var url = "bread%20%2b%20cinnamon%20%26%20sugar%20%3d%20100%25%20yum.jpg";

            Assert.AreEqual(url, UrlPathString.CreateFromUrlEncodedString(url).UrlEncoded);
        }
 public void CreateFromHtmlXmlEncodedString_WithAmpersand_UnencodedAsExpected()
 {
     Assert.AreEqual("one&two", UrlPathString.CreateFromHtmlXmlEncodedString("one&amp;two").NotEncoded);
 }
 public void EqualityOperator_OneIsNull_False()
 {
     Assert.IsFalse(UrlPathString.CreateFromUrlEncodedString("test me") == null);
 }
 public void EqualityOperator_AreEqual_True()
 {
     Assert.IsTrue(UrlPathString.CreateFromUrlEncodedString("test me") == UrlPathString.CreateFromUrlEncodedString("test " + "me"));
 }
 public void Equals_AreEqual_True()
 {
     Assert.IsTrue(UrlPathString.CreateFromUrlEncodedString("test me").Equals(UrlPathString.CreateFromUrlEncodedString("test " + "me")));
 }
 public void UnencodedWithPlusAndSpace_RoundTripable()
 {
     Assert.AreEqual("test + me", UrlPathString.CreateFromUnencodedString("test + me").NotEncoded);
 }
 public void CreateFromUnencodedString_ObviousStringWasAlreadyEncoded_Adapts()
 {
     Assert.AreEqual("test me", UrlPathString.CreateFromUnencodedString("test%20me").NotEncoded);
     Assert.AreEqual("test%me", UrlPathString.CreateFromUnencodedString("test%25me").NotEncoded);
     Assert.AreEqual("John&John", UrlPathString.CreateFromUnencodedString("John%26John").NotEncoded);
 }
 public void Unencoded_toUnencoded_Correct()
 {
     Assert.AreEqual("test me", UrlPathString.CreateFromUnencodedString("test me").NotEncoded);
 }
 public void UrlEncoded_withSpace_toUrlEncoded_SpaceEntityRetained()
 {
     Assert.AreEqual("test%20me", UrlPathString.CreateFromUrlEncodedString("test%20me").UrlEncoded);
 }
 public void UrlEncoded_withPercent_toNotEncoded_PercentRetained()
 {
     Assert.AreEqual("OneHundred%", UrlPathString.CreateFromUrlEncodedString("OneHundred%25").NotEncoded);
 }
 public void UrlEncodedWithPlusToMeanPlus_UnEncoded_PlusRetained()
 {
     //sometimes things encode a space a '+' instead of %20
     Assert.AreEqual("one+one = two", UrlPathString.CreateFromUrlEncodedString("one+one%20=%20two").NotEncoded);
 }
Exemple #13
0
        /// <summary>
        /// When publishing videos in any form but PDF, we want to trim the actual video to just the part that
        /// the user wants to see and add the controls attribute, so that the video controls are visible.
        /// </summary>
        /// <param name="videoContainerElement">bloom-videoContainer element from copied DOM</param>
        /// <param name="sourceBookFolder">This is assumed to be a staging folder, we may replace videos here!</param>
        /// <returns>the new filepath if a video file exists and was copied, empty string if no video file was found</returns>
        public static string PrepareVideoForPublishing(XmlElement videoContainerElement,
                                                       string sourceBookFolder, bool videoControls)
        {
            var videoFolder = Path.Combine(sourceBookFolder, "video");

            var videoElement = videoContainerElement.SelectSingleNode("video") as XmlElement;

            if (videoElement == null)
            {
                return(string.Empty);
            }

            // In each valid video element, we remove any timings in the 'src' attribute of the source element.
            var sourceElement = videoElement.SelectSingleNode("source") as XmlElement;
            var srcAttrVal    = sourceElement?.Attributes["src"]?.Value;

            if (srcAttrVal == null)
            {
                return(string.Empty);
            }

            string timings;
            var    videoUrl = StripTimingFromVideoUrl(srcAttrVal, out timings);

            // Check for valid video file to match url
            var urlWithoutPrefix      = UrlPathString.CreateFromUrlEncodedString(videoUrl.Substring(6));        // grab everything after 'video/'
            var originalVideoFilePath = Path.Combine(videoFolder, urlWithoutPrefix.NotEncoded);                 // any query already removed

            if (!RobustFile.Exists(originalVideoFilePath))
            {
                return(string.Empty);
            }

            var tempName = originalVideoFilePath;

            if (!string.IsNullOrEmpty(FfmpegProgram) && !string.IsNullOrEmpty(timings) &&
                IsVideoMarkedForTrimming(sourceBookFolder, videoUrl, timings))
            {
                tempName = Path.Combine(videoFolder, GetNewVideoFileName());
                var successful = TrimVideoUsingFfmpeg(originalVideoFilePath, tempName, timings);
                if (successful)
                {
                    RobustFile.Delete(originalVideoFilePath);
                    var trimmedFileName = BookStorage.GetVideoFolderName + Path.GetFileName(tempName);
                    HtmlDom.SetVideoElementUrl(new ElementProxy(videoContainerElement), UrlPathString.CreateFromUnencodedString(trimmedFileName, true), false);
                }
                else
                {
                    // probably doesn't exist, but if it does we don't need it.
                    // File.Delete (underneath RobustFile.Delete) does not throw if the file doesn't exist.
                    RobustFile.Delete(tempName);
                    tempName = originalVideoFilePath;
                }
            }

            if (videoControls)
            {
                // Add playback controls needed for videos to work in Readium and possibly other epub readers.
                videoElement.SetAttribute("controls", string.Empty);
            }

            return(tempName);
        }
Exemple #14
0
        /// <summary>
        /// In case the user has been editing video files outside of Bloom, for example,
        /// after using the Show In Folder command in the sign language toolbox,
        /// we want to update the pages that show those videos to use the new versions.
        /// Unfortunately something, probably the browser itself, seems to be very
        /// persistent about caching videos, even though our server tells it not to cache
        /// things in the book folder (and debug builds don't ever tell it to cache anything).
        /// We prevent cache hits on modified files by adding a fake param to the URI.
        /// (Also we will remove any trimming...see comment below.)
        /// This function is called whenever Bloom is activated in Edit mode.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="eventArgs"></param>
        public void CheckForChangedVideoOnActivate(object sender, EventArgs eventArgs)
        {
            // We're only going to check for video changes on the current book, if any.
            // It's not inconceivable that whatever caches videos will keep a cached one
            // for another book, but I think trying to modify books we don't even have open
            // is to dangerous, as well as quite difficult.
            if (CurrentBook == null)
            {
                return;
            }
            // These two mechanisms are in danger of fighting over the change. If we're in the
            // middle of editing a single file from the Bloom command, don't do anything here.
            // Note: this means we _could_ miss another edit the user did while he was off doing
            // the edit outside. But the race condition between the event handlers for Bloom activated
            // and the end of the edit-outside process is a real one (different threads) and they
            // did really overlap before I put this in. The edit-outside process is looking for the most recently
            // modified video to replace the current one, so things are likely to get confused
            // anyway if the user is trying to use both mechanisms at once. This mechanism can't
            // just replace that one (at least as it stands), because we're expecting the outside
            // program to make a new file, allowing Bloom to save the old one as an original, while
            // this is looking for in-place changes.
            // A downside is that if the user never closes the edit-outside program, this mechanism
            // will stay disabled. But I don't see a better answer, at least if we keep both commands.
            if (_doingEditOutsideBloom)
            {
                return;
            }
            // On Linux, this method interferes with successfully referencing a newly imported video file.
            // See https://issues.bloomlibrary.org/youtrack/issue/BL-6723.  Ignoring just the one call to
            // this method suffices for things  to work.  (On Windows, the sequence of events differs, but
            // this change is safe.)
            if (_importedVideoIntoBloom)
            {
                _importedVideoIntoBloom = false;
                return;
            }
            var videoFolderPath = BookStorage.GetVideoDirectoryAndEnsureExistence(CurrentBook.FolderPath);
            var filesModifiedSinceDeactivate = new DirectoryInfo(videoFolderPath)
                                               .GetFiles("*.mp4")
                                               .Where(f => GetRealLastModifiedTime(f) > DeactivateTime)
                                               .Select(f => f.FullName)
                                               .ToList();

            if (!filesModifiedSinceDeactivate.Any())
            {
                return;
            }

            // We might modify the current page, but the user may also have modified it
            // without doing anything to cause a Save before the deactivate. So save their
            // changes before we go to work on it.
            Model.SaveNow();

            foreach (var videoPath in filesModifiedSinceDeactivate)
            {
                var expectedSrcAttr = UrlPathString.CreateFromUnencodedString(BookStorage.GetVideoFolderName + Path.GetFileName(videoPath));
                var videoElts       = CurrentBook.RawDom.SafeSelectNodes($"//video/source[contains(@src,'{expectedSrcAttr.UrlEncodedForHttpPath}')]");
                if (videoElts.Count == 0)
                {
                    continue;                     // not used in book, ignore
                }
                // OK, the user has modified the file outside of Bloom. Something is determined to cache video.
                // Defeat it by setting a fake param.
                // Note that doing this will discard any fragment in the existing URL, typically trimming.
                // I think this is good...if the user has edited the video, we should start over assuming he
                // wants all of it.

                var newSrcAttr = UrlPathString.CreateFromUnencodedString(BookStorage.GetVideoFolderName + Path.GetFileName(videoPath));
                HtmlDom.SetSrcOfVideoElement(newSrcAttr, new ElementProxy((XmlElement)videoElts[0]), true, "?now=" + DateTime.Now.Ticks);
            }

            // We could try to figure out whether one of the modified videos is on the current page.
            // But that's the most likely video to be modified, and it doesn't take long to reload,
            // and this only happens in the very special case that the user has modified a video outside
            // of Bloom.
            View.UpdateSingleDisplayedPage(_pageSelection.CurrentSelection);

            // Likewise, this is probably overkill, but it's a probably-rare case.
            View.UpdateAllThumbnails();
        }
Exemple #15
0
        private bool ProcessAnyFileContent(IRequestInfo info, string localPath)
        {
            string modPath  = localPath;
            string path     = null;
            var    urlPath  = UrlPathString.CreateFromUrlEncodedString(modPath);
            var    tempPath = urlPath.NotEncoded;

            if (RobustFile.Exists(tempPath))
            {
                modPath = tempPath;
            }
            try
            {
                if (localPath.Contains("favicon.ico"))                 //need something to pacify Chrome
                {
                    path = FileLocator.GetFileDistributedWithApplication("BloomPack.ico");
                }

                // Is this request the full path to an image file? For most images, we just have the filename. However, in at
                // least one use case, the image we want isn't in the folder of the PDF we're looking at. That case is when
                // we are looking at a "folio", a book that gathers up other books into one big PDF. In that case, we want
                // to find the image in the correct book folder.  See AddChildBookContentsToFolio();
                var possibleFullImagePath = localPath;
                // "OriginalImages/" at the beginning means we're generating a pdf and want full images,
                // but it has nothing to do with the actual file location.
                if (localPath.StartsWith(OriginalImageMarker + "/"))
                {
                    possibleFullImagePath = localPath.Substring(15);
                }
                if (RobustFile.Exists(possibleFullImagePath) && Path.IsPathRooted(possibleFullImagePath))
                {
                    path = possibleFullImagePath;
                }
                else
                {
                    // Surprisingly, this method will return localPath unmodified if it is a fully rooted path
                    // (like C:\... or \\localhost\C$\...) to a file that exists. So this execution path
                    // can return contents of any file that exists if the URL gives its full path...even ones that
                    // are generated temp files most certainly NOT distributed with the application.
                    path = FileLocator.GetFileDistributedWithApplication(BloomFileLocator.BrowserRoot, modPath);
                }
            }
            catch (ApplicationException e)
            {
                // Might be from GetFileDistributedWithApplication above, but we could be checking templates that
                // are NOT distributed with the application.
                // Otherwise ignore. Assume this means that this class/method cannot serve that request,
                // but something else may.
                if (e.Message.StartsWith("Could not locate the required file"))
                {
                    // LocateFile includes userInstalledSearchPaths (e.g. a shortcut to a collection in a non-standard location)
                    path = BloomFileLocator.sTheMostRecentBloomFileLocator.LocateFile(localPath);
                }
            }

            //There's probably a eventual way to make this problem go away,
            // but at the moment FF, looking for source maps to go with css, is
            // looking for those maps where we said the css was, which is in the actual
            // book folders. So instead redirect to our browser file folder.
            if (string.IsNullOrEmpty(path) || !RobustFile.Exists(path))
            {
                var startOfBookLayout = localPath.IndexOf("bookLayout");
                if (startOfBookLayout > 0)
                {
                    path = BloomFileLocator.GetBrowserFile(false, localPath.Substring(startOfBookLayout));
                }
                var startOfBookEdit = localPath.IndexOf("bookEdit");
                if (startOfBookEdit > 0)
                {
                    path = BloomFileLocator.GetBrowserFile(false, localPath.Substring(startOfBookEdit));
                }
            }

            if (!RobustFile.Exists(path) && localPath.StartsWith("pageChooser/") && IsImageTypeThatCanBeReturned(localPath))
            {
                // if we're in the page chooser dialog and looking for a thumbnail representing an image in a
                // template page, look for that thumbnail in the book that is the template source,
                // rather than in the folder that stores the page choose dialog HTML and code.
                var templateBook = _bookSelection.CurrentSelection.FindTemplateBook();
                if (templateBook != null)
                {
                    var pathMinusPrefix = localPath.Substring("pageChooser/".Length);
                    var templatePath    = Path.Combine(templateBook.FolderPath, pathMinusPrefix);
                    if (RobustFile.Exists(templatePath))
                    {
                        info.ReplyWithImage(templatePath);
                        return(true);
                    }
                    // Might be a page from a different template than the one we based this book on
                    path = BloomFileLocator.sTheMostRecentBloomFileLocator.LocateFile(pathMinusPrefix);
                    if (!string.IsNullOrEmpty(path))
                    {
                        info.ReplyWithImage(path);
                        return(true);
                    }
                }
            }
            // Use '%25' to detect that the % in a Url encoded character (for example space encoded as %20) was encoded as %25.
            // In this example we would have %2520 in info.RawUrl and %20 in localPath instead of a space.  Note that if an
            // image has a % in the filename, like 'The other 50%', and it isn't doubly encoded, then this shouldn't be a
            // problem because we're triggering here only if the file isn't found.
            if (!RobustFile.Exists(localPath) && info.RawUrl.Contains("%25"))
            {
                // possibly doubly encoded?  decode one more time and try.  See https://silbloom.myjetbrains.com/youtrack/issue/BL-3835.
                // Some existing books have somehow acquired Url encoded coverImage data like the following:
                // <div data-book="coverImage" lang="*">
                //     The%20Moon%20and%20The%20Cap_Cover.png
                // </div>
                // This leads to data being stored doubly encoded in the program's run-time data.  The coverImage data is supposed to be
                // Html/Xml encoded (using &), not Url encoded (using %).
                path = System.Web.HttpUtility.UrlDecode(localPath);
            }
            if (!RobustFile.Exists(path) && IsImageTypeThatCanBeReturned(localPath) && _bookSelection.CurrentSelection != null)
            {
                // last resort...maybe we are in the process of renaming a book (BL-3345) and something mysteriously is still using
                // the old path. For example, I can't figure out what hangs on to the old path when an image is changed after
                // altering the main book title.
                var currentFolderPath = Path.Combine(_bookSelection.CurrentSelection.FolderPath, Path.GetFileName(localPath));
                if (RobustFile.Exists(currentFolderPath))
                {
                    info.ReplyWithImage(currentFolderPath);
                    return(true);
                }
            }

            if (!RobustFile.Exists(path))
            {
                // On developer machines, we can lose part of path earlier.  Try one more thing.
                path = info.LocalPathWithoutQuery.Substring(7);                 // skip leading "/bloom/");
            }
            if (!RobustFile.Exists(path))
            {
                if (ShouldReportFailedRequest(info, CurrentBook?.FolderPath))
                {
                    ReportMissingFile(localPath, path);
                }
                return(false);                // from here we head off to ServerBase.MakeReply() which now uses the same ShouldReportFailedRequest() method.
            }
            info.ContentType = GetContentType(Path.GetExtension(modPath));
            info.ReplyWithFileContent(path);
            return(true);
        }
 public void PathOnly_HasQuery_StripsQuery()
 {
     Assert.AreEqual("test me", UrlPathString.CreateFromUnencodedString("test%20me?12345").PathOnly.NotEncoded);
 }
 public void CreateFromUnencodedString_LooksEncodedButSetStrictlyTreatAsEncodedTrue_RoundTrips()
 {
     Assert.AreEqual("test%20me", UrlPathString.CreateFromUnencodedString("test%20me", true).NotEncoded);
 }
 public void PathOnly_HasNoQuery_ReturnsAll()
 {
     Assert.AreEqual("test me", UrlPathString.CreateFromUnencodedString("test%20me").PathOnly.NotEncoded);
 }
 public void UrlEncodedWithPlusToMeanSpace_toUrlEncoded_Retained()
 {
     //sometimes things encode a space a '+' instead of %20
     Assert.AreEqual("test%20me", UrlPathString.CreateFromUrlEncodedString("test+me").UrlEncoded);
 }
 public void PathOnly_AmbiguousInput_RoundTrips()
 {
     Assert.AreEqual("test+me", UrlPathString.CreateFromUnencodedString("test+me").PathOnly.NotEncoded);
 }
 public void UnencodedWithAmpersand_RoundTripable()
 {
     Assert.AreEqual("test&me", UrlPathString.CreateFromUnencodedString("test&me").NotEncoded);
     Assert.AreEqual("test & me", UrlPathString.CreateFromUnencodedString("test & me").NotEncoded);
 }
Exemple #22
0
        public void HandleI18nRequest(ApiRequest request)
        {
            var lastSegment = request.LocalPath().Split(new char[] { '/' }).Last();

            switch (lastSegment)
            {
            case "loadStrings":
                var d    = new Dictionary <string, string>();
                var post = request.GetPostDataWhenFormEncoded();

                if (post != null)
                {
                    foreach (string key in post.Keys)
                    {
                        try
                        {
                            if (d.ContainsKey(key))
                            {
                                continue;
                            }

                            var translation = GetTranslationDefaultMayNotBeEnglish(key, post[key]);
                            d.Add(key, translation);
                        }
                        catch (Exception error)
                        {
                            Debug.Fail("Debug Only:" + error.Message + Environment.NewLine + "A bug reported at this location is BL-923");
                            //Until BL-923 is fixed (hard... it's a race condition, it's better to swallow this for users
                        }
                    }
                }
                request.ReplyWithJson(JsonConvert.SerializeObject(d));
                break;

            case "translate":
                var    parameters          = request.Parameters;
                string id                  = parameters["key"];
                string englishText         = parameters["englishText"];
                string langId              = parameters["langId"];
                string dontWarnIfMissing   = parameters["dontWarnIfMissing"];
                bool   isDontWarnIfMissing = dontWarnIfMissing != null && dontWarnIfMissing.Equals("true");
                string isoCode1;
                string isoCodeM1;
                string isoCodeM2;
                if (request.CurrentBook != null)
                {
                    isoCode1  = request.CurrentBook.BookData.Language1IsoCode;
                    isoCodeM1 = request.CurrentBook.BookData.MetadataLanguage1IsoCode;
                    isoCodeM2 = request.CurrentBook.BookData.MetadataLanguage2IsoCode;
                }
                else
                {
                    isoCode1  = request.CurrentCollectionSettings.Language1.Iso639Code;
                    isoCodeM1 = request.CurrentCollectionSettings.Language2.Iso639Code;
                    isoCodeM2 = request.CurrentCollectionSettings.Language3?.Iso639Code ?? "";
                }
                langId = langId.Replace("V", isoCode1);
                langId = langId.Replace("N1", isoCodeM1);
                langId = langId.Replace("N2", isoCodeM2);
                langId = langId.Replace("UI", LocalizationManager.UILanguageId);
                if (GetSomeTranslation(id, langId, out var localizedString))
                {
                    // Ensure that we actually have a value for localized string.  (This should already be true, but I'm paranoid.)
                    if (localizedString == null)
                    {
                        localizedString = englishText;
                    }
                    request.ReplyWithJson(new { text = localizedString, success = true });
                }
                else
                {
                    var idFound = true;
                    // Don't report missing strings if they are numbers
                    // Enhance: We might get the Javascript to do locale specific numbers someday
                    // The C# side doesn't currently have the smarts to do DigitSubstitution
                    // See Remark at https://msdn.microsoft.com/en-us/library/system.globalization.numberformatinfo.digitsubstitution(v=vs.110).aspx
                    if (IsInteger(id))
                    {
                        englishText = id;
                    }
                    else
                    {
                        // Now that end users can create templates, it's annoying to report that their names,
                        // page labels, and page descriptions don't have localizations.
                        if (IsTemplateBookKey(id))
                        {
                            englishText = englishText.Trim();
                        }
                        else
                        {
                            // it's ok if we don't have a translation, but if the string isn't even in the list of things that need translating,
                            // then we want to remind the developer to add it to the english xlf file.
                            if (!LocalizationManager.GetIsStringAvailableForLangId(id, "en"))
                            {
                                if (!isDontWarnIfMissing)
                                {
                                    ReportL10NMissingString(id, englishText, UrlPathString.CreateFromUrlEncodedString(parameters["comment"] ?? "").NotEncoded);
                                }
                                idFound = false;
                            }
                            else
                            {
                                //ok, so we don't have it translated yet. Make sure it's at least listed in the things that can be translated.
                                // And return the English string, which is what we would do the next time anyway.  (BL-3374)
                                LocalizationManager.GetDynamicString("Bloom", id, englishText);
                            }
                        }
                    }
                    request.ReplyWithJson(new { text = englishText, success = idFound });
                }
                break;

            case "uilang":
                request.ReplyWithText(LocalizationManager.UILanguageId);
                break;

            default:
                request.Failed();
                break;
            }
        }
 public void Equals_AreNotEqual_False()
 {
     Assert.IsFalse(UrlPathString.CreateFromUrlEncodedString("test me").Equals(UrlPathString.CreateFromUrlEncodedString("test him")));
 }
Exemple #24
0
        public static bool HandleRequest(string localPath, IRequestInfo info, CollectionSettings currentCollectionSettings)
        {
            var lastSep     = localPath.IndexOf("/", System.StringComparison.Ordinal);
            var lastSegment = (lastSep > -1) ? localPath.Substring(lastSep + 1) : localPath;

            switch (lastSegment)
            {
            case "loadStrings":

                while (_localizing)
                {
                    Thread.Sleep(0);
                }

                try
                {
                    _localizing = true;

                    var d    = new Dictionary <string, string>();
                    var post = info.GetPostDataWhenFormEncoded();

                    if (post != null)
                    {
                        foreach (string key in post.Keys)
                        {
                            try
                            {
                                if (d.ContainsKey(key))
                                {
                                    continue;
                                }

                                // Now that end users can create templates, it's annoying to report that their names,
                                // page labels, and page descriptions don't have localizations.
                                if (IsTemplateBookKey(key))
                                {
                                    continue;
                                }

                                var translation = GetTranslationDefaultMayNotBeEnglish(key, post[key]);
                                d.Add(key, translation);
                            }
                            catch (Exception error)
                            {
                                Debug.Fail("Debug Only:" + error.Message + Environment.NewLine + "A bug reported at this location is BL-923");
                                //Until BL-923 is fixed (hard... it's a race condition, it's better to swallow this for users
                            }
                        }
                    }

                    info.ContentType = "application/json";
                    info.WriteCompleteOutput(JsonConvert.SerializeObject(d));
                    return(true);
                }
                finally
                {
                    _localizing = false;
                }
                break;

            case "translate":
                var    parameters  = info.GetQueryParameters();
                string id          = parameters["key"];
                string englishText = parameters["englishText"];
                string langId      = parameters["langId"];
                langId = langId.Replace("V", currentCollectionSettings.Language1Iso639Code);
                langId = langId.Replace("N1", currentCollectionSettings.Language2Iso639Code);
                langId = langId.Replace("N2", currentCollectionSettings.Language3Iso639Code);
                langId = langId.Replace("UI", LocalizationManager.UILanguageId);
                if (LocalizationManager.GetIsStringAvailableForLangId(id, langId))
                {
                    info.ContentType = "text/plain";
                    info.WriteCompleteOutput(LocalizationManager.GetDynamicStringOrEnglish("Bloom", id, englishText, null, langId));
                    return(true);
                }
                else
                {
                    // Don't report missing strings if they are numbers
                    // Enhance: We might get the Javascript to do locale specific numbers someday
                    // The C# side doesn't currently have the smarts to do DigitSubstitution
                    // See Remark at https://msdn.microsoft.com/en-us/library/system.globalization.numberformatinfo.digitsubstitution(v=vs.110).aspx
                    if (IsInteger(id))
                    {
                        englishText = id;
                    }
                    else
                    {
                        // Now that end users can create templates, it's annoying to report that their names,
                        // page labels, and page descriptions don't have localizations.
                        if (IsTemplateBookKey(id))
                        {
                            englishText = englishText.Trim();
                        }
                        else
                        {
                            // it's ok if we don't have a translation, but if the string isn't even in the list of things that need translating,
                            // then we want to remind the developer to add it to the english xlf file.
                            if (!LocalizationManager.GetIsStringAvailableForLangId(id, "en"))
                            {
                                ReportL10NMissingString(id, englishText, UrlPathString.CreateFromUrlEncodedString(parameters["comment"] ?? "").NotEncoded);
                            }
                            else
                            {
                                //ok, so we don't have it translated yet. Make sure it's at least listed in the things that can be translated.
                                // And return the English string, which is what we would do the next time anyway.  (BL-3374)
                                LocalizationManager.GetDynamicString("Bloom", id, englishText);
                            }
                        }
                    }
                    info.ContentType = "text/plain";
                    info.WriteCompleteOutput(englishText);
                    return(true);
                }
                break;
            }

            return(false);
        }
 public void EqualityOperator_AreNotEqual_False()
 {
     Assert.IsFalse(UrlPathString.CreateFromUrlEncodedString("test me") == UrlPathString.CreateFromUrlEncodedString("different"));
 }
 public void PathOnly_LooksEncodedButSetStrictlyTreatAsEncodedTrue_RoundTrips()
 {
     //this checks that PathOnly doesn't do processing in ambiguous mode, undoing the information we gave it to be strict
     Assert.AreEqual("test%20me", UrlPathString.CreateFromUnencodedString("test%20me", true).PathOnly.NotEncoded);
 }
        public void HtmlEncodedWithAmpersand_RoundTripable()
        {
            var s = "one&amp;two";

            Assert.AreEqual(s, UrlPathString.CreateFromHtmlXmlEncodedString(s).HtmlXmlEncoded);
        }
 public void QueryOnly_HasQuery_ReturnsIt()
 {
     Assert.That(UrlPathString.CreateFromUnencodedString("test%20me?12345").QueryOnly.NotEncoded, Is.EqualTo("?12345"));
 }
        public void Unencoded_RoundTripTortureTest()
        {
            var fileName = "bread + cinnamon & sugar = 100% yum.JPG";

            Assert.AreEqual(fileName, UrlPathString.CreateFromUnencodedString(fileName).NotEncoded);
        }
 public void QueryOnly_NoQuery_ReturnsEmpty()
 {
     Assert.That(UrlPathString.CreateFromUnencodedString("test%20me").QueryOnly.NotEncoded, Is.EqualTo(""));
 }
Exemple #31
0
 /// <summary>
 /// Sets the url attribute either of an img (the src attribute)
 /// or a div with an inline style with an background-image rule
 /// </summary>
 public static void SetImageElementUrl(ElementProxy imgOrDivWithBackgroundImage, UrlPathString url)
 {
     if(imgOrDivWithBackgroundImage.Name.ToLower() == "img")
     {
         imgOrDivWithBackgroundImage.SetAttribute("src", url.UrlEncoded);
     }
     else
     {
         imgOrDivWithBackgroundImage.SetAttribute("style", string.Format("background-image:url('{0}')", url.UrlEncoded));
     }
 }
Exemple #32
0
        // Needs to be thread-safe
        private string GetBookStatusJson(string bookFolderName, Book.Book book)
        {
            string   whoHasBookLocked = null;
            DateTime whenLocked       = DateTime.MaxValue;
            bool     problem          = false;
            // bookFolderName may be null when no book is selected, e.g., after deleting one.
            var status = bookFolderName == null ? null :_tcManager.CurrentCollection?.GetStatus(bookFolderName);
            // At this level, we know this is the path to the .bloom file in the repo
            // (though if we implement another backend, we'll have to generalize the notion somehow).
            // For the Javascript, it's just an argument to pass to
            // CommonMessages.GetPleaseClickHereForHelpMessage(). It's only used if hasInvalidRepoData is non-empty.
            string clickHereArg = "";
            var    folderTC     = _tcManager.CurrentCollection as FolderTeamCollection;

            if (folderTC != null && bookFolderName != null)
            {
                clickHereArg = UrlPathString.CreateFromUnencodedString(folderTC.GetPathToBookFileInRepo(bookFolderName))
                               .UrlEncoded;
            }

            string hasInvalidRepoData = (status?.hasInvalidRepoData ?? false) ?
                                        (folderTC)?.GetCouldNotOpenCorruptZipMessage()
                                : "";

            if (bookFolderName == null)
            {
                return(JsonConvert.SerializeObject(
                           new
                {
                    // Keep this in sync with IBookTeamCollectionStatus defined in TeamCollectionApi.tsx
                    who = "",
                    whoFirstName = "",
                    whoSurname = "",
                    when = DateTime.Now.ToShortDateString(),
                    where = "",
                    currentUser = CurrentUser,
                    currentUserName = TeamCollectionManager.CurrentUserFirstName,
                    currentMachine = TeamCollectionManager.CurrentMachine,
                    problem = "",
                    hasInvalidRepoData = false,
                    clickHereArg = "",
                    changedRemotely = false,
                    disconnected = false,
                    newLocalBook = true,
                    checkinMessage = "",
                    isUserAdmin = _tcManager.OkToEditCollectionSettings
                }));
            }

            bool newLocalBook = false;

            try
            {
                whoHasBookLocked =
                    _tcManager.CurrentCollectionEvenIfDisconnected?.WhoHasBookLocked(bookFolderName);
                // It's debatable whether to use CurrentCollectionEvenIfDisconnected everywhere. For now, I've only changed
                // it for the two bits of information actually needed by the status panel when disconnected.
                whenLocked = _tcManager.CurrentCollection?.WhenWasBookLocked(bookFolderName) ??
                             DateTime.MaxValue;
                if (whoHasBookLocked == TeamCollection.FakeUserIndicatingNewBook)
                {
                    // This situation comes about from two different scenarios:
                    // 1) The user is creating a new book and TeamCollection status doesn't matter
                    // 2) The user is trying to check out an existing book and TeamCollectionManager
                    //    discovers [through CheckConnection()] that it is suddenly in a disconnected
                    //    state.
                    // In both cases, the current selected book is in view. The only way to tell
                    // these two situations apart is that in (1) book.IsSaveable is true
                    // and in (2) it is not.
                    // Or, book may be null because we're just getting a status to show in the list
                    // of all books. In that case, book is null, but it's fairly safe to assume it's a new local book.
                    if (book?.IsSaveable ?? true)
                    {
                        whoHasBookLocked = CurrentUser;
                        newLocalBook     = true;
                    }
                    else
                    {
                        whoHasBookLocked = null;
                    }
                }
                problem = _tcManager.CurrentCollection?.HasLocalChangesThatMustBeClobbered(bookFolderName) ?? false;
            }
            catch (Exception e) when(e is ICSharpCode.SharpZipLib.Zip.ZipException || e is IOException)
            {
                hasInvalidRepoData = (_tcManager.CurrentCollection as FolderTeamCollection)?.GetCouldNotOpenCorruptZipMessage();
            }

            // If the request asked for the book by name, we don't have an actual Book object.
            // However, it happens that those requests don't need the checkinMessage.
            var checkinMessage = book == null ? "" : BookHistory.GetPendingCheckinMessage(book);

            return(JsonConvert.SerializeObject(
                       new
            {
                // Keep this in sync with IBookTeamCollectionStatus defined in TeamCollectionApi.tsx
                who = whoHasBookLocked,
                whoFirstName = _tcManager.CurrentCollection?.WhoHasBookLockedFirstName(bookFolderName),
                whoSurname = _tcManager.CurrentCollection?.WhoHasBookLockedSurname(bookFolderName),
                when = whenLocked.ToLocalTime().ToShortDateString(),
                where = _tcManager.CurrentCollectionEvenIfDisconnected?.WhatComputerHasBookLocked(bookFolderName),
                currentUser = CurrentUser,
                currentUserName = TeamCollectionManager.CurrentUserFirstName,
                currentMachine = TeamCollectionManager.CurrentMachine,
                problem,
                hasInvalidRepoData,
                clickHereArg,
                changedRemotely = _tcManager.CurrentCollection?.HasBeenChangedRemotely(bookFolderName),
                disconnected = _tcManager.CurrentCollectionEvenIfDisconnected?.IsDisconnected,
                newLocalBook,
                checkinMessage,
                isUserAdmin = _tcManager.OkToEditCollectionSettings
            }));
        }