Пример #1
0
        internal static void MakeSizedThumbnail(Book book, string destinationFolder, int heightAndWidth)
        {
            // If this fails to create a 'coverImage200.jpg', either the cover image is missing or it's only a placeholder.
            // If this is a new book, the file may exist already, but we want to make sure it's up-to-date.
            // If this is an older book, we need the .bloompub to have it so that Harvester will be able to access it.
            BookThumbNailer.GenerateImageForWeb(book);

            var coverImagePath = book.GetCoverImagePath();

            if (coverImagePath == null)
            {
                var blankImage = Path.Combine(FileLocationUtilities.DirectoryOfApplicationOrSolution, "DistFiles", "Blank.png");
                if (RobustFile.Exists(blankImage))
                {
                    coverImagePath = blankImage;
                }
            }
            if (coverImagePath != null)
            {
                var thumbPath = Path.Combine(destinationFolder, "thumbnail.png");

                RuntimeImageProcessor.GenerateEBookThumbnail(coverImagePath, thumbPath, heightAndWidth, heightAndWidth, System.Drawing.ColorTranslator.FromHtml(book.GetCoverColor()));
            }
        }
Пример #2
0
        public static string GetOrCreateCompressedAudio(string bookFolderPath, string recordingSegmentId)
        {
            if (string.IsNullOrEmpty(recordingSegmentId))
            {
                return(null);
            }

            var root = GetAudioFolderPath(bookFolderPath);

            var publishablePath = Path.Combine(root, Path.ChangeExtension(recordingSegmentId, AudioRecording.kPublishableExtension));

            if (RobustFile.Exists(publishablePath))
            {
                return(publishablePath);
            }

            var recordablePath = Path.ChangeExtension(publishablePath, AudioRecording.kRecordableExtension);

            if (!RobustFile.Exists(recordablePath))
            {
                return(null);
            }
            return(MakeCompressedAudio(recordablePath));
        }
Пример #3
0
        // We want to move the file specified in the first path to a new location to use
        // as a backup while we typically replace it.
        // A previous backup, possibly of the same or another file, is no longer needed (if it exists)
        // and should be deleted, if possible, on a background thread.
        // The path to the backup will be updated to the new backup.
        // Typically the new name matches the original with the extension changed to .bak.
        // If necessary (because the desired backup file already exists), we will add a counter
        // to get the a name that is not in use.
        // A goal is (for performance reasons) not to have to wait while a file is deleted
        // (and definitely not while one is copied).
        private static bool PrepareBackupFile(string path, ref string backupPath, ApiRequest request)
        {
            int counter = 0;

            backupPath = path + ".bak";
            var originalExtension   = Path.GetExtension(path);
            var pathWithNoExtension = Path.GetFileNameWithoutExtension(path);

            while (File.Exists(backupPath))
            {
                counter++;
                backupPath = pathWithNoExtension + counter + originalExtension + ".bak";
            }
            // An earlier version copied the file to a temp file. We can't MOVE to a file in the system temp
            // directory, though, because we're not sure it is on the same volume. And sometimes the time
            // required to copy the file was noticeable and resulted in the user starting to speak before
            // the system started recording. So we pay the price of a small chance of backups being left
            // around the book directory to avoid that danger.
            if (RobustFile.Exists(path))
            {
                try
                {
                    RobustFile.Move(path, backupPath);
                }
                catch (Exception err)
                {
                    ErrorReport.NotifyUserOfProblem(err,
                                                    "The old copy of the recording at " + path +
                                                    " is locked up, so Bloom can't record over it at the moment. If it remains stuck, you may need to restart your computer.");
                    request.Failed("Audio file locked");
                    return(false);
                }
            }

            return(true);
        }
Пример #4
0
        /// <returns>true if the recording started successfully</returns>
        public void HandleStartRecording(ApiRequest request)
        {
            if (Recording)
            {
                request.Failed("Already recording");
                return;
            }

            string segmentId = request.RequiredParam("id");

            PathToCurrentAudioSegment = GetPathToSegment(segmentId);
            PathToTemporaryWav        = Path.GetTempFileName();

            if (Recorder.RecordingState == RecordingState.RequestedStop)
            {
                request.Failed(LocalizationManager.GetString("EditTab.Toolbox.TalkingBook.BadState",
                                                             "Bloom recording is in an unusual state, possibly caused by unplugging a microphone. You will need to restart.", "This is very low priority for translation."));
            }

            // If someone unplugged the microphone we were planning to use switch to another.
            // This also triggers selecting the first one initially.
            if (!RecordingDevice.Devices.Contains(RecordingDevice))
            {
                RecordingDevice = RecordingDevice.Devices.FirstOrDefault();
            }
            if (RecordingDevice == null)
            {
                ReportNoMicrophone();
                request.Failed("No Microphone");
                return;
            }

            if (Recording)
            {
                request.Failed("Already Recording");
                return;
            }

            if (RobustFile.Exists(PathToCurrentAudioSegment))
            {
                //Try to deal with _backPath getting locked (BL-3160)
                try
                {
                    RobustFile.Delete(_backupPath);
                }
                catch (IOException)
                {
                    _backupPath = System.IO.Path.GetTempFileName();
                }
                try
                {
                    RobustFile.Copy(PathToCurrentAudioSegment, _backupPath, true);
                }
                catch (Exception err)
                {
                    ErrorReport.NotifyUserOfProblem(err,
                                                    "Bloom cold not copy " + PathToCurrentAudioSegment + " to " + _backupPath + " If things remains stuck, you may need to restart your computer.");
                    request.Failed("Problem with backup file");
                    return;
                }
                try
                {
                    RobustFile.Delete(PathToCurrentAudioSegment);
                    //DesktopAnalytics.Analytics.Track("Re-recorded a clip", ContextForAnalytics);
                }
                catch (Exception err)
                {
                    ErrorReport.NotifyUserOfProblem(err,
                                                    "The old copy of the recording at " + PathToCurrentAudioSegment + " is locked up, so Bloom can't record over it at the moment. If it remains stuck, you may need to restart your computer.");
                    request.Failed("Audio file locked");
                    return;
                }
            }
            else
            {
                RobustFile.Delete(_backupPath);
                //DesktopAnalytics.Analytics.Track("Recording clip", ContextForAnalytics);
            }
            _startRecording = DateTime.Now;
            _startRecordingTimer.Start();
            request.ReplyWithText("starting record soon");
            return;
        }
        /// <summary>
        /// Process the input PDF file by compressing images and/or by converting color to CMYK.  The operations
        /// to perform are established by the constructor.
        /// </summary>
        public void ProcessPdfFile(string inputFile, string outputFile, bool removeEvenPages = false)
        {
            _inputPdfPath  = inputFile;
            _outputPdfPath = outputFile;
            var exePath = "/usr/bin/gs";

            if (SIL.PlatformUtilities.Platform.IsWindows)
            {
                exePath = FindGhostcriptOnWindows();
            }
            if (!String.IsNullOrWhiteSpace(exePath) && File.Exists(exePath))
            {
                if (_worker != null)
                {
                    _worker.ReportProgress(0, GetSpecificStatus());
                }
                using (var tempPdfFile = TempFile.WithExtension(".pdf"))
                {
                    var runner        = new CommandLineRunner();
                    var arguments     = GetArguments(tempPdfFile.Path, null, removeEvenPages);
                    var fromDirectory = String.Empty;
                    var progress      = new NullProgress();                     // I can't figure out how to use any IProgress based code, but we show progress okay as is.
                    var res           = runner.Start(exePath, arguments, Encoding.UTF8, fromDirectory, 3600, progress, ProcessGhostcriptReporting);
                    if (res.ExitCode != 0)
                    {
                        // On Linux, ghostscript doesn't deal well with some Unicode filenames.  Try renaming the input
                        // file temporarily to something innocuous to see if this makes the ghostscript process succeed.
                        // See https://issues.bloomlibrary.org/youtrack/issue/BL-7177.
                        using (var tempInputFile = TempFile.WithExtension(".pdf"))
                        {
                            RobustFile.Delete(tempInputFile.Path);                                      // Move won't replace even empty files.
                            RobustFile.Move(_inputPdfPath, tempInputFile.Path);
                            arguments = GetArguments(tempPdfFile.Path, tempInputFile.Path, removeEvenPages);
                            res       = runner.Start(exePath, arguments, Encoding.UTF8, fromDirectory, 3600, progress, ProcessGhostcriptReporting);
                            RobustFile.Move(tempInputFile.Path, _inputPdfPath);
                        }
                    }
                    if (res.ExitCode != 0 || res.DidTimeOut || !RobustFile.Exists(tempPdfFile.Path))
                    {
                        if (_inputPdfPath != _outputPdfPath)
                        {
                            RobustFile.Copy(_inputPdfPath, _outputPdfPath, true);
                        }
                        return;
                    }
                    // If the process made the file larger and didn't change the color scheme and we're not removing blank pages, ignore the result.
                    var oldInfo = new FileInfo(_inputPdfPath);
                    var newInfo = new FileInfo(tempPdfFile.Path);
                    if (newInfo.Length < oldInfo.Length || _type == OutputType.Printshop || removeEvenPages)
                    {
                        RobustFile.Copy(tempPdfFile.Path, _outputPdfPath, true);
                    }
                    else if (_inputPdfPath != _outputPdfPath)
                    {
                        RobustFile.Copy(_inputPdfPath, _outputPdfPath, true);
                    }
                }
            }
            else
            {
                // This shouldn't happen.  Linux Bloom package depends on the ghostscript package, and we'll include
                // ghostscript files in our installer to ensure it's available on Windows.  But we have this code here
                // as a failsafe fallback reminding the developers to ensure this installation work happens.
                Debug.WriteLine("ghostscript is not installed, so Bloom cannot process the PDF file.");
                if (_inputPdfPath != _outputPdfPath)
                {
                    RobustFile.Copy(_inputPdfPath, _outputPdfPath, true);
                }
            }
        }
        /// ------------------------------------------------------------------------------------
        public void Load()
        {
            try
            {
                XElement library = SIL.IO.RobustIO.LoadXElement(SettingsFilePath);
                Language1Iso639Code = GetValue(library, "Language1Iso639Code", /* old name */ GetValue(library, "Language1Iso639Code", ""));
                Language2Iso639Code = GetValue(library, "Language2Iso639Code", /* old name */ GetValue(library, "National1Iso639Code", "en"));
                Language3Iso639Code = GetValue(library, "Language3Iso639Code", /* old name */ GetValue(library, "National2Iso639Code", ""));
                XMatterPackName     = GetValue(library, "XMatterPack", "Factory");

                var style = GetValue(library, "PageNumberStyle", "Decimal");
                PageNumberStyle = PageNumberStyleKeys.Contains(style) ? style : "Decimal";

                BrandingProjectName = GetValue(library, "BrandingProjectName", "Default");

                Language1Name             = GetValue(library, "Language1Name", /* old name */ GetValue(library, "LanguageName", ""));
                DefaultLanguage1FontName  = GetValue(library, "DefaultLanguage1FontName", GetDefaultFontName());
                DefaultLanguage2FontName  = GetValue(library, "DefaultLanguage2FontName", GetDefaultFontName());
                DefaultLanguage3FontName  = GetValue(library, "DefaultLanguage3FontName", GetDefaultFontName());
                OneTimeCheckVersionNumber = GetIntegerValue(library, "OneTimeCheckVersionNumber", 0);
                IsLanguage1Rtl            = GetBoolValue(library, "IsLanguage1Rtl", false);
                IsLanguage2Rtl            = GetBoolValue(library, "IsLanguage2Rtl", false);
                IsLanguage3Rtl            = GetBoolValue(library, "IsLanguage3Rtl", false);
                Language1LineHeight       = GetDecimalValue(library, "Language1LineHeight", 0);
                Language2LineHeight       = GetDecimalValue(library, "Language2LineHeight", 0);
                Language3LineHeight       = GetDecimalValue(library, "Language3LineHeight", 0);

                Country            = GetValue(library, "Country", "");
                Province           = GetValue(library, "Province", "");
                District           = GetValue(library, "District", "");
                AllowNewBooks      = GetBoolValue(library, "AllowNewBooks", true);
                IsSourceCollection = GetBoolValue(library, "IsSourceCollection", GetBoolValue(library, "IsShellLibrary" /*the old name*/, GetBoolValue(library, "IsShellMakingProject" /*an even older name*/, false)));
            }
            catch (Exception e)
            {
                string settingsContents = "";
                try
                {
                    settingsContents = RobustFile.ReadAllText(SettingsFilePath);
                }
                catch (Exception error)
                {
                    settingsContents = error.Message;
                }
                Logger.WriteEvent("Contents of " + SettingsFilePath + ": /r/n" + settingsContents);
                SIL.Reporting.ErrorReport.NotifyUserOfProblem(e, "There was an error reading the file {0}.  Please report this error to the developers. To get access to your books, you should make a new collection, then copy your book folders from this broken collection into the new one, then run Bloom again.", SettingsFilePath);
                throw;
            }

            try
            {
                string oldcustomCollectionStylesPath = FolderPath.CombineForPath("collection.css");
                if (RobustFile.Exists(oldcustomCollectionStylesPath))
                {
                    string newcustomCollectionStylesPath = FolderPath.CombineForPath("customCollectionStyles.css");

                    RobustFile.Move(oldcustomCollectionStylesPath, newcustomCollectionStylesPath);
                }
            }
            catch (Exception)
            {
                //ah well, we tried, no big deal, only a couple of beta testers used this old name
            }

            // Check if we need to do a one time check (perhaps migrate to a new Settings value)
            if (OneTimeCheckVersionNumber < kCurrentOneTimeCheckVersionNumber)
            {
                DoOneTimeCheck();
            }
        }
