Copied to clipboard

Flag this post as spam?

This post will be reported to the moderators as potential spam to be looked at


  • Nicholas Westby 2054 posts 7103 karma points c-trib
    Nov 18, 2014 @ 01:20
    Nicholas Westby
    0

    Change Action Value on Surface Controller Form?

    Umbraco 7.1.8.

    Since I'm rewriting URL's for an international site (e.g., site.com/en-us/page rewrites to site.com/page?lang=en-us), I need to change my forms so the action is set to the raw URL rather than the rewritten URL. In Umbraco 4 and Umbraco 5, I accomplished this with a ControlAdapter that I set in the Form.browser. However, this seems to be completely ignored by Umbraco 7... my guess is that Html.BeginUmbracoForm bypasses the usual way of rendering form tags.

    I'm basically trying to create a class like this to change the value of the action attribute on forms: https://github.com/umbraco/Umbraco-CMS/blob/eae00873073f20c60e355ec6e95ff6259ad2652b/src/Umbraco.Web/umbraco.presentation/umbraco/urlRewriter/UrlRewriterFormWriter.cs

    I am also modifying the form.browser file to point to my class, as is done here: https://github.com/umbraco/Umbraco-CMS/blob/eae00873073f20c60e355ec6e95ff6259ad2652b/src/Umbraco.Web.UI/App_Browsers/Form.browser

    However, when I set a breakpoint in my render function, it is never hit.

    Basically, instead of this:

    <form action="/page?lang=en-us" enctype="multipart/form-data" method="post">
        <input id="Username" name="Username" type="text" value="">
        <input id="Password" name="Password" type="password">
        <button type="submit">Log In</button>
        <input name="ufprt" type="hidden" value="long-encoded-value">
    </form>
    

    I want to do this:

    <form action="/en-us/page" enctype="multipart/form-data" method="post">
        <input id="Username" name="Username" type="text" value="">
        <input id="Password" name="Password" type="password">
        <button type="submit">Log In</button>
        <input name="ufprt" type="hidden" value="long-encoded-value">
    </form>
    

    Which will prevent this:

    enter image description here

    I've tried a few other things, but Umbraco seems to take over and changes the form action to the rewritten URL. For reference, this is how I'm rendering the surface controller:

    @using (Html.BeginUmbracoForm<MemberLoginSurfaceController>("MemberLogin",
        new { action = Request.RawUrl }, FormMethod.Post))
    {
        <h2>@Request.RawUrl</h2>
        @Html.TextBoxFor(m => m.Username, new { placeholder = "Username" })
        @Html.PasswordFor(m => m.Password, new { placeholder = "Password" })
        <button type="submit">Log In</button>
        @Html.ValidationSummary()
    }
    
  • Nicholas Westby 2054 posts 7103 karma points c-trib
    Nov 18, 2014 @ 17:20
    Nicholas Westby
    0

    To add some more info to this, here is my rewrite rule (I only have one) from my UrlRewriting.config:

    <add
      name="Localize URL" rewriteUrlParameter="ExcludeFromClientQueryString" ignoreCase="true"
      virtualUrl="^~/(?&lt;culture&gt;[a-z]{2}-[a-z]{2})(?&lt;url&gt;/.+$|$)"
      destinationUrl="~${url}?lang=${culture}" />
    

    Here is my form.browser file:

    <browsers>
      <browser refID="Default">
        <controlAdapters>
          <adapter
            controlType="System.Web.UI.HtmlControls.HtmlForm"
            adapterType="MySite.Extensions.ControlAdapters.MySiteFormRewriter" />
        </controlAdapters>
      </browser>
    </browsers>
    

    Here is my control adapter:

    namespace MySite.Extensions.ControlAdapters
    {
    
        using System;
        using System.IO;
        using System.Web;
        using System.Web.UI;
    
        public class MySiteFormRewriter : System.Web.UI.Adapters.ControlAdapter
        {
    
            protected override void Render(HtmlTextWriter writer)
            {
                // Breakpoint here is never hit.
                base.Render(new MySiteFormHtmlWriter(writer));
            }
    
            public class MySiteFormHtmlWriter : HtmlTextWriter
            {
    
                public MySiteFormHtmlWriter(HtmlTextWriter writer)
                    : base(writer)
                {
                    this.InnerWriter = writer.InnerWriter;
                }
    
                public MySiteFormHtmlWriter(TextWriter writer)
                    : base(writer)
                {
                    this.InnerWriter = writer;
                }
    
                public override void WriteAttribute(string name, string value, bool encode)
                {
                    // ...Code omitted for brevity...
                    base.WriteAttribute(name, value, encode);
                }
    
            }
    
        }
    
    }
    

    I renamed a few things and omitted some code, but this should give you a good idea of what I'm trying to do.

  • Nicholas Westby 2054 posts 7103 karma points c-trib
    Nov 19, 2014 @ 01:51
    Nicholas Westby
    0

    I'm thinking control adapters only work on server-side controls, and I would guess forms created in razor with Html.BeginUmbracoForm don't qualify. I'm thinking that means I'll have to reimplement BeginUmbracoForm myself. Shouldn't be too hard... basically, looks like a normal form, along with a hidden input field, ufprt, that gets created here:

    https://github.com/umbraco/Umbraco-CMS/blob/eae00873073f20c60e355ec6e95ff6259ad2652b/src/Umbraco.Web/HtmlHelperRenderExtensions.cs#L240

    I will post some code later if I get this working. I'm also thinking the inability to set the form action when using BeginUmbracoForm is a bug, so I'll submit that too and paste a link to it here later.

  • Nicholas Westby 2054 posts 7103 karma points c-trib
    Nov 19, 2014 @ 23:33
    Nicholas Westby
    0

    The code isn't pretty, but I got it working:

    enter image description here

    Using the aforementioned Umbraco code, this is the workaround I created:

    @{
        var controller = WebUtility.HtmlEncode("MemberLoginSurface");
        var action = WebUtility.HtmlEncode("MemberLogin");
        var area = "";
        var surfaceRouteParams = string.Format("c={0}&a={1}&ar={2}", controller, action, area);
        var routeValue = surfaceRouteParams.EncryptWithMachineKey();
        var url = Request.RawUrl;
    }
    
    <form action="@url" method="post">
        <h2>@Request.RawUrl</h2>
        @Html.TextBoxFor(m => m.Username, new { placeholder = "Username" })
        @Html.PasswordFor(m => m.Password, new { placeholder = "Password" })
        <button type="submit">Log In</button>
        <input name="ufprt" type="hidden" value="@Html.Raw(routeValue)" />
        @Html.ValidationSummary()
    </form>
    

    Basically, all I did was created a normal form and added the ufprt hidden field with an encrypted value (used by Umbraco to determine which controller/action to route to).

  • Nicholas Westby 2054 posts 7103 karma points c-trib
    Nov 19, 2014 @ 23:38
    Nicholas Westby
    102

    Doh! I realized I was confusing route values and HTML attributes. This is the newer (better) way of getting this to work:

    @using (Html.BeginUmbracoForm<MemberLoginSurfaceController>("MemberLogin",
        null, new { action = Request.RawUrl }, FormMethod.Post))
    {
        <h2>@Request.RawUrl</h2>
        @Html.TextBoxFor(m => m.Username, new { placeholder = "Username" })
        @Html.PasswordFor(m => m.Password, new { placeholder = "Password" })
        <button type="submit">Log In</button>
        @Html.ValidationSummary()
    }
    

    All I did was set the action HTML attribute to the raw URL.

Please Sign in or register to post replies

Write your reply to:

Draft