MCMS: Retaining URL on form postback

One major problem with MCMS is that it tends to use javascript to modify the action attribute of the form. While this works in IE with Javascript on, it doens’t help anyone else.

To solve this I extended the HtmlForm class to modify the attribute on the way out (but still preserve any other attribute it adds). This had to be done as HtmlForm actually removes ‘action’ from the attributes list and adds its own. The finished product is shown below.

Edit (2006-09-22): Changed the base class of ConsoleEx from CmsConsole (an import alias) to Microsoft.ContentManagement.WebControls.ConsoleControls.Console

public class CmsForm : HtmlForm
{
 public CmsForm()
 {
 }

 // Overriding the placement of attributes to put our own 'action' in
 protected override void RenderAttributes(HtmlTextWriter writer)
 {
  StringWriter sw = new StringWriter();

  // Render attributes to a buffer
  HtmlTextWriter htw = new HtmlTextWriter(sw);
  base.RenderAttributes( htw );
  string attributes =  sw.ToString();
  int actionIndex = attributes.IndexOf("action=");

  // Find the rendered action attribute and remove it
  int endActionIndex = attributes.IndexOf("\"", actionIndex + "action=".Length + 1);
  if (actionIndex!=0) writer.Write( attributes.Substring(0, actionIndex) );


  // Add all base attributes (exception action)
  writer.Write( attributes.Substring(endActionIndex+1) );

  // Add our custom action
  writer.WriteAttribute("action", this.GetActionAttribute(), true);
 }

 // Return the custom action attribute
 private string GetActionAttribute()
 {
  try
  {
   CmsHttpContext cmsContext = CmsHttpContext.Current;

   // If we are in published mode, return the current posting's path
   if (cmsContext.Mode==PublishingMode.Published && cmsContext.Posting!=null)
   {
    // Get the querystring without NR (CMS) items
    string publishedQueryString = this.GetQueryString();

    return (publishedQueryString.Length!=0)
     ? cmsContext.Posting.Url + "?" + publishedQueryString
     : cmsContext.Posting.Url;
   }
  }
  catch(CmsAccessDeniedException)
  {
  }

  string scriptPath = Context.Request.FilePath;
  string queryString = Context.Request.Url.Query.TrimStart('?');

  return (queryString.Length!=0)
   ? scriptPath + "?" + queryString
   : scriptPath;
 }

 // Retrieves the querystring without CMS items (start with NR)
 private string GetQueryString()
 {
  StringBuilder sb = new StringBuilder();

  foreach(string key in Context.Request.QueryString.Keys)
   if (!key.StartsWith("NR"))
    sb.AppendFormat("{0}={1}&", key, Context.Request.QueryString[key]);

  return (sb.Length==0)
   ? String.Empty
   : sb.ToString(0, sb.Length-1);
 }
}

Enter problem #2. Seems the Console does not do the preferred var is TypeName method of checking types, but goes all out to var.GetType()==typeof(HtmlForm). This prevents subclasses of HtmlForm from being used. The only way around this, unfortunately, is to subclass the Console class and perform the check properly (and re-implement any private methods needed). So, with a little help from Reflector, we have:

public class ConsoleEx : Microsoft.ContentManagement.WebControls.ConsoleControls.Console
{
 private HtmlForm _webForm;

 public ConsoleEx()
 {
 }

 protected override void OnInit(EventArgs e)
 {
  try
  {
   // Attempt normal functionality
   base.OnInit(e);
  }
  catch(WebAuthorException)
  {
   // Peform custom setting of form
   this.SetPageForm();
  }
 }

 protected override void OnLoad(EventArgs e)
 {
  if (_webForm==null)
  {
   base.OnLoad(e);
  }
  else
  {
   this.RegisterMandatoryScript();
   if (CmsHttpContext.Current.UserCanModifySite)
   {
    this.RegisterUserCanModifySiteScript();
    if ((WebAuthorContext.Current.Mode == WebAuthorContextMode.AuthoringNew) || (WebAuthorContext.Current.Mode == WebAuthorContextMode.AuthoringReedit))
    {
     this.RegisterAuthoringScript();
    }
   }
  }
 }