Пример #7
0
        public void ReplyWithFileContent(string path)
        {
            //Deal with BL-3153, where the file was still open in another thread
            FileStream fs;

            if (!RobustFile.Exists(path))
            {
                //for audio, at least, this is not really an error. We constantly are asking if audio already exists for the current segment
                //enhance: maybe audio should go through a different path, e.g. "/bloom/audio/somefile.wav"
                //then this path COULD write and error
                //Logger.WriteError("Server could not find" + path);
                _actualContext.Response.StatusCode = 404;
                return;
            }

            try
            {
                fs = RobustFile.OpenRead(path);
            }
            catch (Exception error)
            {
                Logger.WriteError("Server could not read " + path, error);
                _actualContext.Response.StatusCode = 500;
                return;
            }

            using (fs)
            {
                _actualContext.Response.ContentLength64 = fs.Length;
                _actualContext.Response.AppendHeader("PathOnDisk", HttpUtility.UrlEncode(path));
                //helps with debugging what file is being chosen

                // A HEAD request (rather than a GET or POST request) is a request for just headers, and nothing can be written
                // to the OutputStream. It is normally used to check if the contents of the file have changed without taking the
                // time and bandwidth needed to download the full contents of the file. The 2 pieces of information being returned
                // are the Content-Length and Last-Modified headers. The requestor can use this information to determine if the
                // contents of the file have changed, and if they have changed the requestor can then decide if the file needs to
                // be reloaded. It is useful when debugging with tools which automatically reload the page when something changes.
                if (_actualContext.Request.HttpMethod == "HEAD")
                {
                    var lastModified = RobustFile.GetLastWriteTimeUtc(path).ToString("R");

                    // Originally we were returning the Last-Modified header with every response, but we discovered that this was
                    // causing Geckofx to cache the contents of the files. This made debugging difficult because, even if the file
                    // changed, Geckofx would use the cached file rather than requesting the updated file from the localhost.
                    _actualContext.Response.AppendHeader("Last-Modified", lastModified);
                }
                else
                {
                    var buffer = new byte[1024 * 512];                   //512KB
                    int read;
                    while ((read = fs.Read(buffer, 0, buffer.Length)) > 0)
                    {
                        _actualContext.Response.OutputStream.Write(buffer, 0, read);
                    }
                }
            }

            _actualContext.Response.OutputStream.Close();
            HaveOutput = true;
        }
Пример #8
0
        /// <summary>
        /// Makes the image a png if it's not a jpg and saves in the book's folder.
        /// If the image has a filename, replaces any file with the same name.
        ///
        /// WARNING: imageInfo.Image could be replaced (causing the original to be disposed)
        /// </summary>
        /// <returns>The name of the file, now in the book's folder.</returns>
        public static string ProcessAndSaveImageIntoFolder(PalasoImage imageInfo, string bookFolderPath, bool isSameFile)
        {
            LogMemoryUsage();
            bool isEncodedAsJpeg = false;

            try
            {
                isEncodedAsJpeg = AppearsToBeJpeg(imageInfo);
                if (!isEncodedAsJpeg)
                {
                    // The original imageInfo.Image is disposed of in the setter.
                    // As of now (9/2016) this is safe because there are no other references to it higher in the stack.
                    imageInfo.Image = CreateImageWithoutTransparentBackground(imageInfo.Image);
                }

                var    shouldConvertToJpeg = !isEncodedAsJpeg && ShouldChangeFormatToJpeg(imageInfo.Image);
                string imageFileName;
                if (!shouldConvertToJpeg && isSameFile)
                {
                    imageFileName = imageInfo.FileName;
                }
                else
                {
                    imageFileName = GetFileNameToUseForSavingImage(bookFolderPath, imageInfo, isEncodedAsJpeg || shouldConvertToJpeg);
                }

                if (!Directory.Exists(bookFolderPath))
                {
                    throw new DirectoryNotFoundException(bookFolderPath + " does not exist");
                }

                var destinationPath = Path.Combine(bookFolderPath, imageFileName);
                if (shouldConvertToJpeg)
                {
                    SaveAsTopQualityJpeg(imageInfo.Image, destinationPath);
                }
                PalasoImage.SaveImageRobustly(imageInfo, destinationPath);

                return(imageFileName);

                /* I (Hatton) have decided to stop compressing images until we have a suite of
                 * tests based on a number of image exemplars. Compression can be great, but it
                 * can also lead to very long waits; this is a "first, do no harm" decision.
                 *
                 * //nb: there are cases (undefined) where we get out of memory if we are not operating on a copy
                 * using (var image = new Bitmap(imageInfo.Image))
                 * {
                 *      using (var tmp = new TempFile())
                 *      {
                 *              RobustImageIO.SaveImage(image, tmp.Path, isJpeg ? ImageFormat.Jpeg : ImageFormat.Png);
                 *              SIL.IO.FileUtils.ReplaceFileWithUserInteractionIfNeeded(tmp.Path, destinationPath, null);
                 *      }
                 *
                 * }
                 *
                 * using (var dlg = new ProgressDialogBackground())
                 * {
                 *      dlg.ShowAndDoWork((progress, args) => ImageUpdater.CompressImage(dest, progress));
                 * }*/
            }
            catch (IOException)
            {
                throw;                 //these are informative on their own
            }

            /* No. OutOfMemory is almost meaningless when it comes to image errors. Better not to confuse people
             * catch (OutOfMemoryException error)
             * {
             * //Enhance: it would be great if we could bring up that problem dialog ourselves, and offer this picture as an attachment
             * throw new ApplicationException("Bloom ran out of memory while trying to import the picture. We suggest that you quit Bloom, run it again, and then try importing this picture again. If that fails, please go to the Help menu and choose 'Report a Problem'", error);
             * }*/
            catch (Exception error)
            {
                if (!string.IsNullOrEmpty(imageInfo.FileName) && RobustFile.Exists(imageInfo.OriginalFilePath))
                {
                    var megs = new System.IO.FileInfo(imageInfo.OriginalFilePath).Length / (1024 * 1000);
                    if (megs > 2)
                    {
                        var msg =
                            string.Format(
                                "Bloom was not able to prepare that picture for including in the book. \r\nThis is a rather large image to be adding to a book --{0} Megs--.",
                                megs);
                        if (isEncodedAsJpeg)
                        {
                            msg +=
                                "\r\nNote, this file is a jpeg, which is normally used for photographs, and complex color artwork. Bloom can handle smallish jpegs, large ones are difficult to handle, especialy if memory is limited.";
                        }
                        throw new ApplicationException(msg, error);
                    }
                }

                throw new ApplicationException(
                          "Bloom was not able to prepare that picture for including in the book. We'd like to investigate, so if possible, would you please email it to [email protected]?" +
                          System.Environment.NewLine + imageInfo.FileName, error);
            }
        }
Пример #9
0
        private bool ProcessAnyFileContent(IRequestInfo info, string localPath)
        {
            string modPath = localPath;
            string path    = null;
            // When JavaScript inserts our path into the html it replaces the three magic html characters with these substitutes.
            // We need to convert back in order to match our key. Then, reverse the change we made to deal with quotation marks.
            string tempPath = UnescapeUrlQuotes(modPath.Replace("&lt;", "<").Replace("&gt;", ">").Replace("&amp;", "&"));

            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(localPath.Substring(startOfBookLayout));
                }
                var startOfBookEdit = localPath.IndexOf("bookEdit");
                if (startOfBookEdit > 0)
                {
                    path = BloomFileLocator.GetBrowserFile(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))
            {
                // 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))
            {
                ReportMissingFile(localPath, path);
                return(false);
            }
            info.ContentType = GetContentType(Path.GetExtension(modPath));
            info.ReplyWithFileContent(path);
            return(true);
        }
