0

i wrote this piece of code to use TinyMCE (javascript "richtext" editor) on a ASP page. The ASP page itself contains a textbox called "art_content", that generates a ClientID like "ctl00_hold_selectionblock_art_content".

One problem i had was to make a "safe" code that is stored by my aplication, and loaded back to HTML when i goes to the client, then it return to it's "safe" mode before submiting (or postback) to prevent the HTML check error on server side.

My Javascript seems to work properly, but i get the error anyway. It looks like it's not working, although the function is triggering.

Help!

tinymce.init
(
  {
    selector:'#ctl00_hold_selectionblock_art_content',
    plugins:
      ['table link image textcolor pagebreak code visualblocks charmap'],
    toolbar:
      'bold,italic,underline'
  }
);
function SafeCms(text,encode)
{
  if(encode)
  {
    text=text.replaceAll('<','{{');
    text=text.replaceAll('>','}}');
  }
  else
  {
    text=text.replaceAll('{{','<');
    text=text.replaceAll('}}','>');
  }
  return text;
}
$(document).ready
(
  function()
  {
$('#ctl00_hold_selectionblock_art_content').val(SafeCms($('#ctl00_hold_selectionblock_art_content').val(),false));
    $("form").submit
    (
      function()
      {
        tinymce.triggerSave();
        $('#ctl00_hold_selectionblock_art_content').val(SafeCms($('#ctl00_hold_selectionblock_art_content').val(),true));
      }
    );
  }
);

UPDATE: Error presented by server side (page)

A potentially dangerous Request.Form value was detected from the client (ctl00$hold$selectionblock$art_content="<p>ab<em>acac</em>ac...").

CallStack:
[HttpRequestValidationException (0x80004005): Um valor possivelmente perigoso Request.Form foi detectado no cliente (ctl00$hold$selectionblock$art_content="<p>ab<em>acac</em>ac...").]
  System.Web.HttpRequest.ValidateString(String s, String valueName, String collectionName) +8818818
  System.Web.HttpRequest.ValidateNameValueCollection(NameValueCollection nvc, String collectionName) +111
  System.Web.HttpRequest.get_Form() +129
  System.Web.HttpRequest.get_HasForm() +8818919
  System.Web.UI.Page.GetCollectionBasedOnMethod(Boolean dontReturnNull) +97
  System.Web.UI.Page.DeterminePostBackMode() +63
  System.Web.UI.Page.ProcessRequestMain(Boolean includeStagesBeforeAsyncPoint, Boolean includeStagesAfterAsyncPoint) +6785
  System.Web.UI.Page.ProcessRequest(Boolean includeStagesBeforeAsyncPoint, Boolean includeStagesAfterAsyncPoint) +242
  System.Web.UI.Page.ProcessRequest() +80
  System.Web.UI.Page.ProcessRequestWithNoAssert(HttpContext context) +21
  System.Web.UI.Page.ProcessRequest(HttpContext context) +49
  ASP.content_quality_knownledges_aspx.ProcessRequest(HttpContext context) in c:\Users\Sammuel\AppData\Local\Temp\Temporary ASP.NET Files\root\a3cdd555\dbee70c6\App_Web_e_7yzdu3.2.cs:0
  System.Web.CallHandlerExecutionStep.System.Web.HttpApplication.IExecutionStep.Execute() +181
  System.Web.HttpApplication.ExecuteStep(IExecutionStep step, Boolean& completedSynchronously) +75
ConnorsFan
  • 70,558
  • 13
  • 122
  • 146
SammuelMiranda
  • 420
  • 4
  • 29

3 Answers3

1

I had this problem, in your web.config Add:

<system.web>
    <pages validateRequest="false" />
</system.web>

If you are on .net 4.0 make sure you add this in your web.config

<httpRuntime requestValidationMode="2.0" />
CMedina
  • 4,034
  • 3
  • 24
  • 39
  • thanks for the answer, but the idea is not to do that. see that my javascript should replace the HTML in the textarea, prior to the submit so that command (validateRequest=false) would not be needed. i wrote that entire function with the purpose of making the content "safe" to travel. – SammuelMiranda Dec 01 '16 at 10:17
  • 1
    What a very very bad practice to disable request validation for **all** pages. Better to disable it only at the page you want `<%@ Page validateRequest="false"` – Jérôme MEVEL Dec 02 '16 at 08:06
0

As suggested in this article, you can remove the offending HTML characters from the editor content in the SaveContent event. If necessary, you can restore them in the BeforeSetContent event.

For TinyMCE 4, you can use this code:

tinymce.init({
    selector: '#ctl00_hold_selectionblock_art_content',
    plugins: ['table link image textcolor pagebreak code visualblocks charmap'],
    toolbar: 'bold,italic,underline',
    setup: function (editor) {
        editor.on("SaveContent", function (e) {
            e.content = SafeCms(e.content, true);
        });
        editor.on('BeforeSetContent', function (e) {
            e.content = SafeCms(e.content, false);
        });
    }
});

function SafeCms(text, encode) {
    if (encode) {
        return text.replace(/</g, '{{').replace(/>/g, '}}');
    }
    else {
        return text.replace(/{{/g, '<').replace(/}}/g, '>');
    }
}

I use replace with a regular expression in SafeCms instead of replaceAll (which does not exist by default in Javascript).

ConnorsFan
  • 70,558
  • 13
  • 122
  • 146
  • You are welcome. By the way, you could avoid the mangled `ctl00_hold_selectionblock_art_content` ID with one of these two methods: (1) by setting `ClientIDMode="Static"` for the TextBox and using `selector: '#art_content'`, or (2) with the binding expression syntax `selector: '#<%= art_content.ClientID %>'`. – ConnorsFan Dec 01 '16 at 13:40
  • 1
    yes i know, i posted the code with the ID resolved for example porpuses, it's actually a custom control that i made that writes the javascript code by it self on "render" method – SammuelMiranda Dec 01 '16 at 14:11
0

Actually you don't need to manually replace characters, you might always end-up with special cases that you didn't think about and ASP.NET blocks because of security issue.

The best option in my opinion is to encode the content then decode it server side.

I copy my TinyMCE content to an invisible textbox (so user does't end-up seeing an encoded content in his HTML editor)

function encodeContent(copyToId, htmlContent) {
    var encodedHtml = encodeURIComponent(htmlContent);
    $('#' + copyToId).val(encodedHtml);
}

Then on server side I just get the content of this textbox and decode it

var decodedHtml = HttpUtility.UrlDecode(this.TextBoxNewContent.Text);

I don't know how you initialize your TinyMCE but I personally wrap it in a JS function that I register for calling in the Page_PreRender method. In real I've many more parameters, here is a simplified code:

protected void Page_PreRender(object sender, EventArgs e)
{
    var script = "createHtmlEditor('"
       + this.tinyMceEditor.ClientID + "','"
       + this.TextBoxNewContent.ClientID + "');";

    var scriptId = "createHtmlEditor_" + this.tinyMceEditor.ClientID;
    ScriptManager.RegisterStartupScript(this, this.GetType(), scriptId, script, true);
}

encodeURIComponent is a native JavaScript function and HttpUtility static class is located in System.Web namespace

NOTE: when I say "invisible" textbox I'm talking about CSS invisible. Because in ASP.NET Visibility="false" will result of your HTML element not created!

Jérôme MEVEL
  • 7,031
  • 6
  • 46
  • 78