 private void SetPageForm()
 {
  for (Control control1 = this.Parent; control1 != null; control1 = control1.Parent)
  {
   // Allows for subclassed HtmlForm's
   if (control1 is HtmlForm)
   {
    this._webForm = (HtmlForm) control1;
    return;
   }
  }
  throw new WebAuthorException("The Console control must be embedded within the ASPX Web Form.");
 }

 // "Borrowed" from original implementation
 private void RegisterAuthoringScript()
 {
  if (this.CmsPageFrame)
  {
   if (this.EnableLeaveAuthoringWarning)
   {
    this.Page.RegisterStartupScript("OverrideASPXPostback", "\n\n\n\n\n\n\n");
   }
   else
   {
    this.Page.RegisterStartupScript("TurnOffLeaveAuthoringDialog", "\n\n\n\n\n\n\n");
   }
  }
 }

 // "Borrowed" from original implementation
 private void RegisterMandatoryScript()
 {
  new Uri(this.Page.Request.Url.AbsoluteUri);
  string text1 = JavaScriptEncode( this.Page.Request.Url.PathAndQuery );
  string[] textArray1 = new string[] { "\n\n\n\n\n" } ;
  string text2 = string.Concat(textArray1);
  this.Page.RegisterClientScriptBlock("__CMS_Page", text2);
  
  //this.Page.RegisterStartupScript("ResetFormActionScript", "\n\n\n\n\n\n\n");
 }

 // "Borrowed" from original implementation
 private void RegisterUserCanModifySiteScript()
 {
  if (this.CmsPageFrame)
  {
   string text1 = "";
   text1 = text1 + "";
   text1 = text1 + "";
   text1 = text1 + "";
   text1 = text1 + "";
   text1 = text1 + "";
   if ((WebAuthorContext.Current.Mode == WebAuthorContextMode.AuthoringNew) || (WebAuthorContext.Current.Mode == WebAuthorContextMode.AuthoringReedit))
   {
    text1 = text1 + "";
   }
   this.Page.RegisterClientScriptBlock("ScriptFileReferences", text1);
  }
  CmsHttpContext context1 = CmsHttpContext.Current;
  WebAuthorContext context2 = WebAuthorContext.Current;
  Posting posting1 = context1.Posting;
  Channel channel1 = context1.Channel;
  bool flag1 = WebAuthorContext.Current.RegisteredPlaceholderControls != null;
  string text2 = "";
  if (context1.ChannelItem != null)
  {
   text2 = context1.ChannelItem.QueryString;
  }

  string[] textArray1 = new string[] { "\n\n\n\n\n" } ;
  string text3 = string.Concat(textArray1);
  this.Page.RegisterClientScriptBlock("__CMS_Console", text3);
  this.Page.RegisterHiddenField("__CMS_ConfirmedDelete", "false");
 }

 // "Borrowed" from original implementation
 private string JavaScriptEncode(string source)
 {
  string text1 = source;
  text1 = text1.Replace(@"\", @"\\");
  text1 = text1.Replace("\"", "\\\"");
  text1 = text1.Replace("'", @"\'");
  text1 = text1.Replace("\r", @"\r");
  text1 = text1.Replace("\n", @"\n");
  text1 = text1.Replace("\t", @"\t");
  text1 = text1.Replace("\f", @"\f");
  return text1.Replace("\v", @"\v");
 }
}
This entry was posted in Uncategorized. Bookmark the permalink.

2 Responses to MCMS: Retaining URL on form postback

  1. Anonymous says:

    Hi Richard,I am struggling to understand the 2nd part of your solution I cannot find the CmsConsole class to subclass from and when I subclass from Console and try to make reference to that nothing works on the console. Is it possible to aloaborate please.

  2. Richard says:

    Apologies, CmsConsole was an import shortcut I had made to Microsoft.ContentManagement.WebControls.ConsoleControls.Console.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Connecting to %s