Пример #10
0
        internal static void HandleSquirrelInstallEvent(string[] args)
        {
#if __MonoCS__
            Debug.Fail("HandleSquirrelInstallEvent should not run on Linux!");                  // and the code below doesn't compile on Linux
            return;
#else
            bool firstTime       = false;
            var  updateUrlResult = LookupUrlOfSquirrelUpdate();
            // Should only be null if we're not online. Not sure how squirrel will handle that,
            // but at least one of these operations is responsible for setting up shortcuts to the program,
            // which we'd LIKE to work offline. Passing it a plausible url, even though it will presumably fail,
            // seems less likely to cause problems than passing null.
            if (string.IsNullOrEmpty(updateUrlResult.URL))
            {
                updateUrlResult.URL = @"https://s3.amazonaws.com/bloomlibrary.org/squirrel";
            }
            if (args[0] == "--squirrel-uninstall")
            {
                RemoveBloomRegistryEntries();
            }
            if (args[0] == "--squirrel-updated")
            {
                var props = new Dictionary <string, string>();
                if (args.Length > 1)
                {
                    props["newVersion"] = args[1];
                }
                props["channel"] = ApplicationUpdateSupport.ChannelName;
                Analytics.Track("Update Version", props);
            }
            string iconPath = null;
            if (args[0] == "--squirrel-install")
            {
                //Using an icon in the root folder fixes the problem of losing the shortcut icon when we
                //upgrade, lose the original, and eventually the windows explorer cache loses it.
                //There was another attempt at fixing this by finding all the shortcuts and updating them, but that didn't work in our testing and this seems simpler and more robust.
                //There may be some other reason for the old approach of pointing at the icon of the app itself (e.g. could be a different icon)?
                var exePath          = Application.ExecutablePath;
                var rootAppDirectory = Path.GetDirectoryName(Path.GetDirectoryName(exePath));
                // directory that holds e.g. /3.6/Bloom.exe
                var versionIconPath = Path.ChangeExtension(exePath, "ico");                 // where this installation has icon
                iconPath = Path.ChangeExtension(Path.Combine(rootAppDirectory, Path.GetFileName(exePath)), "ico");
                // where we will put a version-independent icon
                try
                {
                    if (RobustFile.Exists(versionIconPath))
                    {
                        RobustFile.Copy(versionIconPath, iconPath, true);
                    }
                }
                catch (Exception)
                {
                    // ignore...most likely some earlier version of the icon is locked somehow, fairly harmless.
                }
                // Normally this is done on every run of the program, but if we're doing a silent allUsers install,
                // this is our only time running with admin privileges so we can actually make the entries for all users.
                MakeBloomRegistryEntries(args);
            }
            switch (args[0])
            {
            // args[1] is version number
            case "--squirrel-install":                     // (first?) installed
            case "--squirrel-updated":                     // updated to specified version
            case "--squirrel-obsolete":                    // this version is no longer newest
            case "--squirrel-uninstall":                   // being uninstalled
                using (var mgr = new UpdateManager(updateUrlResult.URL, Application.ProductName))
                {
                    // WARNING, in most of these scenarios, the app exits at the end of HandleEvents;
                    // thus, the method call does not return and nothing can be done after it!
                    // We replace two of the usual calls in order to take control of where shortcuts are installed.
                    SquirrelAwareApp.HandleEvents(

                        onInitialInstall: v =>
                    {
                        mgr.CreateShortcutsForExecutable(Path.GetFileName(Assembly.GetEntryAssembly().Location),
                                                         StartMenuLocations,
                                                         false,               // not just an update, since this is case initial install
                                                         null,                // can provide arguments to pass to Update.exe in shortcut, defaults are OK
                                                         iconPath,
                                                         SharedByAllUsers());
                        // Normally we can't do this in our quick silent run as part of install, because of the need to escalate
                        // privilege. But if we're being installed for all users we must already be running as admin.
                        // We don't need to do an extra restart of Bloom because this install-setup run of Bloom will finish
                        // right away anyway. We do this last because we've had some trouble (BL-3342) with timeouts
                        // if this install somehow ties up the CPU until Squirrel thinks Bloom is taking too long to do its
                        // install-only run.
                        if (SharedByAllUsers())
                        {
                            FontInstaller.InstallFont("AndikaNewBasic", needsRestart: false);
                        }
                    },
                        onAppUpdate: v => HandleAppUpdate(mgr),
                        onAppUninstall: v => mgr.RemoveShortcutsForExecutable(Path.GetFileName(Assembly.GetEntryAssembly().Location), StartMenuLocations, SharedByAllUsers()),
                        onFirstRun: () => firstTime = true,
                        arguments: args);
                }
                break;
            }
#endif
        }
Пример #11
0
        /// <summary>
        /// If there is a file sitting next to the english one with the desired language, get that path.
        /// Otherwise, returns the English path.
        /// </summary>
        public static string GetBestLocalizedFile(string pathToEnglishFile)
        {
            var pathInDesiredLanguage = pathToEnglishFile.Replace("-en.", "-" + LocalizationManager.UILanguageId + ".");

            return(RobustFile.Exists(pathInDesiredLanguage) ? pathInDesiredLanguage : pathToEnglishFile);
        }
Пример #12
0
        private void HandleCopyImageCreditsForWholeBook(ApiRequest request)
        {
            // This method is called on a fileserver thread. To minimize the chance that the current selection somehow
            // changes while it is running, we capture the things that depend on it in variables right at the start.
            var domBody                    = _bookSelection.CurrentSelection.RawDom.DocumentElement.SelectSingleNode("//body");
            var imageNameToPages           = GetFilteredImageNameToPagesDictionary(domBody);
            var currentSelectionFolderPath = _bookSelection.CurrentSelection.FolderPath;
            IEnumerable <string> langs;

            if (request.CurrentCollectionSettings != null)
            {
                langs = request.CurrentCollectionSettings.LicenseDescriptionLanguagePriorities;
            }
            else
            {
                langs = new List <string> {
                    "en"
                }
            };                                                                  // emergency fall back -- probably never used.
            var credits        = new Dictionary <string, List <string> >();
            var missingCredits = new List <string>();

            foreach (var kvp in imageNameToPages)
            {
                var path = currentSelectionFolderPath.CombineForPath(kvp.Key);
                if (!RobustFile.Exists(path))
                {
                    continue;
                }

                var    meta = Metadata.FromFile(path);
                string dummy;
                var    credit = meta.MinimalCredits(langs, out dummy);
                if (string.IsNullOrEmpty(credit))
                {
                    missingCredits.Add(kvp.Key);
                }
                if (!string.IsNullOrEmpty(credit))
                {
                    var pageList = kvp.Value;
                    BuildCreditsDictionary(credits, credit, pageList);
                }
            }
            var collectedCredits = CollectFormattedCredits(credits);
            var total            = collectedCredits.Aggregate(new StringBuilder(), (all, credit) => {
                all.AppendFormat("<p>{0}</p>{1}", credit, Environment.NewLine);
                return(all);
            });

            // Notify the user of images with missing credits.
            if (missingCredits.Count > 0)
            {
                var missing      = LocalizationManager.GetString("EditTab.FrontMatter.PasteMissingCredits", "Missing credits:");
                var missingImage = LocalizationManager.GetString("EditTab.FrontMatter.ImageCreditMissing", " {0} (page {1})",
                                                                 "The {0} is replaced by the filename of an image.  The {1} is replaced by a reference to the first page in the book where that image occurs.");
                total.AppendFormat("<p>{0}", missing);
                for (var i = 0; i < missingCredits.Count; ++i)
                {
                    if (i > 0)
                    {
                        total.Append(",");
                    }
                    total.AppendFormat(missingImage, missingCredits[i], imageNameToPages[missingCredits[i]].First());
                }
                total.AppendFormat("</p>{0}", System.Environment.NewLine);
            }
            request.ReplyWithText(total.ToString());
        }
Пример #13
0
        /// <summary>
        /// Get a json of stats about the image. It is used to populate a tooltip when you hover over an image container
        /// </summary>
        private void HandleImageInfo(ApiRequest request)
        {
            try
            {
                var fileName = request.RequiredParam("image");
                Guard.AgainstNull(_bookSelection.CurrentSelection, "CurrentBook");
                var path = Path.Combine(_bookSelection.CurrentSelection.FolderPath, fileName);
                while (!RobustFile.Exists(path) && fileName.Contains('%'))
                {
                    var fileName1 = fileName;
                    // We can be fed doubly-encoded filenames.  So try to decode a second time and see if that works.
                    // See https://silbloom.myjetbrains.com/youtrack/issue/BL-3749.
                    // Effectively triple-encoded filenames have cropped up for particular books.  Such files are
                    // already handled okay by EnhancedImageServer.ProcessAnyFileContent().  This code can handle
                    // any depth of url-encoding.
                    // See https://silbloom.myjetbrains.com/youtrack/issue/BL-5757.
                    fileName = System.Web.HttpUtility.UrlDecode(fileName);
                    if (fileName == fileName1)
                    {
                        break;
                    }
                    path = Path.Combine(_bookSelection.CurrentSelection.FolderPath, fileName);
                }
                RequireThat.File(path).Exists();
                var     fileInfo = new FileInfo(path);
                dynamic result   = new ExpandoObject();
                result.name  = fileName;
                result.bytes = fileInfo.Length;

                // Using a stream this way, according to one source,
                // http://stackoverflow.com/questions/552467/how-do-i-reliably-get-an-image-dimensions-in-net-without-loading-the-image,
                // supposedly avoids loading the image into memory when we only want its dimensions
                using (var stream = RobustFile.OpenRead(path))
                    using (var img = Image.FromStream(stream, false, false))
                    {
                        result.width  = img.Width;
                        result.height = img.Height;
                        switch (img.PixelFormat)
                        {
                        case PixelFormat.Format32bppArgb:
                        case PixelFormat.Format32bppRgb:
                        case PixelFormat.Format32bppPArgb:
                            result.bitDepth = "32";
                            break;

                        case PixelFormat.Format24bppRgb:
                            result.bitDepth = "24";
                            break;

                        case PixelFormat.Format16bppArgb1555:
                        case PixelFormat.Format16bppGrayScale:
                            result.bitDepth = "16";
                            break;

                        case PixelFormat.Format8bppIndexed:
                            result.bitDepth = "8";
                            break;

                        case PixelFormat.Format1bppIndexed:
                            result.bitDepth = "1";
                            break;

                        default:
                            result.bitDepth = "unknown";
                            break;
                        }
                    }
                request.ReplyWithJson((object)result);
            }
            catch (Exception e)
            {
                Logger.WriteError("Error in server imageInfo/: url was " + request.LocalPath(), e);
                request.Failed(e.Message);
            }
        }
