public void RegisterWithApiHandler(BloomApiHandler apiHandler) { apiHandler.RegisterEndpointLegacy(kApiUrlPart + "checkAutoSegmentDependencies", CheckAutoSegmentDependenciesMet, handleOnUiThread: false, requiresSync: false); apiHandler.RegisterEndpointLegacy(kApiUrlPart + "autoSegmentAudio", AutoSegment, handleOnUiThread: false, requiresSync: false); apiHandler.RegisterEndpointLegacy(kApiUrlPart + "getForcedAlignmentTimings", GetForcedAlignmentTimings, handleOnUiThread: false, requiresSync: false); apiHandler.RegisterEndpointLegacy(kApiUrlPart + "eSpeakPreview", ESpeakPreview, handleOnUiThread: false, requiresSync: false); }
public void RegisterWithApiHandler(BloomApiHandler apiHandler) { // Both of these display UI, expect to require UI thread. apiHandler.RegisterEndpointLegacy("addPage", HandleAddPage, true).Measureable("Add Page"); apiHandler.RegisterEndpointLegacy("changeLayout", HandleChangeLayout, true).Measureable("Change Layout"); ; }
public void RegisterWithApiHandler(BloomApiHandler apiHandler) { // HandleStartRecording seems to need to be on the UI thread in order for HandleEndRecord() to detect the correct state. apiHandler.RegisterEndpointLegacy("audio/startRecord", HandleStartRecording, true).Measureable(); // Note: This handler locks and unlocks a shared resource (_completeRecording lock). // Any other handlers depending on this resource should not wait on the same thread (i.e. the UI thread) or deadlock can occur. apiHandler.RegisterEndpointLegacy("audio/endRecord", HandleEndRecord, true).Measureable(); // Any handler which retrieves information from the audio folder SHOULD wait on the _completeRecording lock (call WaitForRecordingToComplete()) to ensure that it sees // a consistent state of the audio folder, and therefore should NOT run on the UI thread. // Also, explicitly setting requiresSync to true (even tho that's default anyway) to make concurrency less complicated to think about apiHandler.RegisterEndpointLegacy("audio/checkForAnyRecording", HandleCheckForAnyRecording, false, true); apiHandler.RegisterEndpointLegacy("audio/checkForAllRecording", HandleCheckForAllRecording, false, true); apiHandler.RegisterEndpointLegacy("audio/deleteSegment", HandleDeleteSegment, false, true); apiHandler.RegisterEndpointLegacy("audio/checkForSegment", HandleCheckForSegment, false, true); apiHandler.RegisterEndpointLegacy("audio/wavFile", HandleAudioFileRequest, false, true); // Doesn't matter whether these are on UI thread or not, so using the old default which was true apiHandler.RegisterEndpointLegacy("audio/currentRecordingDevice", HandleCurrentRecordingDevice, true); apiHandler.RegisterEndpointLegacy("audio/devices", HandleAudioDevices, true); apiHandler.RegisterEndpointLegacy("audio/copyAudioFile", HandleCopyAudioFile, false); Debug.Assert(BloomServer.portForHttp > 0, "Need the server to be listening before this can be registered (BL-3337)."); }
public void RegisterWithApiHandler(BloomApiHandler apiHandler) { apiHandler.RegisterEndpointLegacy("signLanguage/recordedVideo", HandleRecordedVideoRequest, true).Measureable("Process recorded video"); apiHandler.RegisterEndpointLegacy("signLanguage/deleteVideo", HandleDeleteVideoRequest, true).Measureable("Delete video");; apiHandler.RegisterEndpointLegacy("signLanguage/importVideo", HandleImportVideoRequest, true); // has dialog, so measure internally after the dialog. apiHandler.RegisterEndpointLegacy("signLanguage/getStats", HandleVideoStatisticsRequest, true); }
public void RegisterWithApiHandler(BloomApiHandler apiHandler) { // We could probably get away with using the server thread here, but the code interacts quite a bit with the // current book and other state. apiHandler.RegisterEndpointLegacy("pageTemplates", HandleTemplatesRequest, true); // Being on the UI thread causes a deadlock on Linux/Mono. See https://silbloom.myjetbrains.com/youtrack/issue/BL-3818. apiHandler.RegisterEndpointLegacy("pageTemplateThumbnail", HandleThumbnailRequest, false); }
public void RegisterWithApiHandler(BloomApiHandler apiHandler) { // Must not require sync, because it launches a Bloom dialog, which will make other api requests // that will be blocked if this is locked. apiHandler.RegisterEndpointHandler("bookCommand/exportToSpreadsheet", (request) => { _spreadsheetApi.ShowExportToSpreadsheetUI(GetBookObjectFromPost(request)); request.PostSucceeded(); }, true, false); apiHandler.RegisterEndpointHandler("bookCommand/enhanceLabel", (request) => { // We want this to be fast...many things are competing for api handling threads while // Bloom is starting up, including many buttons sending this request... // so we'll postpone until idle even searching for the right BookInfo. var collection = request.RequiredParam("collection-id").Trim(); var id = request.RequiredParam("id"); RequestButtonLabelUpdate(collection, id); request.PostSucceeded(); }, false, false); apiHandler.RegisterBooleanEndpointHandler("bookCommand/decodable", request => { return(_collectionModel.IsBookDecodable); }, (request, b) => { _collectionModel.SetIsBookDecodable(b); }, true); apiHandler.RegisterBooleanEndpointHandler("bookCommand/leveled", request => { return(_collectionModel.IsBookLeveled); }, (request, b) => { _collectionModel.SetIsBookLeveled(b); }, true); apiHandler.RegisterEndpointLegacy("bookCommand/", HandleBookCommand, true); }
public void RegisterWithApiHandler(BloomApiHandler apiHandler) { // Note: all book commands, including import and export spreadsheets, // go through a single "bookCommand/" API, so we don't register that here. // Instead all we need to register is any api enpoints used by our own spreadsheet dialogs apiHandler.RegisterEndpointLegacy("spreadsheet/export", ExportToSpreadsheet, true); }
public static void RegisterWithApiHandler(BloomApiHandler apiHandler) { apiHandler.RegisterEndpointLegacy("help/.*", (request) => { var topic = request.LocalPath().Replace("api/help", ""); Show(Application.OpenForms.Cast <Form>().Last(), topic); //if this is called from a simple html anchor, we don't want the browser to do anything request.ExternalLinkSucceeded(); }, true); // opening a form, definitely UI thread }
public void RegisterWithApiHandler(BloomApiHandler apiHandler) { apiHandler.RegisterEndpointLegacy("dialog/close", (ApiRequest request) => { // Closes the current dialog. BrowserDialog.CloseDialog(); request.PostSucceeded(); }, true); }
public void RegisterWithApiHandler(BloomApiHandler apiHandler) { apiHandler.RegisterEndpointLegacy("performance/showCsvFile", (request) => { Process.Start(_csvFilePath); request.PostSucceeded(); }, false); apiHandler.RegisterEndpointLegacy("performance/applicationInfo", (request) => { request.ReplyWithText($"Bloom {Shell.GetShortVersionInfo()} {ApplicationUpdateSupport.ChannelName}"); }, false); apiHandler.RegisterEndpointLegacy("performance/allMeasurements", (request) => { List <object> l = new List <object>(); foreach (var measurement in _measurements) { l.Add(measurement.GetSummary()); } request.ReplyWithJson(l.ToArray()); }, false); }
public void RegisterWithApiHandler(BloomApiHandler apiHandler) { apiHandler.RegisterEndpointLegacy("keyboarding/useLongpress", (ApiRequest request) => { try { //detect if some keyboarding system is active, e.g. KeyMan. If it is, don't enable LongPress var form = Application.OpenForms.Cast <Form>().Last(); request.ReplyWithText(SIL.Windows.Forms.Keyboarding.KeyboardController.IsFormUsingInputProcessor(form) ? "false" : "true"); } catch (Exception error) { request.ReplyWithText("true"); // This is arbitrary. I don't know if it's better to assume keyman, or not. NonFatalProblem.Report(ModalIf.None, PassiveIf.All, "Error checking for keyman", "", error); } }, handleOnUiThread: false); }
public void RegisterWithApiHandler(BloomApiHandler apiHandler) { apiHandler.RegisterEndpointLegacy("pageList/pages", HandlePagesRequest, false).Measureable(); apiHandler.RegisterEndpointLegacy("pageList/pageContent", HandlePageContentRequest, false); apiHandler.RegisterEndpointLegacy("pageList/pageMoved", HandlePageMovedRequest, true).Measureable(); apiHandler.RegisterEndpointLegacy("pageList/pageClicked", HandlePageClickedRequest, true); apiHandler.RegisterEndpointLegacy("pageList/menuClicked", HandleShowMenuRequest, true).Measureable(); apiHandler.RegisterEndpointLegacy("pageList/bookAttributesThatMayAffectDisplay", (request) => { var attrs = _bookSelection.CurrentSelection.OurHtmlDom.GetBodyAttributesThatMayAffectDisplay(); // Surely there's a way to do this more safely with JSON.net but I haven't found it yet var props = string.Join(",", attrs.Select(a => ("\"" + a.Name + "\": \"" + a.Value.Replace("\"", "\\\"") + "\""))); request.ReplyWithJson("{" + props + "}"); }, true); }
public void RegisterWithApiHandler(BloomApiHandler apiHandler) { bool requiresSync = false; // Lets us open the dialog while the epub preview is being generated apiHandler.RegisterEndpointLegacy("book/metadata", HandleBookMetadata, false, requiresSync); }
public void RegisterWithApiHandler(BloomApiHandler apiHandler) { apiHandler.RegisterEndpointLegacy("fileIO/chooseFile", ChooseFile, true); apiHandler.RegisterEndpointLegacy("fileIO/getSpecialLocation", GetSpecialLocation, true); apiHandler.RegisterEndpointLegacy("fileIO/copyFile", CopyFile, true); }
public void RegisterWithApiHandler(BloomApiHandler apiHandler) { apiHandler.RegisterEndpointLegacy("toolbox/enabledTools", HandleEnabledToolsRequest, true); apiHandler.RegisterEndpointLegacy("toolbox/fileExists", HandleFileExistsRequest, true); }
public void RegisterWithApiHandler(BloomApiHandler apiHandler) { apiHandler.RegisterEndpointLegacy("uiLanguages", HandleUiLanguages, false); // App apiHandler.RegisterEndpointLegacy("currentUiLanguage", HandleCurrentUiLanguage, false); // App apiHandler.RegisterEndpointLegacy("bubbleLanguages", HandleBubbleLanguages, false); // Move to EditingViewApi apiHandler.RegisterEndpointLegacy("authorMode", HandleAuthorMode, false); // Move to EditingViewApi apiHandler.RegisterEndpointLegacy("topics", HandleTopics, false); // Move to EditingViewApi apiHandler.RegisterEndpointLegacy("common/error", HandleJavascriptError, false); // Common apiHandler.RegisterEndpointLegacy("common/preliminaryError", HandlePreliminaryJavascriptError, false); // Common apiHandler.RegisterEndpointLegacy("common/saveChangesAndRethinkPageEvent", RethinkPageAndReloadIt, true); // Move to EditingViewApi apiHandler.RegisterEndpointLegacy("common/chooseFolder", HandleChooseFolder, true); apiHandler.RegisterEndpointLegacy("common/showInFolder", HandleShowInFolderRequest, true); // Common apiHandler.RegisterEndpointLegacy("common/canModifyCurrentBook", HandleCanModifyCurrentBook, true); apiHandler.RegisterEndpointLegacy("common/showSettingsDialog", HandleShowSettingsDialog, false); // Common apiHandler.RegisterEndpointLegacy("common/problemWithBookMessage", request => { request.ReplyWithText(CommonMessages.GetProblemWithBookMessage(Path.GetFileName(_bookSelection.CurrentSelection?.FolderPath))); }, false); apiHandler.RegisterEndpointLegacy("common/clickHereForHelp", request => { var problemFilePath = UrlPathString.CreateFromUrlEncodedString(request.RequiredParam("problem")).NotEncoded; request.ReplyWithText(CommonMessages.GetPleaseClickHereForHelpMessage(problemFilePath)); }, false); // Used when something in JS land wants to copy text to or from the clipboard. For POST, the text to be put on the // clipboard is passed as the 'text' property of a JSON requestData. // Somehow the get version of this fires while initializing a page (probably hooking up CkEditor, an unwanted // invocation of the code that decides whether to enable the paste hyperlink button). This causes a deadlock // unless we make this endpoint requiresSync:false. I think this is safe as it doesn't interact with any other // Bloom objects. apiHandler.RegisterEndpointLegacy("common/clipboardText", request => { if (request.HttpMethod == HttpMethods.Get) { string result = ""; // initial value is not used, delegate will set it. Program.MainContext.Send(o => { try { result = PortableClipboard.GetText(); } catch (Exception e) { // Need to make sure to handle exceptions. // If the worker thread dies with an unhandled exception, // it causes the whole program to immediately crash without opportunity for error reporting NonFatalProblem.Report(ModalIf.All, PassiveIf.None, "Error pasting text", exception: e); } }, null); request.ReplyWithText(result); } else { // post var requestData = DynamicJson.Parse(request.RequiredPostJson()); string content = requestData.text; if (!string.IsNullOrEmpty(content)) { Program.MainContext.Post(o => { try { PortableClipboard.SetText(content); } catch (Exception e) { // Need to make sure to handle exceptions. // If the worker thread dies with an unhandled exception, // it causes the whole program to immediately crash without opportunity for error reporting NonFatalProblem.Report(ModalIf.All, PassiveIf.None, "Error copying text", exception: e); } }, null); } request.PostSucceeded(); } }, false, false); apiHandler.RegisterEndpointLegacy("common/checkForUpdates", request => { WorkspaceView.CheckForUpdates(); request.PostSucceeded(); }, false); apiHandler.RegisterEndpointLegacy("common/channel", request => { request.ReplyWithText(ApplicationUpdateSupport.ChannelName); }, false); // This is useful for debugging TypeScript code, especially on Linux. I wouldn't necessarily expect // to see it used anywhere in code that gets submitted and merged. apiHandler.RegisterEndpointLegacy("common/debugMessage", request => { var message = request.RequiredPostString(); Debug.WriteLine("FROM JS: " + message); request.PostSucceeded(); }, false); apiHandler.RegisterEndpointLegacy("common/loginData", request => { var requestData = DynamicJson.Parse(request.RequiredPostJson()); string token = requestData.sessionToken; string email = requestData.email; string userId = requestData.userId; //Debug.WriteLine("Got login data " + email + " with token " + token + " and id " + userId); _parseClient.SetLoginData(email, userId, token, BookUpload.Destination); _doWhenLoggedIn?.Invoke(); request.PostSucceeded(); }, false); // At this point we open dialogs from c# code; if we opened dialogs from javascript, we wouldn't need this // api to do it. We just need a way to close a c#-opened dialog from javascript (e.g. the Close button of the dialog). // // This must set requiresSync:false because the API call which opened the dialog may already have // the lock in which case we would be deadlocked. // ErrorReport.NotifyUserOfProblem is a particularly problematic case. We tried to come up with some // other solutions for that including opening the dialog on Application.Idle. But the dialog needs // to give a real-time result so callers can know what do with button presses. Since some of those // callers are in libpalaso, we can't just ignore the result and handle the actions ourselves. apiHandler.RegisterEndpointLegacy("common/closeReactDialog", request => { ReactDialog.CloseCurrentModal(request.GetPostStringOrNull()); request.PostSucceeded(); }, true, requiresSync: false); // TODO: move to the new App API (BL-9635) apiHandler.RegisterEndpointLegacy("common/reloadCollection", HandleReloadCollection, true); }
public void RegisterWithApiHandler(BloomApiHandler apiHandler) { apiHandler.RegisterEndpointLegacy(kApiUrlPart + "bookName", request => { request.ReplyWithText(request.CurrentBook.NameBestForUserDisplay); }, false); apiHandler.RegisterEndpointLegacy(kApiUrlPart + "showAccessibilityChecker", request => { AccessibilityCheckWindow.StaticShow(() => _webSocketServer.SendEvent(kWebSocketContext, kWindowActivated)); request.PostSucceeded(); }, true); apiHandler.RegisterEndpointLegacy(kApiUrlPart + "descriptionsForAllImages", request => { var problems = AccessibilityCheckers.CheckDescriptionsForAllImages(request.CurrentBook); var resultClass = problems.Any() ? "failed" : "passed"; request.ReplyWithJson(new { resultClass = resultClass, problems = problems }); }, false); apiHandler.RegisterEndpointLegacy(kApiUrlPart + "audioForAllImageDescriptions", request => { var problems = AccessibilityCheckers.CheckAudioForAllImageDescriptions(request.CurrentBook); var resultClass = problems.Any() ? "failed" : "passed"; request.ReplyWithJson(new { resultClass = resultClass, problems = problems }); }, false); apiHandler.RegisterEndpointLegacy(kApiUrlPart + "audioForAllText", request => { var problems = AccessibilityCheckers.CheckAudioForAllText(request.CurrentBook); var resultClass = problems.Any() ? "failed" : "passed"; request.ReplyWithJson(new { resultClass = resultClass, problems = problems }); }, false); // Just a checkbox that the user ticks to say "yes, I checked this" // At this point, we don't have a way to clear that when the book changes. apiHandler.RegisterBooleanEndpointHandler(kApiUrlPart + "noEssentialInfoByColor", request => request.CurrentBook.BookInfo.MetaData.A11y_NoEssentialInfoByColor, (request, b) => { request.CurrentBook.BookInfo.MetaData.A11y_NoEssentialInfoByColor = b; request.CurrentBook.Save(); }, false); // Just a checkbox that the user ticks to say "yes, I checked this" // At this point, we don't have a way to clear that when the book changes. apiHandler.RegisterBooleanEndpointHandler(kApiUrlPart + "noTextIncludedInAnyImages", request => request.CurrentBook.BookInfo.MetaData.A11y_NoTextIncludedInAnyImages, (request, b) => { request.CurrentBook.BookInfo.MetaData.A11y_NoTextIncludedInAnyImages = b; request.CurrentBook.Save(); }, false); //enhance: this might have to become async to work on large books on slow computers apiHandler.RegisterEndpointLegacy(kApiUrlPart + "aceByDaisyReportUrl", request => { MakeAceByDaisyReport(request); }, false, false ); // A checkbox that the user ticks in the Accessible Image tool to request a preview // of how things might look with cataracts. // For now this doesn't seem worth persisting, except for the session so it sticks from page to page. apiHandler.RegisterBooleanEndpointHandler(kApiUrlPart + "cataracts", request => _simulateCataracts, (request, b) => { _simulateCataracts = b; }, false); // A checkbox that the user ticks in the Accessible Image tool to request a preview // of how things might look with color-blindness, and a set of radio buttons // for choosing different kinds of color-blindness. // For now these doesn't seem worth persisting, except for the session so it sticks from page to page. apiHandler.RegisterBooleanEndpointHandler(kApiUrlPart + "colorBlindness", request => _simulateColorBlindness, (request, b) => { _simulateColorBlindness = b; }, false); apiHandler.RegisterEnumEndpointHandler(kApiUrlPart + "kindOfColorBlindness", request => _kindOfColorBlindness, (request, kind) => _kindOfColorBlindness = kind, false); }
public void RegisterWithApiHandler(BloomApiHandler apiHandler) { apiHandler.RegisterEndpointLegacy("editView/setModalState", HandleSetModalState, true); apiHandler.RegisterEndpointLegacy("editView/chooseWidget", HandleChooseWidget, true); apiHandler.RegisterEndpointLegacy("editView/getBookColors", HandleGetColors, true); apiHandler.RegisterEndpointLegacy("editView/editPagePainted", HandleEditPagePainted, true); apiHandler.RegisterEndpointLegacy("editView/saveToolboxSetting", HandleSaveToolboxSetting, true); apiHandler.RegisterEndpointLegacy("editView/setTopic", HandleSetTopic, true); apiHandler.RegisterEndpointLegacy("editView/isTextSelected", HandleIsTextSelected, false); apiHandler.RegisterEndpointLegacy("editView/getBookLangs", HandleGetBookLangs, false); apiHandler.RegisterEndpointLegacy("editView/isClipboardBookHyperlink", HandleIsClipboardBookHyperlink, false); apiHandler.RegisterEndpointLegacy("editView/requestTranslationGroupContent", RequestDefaultTranslationGroupContent, true); apiHandler.RegisterEndpointLegacy("editView/duplicatePageMany", HandleDuplicatePageMany, true); }
public void RegisterWithApiHandler(BloomApiHandler apiHandler) { apiHandler.RegisterEndpointLegacy("progress/cancel", Cancel, false, false); }
public void RegisterWithApiHandler(BloomApiHandler apiHandler) { apiHandler.RegisterEndpointLegacy("libraryPublish/upload", HandleUpload, true); apiHandler.RegisterEndpointLegacy("libraryPublish/cancel", HandleCancel, true); }
public void RegisterWithApiHandler(BloomApiHandler apiHandler) { apiHandler.RegisterEndpointLegacy(kApiUrlPart + "requestState", request => { if (request.HttpMethod == HttpMethods.Get) { request.ReplyWithJson(CurrentStateString); } else // post { Debug.Fail("We shouldn't ever be using the 'post' version."); request.PostSucceeded(); } }, true); apiHandler.RegisterEndpointLegacy(kApiUrlPart + "addPage", request => { AddPageButton_Click(); request.PostSucceeded(); }, true).Measureable(); apiHandler.RegisterEndpointLegacy(kApiUrlPart + "duplicatePage", request => { _editingModel.OnDuplicatePage(); request.PostSucceeded(); }, true).Measureable(); apiHandler.RegisterEndpointLegacy(kApiUrlPart + "deletePage", request => { if (ConfirmRemovePageDialog.Confirm()) { _editingModel.OnDeletePage(); } request.PostSucceeded(); }, true).Measureable(); apiHandler.RegisterEndpointLegacy(kApiUrlPart + "lockBook", request => { _editingModel.SaveNow(); // BL-5421 lock and unlock lose typing _editingModel.CurrentBook.TemporarilyUnlocked = false; request.PostSucceeded(); UpdateState(); // because we aren't selecting a new page _editingModel.RefreshDisplayOfCurrentPage(); }, true); apiHandler.RegisterEndpointLegacy(kApiUrlPart + "unlockBook", request => { _editingModel.SaveNow(); // BL-5421 lock and unlock lose typing _editingModel.CurrentBook.TemporarilyUnlocked = true; request.PostSucceeded(); UpdateState(); // because we aren't selecting a new page _editingModel.RefreshDisplayOfCurrentPage(); }, true); apiHandler.RegisterEndpointLegacy(kApiUrlPart + "cleanup", request => { SendCleanupState(); request.PostSucceeded(); }, true); apiHandler.RegisterEndpointLegacy(kApiUrlPart + "zoomMinus", request => { _editingModel.AdjustPageZoom(-10); request.PostSucceeded(); }, true); apiHandler.RegisterEndpointLegacy(kApiUrlPart + "zoomPlus", request => { _editingModel.AdjustPageZoom(10); request.PostSucceeded(); }, true); apiHandler.RegisterEndpointLegacy(kApiUrlPart + "requestVideoPlaceHolder", request => { _editingModel.RequestVideoPlaceHolder(); request.PostSucceeded(); }, true); apiHandler.RegisterEndpointLegacy(kApiUrlPart + "requestWidgetPlaceHolder", request => { _editingModel.RequestWidgetPlaceHolder(); request.PostSucceeded(); }, true); }
public static void RegisterWithApiHandler(BloomApiHandler apiHandler) { // Opening a page, better be in UI thread. apiHandler.RegisterEndpointLegacy(kPrefix + "/.*", HandleRequest, true); }
public void RegisterWithApiHandler(BloomApiHandler apiHandler) { apiHandler.RegisterEndpointLegacy("music/ui/chooseFile", HandleRequest, true); }
public void RegisterWithApiHandler(BloomApiHandler apiHandler) { apiHandler.RegisterEndpointHandler(kApiUrlPart + "save", HandleEpubSave, true, false); apiHandler.RegisterEndpointLegacy(kApiUrlPart + "epubSettings", request => { if (request.HttpMethod == HttpMethods.Get) { request.ReplyWithJson(GetEpubSettings()); } else { // post is deprecated. throw new ApplicationException("epubSettings POST is deprecated"); } }, false); // The backend here was written with an enum that had two choices for how to publish descriptions, but we only ever // have used one of them so far in the UI. So this is a boolean api that converts to an enum underlying value. apiHandler.RegisterBooleanEndpointHandler(kApiUrlPart + "imageDescriptionSetting", request => request.CurrentBook.BookInfo.PublishSettings.Epub.HowToPublishImageDescriptions == BookInfo.HowToPublishImageDescriptions.OnPage, (request, onPage) => { request.CurrentBook.BookInfo.PublishSettings.Epub.HowToPublishImageDescriptions = onPage ? BookInfo.HowToPublishImageDescriptions.OnPage : BookInfo.HowToPublishImageDescriptions.None; request.CurrentBook.BookInfo.Save(); var newSettings = _desiredEpubSettings.Clone(); newSettings.howToPublishImageDescriptions = request.CurrentBook.BookInfo.PublishSettings.Epub.HowToPublishImageDescriptions; RefreshPreview(newSettings); }, false); // Saving a checkbox setting that the user ticks to say "Use my E-reader's font sizes" apiHandler.RegisterBooleanEndpointHandler(kApiUrlPart + "removeFontSizesSetting", request => request.CurrentBook.BookInfo.PublishSettings.Epub.RemoveFontSizes, (request, booleanSetting) => { request.CurrentBook.BookInfo.PublishSettings.Epub.RemoveFontSizes = booleanSetting; request.CurrentBook.BookInfo.Save(); var newSettings = _desiredEpubSettings.Clone(); newSettings.removeFontSizes = booleanSetting; RefreshPreview(newSettings); }, false); apiHandler.RegisterEndpointLegacy(kApiUrlPart + "updatePreview", request => { RefreshPreview(_desiredEpubSettings); request.PostSucceeded(); if (request.CurrentBook?.ActiveLanguages != null) { var message = new LicenseChecker().CheckBook(request.CurrentBook, request.CurrentBook.ActiveLanguages.ToArray()); _webSocketServer.SendString(kWebsocketContext, kWebsocketState_LicenseOK, (message == null) ? "true" : "false"); } }, false); // in fact, must NOT be on UI thread apiHandler.RegisterEndpointLegacy(kApiUrlPart + "abortPreview", request => { AbortMakingEpub(); request.PostSucceeded(); }, false, false); }
private static IEnumerable <string> _additionalPathsToInclude; // typically a problem image file public void RegisterWithApiHandler(BloomApiHandler apiHandler) { // This one is an exception: it's not used BY the problem report dialog, but to launch one // from Javascript. However, it also must not require the lock, because if it holds it, // no calls that need it can run (such as one put forth by the Cancel button). apiHandler.RegisterEndpointLegacy("problemReport/showDialog", HandleShowDialog, true, false); // Similarly, but this launches from a button shown in Book.ErrorDom apiHandler.RegisterEndpointHandler("problemReport/unreadableBook", HandleUnreadableBook, true, false); // For the paranoid - We could also have showProblemReport block these handlers while _reportInfo is being populated. // I think it's unnecessary since the problem report dialog's URL isn't even set until after the _reportInfo is populated, // and we assume that nothing sends problemReport API requests except the problemReportDialog that this class loads. // ProblemDialog.tsx uses this endpoint to get the string to show at the top of the main dialog apiHandler.RegisterEndpointHandlerUsedByOthers("problemReport/reportHeadingHtml", (ApiRequest request) => { request.ReplyWithText(_reportInfo.HeadingHtml ?? ""); }, false); // ProblemDialog.tsx uses this endpoint to get the screenshot image. apiHandler.RegisterEndpointHandlerUsedByOthers("problemReport/screenshot", (ApiRequest request) => { // Wait until the screenshot is finished. // If not available within the time limit, just continue anyway (so we don't deadlock or anything) and hope for the best. bool isLockTaken = _takingScreenshotLock.Wait(millisecondsTimeout: 5000); if (_reportInfo?.ScreenshotTempFile == null) { request.Failed(); } else { request.ReplyWithImage(_reportInfo.ScreenshotTempFile.Path); } if (isLockTaken) { _takingScreenshotLock.Release(); } }, true); // ProblemDialog.tsx uses this endpoint to get the name of the book. apiHandler.RegisterEndpointHandlerUsedByOthers("problemReport/bookName", (ApiRequest request) => { request.ReplyWithText(_reportInfo.BookName ?? "??"); }, true); // ProblemDialog.tsx uses this endpoint to get the registered user's email address. apiHandler.RegisterEndpointHandlerUsedByOthers("problemReport/emailAddress", (ApiRequest request) => { request.ReplyWithText(_reportInfo.UserEmail); }, true); // PrivacyScreen.tsx uses this endpoint to show the user what info will be included in the report. apiHandler.RegisterEndpointHandlerUsedByOthers("problemReport/diagnosticInfo", (ApiRequest request) => { var userWantsToIncludeBook = request.RequiredParam("includeBook") == "true"; var userInput = request.RequiredParam("userInput"); var userEmail = request.RequiredParam("email"); request.ReplyWithText(GetDiagnosticInfo(userWantsToIncludeBook, userInput, userEmail)); }, true); // ProblemDialog.tsx uses this endpoint in its AttemptSubmit method; // it expects a response that it will use to show the issue link to the user. apiHandler.RegisterEndpointHandlerUsedByOthers("problemReport/submit", (ApiRequest request) => { var report = DynamicJson.Parse(request.RequiredPostJson()); string issueLink = SubmitToYouTrack(report.kind, report.userInput, report.email, report.includeBook, report.includeScreenshot, null); object linkToNewIssue = new { issueLink }; request.ReplyWithJson(linkToNewIssue); }, true); }
public static void RegisterWithApiHandler(BloomApiHandler apiHandler) { apiHandler.RegisterEndpointLegacy("toolbox/settings", HandleSettings, false); }
public void RegisterWithApiHandler(BloomApiHandler apiHandler) { // This is just for storing the user preference of method // If we had a couple of these, we could just have a generic preferences api // that browser-side code could use. apiHandler.RegisterEndpointLegacy(kApiUrlPart + "method", request => { if (request.HttpMethod == HttpMethods.Get) { var method = Settings.Default.PublishAndroidMethod; if (!new string[] { "wifi", "usb", "file" }.Contains(method)) { method = "wifi"; } request.ReplyWithText(method); } else // post { Settings.Default.PublishAndroidMethod = request.RequiredPostString(); #if __MonoCS__ if (Settings.Default.PublishAndroidMethod == "usb") { _progress.MessageWithoutLocalizing("Sorry, this method is not available on Linux yet."); } #endif request.PostSucceeded(); } }, true); apiHandler.RegisterEndpointLegacy(kApiUrlPart + "backColor", request => { if (request.HttpMethod == HttpMethods.Get) { if (request.CurrentBook != _coverColorSourceBook) { _coverColorSourceBook = request.CurrentBook; ImageUtils.TryCssColorFromString(request.CurrentBook?.GetCoverColor() ?? "", out _thumbnailBackgroundColor); } request.ReplyWithText(ToCssColorString(_thumbnailBackgroundColor)); } else // post { // ignore invalid colors (very common while user is editing hex) Color newColor; var newColorAsString = request.RequiredPostString(); if (ImageUtils.TryCssColorFromString(newColorAsString, out newColor)) { _thumbnailBackgroundColor = newColor; request.CurrentBook.SetCoverColor(newColorAsString); } request.PostSucceeded(); } }, true); apiHandler.RegisterBooleanEndpointHandler(kApiUrlPart + "motionBookMode", readRequest => { // If the user has taken off all possible motion, force not having motion in the // Bloom Reader book. See https://issues.bloomlibrary.org/youtrack/issue/BL-7680. if (!readRequest.CurrentBook.HasMotionPages) { readRequest.CurrentBook.BookInfo.PublishSettings.BloomPub.Motion = false; } return(readRequest.CurrentBook.BookInfo.PublishSettings.BloomPub.Motion); }, (writeRequest, value) => { writeRequest.CurrentBook.BookInfo.PublishSettings.BloomPub.Motion = value; writeRequest.CurrentBook.BookInfo.SavePublishSettings(); _webSocketServer.SendEvent("publish", "motionChanged"); } , true); apiHandler.RegisterEndpointHandler(kApiUrlPart + "updatePreview", request => { MakeBloompubPreview(request, false); }, false); apiHandler.RegisterEndpointLegacy(kApiUrlPart + "thumbnail", request => { var coverImage = request.CurrentBook.GetCoverImagePath(); if (coverImage == null) { request.Failed("no cover image"); } else { // We don't care as much about making it resized as making its background transparent. using (var thumbnail = TempFile.CreateAndGetPathButDontMakeTheFile()) { if (_thumbnailBackgroundColor == Color.Transparent) { ImageUtils.TryCssColorFromString(request.CurrentBook?.GetCoverColor(), out _thumbnailBackgroundColor); } RuntimeImageProcessor.GenerateEBookThumbnail(coverImage, thumbnail.Path, 256, 256, _thumbnailBackgroundColor); request.ReplyWithImage(thumbnail.Path); } } }, true); apiHandler.RegisterEndpointHandler(kApiUrlPart + "usb/start", request => { #if !__MonoCS__ SetState("UsbStarted"); UpdatePreviewIfNeeded(request); _usbPublisher.Connect(request.CurrentBook, _thumbnailBackgroundColor, GetSettings()); #endif request.PostSucceeded(); }, true); apiHandler.RegisterEndpointHandler(kApiUrlPart + "usb/stop", request => { #if !__MonoCS__ _usbPublisher.Stop(disposing: false); SetState("stopped"); #endif request.PostSucceeded(); }, true); apiHandler.RegisterEndpointLegacy(kApiUrlPart + "wifi/start", request => { SetState("ServingOnWifi"); UpdatePreviewIfNeeded(request); _wifiPublisher.Start(request.CurrentBook, request.CurrentCollectionSettings, _thumbnailBackgroundColor, GetSettings()); request.PostSucceeded(); }, true); apiHandler.RegisterEndpointLegacy(kApiUrlPart + "wifi/stop", request => { _wifiPublisher.Stop(); SetState("stopped"); request.PostSucceeded(); }, true); apiHandler.RegisterEndpointLegacy(kApiUrlPart + "file/save", request => { UpdatePreviewIfNeeded(request); FilePublisher.Save(request.CurrentBook, _bookServer, _thumbnailBackgroundColor, _progress, GetSettings()); SetState("stopped"); request.PostSucceeded(); }, true); apiHandler.RegisterEndpointLegacy(kApiUrlPart + "file/bulkSaveBloomPubsParams", request => { request.ReplyWithJson(JsonConvert.SerializeObject(_collectionSettings.BulkPublishBloomPubSettings)); }, true); apiHandler.RegisterEndpointLegacy(kApiUrlPart + "file/bulkSaveBloomPubs", request => { // update what's in the collection so that we remember for next time _collectionSettings.BulkPublishBloomPubSettings = request.RequiredPostObject <BulkBloomPubPublishSettings>(); _collectionSettings.Save(); _bulkBloomPubCreator.PublishAllBooks(_collectionSettings.BulkPublishBloomPubSettings); SetState("stopped"); request.PostSucceeded(); }, true); apiHandler.RegisterEndpointLegacy(kApiUrlPart + "textToClipboard", request => { PortableClipboard.SetText(request.RequiredPostString()); request.PostSucceeded(); }, true); apiHandler.RegisterBooleanEndpointHandler(kApiUrlPart + "canHaveMotionMode", request => { return(request.CurrentBook.HasMotionPages); }, null, // no write action false, true); // we don't really know, just safe default apiHandler.RegisterBooleanEndpointHandler(kApiUrlPart + "canRotate", request => { return(request.CurrentBook.BookInfo.PublishSettings.BloomPub.Motion && request.CurrentBook.HasMotionPages); }, null, // no write action false, true); // we don't really know, just safe default apiHandler.RegisterBooleanEndpointHandler(kApiUrlPart + "defaultLandscape", request => { return(request.CurrentBook.GetLayout().SizeAndOrientation.IsLandScape); }, null, // no write action false, true); // we don't really know, just safe default apiHandler.RegisterEndpointLegacy(kApiUrlPart + "languagesInBook", request => { try { InitializeLanguagesInBook(request); Dictionary <string, InclusionSetting> textLangsToPublish = request.CurrentBook.BookInfo.PublishSettings.BloomPub.TextLangs; Dictionary <string, InclusionSetting> audioLangsToPublish = request.CurrentBook.BookInfo.PublishSettings.BloomPub.AudioLangs; var result = "[" + string.Join(",", _allLanguages.Select(kvp => { string langCode = kvp.Key; bool includeText = false; if (textLangsToPublish != null && textLangsToPublish.TryGetValue(langCode, out InclusionSetting includeTextSetting)) { includeText = includeTextSetting.IsIncluded(); } bool includeAudio = false; if (audioLangsToPublish != null && audioLangsToPublish.TryGetValue(langCode, out InclusionSetting includeAudioSetting)) { includeAudio = includeAudioSetting.IsIncluded(); } var value = new LanguagePublishInfo() { code = kvp.Key, name = request.CurrentBook.PrettyPrintLanguage(langCode), complete = kvp.Value, includeText = includeText, containsAnyAudio = _languagesWithAudio.Contains(langCode), includeAudio = includeAudio }; var json = JsonConvert.SerializeObject(value); return(json); })) + "]"; request.ReplyWithText(result); }
public void RegisterWithApiHandler(BloomApiHandler apiHandler) { // These are both just retrieving information about files, apart from using _bookSelection.CurrentSelection.FolderPath. apiHandler.RegisterEndpointLegacy("image/info", HandleImageInfo, false); apiHandler.RegisterEndpointLegacy("image/imageCreditsForWholeBook", HandleCopyImageCreditsForWholeBook, false); }