diff --git a/GanjooRazor/Pages/GanjoorPage.cshtml.cs b/GanjooRazor/Pages/GanjoorPage.cshtml.cs
index cb9e0716..8802af7c 100644
--- a/GanjooRazor/Pages/GanjoorPage.cshtml.cs
+++ b/GanjooRazor/Pages/GanjoorPage.cshtml.cs
@@ -239,10 +239,14 @@ namespace GanjooRazor.Pages
});
}
+ string rawError = await ReadErrorMessageAsync(response);
+ var sanitizerInfo = TryParseSanitizerTextDroppedError(rawError);
+
return Partial("~/Pages/Partials/GanjoorPage/_CommentPartial.cshtml", new _CommentPartialModel()
{
Comment = null,
- Error = await ReadErrorMessageAsync(response),
+ Error = sanitizerInfo != null ? sanitizerInfo.Message : rawError,
+ SanitizerRemainingText = sanitizerInfo?.RemainingText,
InReplyTo = null,
LoggedIn = !string.IsNullOrEmpty(Request.Cookies["Token"]),
PoemId = poemId,
@@ -270,7 +274,7 @@ namespace GanjooRazor.Pages
var response = await secureClient.PutAsync($"{APIRoot.Url}/api/ganjoor/comment/{id}", new StringContent(JsonConvert.SerializeObject(comment), Encoding.UTF8, "application/json"));
if (!response.IsSuccessStatusCode)
{
- return new BadRequestObjectResult(await ReadErrorMessageAsync(response));
+ return await BadRequestFromApiErrorAsync(response);
}
return new JsonResult(true);
});
@@ -1145,7 +1149,7 @@ namespace GanjooRazor.Pages
var response = await secureClient.PutAsync($"{APIRoot.Url}/api/ganjoor/bookmark/{id}", new StringContent(JsonConvert.SerializeObject(note), Encoding.UTF8, "application/json"));
if (!response.IsSuccessStatusCode)
{
- return new BadRequestObjectResult(await ReadErrorMessageAsync(response));
+ return await BadRequestFromApiErrorAsync(response);
}
return new JsonResult(true);
});
diff --git a/GanjooRazor/Pages/GanjoorPageModelBase.cs b/GanjooRazor/Pages/GanjoorPageModelBase.cs
index 738cd5e3..b35a0679 100644
--- a/GanjooRazor/Pages/GanjoorPageModelBase.cs
+++ b/GanjooRazor/Pages/GanjoorPageModelBase.cs
@@ -49,6 +49,41 @@ namespace GanjooRazor.Pages
return JsonConvert.DeserializeObject(await response.Content.ReadAsStringAsync());
}
+ ///
+ /// Shape of the special error string the API sends (still just a plain string, see
+ /// GanjoorService._BuildSanitizerDroppedTextError on the RMuseum side) when a comment/note/
+ /// suggestion was rejected because sanitizing it had to drop real text. Message is the
+ /// human-readable explanation; RemainingText is the plain text that would have remained,
+ /// so the client can diff it against what the user actually typed and show exactly what
+ /// would have been dropped.
+ ///
+ protected class SanitizerTextDroppedInfo
+ {
+ public bool SanitizerTextDropped { get; set; }
+ public string Message { get; set; }
+ public string RemainingText { get; set; }
+ }
+
+ ///
+ /// Returns the parsed if rawErrorMessage is that
+ /// special shape, or null for any ordinary plain-text error (including when rawErrorMessage
+ /// isn't JSON at all, which is the common case).
+ ///
+ protected static SanitizerTextDroppedInfo TryParseSanitizerTextDroppedError(string rawErrorMessage)
+ {
+ if (string.IsNullOrWhiteSpace(rawErrorMessage) || rawErrorMessage.TrimStart()[0] != '{')
+ return null;
+ try
+ {
+ var info = JsonConvert.DeserializeObject(rawErrorMessage);
+ return (info != null && info.SanitizerTextDropped) ? info : null;
+ }
+ catch
+ {
+ return null;
+ }
+ }
+
///
/// Runs against an HttpClient authenticated from the current
/// session cookies (via ). If the session
@@ -61,6 +96,29 @@ namespace GanjooRazor.Pages
/// (rather than a bare 400) should keep their own using/PrepareClient block instead - wrapping
/// those here would silently change what the browser shows on a real (non-AJAX) form submit.
///
+ ///
+ /// Builds the BadRequest to return for an AJAX handler from a failed API response: the
+ /// plain error string as before for an ordinary error, or - when it's the "sanitizing had
+ /// to drop real text" case - a small JSON object ({ sanitizerTextDropped, message,
+ /// remainingText }) so the client's error callback can show the user what got dropped
+ /// instead of just displaying raw text.
+ ///
+ protected static async Task BadRequestFromApiErrorAsync(HttpResponseMessage response)
+ {
+ string rawError = await ReadErrorMessageAsync(response);
+ var sanitizerInfo = TryParseSanitizerTextDroppedError(rawError);
+ if (sanitizerInfo != null)
+ {
+ return new BadRequestObjectResult(new
+ {
+ sanitizerTextDropped = true,
+ message = sanitizerInfo.Message,
+ remainingText = sanitizerInfo.RemainingText
+ });
+ }
+ return new BadRequestObjectResult(rawError);
+ }
+
protected async Task WithSecureClientAsync(
Func> operation,
IActionResult unauthorizedResult = null)
diff --git a/GanjooRazor/Pages/Misc/Photos.cshtml b/GanjooRazor/Pages/Misc/Photos.cshtml
index 27703ad4..a0d57c57 100644
--- a/GanjooRazor/Pages/Misc/Photos.cshtml
+++ b/GanjooRazor/Pages/Misc/Photos.cshtml
@@ -61,6 +61,7 @@
var form = $(this);
var url = form.attr('action');
+ var originalHtml = form.find('textarea').val();
$.ajax({
type: "POST",
@@ -72,10 +73,18 @@
$(buttonSelector).prop("disabled", false);
},
success: function(data) {
- $(data).appendTo(parent1);
+ var $rendered = $(data);
+ var hadAnyError = $rendered.is('#comment-error');
+ var hadSanitizerError = checkAppendedHtmlForSanitizerError($rendered, originalHtml);
+
+ if (!hadSanitizerError) {
+ $rendered.appendTo(parent1);
+ }
$(buttonSelector).text('پیشنهاد');
$(buttonSelector).prop("disabled", false);
- form[0].reset();
+ if (!hadAnyError) {
+ form[0].reset();
+ }
},
});
diff --git a/GanjooRazor/Pages/Misc/Photos.cshtml.cs b/GanjooRazor/Pages/Misc/Photos.cshtml.cs
index c7107603..88149deb 100644
--- a/GanjooRazor/Pages/Misc/Photos.cshtml.cs
+++ b/GanjooRazor/Pages/Misc/Photos.cshtml.cs
@@ -113,7 +113,7 @@ namespace GanjooRazor.Pages
return Page();
}
- private IActionResult SpecLineErrorPartial(string error)
+ private IActionResult SpecLineErrorPartial(string error, string remainingText = null)
{
return Partial("_PoetSpecLinePartial", new _PoetSpecLinePartialModel()
{
@@ -121,7 +121,8 @@ namespace GanjooRazor.Pages
{
Id = 0,
Contents = error
- }
+ },
+ SanitizerRemainingText = remainingText
});
}
@@ -155,7 +156,9 @@ namespace GanjooRazor.Pages
});
}
- return SpecLineErrorPartial(await ReadErrorMessageAsync(response));
+ string rawError = await ReadErrorMessageAsync(response);
+ var sanitizerInfo = TryParseSanitizerTextDroppedError(rawError);
+ return SpecLineErrorPartial(sanitizerInfo != null ? sanitizerInfo.Message : rawError, sanitizerInfo?.RemainingText);
}, SpecLineErrorPartial(NotLoggedInMessage));
}
diff --git a/GanjooRazor/Pages/Misc/_PoetSpecLinePartial.cshtml b/GanjooRazor/Pages/Misc/_PoetSpecLinePartial.cshtml
index b5e85ca7..b7519eb4 100644
--- a/GanjooRazor/Pages/Misc/_PoetSpecLinePartial.cshtml
+++ b/GanjooRazor/Pages/Misc/_PoetSpecLinePartial.cshtml
@@ -3,8 +3,15 @@
Layout = null;
}
-
خطا
- @Html.Raw(Model.Error) + @Model.Error