Пример #14
0
        public static void WriteSpreadsheet(InternalSpreadsheet spreadsheet, string outputPath, bool retainMarkup, IWebSocketProgress progress = null)
        {
            using (var package = new ExcelPackage())
            {
                var worksheet = package.Workbook.Worksheets.Add("BloomBook");

                worksheet.DefaultColWidth = languageColumnWidth;
                for (int i = 1; i <= spreadsheet.StandardLeadingColumns.Length; i++)
                {
                    worksheet.Column(i).Width = standardLeadingColumnWidth;
                }

                var imageSourceColumn    = spreadsheet.GetColumnForTag(InternalSpreadsheet.ImageSourceColumnLabel);
                var imageThumbnailColumn = spreadsheet.GetColumnForTag(InternalSpreadsheet.ImageThumbnailColumnLabel);
                // Apparently the width is in some approximation of 'characters'. This empirically determined
                // conversion factor seems to do a pretty good job.
                worksheet.Column(imageThumbnailColumn + 1).Width = defaultImageWidth / 6.88;

                int r = 0;
                foreach (var row in spreadsheet.AllRows())
                {
                    r++;
                    for (var c = 0; c < row.Count; c++)
                    {
                        // Enhance: Excel complains about cells that contain pure numbers
                        // but are created as strings. We could possibly tell it that cells
                        // that contain simple numbers can be treated accordingly.
                        // It might be helpful for some uses of the group-on-page-index
                        // if Excel knew to treat them as numbers.

                        var sourceCell = row.GetCell(c);
                        var content    = sourceCell.Content;
                        // Parse xml for markdown formatting on language columns,
                        // Display formatting in excel spreadsheet
                        ExcelRange currentCell = worksheet.Cells[r, c + 1];
                        if (!string.IsNullOrEmpty(sourceCell.Comment))
                        {
                            // Second arg is supposed to be the author.
                            currentCell.AddComment(sourceCell.Comment, "Bloom");
                        }

                        if (!retainMarkup &&
                            IsWysiwygFormattedColumn(row, c) &&
                            IsWysiwygFormattedRow(row))
                        {
                            MarkedUpText markedUpText = MarkedUpText.ParseXml(content);
                            if (markedUpText.HasFormatting)
                            {
                                currentCell.IsRichText = true;
                                foreach (MarkedUpTextRun run in markedUpText.Runs)
                                {
                                    if (!run.Text.Equals(""))
                                    {
                                        ExcelRichText text = currentCell.RichText.Add(run.Text);
                                        text.Bold      = run.Bold;
                                        text.Italic    = run.Italic;
                                        text.UnderLine = run.Underlined;
                                        if (run.Superscript)
                                        {
                                            text.VerticalAlign = ExcelVerticalAlignmentFont.Superscript;
                                        }
                                    }
                                }
                            }
                            else
                            {
                                currentCell.Value = markedUpText.PlainText();
                            }
                        }
                        else
                        {
                            // Either the retainMarkup flag is set, or this is not book text. It could be header or leading column.
                            // Generally, we just want to blast our cell content into the spreadsheet cell.
                            // However, there are cases where we put an error message in an image thumbnail cell when processing the image path.
                            // We don't want to overwrite these. An easy way to prevent it is to not overwrite any cell that already has content.
                            // Since export is creating a new spreadsheet, cells we want to write will always be empty initially.
                            if (currentCell.Value == null)
                            {
                                currentCell.Value = content;
                            }
                        }


                        //Embed any images in the excel file
                        if (c == imageSourceColumn)
                        {
                            var imageSrc = sourceCell.Content;

                            // if this row has an image source value that is not a header
                            if (imageSrc != "" && !row.IsHeader)
                            {
                                var sheetFolder = Path.GetDirectoryName(outputPath);
                                var imagePath   = Path.Combine(sheetFolder, imageSrc);
                                //Images show up in the cell 1 row greater and 1 column greater than assigned
                                //So this will put them in row r, column imageThumbnailColumn+1 like we want
                                var rowHeight = embedImage(imagePath, r - 1, imageThumbnailColumn);
                                worksheet.Row(r).Height = rowHeight * 72 / 96 + 3;                               //so the image is visible; height seems to be points
                            }
                        }
                    }

                    if (row is HeaderRow)
                    {
                        using (ExcelRange rng = GetRangeForRow(worksheet, r))
                            rng.Style.Font.Bold = true;
                    }

                    if (row.Hidden)
                    {
                        worksheet.Row(r).Hidden = true;
                        SetBackgroundColorOfRow(worksheet, r, InternalSpreadsheet.HiddenColor);
                    }
                    else if (row.BackgroundColor != default(Color))
                    {
                        SetBackgroundColorOfRow(worksheet, r, row.BackgroundColor);
                    }
                }
                worksheet.Cells[1, 1, r, spreadsheet.ColumnCount].Style.WrapText = true;


                int embedImage(string imageSrcPath, int rowNum, int colNum)
                {
                    int finalHeight = 30;                     //  a reasonable default if we don't manage to embed an image.

                    try
                    {
                        using (Image image = Image.FromFile(imageSrcPath))
                        {
                            string imageName       = Path.GetFileNameWithoutExtension(imageSrcPath);
                            var    origImageHeight = image.Size.Height;
                            var    origImageWidth  = image.Size.Width;
                            int    finalWidth      = defaultImageWidth;
                            finalHeight = (int)(finalWidth * origImageHeight / origImageWidth);
                            var size = new Size(finalWidth, finalHeight);
                            using (Image thumbnail = ImageUtils.ResizeImageIfNecessary(size, image, false))
                            {
                                var excelImage = worksheet.Drawings.AddPicture(imageName, thumbnail);
                                excelImage.SetPosition(rowNum, 2, colNum, 2);
                            }
                        }
                    }
                    catch (Exception)
                    {
                        string errorText;
                        if (!RobustFile.Exists(imageSrcPath))
                        {
                            errorText = "Missing";
                        }
                        else if (Path.GetExtension(imageSrcPath).ToLowerInvariant().Equals(".svg"))
                        {
                            errorText = "Can't display SVG";
                        }
                        else
                        {
                            errorText = "Bad image file";
                        }
                        progress?.MessageWithoutLocalizing(errorText + ": " + imageSrcPath);
                        worksheet.Cells[r, imageThumbnailColumn + 1].Value = errorText;
                    }
                    return(Math.Max(finalHeight, 30));
                }

                foreach (var iColumn in spreadsheet.HiddenColumns)
                {
                    // This is pretty yucky... our internal spreadsheet is all 0-based, but the EPPlus library is all 1-based...
                    var iColumn1Based = iColumn + 1;

                    worksheet.Column(iColumn1Based).Hidden = true;
                    SetBackgroundColorOfColumn(worksheet, iColumn1Based, InternalSpreadsheet.HiddenColor);
                }

                try
                {
                    RobustFile.Delete(outputPath);
                    var xlFile = new FileInfo(outputPath);
                    package.SaveAs(xlFile);
                }
                catch (IOException ex) when((ex.HResult & 0x0000FFFF) == 32)                 //ERROR_SHARING_VIOLATION
                {
                    Console.WriteLine("Writing Spreadsheet failed. Do you have it open in another program?");
                    Console.WriteLine(ex.Message);
                    Console.WriteLine(ex.StackTrace);

                    progress?.Message("Spreadsheet.SpreadsheetLocked", "",
                                      "Bloom could not write to the spreadsheet because another program has it locked. Do you have it open in another program?",
                                      ProgressKind.Error);
                }
                catch (Exception ex)
                {
                    progress?.Message("Spreadsheet.ExportFailed", "",
                                      "Export failed: " + ex.Message,
                                      ProgressKind.Error);
                }
            }
        }
Пример #15
0
        /// <summary>
        /// This method checks 'path' for being in a Dropbox folder.  If so, it displays a warning message.
        /// </summary>
        public static void CheckForBeingInDropboxFolder(string path)
        {
            if (string.IsNullOrEmpty(path))
            {
                return;
            }

            try
            {
                if (_dropboxFolders == null)
                {
                    _dropboxFolders = new List <string>();
                    string dropboxInfoFile;
                    // On Windows, Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData) returns
                    // the path of the user's AppData/Roaming subdirectory.  I know the name looks like it should
                    // return the AppData directory itself, but it returns the Roaming subdirectory (although
                    // there seems to be some confusion about this on stackoverflow.)  MSDN has this to say to
                    // describe this enumeration value:
                    //    The directory that serves as a common repository for application-specific data for the
                    //    current roaming user.
                    // My tests on Windows 7/.Net 4.0 empirically show the return value looks something like
                    //    C:\Users\username\AppData\Roaming
                    // On Linux/Mono 3, the return value looks something like
                    //    /home/username/.config
                    // but Dropbox places its .dropbox folder in the user's home directory so we need to strip
                    // one directory level from that return value.
                    var baseFolder = Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData);
                    if (SIL.PlatformUtilities.Platform.IsWindows)
                    {
                        dropboxInfoFile = Path.Combine(baseFolder, @"Dropbox\info.json");
                    }
                    else
                    {
                        dropboxInfoFile = Path.Combine(Path.GetDirectoryName(baseFolder), @".dropbox/info.json");
                    }

                    //on my windows 10 box, the file we want is in AppData\Local\Dropbox
                    if (!RobustFile.Exists(dropboxInfoFile))
                    {
                        baseFolder = Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData);
                        if (SIL.PlatformUtilities.Platform.IsWindows)
                        {
                            dropboxInfoFile = Path.Combine(baseFolder, @"Dropbox\info.json");
                        }
                        else
                        {
                            dropboxInfoFile = Path.Combine(Path.GetDirectoryName(baseFolder), @".dropbox/info.json");
                        }
                        if (!RobustFile.Exists(dropboxInfoFile))
                        {
                            return;                             // User appears to not have Dropbox installed
                        }
                    }

                    var info    = RobustFile.ReadAllText(dropboxInfoFile);
                    var matches = Regex.Matches(info, @"{""path"": ""([^""]+)"",");
                    foreach (Match match in matches)
                    {
                        var folder = match.Groups[1].Value;
                        if (SIL.PlatformUtilities.Platform.IsWindows)
                        {
                            folder = folder.Replace("\\\\", "\\");
                            folder = folder.ToLowerInvariant();
                        }
                        _dropboxFolders.Add(folder + Path.DirectorySeparatorChar);
                    }
                }

                if (_dropboxFolders.Count == 0)
                {
                    return;                     // User appears to not have Dropbox installed
                }
                if (SIL.PlatformUtilities.Platform.IsWindows)
                {
                    path = path.ToLowerInvariant();                     // We do a case-insensitive compare on Windows.
                }
                foreach (var folder in _dropboxFolders)
                {
                    if (path.StartsWith(folder))
                    {
                        var msg = L10NSharp.LocalizationManager.GetString("OpenCreateCloneControl.InDropboxMessage",
                                                                          "Bloom detected that this collection is located in your Dropbox folder. This can cause problems as Dropbox sometimes locks Bloom out of its own files. If you have problems, we recommend that you move your collection somewhere else or disable Dropbox while using Bloom.",
                                                                          "");
                        SIL.Reporting.ErrorReport.NotifyUserOfProblem(msg);
                        return;
                    }
                }
            }
            catch (Exception e)
            {
                // To help fix BL-1246, we enable this:
                SIL.Reporting.ErrorReport.NotifyUserOfProblem(e,
                                                              "For some reason Bloom could not check your Dropbox settings. This should not cause you any problems, but please report it so we can fix it.");
                SIL.Reporting.Logger.WriteEvent("*** In CheckForBeingInDropboxFolder(), got " + e.Message + Environment.NewLine + e.StackTrace);
                Debug.Fail(e.Message);
            }
        }
Пример #16
0
        /// <summary>
        /// We've had a number of user reports that suggest that files were either missing or inaccessible.
        /// The idea here is to check a set of files and folders at the start of each launch, and generate
        /// a useful report if anything is missing.
        /// </summary>
        /// <returns>true if all is well. Application should exit if this returns false.</returns>
        public static bool CheckIntegrity()
        {
            var errors = new StringBuilder();
            var files  = new[] { "Bloom.chm", "PdfDroplet.exe", "BloomPdfMaker.exe" };

            string[] dirs;
            if (Platform.IsWindows)
            {
                dirs = new[] { "AndikaNewBasic", "localization", "xslts", "icons" }
            }
            ;
            else
            {
                dirs = new[] { "localization", "xslts", "icons" }
            };

            foreach (var fileName in files)
            {
                if (FileLocationUtilities.GetFileDistributedWithApplication(true, fileName) == null)
                {
                    //In a code directory, the FileLocator considers the solution the root, so it can't find files in output\debug
                    if (!RobustFile.Exists(Path.Combine(FileLocationUtilities.DirectoryOfTheApplicationExecutable, fileName)))
                    {
                        //maybe it's an exe in distfiles?
                        if (fileName.EndsWith(".exe") && RobustFile.Exists(Path.Combine(FileLocationUtilities.DirectoryOfApplicationOrSolution, "DistFiles")))
                        {
                            continue;
                        }
                        errors.AppendFormat("<p>Missing File: {0}</p>{1}", fileName, Environment.NewLine);
                    }
                }
            }
            foreach (var directory in dirs)
            {
                if (FileLocationUtilities.GetDirectoryDistributedWithApplication(true, directory) == null)
                {
                    errors.AppendFormat("<p>Missing Directory: {0}</p>{1}", directory, Environment.NewLine);
                }
            }
            if (errors.Length == 0)
            {
                return(true);
            }

            using (var dlg = new BloomIntegrityDialog())
            {
                const string nonHtmlMessage = "Bloom cannot find some of its own files, and cannot continue. After you submit this report, we will contact you and help you work this out. In the meantime, you can run the Bloom installer again.";
                var          messagePath    = BloomFileLocator.GetBestLocalizableFileDistributedWithApplication(false, "help", "IntegrityFailureAdvice-en.htm");
                string       message;
                if (messagePath == null)                // maybe we can't even get at this file we need for a good description of the problem
                {
                    message = nonHtmlMessage;
                }
                else
                {
                    var installFolder = Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData)
                                        .CombineForPath(Application.ProductName);
                    message = RobustFile.ReadAllText(messagePath).Replace("{{installFolder}}", installFolder); //new
                    message = message.Replace("{installFolder}", installFolder);                               //old
                }

                message = message + Environment.NewLine + Environment.NewLine + errors;
                dlg.htmlTextBox1.HtmlText = message;
                dlg.ShowDialog();
                Logger.WriteEvent("Bloom Integrity Check Failed: " + message);
                // We would like to do this:
                // ProblemReportApi.ShowProblemDialog(null, "fatal");
                // But that can't work because BloomServer isn't running yet.
            }

            return(false);            //Force termination of the current process.
        }
