public ReplyWithJson ( object objectToMakeJson ) : void | ||
objectToMakeJson | object | |
Résultat | void |
/// <summary> /// Get a json of the book's settings. /// </summary> private void HandleBookSettings(ApiRequest request) { switch (request.HttpMethod) { case HttpMethods.Get: dynamic settings = new ExpandoObject(); settings.isRecordedAsLockedDown = _bookSelection.CurrentSelection.RecordedAsLockedDown; settings.unlockShellBook = _bookSelection.CurrentSelection.TemporarilyUnlocked; settings.currentToolBoxTool = _bookSelection.CurrentSelection.BookInfo.CurrentTool; settings.isTemplateBook = GetIsBookATemplate(); request.ReplyWithJson((object)settings); break; case HttpMethods.Post: //note: since we only have this one value, it's not clear yet whether the panel involved here will be more of a //an "edit settings", or a "book settings", or a combination of them. settings = DynamicJson.Parse(request.RequiredPostJson()); _bookSelection.CurrentSelection.TemporarilyUnlocked = settings["unlockShellBook"]; _pageRefreshEvent.Raise(PageRefreshEvent.SaveBehavior.SaveBeforeRefresh); if(((DynamicJson)settings).IsDefined("isTemplateBook")) { UpdateBookTemplateMode(settings.isTemplateBook); } request.Succeeded(); break; default: throw new ArgumentOutOfRangeException(); } }
/// <summary> /// Get a json of the book's settings. /// </summary> private void HandleBookSettings(ApiRequest request) { switch (request.HttpMethod) { case HttpMethods.Get: dynamic settings = new ExpandoObject(); settings.isRecordedAsLockedDown = _bookSelection.CurrentSelection.RecordedAsLockedDown; settings.unlockShellBook = _bookSelection.CurrentSelection.TemporarilyUnlocked; settings.currentToolBoxTool = _bookSelection.CurrentSelection.BookInfo.CurrentTool; request.ReplyWithJson((object)settings); break; case HttpMethods.Post: //note: since we only have this one value, it's not clear yet whether the panel involved here will be more of a //an "edit settings", or a "book settings", or a combination of them. settings = DynamicJson.Parse(request.RequiredPostJson()); _bookSelection.CurrentSelection.TemporarilyUnlocked = settings["unlockShellBook"]; _pageRefreshEvent.Raise(PageRefreshEvent.SaveBehavior.SaveBeforeRefresh); request.Succeeded(); break; default: throw new ArgumentOutOfRangeException(); } }
/// <summary> /// Returns a json string for initializing the AddPage dialog. It gives paths to our current TemplateBook /// and specifies whether the dialog is to be used for adding pages or choosing a different layout. /// </summary> public void HandleTemplatesRequest(ApiRequest request) { dynamic addPageSettings = new ExpandoObject(); addPageSettings.defaultPageToSelect = _templateInsertionCommand.MostRecentInsertedTemplatePage == null ? "" : _templateInsertionCommand.MostRecentInsertedTemplatePage.Id; addPageSettings.orientation = _bookSelection.CurrentSelection.GetLayout().SizeAndOrientation.IsLandScape ? "landscape" : "portrait"; addPageSettings.groups = GetBookTemplatePaths(GetPathToCurrentTemplateHtml(), _sourceCollectionsList.GetSourceBookPaths()) .Select(bookTemplatePath => GetPageGroup(bookTemplatePath)); addPageSettings.currentLayout = _pageSelection.CurrentSelection.IdOfFirstAncestor; request.ReplyWithJson(JsonConvert.SerializeObject(addPageSettings)); }
public void HandleAutoUpdate(ApiRequest request) { if (request.HttpMethod == HttpMethods.Get) { var json = JsonConvert.SerializeObject(new { autoUpdate = Settings.Default.AutoUpdate, dialogShown = Settings.Default.AutoUpdateDialogShown }); request.ReplyWithJson(json); } else // post { var requestData = DynamicJson.Parse(request.RequiredPostJson()); Settings.Default.AutoUpdateDialogShown = (int)requestData.dialogShown; Settings.Default.AutoUpdate = (bool)requestData.autoUpdate; request.PostSucceeded(); } }
/// <summary> /// Get a json of the book's settings. /// </summary> private void HandleBookSettings(ApiRequest request) { switch (request.HttpMethod) { case HttpMethods.Get: dynamic settings = new ExpandoObject(); settings.isRecordedAsLockedDown = _bookSelection.CurrentSelection.RecordedAsLockedDown; settings.unlockShellBook = _bookSelection.CurrentSelection.TemporarilyUnlocked; settings.currentToolBoxTool = _bookSelection.CurrentSelection.BookInfo.CurrentTool; #if UserControlledTemplate settings.isTemplateBook = GetIsBookATemplate(); #endif request.ReplyWithJson((object)settings); break; case HttpMethods.Post: //note: since we only have this one value, it's not clear yet whether the panel involved here will be more of a //an "edit settings", or a "book settings", or a combination of them. settings = DynamicJson.Parse(request.RequiredPostJson()); _bookSelection.CurrentSelection.TemporarilyUnlocked = settings["unlockShellBook"]; // This first refresh saves any changes. _pageRefreshEvent.Raise(PageRefreshEvent.SaveBehavior.SaveBeforeRefresh); #if UserControlledTemplate UpdateBookTemplateMode(settings.isTemplateBook); // Now we need to update the active version of the page with possible new template settings // It's a bit wasteful to raise this twice...but we need to save any changes the user made to the page, // and we have no access to put the editable DOM into the right template/non-template state. _pageRefreshEvent.Raise(PageRefreshEvent.SaveBehavior.JustRedisplay); #endif request.Succeeded(); break; default: throw new ArgumentOutOfRangeException(); } }
/// <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.RequiredFileNameOrPath("image"); Guard.AgainstNull(_bookSelection.CurrentSelection, "CurrentBook"); var plainfilename = fileName.NotEncoded; // The fileName might be URL encoded. See https://silbloom.myjetbrains.com/youtrack/issue/BL-3901. var path = UrlPathString.GetFullyDecodedPath(_bookSelection.CurrentSelection.FolderPath, ref plainfilename); RequireThat.File(path).Exists(); var fileInfo = new FileInfo(path); dynamic result = new ExpandoObject(); result.name = plainfilename; 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.WriteEvent("Error in server imageInfo/: url was " + request.LocalPath()); Logger.WriteEvent("Error in server imageInfo/: exception is " + e.Message); request.Failed(e.Message); NonFatalProblem.Report(ModalIf.None, PassiveIf.Alpha, "Request Error", request.LocalPath(), e); } }
/// <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); if (!File.Exists(path)) { // 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. fileName = System.Web.HttpUtility.UrlDecode(fileName); 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 = File.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.WriteEvent("Error in server imageInfo/: url was " + request.LocalPath()); Logger.WriteEvent("Error in server imageInfo/: exception is " + e.Message); request.Failed(e.Message); } }
/// <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.RequiredFileNameOrPath("image"); Guard.AgainstNull(_bookSelection.CurrentSelection, "CurrentBook"); var plainfilename = fileName.NotEncoded; // The fileName might be URL encoded. See https://silbloom.myjetbrains.com/youtrack/issue/BL-3901. var path = UrlPathString.GetFullyDecodedPath(_bookSelection.CurrentSelection.FolderPath, ref plainfilename); RequireThat.File(path).Exists(); var fileInfo = new FileInfo(path); dynamic result = new ExpandoObject(); result.name = plainfilename; 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.WriteEvent("Error in server imageInfo/: url was " + request.LocalPath()); Logger.WriteEvent("Error in server imageInfo/: exception is " + e.Message); request.Failed(e.Message); NonFatalProblem.Report(ModalIf.None, PassiveIf.Alpha, "Request Error", request.LocalPath(), e); } }
public void HandleI18nRequest(ApiRequest request) { var lastSegment = request.LocalPath().Split(new char[] { '/' }).Last(); switch (lastSegment) { case "loadStrings": var d = new Dictionary <string, string>(); var post = request.GetPostDataWhenFormEncoded(); if (post != null) { foreach (string key in post.Keys) { try { if (d.ContainsKey(key)) { continue; } var translation = GetTranslationDefaultMayNotBeEnglish(key, post[key]); d.Add(key, translation); } catch (Exception error) { Debug.Fail("Debug Only:" + error.Message + Environment.NewLine + "A bug reported at this location is BL-923"); //Until BL-923 is fixed (hard... it's a race condition, it's better to swallow this for users } } } request.ReplyWithJson(JsonConvert.SerializeObject(d)); break; case "translate": var parameters = request.Parameters; string id = parameters["key"]; string englishText = parameters["englishText"]; string langId = parameters["langId"]; langId = langId.Replace("V", request.CurrentCollectionSettings.Language1Iso639Code); langId = langId.Replace("N1", request.CurrentCollectionSettings.Language2Iso639Code); langId = langId.Replace("N2", request.CurrentCollectionSettings.Language3Iso639Code); langId = langId.Replace("UI", LocalizationManager.UILanguageId); string localizedString; if (GetSomeTranslation(id, langId, out localizedString)) { // Ensure that we actually have a value for localized string. (This should already be true, but I'm paranoid.) if (localizedString == null) { localizedString = englishText; } request.ReplyWithJson(new { text = localizedString, success = true }); } else { var idFound = true; // Don't report missing strings if they are numbers // Enhance: We might get the Javascript to do locale specific numbers someday // The C# side doesn't currently have the smarts to do DigitSubstitution // See Remark at https://msdn.microsoft.com/en-us/library/system.globalization.numberformatinfo.digitsubstitution(v=vs.110).aspx if (IsInteger(id)) { englishText = id; } else { // Now that end users can create templates, it's annoying to report that their names, // page labels, and page descriptions don't have localizations. if (IsTemplateBookKey(id)) { englishText = englishText.Trim(); } else { // it's ok if we don't have a translation, but if the string isn't even in the list of things that need translating, // then we want to remind the developer to add it to the english xlf file. if (!LocalizationManager.GetIsStringAvailableForLangId(id, "en")) { ReportL10NMissingString(id, englishText, UrlPathString.CreateFromUrlEncodedString(parameters["comment"] ?? "").NotEncoded); idFound = false; } else { //ok, so we don't have it translated yet. Make sure it's at least listed in the things that can be translated. // And return the English string, which is what we would do the next time anyway. (BL-3374) LocalizationManager.GetDynamicString("Bloom", id, englishText); } } } request.ReplyWithJson(new { text = englishText, success = idFound }); } break; default: request.Failed(); break; } }
public void HandleRequest(ApiRequest request) { if (CurrentBook == null) { Debug.Fail("BL-836 reproduction?"); // ReSharper disable once HeuristicUnreachableCode request.Failed("CurrentBook is null"); return; } if (request.CurrentCollectionSettings == null) { Debug.Fail("BL-836 reproduction?"); // ReSharper disable once HeuristicUnreachableCode request.Failed("CurrentBook.CollectionSettings is null"); return; } var lastSegment = request.LocalPath().Split(new char[] { '/' }).Last(); switch (lastSegment) { case "test": request.Succeeded(); break; case "readerToolSettings": if (request.HttpMethod == HttpMethods.Get) { request.ReplyWithJson(GetReaderSettings(request.CurrentCollectionSettings)); } else { var path = DecodableReaderTool.GetReaderToolsSettingsFilePath(request.CurrentCollectionSettings); var content = request.RequiredPostJson(); RobustFile.WriteAllText(path, content, Encoding.UTF8); request.Succeeded(); } break; //note, this endpoint is confusing because it appears that ultimately we only use the word list out of this file (see "sampleTextsList"). //This ends up being written to a ReaderToolsWords-xyz.json (matching its use, if not it contents). case "synphonyLanguageData": //This is the "post". There is no direct "get", but the name of the file is given in the "sampleTextList" reply, below: SaveSynphonyLanguageData(request.RequiredPostJson()); request.Succeeded(); break; case "sampleTextsList": //note, as part of this reply, we send the path of the "ReaderToolsWords-xyz.json" which is *written* by the "synphonyLanguageData" endpoint above request.ReplyWithText(GetSampleTextsList(request.CurrentCollectionSettings.SettingsFilePath)); break; case "sampleFileContents": request.ReplyWithText(GetTextFileContents(request.RequiredParam("fileName"), WordFileType.SampleFile)); break; case "textOfContentPages": request.ReplyWithText(GetTextOfContentPagesAsJson()); break; case "makeLetterAndWordList": MakeLetterAndWordList(request.RequiredPostValue("settings"), request.RequiredPostValue("allWords")); request.Succeeded(); break; case "openTextsFolder": OpenTextsFolder(); request.Succeeded(); break; case "chooseAllowedWordsListFile": lock (request) { request.ReplyWithText(ShowSelectAllowedWordsFileDialog()); } break; case "allowedWordsList": switch (request.HttpMethod) { case HttpMethods.Delete: RecycleAllowedWordListFile(request.RequiredParam("fileName")); request.Succeeded(); break; case HttpMethods.Get: var fileName = request.RequiredParam("fileName"); request.ReplyWithText(RemoveEmptyAndDupes(GetTextFileContents(fileName, WordFileType.AllowedWordsFile))); break; default: request.Failed("Http verb not handled"); break; } break; default: request.Failed("Don't understand '" + lastSegment + "' in " + request.LocalPath()); break; } }
public void HandleRequest(ApiRequest request) { if (CurrentBook == null) { Debug.Fail("BL-836 reproduction?"); // ReSharper disable once HeuristicUnreachableCode request.Failed("CurrentBook is null"); return; } if (request.CurrentCollectionSettings == null) { Debug.Fail("BL-836 reproduction?"); // ReSharper disable once HeuristicUnreachableCode request.Failed("CurrentBook.CollectionSettings is null"); return; } var lastSegment = request.LocalPath().Split(new char[] { '/' }).Last(); switch (lastSegment) { case "test": request.PostSucceeded(); break; case "readerToolSettings": if (request.HttpMethod == HttpMethods.Get) { request.ReplyWithJson(GetReaderSettings(request.CurrentCollectionSettings)); } else { var path = DecodableReaderToolSettings.GetReaderToolsSettingsFilePath(request.CurrentCollectionSettings); var content = request.RequiredPostJson(); RobustFile.WriteAllText(path, content, Encoding.UTF8); request.PostSucceeded(); } break; //note, this endpoint is confusing because it appears that ultimately we only use the word list out of this file (see "sampleTextsList"). //This ends up being written to a ReaderToolsWords-xyz.json (matching its use, if not it contents). case "synphonyLanguageData": //This is the "post". There is no direct "get", but the name of the file is given in the "sampleTextList" reply, below. // We've had situations (BL-4313 and friends) where reading the posted data fails. This seems to be due to situations // where we have a very large block of data and are rapidly switching between books. But as far as I can tell, the only // case where it's at all important to capture the new language data is if the user has been changing settings and // in particular editing the word list. Timing out the save in that situation seems very unlikely to fail. // So, in the interests of preventing the crash when switching books fast, we will ignore failure to read all the // json, and just not update the file. We would in any case keep only the version of the data sent to us by // the last book which sends it, and that one is unlikely to get interrupted. string langdata; try { langdata = request.RequiredPostJson(); } catch (IOException e) { SIL.Reporting.Logger.WriteError("Saving synphonyLanguageData failed to get Json", e); break; } SaveSynphonyLanguageData(langdata); request.PostSucceeded(); break; case "sampleTextsList": //note, as part of this reply, we send the path of the "ReaderToolsWords-xyz.json" which is *written* by the "synphonyLanguageData" endpoint above request.ReplyWithText(GetSampleTextsList(request.CurrentCollectionSettings.SettingsFilePath)); break; case "sampleFileContents": request.ReplyWithText(GetTextFileContents(request.RequiredParam("fileName"), WordFileType.SampleFile)); break; case "textOfContentPages": request.ReplyWithText(GetTextOfContentPagesAsJson()); break; case "makeLetterAndWordList": MakeLetterAndWordList(request.RequiredPostValue("settings"), request.RequiredPostValue("allWords")); request.PostSucceeded(); break; case "openTextsFolder": OpenTextsFolder(); request.PostSucceeded(); break; case "chooseAllowedWordsListFile": lock (request) { request.ReplyWithText(ShowSelectAllowedWordsFileDialog()); } break; case "allowedWordsList": switch (request.HttpMethod) { case HttpMethods.Delete: RecycleAllowedWordListFile(request.RequiredParam("fileName")); request.PostSucceeded(); break; case HttpMethods.Get: var fileName = request.RequiredParam("fileName"); request.ReplyWithText(RemoveEmptyAndDupes(GetTextFileContents(fileName, WordFileType.AllowedWordsFile))); break; default: request.Failed("Http verb not handled"); break; } break; case "defaultLevel": if (request.HttpMethod == HttpMethods.Get) { request.ReplyWithText(Settings.Default.CurrentLevel.ToString()); } else { int level; if (int.TryParse(request.RequiredParam("level"), out level)) { Settings.Default.CurrentLevel = level; Settings.Default.Save(); } else { // Don't think any sort of runtime failure is worthwhile here. Debug.Fail("could not parse level number"); } request.PostSucceeded(); // technically it didn't if we didn't parse the number } break; case "defaultStage": if (request.HttpMethod == HttpMethods.Get) { request.ReplyWithText(Settings.Default.CurrentStage.ToString()); } else { int stage; if (int.TryParse(request.RequiredParam("stage"), out stage)) { Settings.Default.CurrentStage = stage; Settings.Default.Save(); } else { // Don't think any sort of runtime failure is worthwhile here. Debug.Fail("could not parse stage number"); } request.PostSucceeded(); // technically it didn't if we didn't parse the number } break; default: request.Failed("Don't understand '" + lastSegment + "' in " + request.LocalPath()); break; } }
private static void HandleSettings(ApiRequest request) { if(request.HttpMethod != HttpMethods.Get) throw new ApplicationException(request.LocalPath()+" only implements 'get'"); var settings = new Dictionary<string, object> { {"current", request.CurrentBook.BookInfo.CurrentTool} }; foreach (var tool in GetToolsToDisplay(request.CurrentBook, IdsOfToolsThisVersionKnowsAbout)) { if (!String.IsNullOrEmpty(tool.State)) settings.Add(tool.StateName, tool.State); } request.ReplyWithJson(settings); }
public void HandleRequest(ApiRequest request) { if (CurrentBook == null) { Debug.Fail("BL-836 reproduction?"); // ReSharper disable once HeuristicUnreachableCode request.Failed("CurrentBook is null"); return; } if (request.CurrentCollectionSettings == null) { Debug.Fail("BL-836 reproduction?"); // ReSharper disable once HeuristicUnreachableCode request.Failed("CurrentBook.CollectionSettings is null"); return; } var lastSegment = request.LocalPath().Split(new char[] { '/' }).Last(); switch (lastSegment) { case "test": request.PostSucceeded(); break; case "readerSettingsEditForbidden": request.ReplyWithText(_tcManager.OkToEditCollectionSettings ? "" : WorkspaceView.MustBeAdminMessage); break; case "readerToolSettings": if (request.HttpMethod == HttpMethods.Get) { request.ReplyWithJson(GetReaderSettings(request.CurrentBook.BookData)); } else { var path = DecodableReaderToolSettings.GetReaderToolsSettingsFilePath(request.CurrentCollectionSettings); var content = request.RequiredPostJson(); RobustFile.WriteAllText(path, content, Encoding.UTF8); request.PostSucceeded(); } break; //note, this endpoint is confusing because it appears that ultimately we only use the word list out of this file (see "sampleTextsList"). //This ends up being written to a ReaderToolsWords-xyz.json (matching its use, if not it contents). case "synphonyLanguageData": //This is the "post". There is no direct "get", but the name of the file is given in the "sampleTextList" reply, below. // We've had situations (BL-4313 and friends) where reading the posted data fails. This seems to be due to situations // where we have a very large block of data and are rapidly switching between books. But as far as I can tell, the only // case where it's at all important to capture the new language data is if the user has been changing settings and // in particular editing the word list. Timing out the save in that situation seems very unlikely to fail. // So, in the interests of preventing the crash when switching books fast, we will ignore failure to read all the // json, and just not update the file. We would in any case keep only the version of the data sent to us by // the last book which sends it, and that one is unlikely to get interrupted. string langdata; try { langdata = request.RequiredPostJson(); } catch (IOException e) { SIL.Reporting.Logger.WriteError("Saving synphonyLanguageData failed to get Json", e); break; } SaveSynphonyLanguageData(langdata); request.PostSucceeded(); break; case "sampleTextsList": //note, as part of this reply, we send the path of the "ReaderToolsWords-xyz.json" which is *written* by the "synphonyLanguageData" endpoint above request.ReplyWithText(GetSampleTextsList(request.CurrentCollectionSettings.SettingsFilePath)); break; case "sampleFileContents": request.ReplyWithText(GetTextFileContents(request.RequiredParam("fileName"), WordFileType.SampleFile)); break; case "textOfContentPages": request.ReplyWithText(GetTextOfContentPagesAsJson()); break; case "makeLetterAndWordList": MakeLetterAndWordList(request.RequiredPostString("settings"), request.RequiredPostString("allWords")); request.PostSucceeded(); break; case "openTextsFolder": OpenTextsFolder(); request.PostSucceeded(); break; case "chooseAllowedWordsListFile": lock (request) { request.ReplyWithText(ShowSelectAllowedWordsFileDialog()); } break; case "allowedWordsList": switch (request.HttpMethod) { case HttpMethods.Delete: RecycleAllowedWordListFile(request.RequiredParam("fileName")); request.PostSucceeded(); break; case HttpMethods.Get: var fileName = request.RequiredParam("fileName"); request.ReplyWithText(RemoveEmptyAndDupes(GetTextFileContents(fileName, WordFileType.AllowedWordsFile))); break; default: request.Failed("Http verb not handled"); break; } break; case "defaultLevel": if (request.HttpMethod == HttpMethods.Get) { request.ReplyWithText(Settings.Default.CurrentLevel.ToString()); } else { int level; if (int.TryParse(request.RequiredParam("level"), out level)) { Settings.Default.CurrentLevel = level; Settings.Default.Save(); } else { // Don't think any sort of runtime failure is worthwhile here. Debug.Fail("could not parse level number"); } request.PostSucceeded(); // technically it didn't if we didn't parse the number } break; case "defaultStage": if (request.HttpMethod == HttpMethods.Get) { request.ReplyWithText(Settings.Default.CurrentStage.ToString()); } else { int stage; if (int.TryParse(request.RequiredParam("stage"), out stage)) { Settings.Default.CurrentStage = stage; Settings.Default.Save(); } else { // Don't think any sort of runtime failure is worthwhile here. Debug.Fail("could not parse stage number"); } request.PostSucceeded(); // technically it didn't if we didn't parse the number } break; case "copyBookStatsToClipboard": // See https://issues.bloomlibrary.org/youtrack/issue/BL-10018. string bookStatsString; try { bookStatsString = request.RequiredPostJson(); dynamic bookStats = DynamicJson.Parse(bookStatsString); var headerBldr = new StringBuilder(); var dataBldr = new StringBuilder(); headerBldr.Append("Book Title"); var title = _bookSelection.CurrentSelection.Title; title = title.Replace("\"", "\"\""); // Double double quotes to get Excel to recognize them. dataBldr.AppendFormat("\"{0}\"", title); headerBldr.Append("\tLevel"); dataBldr.AppendFormat("\t\"Level {0}\"", bookStats["levelNumber"]); headerBldr.Append("\tNumber of Pages with Text"); dataBldr.AppendFormat("\t{0}", bookStats["pageCount"]); headerBldr.Append("\tTotal Number of Words"); dataBldr.AppendFormat("\t{0}", bookStats["actualWordCount"]); headerBldr.Append("\tTotal Number of Sentences"); dataBldr.AppendFormat("\t{0}", bookStats["actualSentenceCount"]); headerBldr.Append("\tAverage No of Words per Page with Text"); dataBldr.AppendFormat("\t{0:0.#}", bookStats["actualAverageWordsPerPage"]); headerBldr.Append("\tAverage No of Sentences per Page with Text"); dataBldr.AppendFormat("\t{0:0.#}", bookStats["actualAverageSentencesPerPage"]); headerBldr.Append("\tNumber of Unique Words"); dataBldr.AppendFormat("\t{0}", bookStats["actualUniqueWords"]); headerBldr.Append("\tAverage Word Length"); dataBldr.AppendFormat("\t{0:0.#}", bookStats["actualAverageGlyphsPerWord"]); headerBldr.Append("\tAverage Sentence Length"); dataBldr.AppendFormat("\t{0:0.#}", bookStats["actualAverageWordsPerSentence"]); headerBldr.Append("\tMaximum Word Length"); dataBldr.AppendFormat("\t{0}", bookStats["actualMaxGlyphsPerWord"]); headerBldr.Append("\tMaximum Sentence Length"); dataBldr.AppendFormat("\t{0}", bookStats["actualMaxWordsPerSentence"]); // "actualWordsPerPageBook" is the maximum number of words on a page in the book // It's in the json data, but not asked for in the clipboard copying. var stringToSave = headerBldr.ToString() + Environment.NewLine + dataBldr.ToString(); PortableClipboard.SetText(stringToSave); } catch (IOException e) { SIL.Reporting.Logger.WriteError("Copying book statistics to clipboard failed to get Json", e); break; } request.PostSucceeded(); break; default: request.Failed("Don't understand '" + lastSegment + "' in " + request.LocalPath()); break; } }
/// <summary> /// Returns a json string like {"devices":["microphone", "Logitech Headset"], "productName":"Logitech Headset", "genericName":"Headset"}, /// except that in practice currrently the generic and product names are the same and not as helpful as the above. /// Devices is a list of product names (of available recording devices), the productName and genericName refer to the /// current selection (or will be null, if no current device). /// </summary> public void HandleAudioDevices(ApiRequest request) { #if __MonoCS__ request.Failed("Not supported on Linux"); #else var sb = new StringBuilder("{\"devices\":["); sb.Append(string.Join(",", RecordingDevice.Devices.Select(d => "\""+d.ProductName+"\""))); sb.Append("],\"productName\":"); if (CurrentRecording.RecordingDevice != null) sb.Append("\"" + CurrentRecording.RecordingDevice.ProductName + "\""); else sb.Append("null"); sb.Append(",\"genericName\":"); if (CurrentRecording.RecordingDevice != null) sb.Append("\"" + CurrentRecording.RecordingDevice.GenericName + "\""); else sb.Append("null"); sb.Append("}"); request.ReplyWithJson(sb.ToString()); #endif }