Пример #17
0
        // Every path should return false or send a response.
        // Otherwise we can get a timeout error as the browser waits for a response.
        //
        // NOTE: this method gets called on different threads!
        protected override bool ProcessRequest(IRequestInfo info)
        {
            var localPath = GetLocalPathWithoutQuery(info);

            //enhance: something feeds back these branding logos with a weird URL, that shouldn't be.
            if (localPath.IndexOf("api/branding") > 20)            // this 20 is just arbitrary... the point is, if it doesn't start with api/branding, it is bogus
            {
                return(false);
            }

            if (localPath.ToLower().StartsWith("api/"))
            {
                var endpoint = localPath.Substring(3).ToLowerInvariant().Trim(new char[] { '/' });
                foreach (var pair in _endpointRegistrations.Where(pair =>
                                                                  Regex.Match(endpoint,
                                                                              "^" + //must match the beginning
                                                                              pair.Key.ToLower()
                                                                              ).Success))
                {
                    // A single synchronization object won't do, because when processing a request to create a thumbnail,
                    // we have to load the HTML page the thumbnail is based on. If the page content somehow includes
                    // an api request (api/branding/image is one example), that request will deadlock if the
                    // api/pageTemplateThumbnail request already has the main lock.
                    // To the best of my knowledge, there's no shared data between the thumbnailing process and any
                    // other api requests, so it seems safe to have one lock that prevents working on multiple
                    // thumbnails at the same time, and one that prevents working on other api requests at the same time.
                    var syncOn = SyncObj;
                    if (localPath.ToLowerInvariant().StartsWith("api/pagetemplatethumbnail"))
                    {
                        syncOn = ThumbnailSyncObj;
                    }
                    lock (syncOn)
                    {
                        return(ApiRequest.Handle(pair.Value, info, CurrentCollectionSettings, _bookSelection.CurrentSelection));
                    }
                }
            }

            //OK, no more obvious simple API requests, dive into the rat's nest of other possibilities
            if (base.ProcessRequest(info))
            {
                return(true);
            }

            if (localPath.Contains("CURRENTPAGE"))            //useful when debugging. E.g. http://localhost:8091/bloom/CURRENTPAGE.htm will always show the page we're on.
            {
                localPath = _keyToCurrentPage;
            }

            string content;
            bool   gotSimulatedPage;

            lock (_urlToSimulatedPageContent)
            {
                gotSimulatedPage = _urlToSimulatedPageContent.TryGetValue(localPath, out content);
            }
            if (gotSimulatedPage)
            {
                info.ContentType = "text/html";
                info.WriteCompleteOutput(content ?? "");
                return(true);
            }

            if (localPath.StartsWith(OriginalImageMarker) && IsImageTypeThatCanBeDegraded(localPath))
            {
                // Path relative to simulated page file, and we want the file contents without modification.
                // (Note that the simulated page file's own URL starts with this, so it's important to check
                // for that BEFORE we do this check.)
                localPath = localPath.Substring(OriginalImageMarker.Length + 1);
                return(ProcessAnyFileContent(info, localPath));
            }

            if (localPath.StartsWith("error", StringComparison.InvariantCulture))
            {
                ProcessError(info);
                return(true);
            }
            else if (localPath.StartsWith("i18n/", StringComparison.InvariantCulture))
            {
                if (ProcessI18N(localPath, info))
                {
                    return(true);
                }
            }
            else if (localPath.StartsWith("directoryWatcher/", StringComparison.InvariantCulture))
            {
                return(ProcessDirectoryWatcher(info));
            }
            else if (localPath.StartsWith("localhost/", StringComparison.InvariantCulture))
            {
                var temp = LocalHostPathToFilePath(localPath);
                if (RobustFile.Exists(temp))
                {
                    localPath = temp;
                }
            }
            // this is used only by the readium viewer
            else if (localPath.StartsWith("node_modules/jquery/dist/jquery.js"))
            {
                localPath = BloomFileLocator.GetBrowserFile("jquery.min.js");
                // Avoid having "output/browser/" removed on Linux developer machines.
                // GetBrowserFile adds output to the path on developer machines, but not user installs.
                return(ProcessContent(info, localPath));
            }
            //Firefox debugger, looking for a source map, was prefixing in this unexpected
            //way.
            localPath = localPath.Replace("output/browser/", "");

            return(ProcessContent(info, localPath));
        }
Пример #18
0
        private void UploadBookInternal(IProgress progress, ApplicationContainer container, BookUploadParameters uploadParams,
                                        ref ProjectContext context)
        {
            progress.WriteMessageWithColor("Cyan", "Starting to upload " + uploadParams.Folder);
            // Make sure the files we want to upload are up to date.
            // Unfortunately this requires making a book object, which requires making a ProjectContext, which must be created with the
            // proper parent book collection if possible.
            var parent         = Path.GetDirectoryName(uploadParams.Folder);
            var collectionPath = Directory.GetFiles(parent, "*.bloomCollection").FirstOrDefault();

            if (collectionPath == null || !RobustFile.Exists(collectionPath))
            {
                progress.WriteError("Skipping book because no collection file was found in its parent directory.");
                return;
            }
            _collectionFoldersUploaded.Add(collectionPath);

            // Compute the book hash file and compare it to the existing one for bulk upload.
            var currentHashes = BookUpload.HashBookFolder(uploadParams.Folder);

            progress.WriteMessage(currentHashes);
            var pathToLocalHashInfoFromLastUpload = Path.Combine(uploadParams.Folder, HashInfoFromLastUpload);

            if (!uploadParams.ForceUpload)
            {
                var canSkip = false;
                if (Program.RunningUnitTests)
                {
                    canSkip = _singleBookUploader.CheckAgainstLocalHashfile(currentHashes, pathToLocalHashInfoFromLastUpload);
                }
                else
                {
                    canSkip = _singleBookUploader.CheckAgainstHashFileOnS3(currentHashes, uploadParams.Folder, progress);
                    RobustFile.WriteAllText(pathToLocalHashInfoFromLastUpload, currentHashes);                             // ensure local copy is saved
                }
                if (canSkip)
                {
                    // local copy of hashes file is identical or has been saved
                    progress.WriteMessageWithColor("green", $"Skipping '{Path.GetFileName(uploadParams.Folder)}' because it has not changed since being uploaded.");
                    ++_booksSkipped;
                    return;                             // skip this one; we already uploaded it earlier.
                }
            }
            // save local copy of hashes file: it will be uploaded with the other book files
            RobustFile.WriteAllText(pathToLocalHashInfoFromLastUpload, currentHashes);

            if (context == null || context.SettingsPath != collectionPath)
            {
                context?.Dispose();
                // optimise: creating a context seems to be quite expensive. Probably the only thing we need to change is
                // the collection. If we could update that in place...despite autofac being told it has lifetime scope...we would save some time.
                // Note however that it's not good enough to just store it in the project context. The one that is actually in
                // the autofac object (_scope in the ProjectContext) is used by autofac to create various objects, in particular, books.
                context = container.CreateProjectContext(collectionPath);
                Program.SetProjectContext(context);
            }
            var server   = context.BookServer;
            var bookInfo = new BookInfo(uploadParams.Folder, true);
            var book     = server.GetBookFromBookInfo(bookInfo);

            book.BringBookUpToDate(new NullProgress());
            bookInfo.Bookshelf = book.CollectionSettings.DefaultBookshelf;
            var bookshelfName = String.IsNullOrWhiteSpace(book.CollectionSettings.DefaultBookshelf) ? "(none)" : book.CollectionSettings.DefaultBookshelf;

            progress.WriteMessage($"Bookshelf is '{bookshelfName}'");

            // Assemble the various arguments needed to make the objects normally involved in an upload.
            // We leave some constructor arguments not actually needed for this purpose null.
            var bookSelection = new BookSelection();

            bookSelection.SelectBook(book);
            var currentEditableCollectionSelection = new CurrentEditableCollectionSelection();

            var collection = new BookCollection(collectionPath, BookCollection.CollectionType.SourceCollection, bookSelection);

            currentEditableCollectionSelection.SelectCollection(collection);

            var publishModel = new PublishModel(bookSelection, new PdfMaker(), currentEditableCollectionSelection, context.Settings, server, _thumbnailer);

            publishModel.PageLayout = book.GetLayout();
            var    view           = new PublishView(publishModel, new SelectedTabChangedEvent(), new LocalizationChangedEvent(), _singleBookUploader, null, null, null, null);
            var    blPublishModel = new BloomLibraryPublishModel(_singleBookUploader, book, publishModel);
            string dummy;

            // Normally we let the user choose which languages to upload. Here, just the ones that have complete information.
            var langDict          = book.AllPublishableLanguages();
            var languagesToUpload = langDict.Keys.Where(l => langDict[l]).ToList();

            if (!string.IsNullOrEmpty(book.CollectionSettings.SignLanguageIso639Code) && BookUpload.GetVideoFilesToInclude(book).Any())
            {
                languagesToUpload.Insert(0, book.CollectionSettings.SignLanguageIso639Code);
            }
            if (blPublishModel.MetadataIsReadyToPublish && (languagesToUpload.Any() || blPublishModel.OkToUploadWithNoLanguages))
            {
                if (blPublishModel.BookIsAlreadyOnServer)
                {
                    var msg = $"Overwriting the copy of {uploadParams.Folder} on the server...";
                    progress.WriteWarning(msg);
                }
                using (var tempFolder = new TemporaryFolder(Path.Combine("BloomUpload", Path.GetFileName(book.FolderPath))))
                {
                    BookUpload.PrepareBookForUpload(ref book, server, tempFolder.FolderPath, progress);
                    uploadParams.LanguagesToUpload = languagesToUpload.ToArray();
                    _singleBookUploader.FullUpload(book, progress, view, uploadParams, out dummy);
                }

                progress.WriteMessageWithColor("Green", "{0} has been uploaded", uploadParams.Folder);
                if (blPublishModel.BookIsAlreadyOnServer)
                {
                    ++_booksUpdated;
                }
                else
                {
                    ++_newBooksUploaded;
                }
            }
            else
            {
                // report to the user why we are not uploading their book
                var reason = blPublishModel.GetReasonForNotUploadingBook();
                progress.WriteError("{0} was not uploaded.  {1}", uploadParams.Folder, reason);
                ++_booksWithErrors;
            }
        }
Пример #19
0
        private bool TryGhostcriptPrint()
        {
            string systemSpecificArgs = String.Empty;

#if __MonoCS__
            // Ghostscript is built into the CUPS printer service, which is the standard
            // setup in Ubuntu.  It handles PDF automatically.  gtklp is a graphical
            // front end to the printer service that allows the user to specify the
            // printer, paper size, and other parameters that may need to be tweaked.
            var exePath = "/usr/bin/gtklp";
            systemSpecificArgs = "";
#else
            var gsKey = Registry.LocalMachine.OpenSubKey(@"Software\GPL Ghostscript");
            if (gsKey == null)
            {
                gsKey = Registry.LocalMachine.OpenSubKey(@"Software\AGPL Ghostscript");
            }
            // Just possibly the paid version is present?
            if (gsKey == null)
            {
                var hklm64 = RegistryKey.OpenBaseKey(RegistryHive.LocalMachine, RegistryView.Registry64);
                // maybe the 64-bit version is installed?
                gsKey = hklm64.OpenSubKey(@"Software\GPL Ghostscript");
                if (gsKey == null)
                {
                    gsKey = hklm64.OpenSubKey(@"Software\AGPL Ghostscript");
                }
            }
            if (gsKey == null)
            {
                return(false);                // not able to print this way, GhostScript not present
            }
            string exePath = null;
            foreach (var version in gsKey.GetSubKeyNames())
            {
                var gsVersKey = gsKey.OpenSubKey(version);
                var dllPath   = gsVersKey.GetValue("GS_DLL") as String;
                if (dllPath == null)
                {
                    continue;
                }
                if (!RobustFile.Exists(dllPath))
                {
                    continue;                     // some junk there??
                }
                exePath = Path.Combine(Path.GetDirectoryName(dllPath), "gswin32c.exe");
                if (RobustFile.Exists(exePath))
                {
                    break;
                }
                exePath = Path.Combine(Path.GetDirectoryName(dllPath), "gswin64c.exe");
                if (RobustFile.Exists(exePath))
                {
                    break;
                }
                // some old install in a bad state? Try another subkey
            }
            // -sDEVICE#mswinpr2 makes it display a print dialog so the user can choose printer.
            // -dBATCH -dNOPAUSE -dQUIET make it go ahead without waiting for user input on each page or after last
            // -dQUIET was an attempt to prevent it display per-page messages. Didn't work. Not sure it does any good.
            // -dNORANGEPAGESIZE makes it automatically select the right page orientation.
            systemSpecificArgs = "-sDEVICE#mswinpr2 -dBATCH -dNOPAUSE -dQUIET -dNORANGEPAGESIZE ";
#endif
            if (exePath == null || !RobustFile.Exists(exePath))
            {
                return(false);                // Can't use ghostscript approach
            }
            var proc = new Process
            {
                StartInfo =
                {
                    FileName        = exePath,
                    Arguments       = systemSpecificArgs + "\"" + _pdfPath + "\"",
                    UseShellExecute = false,                  // enables CreateNoWindow
                    CreateNoWindow  = true                    // don't need a DOS box (does not suppress print dialog)
                }
            };
#if __MonoCS__
            proc.EnableRaisingEvents = true;
            proc.Exited += PrintProcessExited;
#endif
            proc.Start();
            return(true);            // we at least think we printed it (unless the user cancels...anyway, don't try again some other way).
        }
Пример #20
0
        public void SetDisplayMode(PublishModel.DisplayModes displayMode)
        {
            // This is only supposed to be active in one mode of PublishView.
            Browser.SuppressJavaScriptErrors = false;
            Browser.ClearCache();             // try to free memory when switching
            // Abort any work we're doing to prepare a preview (at least stop it interfering with other navigation).
            PublishHelper.Cancel();

            if (displayMode != PublishModel.DisplayModes.Upload && _uploadControl != null)
            {
                Controls.Remove(_uploadControl);
                _uploadControl = null;
            }
            if ((displayMode != PublishModel.DisplayModes.Android || displayMode != PublishModel.DisplayModes.EPUB) &&
                _htmlControl != null && Controls.Contains(_htmlControl))
            {
                Controls.Remove(_htmlControl);

                // disposal of the browser is good but it hides a multitude of sins that we'd rather catch and fix during development. E.g. BL-4901
                if (!ApplicationUpdateSupport.IsDevOrAlpha)
                {
                    _htmlControl.Dispose();
                    _htmlControl = null;
                }
            }

            switch (displayMode)
            {
            case PublishModel.DisplayModes.WaitForUserToChooseSomething:
                _printButton.Enabled = _saveButton.Enabled = false;
                Cursor = Cursors.Default;
                _workingIndicator.Visible = false;
                _pdfViewer.Visible        = false;
                break;

            case PublishModel.DisplayModes.Working:
                _printButton.Enabled     = _saveButton.Enabled = false;
                _workingIndicator.Cursor = Cursors.WaitCursor;
                Cursor = Cursors.WaitCursor;
                _workingIndicator.Visible = true;
                _pdfViewer.Visible        = false;
                break;

            case PublishModel.DisplayModes.ShowPdf:
                Logger.WriteEvent("Entering Publish PDF Screen");
                if (RobustFile.Exists(_model.PdfFilePath))
                {
                    _pdfViewer.Visible        = true;
                    _workingIndicator.Visible = false;
                    Cursor = Cursors.Default;
                    _saveButton.Enabled  = true;
                    _printButton.Enabled = _pdfViewer.ShowPdf(_model.PdfFilePath);
                }
                break;

            case PublishModel.DisplayModes.Printing:
                _simpleAllPagesRadio.Enabled = false;
                _bookletCoverRadio.Enabled   = false;
                _bookletBodyRadio.Enabled    = false;
                _printButton.Enabled         = _saveButton.Enabled = false;
                _workingIndicator.Cursor     = Cursors.WaitCursor;
                Cursor = Cursors.WaitCursor;
                _workingIndicator.Visible = true;
                _pdfViewer.Visible        = true;
                break;

            case PublishModel.DisplayModes.ResumeAfterPrint:
                _simpleAllPagesRadio.Enabled = true;
                _pdfViewer.Visible           = true;
                _workingIndicator.Visible    = false;
                Cursor = Cursors.Default;
                _saveButton.Enabled  = true;
                _printButton.Enabled = true;
                _pdfViewer.Visible   = true;
                break;

            case PublishModel.DisplayModes.Upload:
            {
                Logger.WriteEvent("Entering Publish Upload Screen");
                _workingIndicator.Visible = false;                         // If we haven't finished creating the PDF, we will indicate that in the progress window.
                _saveButton.Enabled       = _printButton.Enabled = false;  // Can't print or save in this mode...wouldn't be obvious what would be saved.
                _pdfViewer.Visible        = false;
                Cursor = Cursors.Default;

                if (_uploadControl == null)
                {
                    SetupPublishControl();
                }

                break;
            }

            case PublishModel.DisplayModes.Android:
                _saveButton.Enabled = _printButton.Enabled = false;                         // Can't print or save in this mode...wouldn't be obvious what would be saved.
                BloomReaderFileMaker.ControlForInvoke = ParentForm;                         // something created on UI thread that won't go away
                ShowHtmlPanel(BloomFileLocator.GetBrowserFile(false, "publish", "ReaderPublish", "loader.html"));
                break;

            case PublishModel.DisplayModes.EPUB:
                _saveButton.Enabled = _printButton.Enabled = false;                         // Can't print or save in this mode...wouldn't be obvious what would be saved.
                // We rather mangled the Readium code in the process of cutting away its own navigation
                // and other controls. It produces all kinds of JavaScript errors, but it seems to do
                // what we want in our preview. So just suppress the toasts for all of them. This is unfortunate because
                // we'll lose them for all the other JS code in this pane. But I don't have a better solution.
                // We still get them in the output window, in case we really want to look for one.
                Browser.SuppressJavaScriptErrors = true;
                PublishEpubApi.ControlForInvoke  = ParentForm;                        // something created on UI thread that won't go away
                ShowHtmlPanel(BloomFileLocator.GetBrowserFile(false, "publish", "ePUBPublish", "loader.html"));
                break;
            }
            UpdateSaveButton();
        }
        /// ------------------------------------------------------------------------------------
        public void Load()
        {
            try
            {
                // Previously was SIL.IO.RobustIO.LoadXElement(SettingsFilePath). However, we had problems with this
                // using some non-roman collection names...specifically, one involving the Northern Pashti
                // localization of 'books' (┌й╪к╪з╪и┘И┘Ж┘З)...see BL-5416. It seems that somewhere in the
                // implementation of Linq.XElement.Load() the path is converted to a URL and then back
                // to a path and something changes in that process so that a valid path passed to Load()
                // raises an invalid path exception. Reading the file directly and then parsing the string
                // works around this problem.
                var settingsContent = RobustFile.ReadAllText(SettingsFilePath, Encoding.UTF8);
                var nameMigrations  = new[]
                {
                    new[] { "LanguageName", "Language1Name" },
                    new[] { "IsShellLibrary", "IsSourceCollection" },
                    new[] { "National1Iso639Code", "Language2Iso639Code" },
                    new[] { "National2Iso639Code", "Language3Iso639Code" },
                    new[] { "IsShellMakingProject", "IsSourceCollection" },
                    new[] { "Local Community", "Local-Community" }                   // migrate for 4.4
                };

                foreach (var fromTo in nameMigrations)
                {
                    settingsContent = settingsContent.Replace(fromTo[0], fromTo[1]);
                }

                var xml = XElement.Parse(settingsContent);

                Language1.ReadFromXml(xml, true, "en");
                Language2.ReadFromXml(xml, true, "self");
                Language3.ReadFromXml(xml, true, Language2.Iso639Code);

                SignLanguageIso639Code = ReadString(xml, "SignLanguageIso639Code",                  /* old name */
                                                    ReadString(xml, "SignLanguageIso639Code", ""));
                XMatterPackName = ReadString(xml, "XMatterPack", "Factory");

                var style = ReadString(xml, "PageNumberStyle", "Decimal");

                //for historical (and maybe future?) reasons, we collect the page number style as one of the
                //CSS counter number styles
                PageNumberStyle           = CssNumberStylesToCultureOrDigits.Keys.Contains(style) ? style : "Decimal";
                OneTimeCheckVersionNumber = ReadInteger(xml, "OneTimeCheckVersionNumber", 0);
                BrandingProjectKey        = ReadString(xml, "BrandingProjectName", "Default");
                SubscriptionCode          = ReadString(xml, "SubscriptionCode", null);

                if (BrandingProjectKey != "Default" && BrandingProjectKey != "Local-Community" && !Program.RunningHarvesterMode)
                {
                    // Validate branding, so things can't be circumvented by just typing something into settings
                    var expirationDate = CollectionSettingsApi.GetExpirationDate(SubscriptionCode);
                    if (expirationDate < DateTime.Now || BrandingProject.GetProjectChoices().All(bp => bp.Key != BrandingProjectKey))
                    {
                        InvalidBranding    = BrandingProjectKey;
                        BrandingProjectKey = "Default";                         // keep the code, but don't use it as active branding.
                    }
                }
                SignLanguageName   = ReadString(xml, "SignLanguageName", GetSignLanguageName_NoCache());
                Country            = ReadString(xml, "Country", "");
                Province           = ReadString(xml, "Province", "");
                District           = ReadString(xml, "District", "");
                AllowNewBooks      = ReadBoolean(xml, "AllowNewBooks", true);
                IsSourceCollection = ReadBoolean(xml, "IsSourceCollection", false);

                string audioRecordingModeStr = ReadString(xml, "AudioRecordingMode", "Unknown");
                TalkingBookApi.AudioRecordingMode parsedAudioRecordingMode;
                if (!Enum.TryParse(audioRecordingModeStr, out parsedAudioRecordingMode))
                {
                    parsedAudioRecordingMode = TalkingBookApi.AudioRecordingMode.Unknown;
                }
                AudioRecordingMode = parsedAudioRecordingMode;
                AudioRecordingTrimEndMilliseconds = ReadInteger(xml, "AudioRecordingTrimEndMilliseconds",
                                                                kDefaultAudioRecordingTrimEndMilliseconds);
            }
            catch (Exception e)
            {
                string settingsContents = "";
                try
                {
                    settingsContents = RobustFile.ReadAllText(SettingsFilePath);
                }
                catch (Exception error)
                {
                    settingsContents = error.Message;
                }
                Logger.WriteEvent("Contents of " + SettingsFilePath + ": /r/n" + settingsContents);
                SIL.Reporting.ErrorReport.NotifyUserOfProblem(e, "There was an error reading the file {0}.  Please report this error to the developers. To get access to your books, you should make a new collection, then copy your book folders from this broken collection into the new one, then run Bloom again.", SettingsFilePath);
                throw;
            }

            try
            {
                string oldcustomCollectionStylesPath = FolderPath.CombineForPath("collection.css");
                if (RobustFile.Exists(oldcustomCollectionStylesPath))
                {
                    string newcustomCollectionStylesPath = FolderPath.CombineForPath("customCollectionStyles.css");

                    RobustFile.Move(oldcustomCollectionStylesPath, newcustomCollectionStylesPath);
                }
            }
            catch (Exception)
            {
                //ah well, we tried, no big deal, only a couple of beta testers used this old name
            }

            // Check if we need to do a one time check (perhaps migrate to a new Settings value)
            if (OneTimeCheckVersionNumber < kCurrentOneTimeCheckVersionNumber)
            {
                DoOneTimeCheck();
            }

            SetAnalyticsProperties();
        }
Пример #22
0
        private void OnPrint_Click(object sender, EventArgs e)
        {
            var printSettingsPreviewFolder = FileLocationUtilities.GetDirectoryDistributedWithApplication("printer settings images");
            var printSettingsSamplePrefix  = Path.Combine(printSettingsPreviewFolder,
                                                          _model.PageLayout.SizeAndOrientation + "-" + (isBooklet() ? "Booklet-" : ""));
            string printSettingsSampleName = null;

            if (SIL.PlatformUtilities.Platform.IsLinux)
            {
                printSettingsSampleName = printSettingsSamplePrefix + "Linux-" + LocalizationManager.UILanguageId + ".png";
                if (!RobustFile.Exists(printSettingsSampleName))
                {
                    printSettingsSampleName = printSettingsSamplePrefix + "Linux-en.png";
                }
            }
            if (printSettingsSampleName == null || !RobustFile.Exists(printSettingsSampleName))
            {
                printSettingsSampleName = printSettingsSamplePrefix + LocalizationManager.UILanguageId + ".png";
            }
            if (!RobustFile.Exists(printSettingsSampleName))
            {
                printSettingsSampleName = printSettingsSamplePrefix + "en" + ".png";
            }
            if (RobustFile.Exists(printSettingsSampleName))
            {
                // We display the _previewBox to show sample print settings. We need to get rid of it when the
                // print dialog goes away. For Windows, the only way I've found to know when that happens is
                // that the main Bloom form gets activated again.  For Linux, waiting for process spawned off
                // to print the pdf file to finish seems to be the only way to know it's safe to hide the
                // sample print settings.  (On Linux/Mono, the form activates almost as soon as the print
                // dialog appears.)
#if __MonoCS__
                _pdfViewer.PrintFinished += FormActivatedAfterPrintDialog;
#else
                var form = FindForm();
                form.Activated += FormActivatedAfterPrintDialog;
#endif
                _previewBox.Image    = Image.FromFile(printSettingsSampleName);
                _previewBox.Bounds   = GetPreviewBounds();
                _previewBox.SizeMode = PictureBoxSizeMode.Zoom;
                _previewBox.BringToFront();                 // prevents BL-6001
                _previewBox.Show();
                if (!Settings.Default.DontShowPrintNotification)
                {
                    using (var dlg = new SamplePrintNotification())
                    {
                        dlg.StartPosition = FormStartPosition.CenterParent;
#if __MonoCS__
                        _pdfViewer.PrintFinished -= FormActivatedAfterPrintDialog;
                        dlg.ShowDialog(this);
                        _pdfViewer.PrintFinished += FormActivatedAfterPrintDialog;
#else
                        form.Activated -= FormActivatedAfterPrintDialog;                         // not wanted when we close the dialog.
                        dlg.ShowDialog(this);
                        form.Activated += FormActivatedAfterPrintDialog;
#endif
                        if (dlg.StopShowing)
                        {
                            Settings.Default.DontShowPrintNotification = true;
                            Settings.Default.Save();
                        }
                    }
                }
            }
            _pdfViewer.Print();
            Logger.WriteEvent("Calling Print on PDF Viewer");
            _model.ReportAnalytics("Print PDF");
        }
Пример #23
0
        /// ------------------------------------------------------------------------------------
        public void Load()
        {
            try
            {
                // Previously was SIL.IO.RobustIO.LoadXElement(SettingsFilePath). However, we had problems with this
                // using some non-roman collection names...specifically, one involving the Northern Pashto
                // localization of 'books' (┌й╪к╪з╪и┘И┘Ж┘З)...see BL-5416. It seems that somewhere in the
                // implementation of Linq.XElement.Load() the path is converted to a URL and then back
                // to a path and something changes in that process so that a valid path passed to Load()
                // raises an invalid path exception. Reading the file directly and then parsing the string
                // works around this problem.
                var settingsContent = RobustFile.ReadAllText(SettingsFilePath, Encoding.UTF8);
                var nameMigrations  = new[]
                {
                    new[] { "LanguageName", "Language1Name" },
                    new[] { "IsShellLibrary", "IsSourceCollection" },
                    new[] { "National1Iso639Code", "Language2Iso639Code" },
                    new[] { "National2Iso639Code", "Language3Iso639Code" },
                    new[] { "IsShellMakingProject", "IsSourceCollection" },
                    new[] { "Local Community", "Local-Community" }                   // migrate for 4.4
                };

                foreach (var fromTo in nameMigrations)
                {
                    settingsContent = settingsContent.Replace(fromTo[0], fromTo[1]);
                }

                var xml = XElement.Parse(settingsContent);
                // The default if we don't find one is the arbitrary ID generated when we initialized
                // the variable (at its declaration).
                CollectionId = ReadString(xml, "CollectionId", CollectionId);

                Language1.ReadFromXml(xml, true, "en");
                Language2.ReadFromXml(xml, true, "self");
                Language3.ReadFromXml(xml, true, Language2.Iso639Code);

                SignLanguageIso639Code = ReadString(xml, "SignLanguageIso639Code",                  /* old name */
                                                    ReadString(xml, "SignLanguageIso639Code", ""));
                XMatterPackName = ReadString(xml, "XMatterPack", "Factory");

                var style = ReadString(xml, "PageNumberStyle", "Decimal");

                //for historical (and maybe future?) reasons, we collect the page number style as one of the
                //CSS counter number styles
                PageNumberStyle           = CssNumberStylesToCultureOrDigits.Keys.Contains(style) ? style : "Decimal";
                OneTimeCheckVersionNumber = ReadInteger(xml, "OneTimeCheckVersionNumber", 0);
                BrandingProjectKey        = ReadString(xml, "BrandingProjectName", "Default");
                SubscriptionCode          = ReadString(xml, "SubscriptionCode", null);

                if (BrandingProjectKey != "Default" && BrandingProjectKey != "Local-Community" && !Program.RunningHarvesterMode)
                {
                    // Validate branding, so things can't be circumvented by just typing something random into settings
                    var expirationDate = CollectionSettingsApi.GetExpirationDate(SubscriptionCode);
                    if (expirationDate < DateTime.Now)                      // no longer require branding files to exist yet
                    {
                        InvalidBranding    = BrandingProjectKey;
                        BrandingProjectKey = "Default";                         // keep the code, but don't use it as active branding.
                    }
                }
                SignLanguageName   = ReadString(xml, "SignLanguageName", GetSignLanguageName_NoCache());
                Country            = ReadString(xml, "Country", "");
                Province           = ReadString(xml, "Province", "");
                District           = ReadString(xml, "District", "");
                AllowNewBooks      = ReadBoolean(xml, "AllowNewBooks", true);
                IsSourceCollection = ReadBoolean(xml, "IsSourceCollection", false);

                string audioRecordingModeStr = ReadString(xml, "AudioRecordingMode", "Unknown");
                TalkingBookApi.AudioRecordingMode parsedAudioRecordingMode;
                if (!Enum.TryParse(audioRecordingModeStr, out parsedAudioRecordingMode))
                {
                    parsedAudioRecordingMode = TalkingBookApi.AudioRecordingMode.Unknown;
                }
                AudioRecordingMode = parsedAudioRecordingMode;
                AudioRecordingTrimEndMilliseconds = ReadInteger(xml, "AudioRecordingTrimEndMilliseconds",
                                                                kDefaultAudioRecordingTrimEndMilliseconds);
                Administrators = ReadString(xml, "Administrators", "")
                                 .Split(new[] { "," }, StringSplitOptions.RemoveEmptyEntries);
                var defaultTags         = ReadString(xml, "DefaultBookTags", "").Split(',');
                var defaultBookshelfTag = defaultTags.Where(t => t.StartsWith("bookshelf:")).FirstOrDefault();
                DefaultBookshelf = defaultBookshelfTag == null
                                        ? ""
                                        : defaultBookshelfTag.Substring("bookshelf:".Length);

                var bulkPublishSettingsFromXml = BulkBloomPubPublishSettings.LoadFromXElement(xml);
                if (bulkPublishSettingsFromXml != null)
                {
                    BulkPublishBloomPubSettings = bulkPublishSettingsFromXml;
                }
            }
            catch (Exception)
            {
                string settingsContents;
                try
                {
                    settingsContents = RobustFile.ReadAllText(SettingsFilePath);
                }
                catch (Exception error)
                {
                    settingsContents = error.Message;
                }
                Logger.WriteEvent("Contents of " + SettingsFilePath + ": /r/n" + settingsContents);

                // We used to notify the user of a problem here.
                // But now we decided it is better to catch at a higher level, at OpenProjectWindow(), else we have two different
                // error UI dialogs for the same problem. See BL-9916.
                throw;
            }

            try
            {
                string oldcustomCollectionStylesPath = FolderPath.CombineForPath("collection.css");
                if (RobustFile.Exists(oldcustomCollectionStylesPath))
                {
                    string newcustomCollectionStylesPath = FolderPath.CombineForPath("customCollectionStyles.css");

                    RobustFile.Move(oldcustomCollectionStylesPath, newcustomCollectionStylesPath);
                }
            }
            catch (Exception)
            {
                //ah well, we tried, no big deal, only a couple of beta testers used this old name
            }

            // Check if we need to do a one time check (perhaps migrate to a new Settings value)
            if (OneTimeCheckVersionNumber < kCurrentOneTimeCheckVersionNumber)
            {
                DoOneTimeCheck();
            }

            SetAnalyticsProperties();
        }
Пример #24
0
        public string GetPathToResizedImage(string originalPath, bool getThumbnail = false)
        {
            //don't mess with Bloom UI images
            if (new[] { "/img/", "placeHolder", "Button" }.Any(s => originalPath.Contains(s)))
            {
                return(originalPath);
            }

            var cacheFileName = originalPath;

            if (getThumbnail)
            {
                cacheFileName = "thumbnail_" + cacheFileName;
            }

            // check if this image is in the do-not-process list
            bool test;

            if (_imageFilesToReturnUnprocessed.TryGetValue(cacheFileName, out test))
            {
                return(originalPath);
            }

            lock (this)
            {
                // if there is a cached version, return it
                string pathToProcessedVersion;
                if (_originalPathToProcessedVersionPath.TryGetValue(cacheFileName, out pathToProcessedVersion))
                {
                    if (RobustFile.Exists(pathToProcessedVersion) &&
                        new FileInfo(originalPath).LastWriteTimeUtc <= new FileInfo(pathToProcessedVersion).LastWriteTimeUtc)
                    {
                        return(pathToProcessedVersion);
                    }

                    // the file has changed, remove from cache
                    string valueRemoved;
                    _originalPathToProcessedVersionPath.TryRemove(cacheFileName, out valueRemoved);
                }

                // there is not a cached version, try to make one
                var pathToProcessedImage = Path.Combine(_cacheFolder, Path.GetRandomFileName() + Path.GetExtension(originalPath));

                if (!Directory.Exists(Path.GetDirectoryName(pathToProcessedImage)))
                {
                    Directory.CreateDirectory(Path.GetDirectoryName(pathToProcessedImage));
                }

                // BL-1112: images not loading in page thumbnails
                bool success;
                if (getThumbnail)
                {
                    // The HTML div that contains the thumbnails is 80 pixels wide, so make the thumbnails 80 pixels wide
                    success = GenerateThumbnail(originalPath, pathToProcessedImage, 80);
                }
                else
                {
                    success = MakePngBackgroundTransparent(originalPath, pathToProcessedImage);
                }

                if (!success)
                {
                    // add this image to the do-not-process list so we don't waste time doing this again
                    _imageFilesToReturnUnprocessed.TryAdd(cacheFileName, true);
                    return(originalPath);
                }

                _originalPathToProcessedVersionPath.TryAdd(cacheFileName, pathToProcessedImage);                 //remember it so we can reuse if they show it again, and later delete

                return(pathToProcessedImage);
            }
        }
Пример #25
0
        public void ReplyWithFileContent(string path, string originalPath = null)
        {
            //Deal with BL-3153, where the file was still open in another thread
            FileStream fs;

            if (!RobustFile.Exists(path))
            {
                //for audio, at least, this is not really an error. We constantly are asking if audio already exists for the current segment
                //enhance: maybe audio should go through a different path, e.g. "/bloom/audio/somefile.wav"
                //then this path COULD write and error
                //Logger.WriteError("Server could not find" + path);
                _actualContext.Response.StatusCode = 404;
                return;
            }

            try
            {
                fs = RobustFile.OpenRead(path);
            }
            catch (Exception error)
            {
                Logger.WriteError("Server could not read " + path, error);
                _actualContext.Response.StatusCode = 500;
                return;
            }

            using (fs)
            {
                _actualContext.Response.ContentLength64 = fs.Length;
                _actualContext.Response.AppendHeader("PathOnDisk", HttpUtility.UrlEncode(path));
                if (ShouldCache(path, originalPath))
                {
                    _actualContext.Response.AppendHeader("Cache-Control",
                                                         "max-age=600000"); // about a week...if someone spends longer editing one book, well, files will get loaded one more time...
                }

                // A HEAD request (rather than a GET or POST request) is a request for just headers, and nothing can be written
                // to the OutputStream. It is normally used to check if the contents of the file have changed without taking the
                // time and bandwidth needed to download the full contents of the file. The 2 pieces of information being returned
                // are the Content-Length and Last-Modified headers. The requestor can use this information to determine if the
                // contents of the file have changed, and if they have changed the requestor can then decide if the file needs to
                // be reloaded. It is useful when debugging with tools which automatically reload the page when something changes.
                if (_actualContext.Request.HttpMethod == "HEAD")
                {
                    var lastModified = RobustFile.GetLastWriteTimeUtc(path).ToString("R");

                    // Originally we were returning the Last-Modified header with every response, but we discovered that this was
                    // causing Geckofx to cache the contents of the files. This made debugging difficult because, even if the file
                    // changed, Geckofx would use the cached file rather than requesting the updated file from the localhost.
                    _actualContext.Response.AppendHeader("Last-Modified", lastModified);
                }
                else if (fs.Length < 2 * 1024 * 1024)
                {
                    // This buffer size was picked to be big enough for any of the standard files we load in every page.
                    // Profiling indicates it is MUCH faster to use Response.Close() rather than writing to the output stream,
                    // though the gain may be illusory since the final 'false' argument allows our code to proceed without waiting
                    // for the complete data transfer. At a minimum, it makes this thread available to work on another
                    // request sooner.
                    var buffer = new byte[fs.Length];
                    fs.Read(buffer, 0, (int)fs.Length);
                    _actualContext.Response.Close(buffer, false);
                }
                else
                {
                    // For really big (typically image) files, use the old buffered approach
                    try
                    {
                        var buffer = new byte[1024 * 512];                         //512KB
                        int read;
                        while ((read = fs.Read(buffer, 0, buffer.Length)) > 0)
                        {
                            _actualContext.Response.OutputStream.Write(buffer, 0, read);
                        }
                        _actualContext.Response.OutputStream.Close();
                    }
                    catch (HttpListenerException e)
                    {
                        // If the page is gone and no longer able to accept the data, just log it.
                        ReportHttpListenerProblem(e);
                    }
                }
            }

            HaveOutput = true;
        }
Пример #26
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);
        }
Пример #27
0
        private bool BackupFileExists(string folderPath)
        {
            var bakFiles = Directory.GetFiles(folderPath, BookStorage.BackupFilename);

            return(bakFiles.Length == 1 && RobustFile.Exists(bakFiles[0]));
        }
Пример #28
0
        public void MakePdf(PdfMakingSpecs specs, Control owner, BackgroundWorker worker,
                            DoWorkEventArgs doWorkEventArgs)
        {
            _worker = worker;
#if !__MonoCS__
            // Mono doesn't current provide System.Printing.  Leave the 'if' here to emphasize the
            // system specific nature of the following check.
            if (Platform.IsWindows)
            {
                // Check whether we have a default printer set (or for that matter, any printers).
                // Gecko on Windows requires a default printer for any print operation, even one
                // to a file.  See https://jira.sil.org/browse/BL-1237.
                string errorMessage = null;
                System.Printing.LocalPrintServer printServer = null;
                try
                {
                    printServer = new System.Printing.LocalPrintServer();
                }
                catch (Exception)                 // System.Printing.PrintQueueException isn't in our System.Printing assembly, so... using Exception
                {
                    // http://issues.bloomlibrary.org/youtrack/issue/BL-4060
                    Logger.WriteEvent("reproduced BL-4060 when trying to create LocalPrinterServer");
                }
                if (printServer == null || !printServer.GetPrintQueues().Any())
                {
                    errorMessage = GetNoDefaultPrinterErrorMessage();
                }
                else
                {
                    System.Printing.PrintQueue defaultPrinter;
                    // BL-2535 it's possible get past the above printQueues.Any() but then get
                    // a System.Printing.PrintQueueException exception with "Access Denied" error here, if
                    // the default printer for some reason is no longer "allowed".
                    try
                    {
                        defaultPrinter = System.Printing.LocalPrintServer.GetDefaultPrintQueue();

                        if (defaultPrinter == null || String.IsNullOrEmpty(defaultPrinter.FullName))
                        {
                            errorMessage = GetNoDefaultPrinterErrorMessage();
                        }
                    }
                    catch (Exception error)                    // System.Printing.PrintQueueException isn't in our System.Printing assembly, so... using Exception
                    {
                        defaultPrinter = null;
                        errorMessage   = L10NSharp.LocalizationManager.GetString(@"PublishTab.PDF.Error.PrinterError",
                                                                                 "Bloom requires access to a printer in order to make a PDF, even though you are not printing.  Windows gave this error when Bloom tried to access the default printer: {0}",
                                                                                 @"Error message displayed in a message dialog box");
                        errorMessage = string.Format(errorMessage, error.Message);
                    }
                }

                if (errorMessage != null)
                {
                    var exception = new ApplicationException(errorMessage);
                    // Note that if we're being run by a BackgroundWorker, it will catch the exception.
                    // If not, but the caller provides a DoWorkEventArgs, pass the exception through
                    // that object rather than throwing it.
                    if (worker != null || doWorkEventArgs == null)
                    {
                        throw exception;
                    }
                    doWorkEventArgs.Result = exception;
                    return;
                }
            }
#endif
            if (_worker != null)
            {
                _worker.ReportProgress(0, L10NSharp.LocalizationManager.GetString(@"PublishTab.PdfMaker.MakingFromHtml",
                                                                                  "Making PDF from HTML",
                                                                                  @"Message displayed in a progress report dialog box"));
            }

            var    runner = new CommandLineRunner();
            string exePath;
            var    bldr = new StringBuilder();
            // Codebase is reliable even when Resharper copies the EXE somewhere else for testing.
            var execDir       = BloomFileLocator.GetCodeBaseFolder();
            var fromDirectory = String.Empty;
            var filePath      = Path.Combine(execDir, "BloomPdfMaker.exe");
            if (!RobustFile.Exists(filePath))
            {
                var msg = LocalizationManager.GetString("InstallProblem.BloomPdfMaker",
                                                        "A component of Bloom, BloomPdfMaker.exe, seems to be missing. This prevents previews and printing. Antivirus software sometimes does this. You may need technical help to repair the Bloom installation and protect this file from being deleted again.");
                throw new FileNotFoundException(msg, "BloomPdfMaker.exe");                 // must be this class to trigger the right reporting mechanism.
            }
            if (Platform.IsMono)
            {
                exePath = Path.ChangeExtension(filePath, "sh");
            }
            else
            {
                exePath = filePath;
            }

            SetArguments(bldr, specs);
            var arguments = bldr.ToString();
            var progress  = new NullProgress();
            var res       = runner.Start(exePath, arguments, Encoding.UTF8, fromDirectory, 3600, progress,
                                         ProcessGeckofxReporting);
            if (res.DidTimeOut || !RobustFile.Exists(specs.OutputPdfPath))
            {
                Logger.WriteEvent(@"***ERROR PDF generation failed: res.StandardOutput = " + res.StandardOutput);

                var msg = L10NSharp.LocalizationManager.GetString(@"PublishTab.PDF.Error.Failed",
                                                                  "Bloom was not able to create the PDF file ({0}).{1}{1}Details: BloomPdfMaker (command line) did not produce the expected document.",
                                                                  @"Error message displayed in a message dialog box. {0} is the filename, {1} is a newline character.");

                // This message string is intentionally separate because it was added after the previous string had already been localized in most languages.
                // It's not useful to add if we're already in save memory mode.
                var msg2 = specs.SaveMemoryMode ? "" : L10NSharp.LocalizationManager.GetString(@"PublishTab.PDF.Error.TrySinglePage",
                                                                                               "The book's images might have exceeded the amount of RAM memory available. Please turn on the \"Use Less Memory\" option which is slower but uses less memory.",
                                                                                               @"Error message displayed in a message dialog box") + Environment.NewLine;

                var fullMsg = String.Format(msg, specs.OutputPdfPath, Environment.NewLine) + Environment.NewLine +
                              msg2 + res.StandardOutput;

                var except = new ApplicationException(fullMsg);
                // Note that if we're being run by a BackgroundWorker, it will catch the exception.
                // If not, but the caller provides a DoWorkEventArgs, pass the exception through
                // that object rather than throwing it.
                if (worker != null || doWorkEventArgs == null)
                {
                    throw except;
                }
                else
                {
                    doWorkEventArgs.Result = except;
                }
            }
        }
Пример #29
0
        private void HandleCheckForSegment(ApiRequest request)
        {
            var path = GetPathToSegment(request.RequiredParam("id"));

            request.ReplyWithText(RobustFile.Exists(path) ? "exists" : "not found");
        }
Пример #30
0
 // We only need to make an MP3 if we actually have a corresponding wav file. If not, it's just a hypothetical recording that
 // the user could have made but didn't.
 // Assuming we have a wav file and thus want a corresponding mp3, we need to make it if either it does not exist
 // or it is out of date (older than the wav file).
 // It's of course possible that although it is newer the two don't correspond. I don't know any way to reliably prevent that
 // except to regenerate them all on every publish event, but that is quite time-consuming.
 private static bool Mp3IsNeeded(string wavpath, string mp3path)
 {
     return(RobustFile.Exists(wavpath) &&
            (!RobustFile.Exists(mp3path) || (new FileInfo(wavpath).LastWriteTimeUtc) > new FileInfo(mp3path).LastWriteTimeUtc));
 }