Direct Link to Unpublish

Topics: Troubleshooting
Jan 11, 2013 at 6:48 PM
Edited Jan 11, 2013 at 6:50 PM

I started this on the Customizing Orchard and Administration side, but I am not getting much help. Here is the link.

Any help would be great, just trying to get this to work. Basically I am wanting to use a direct link to unpublish items. This page is only accessible by administrators.

Developer
Jan 11, 2013 at 10:51 PM

Have a look at the AdminController of the Contents module. Once you know which action, controller and area (module) to invoke, it is easy to generate such url (as Bertrand mentions in the other post, you can use the Html.ActionLink helper for that. Or use Url.Action to generate just the url).

However, the fun part may be that the Unpublish action requires to be invoked using a POST request, so you probably want to wrap a <form> element around your link and have the click event submit the form (while preventing the default action of the link's click event of course).

Jan 12, 2013 at 2:12 AM

Thanks for explaining it very well. I believe I can work with it from here. Thanks for your help. I'll be sure to update when I'm finished and show what I've done.

Jan 14, 2013 at 8:03 PM
Edited Jan 14, 2013 at 8:10 PM

I spent some time on the issue today. A friend of mine is a web developer and he helped me determine where this is breaking for me. We placed the following break in the core/shapes/scripts/base.js to determine if I am unable to get the verification token. I am unable to get the token at any place of my site, except the /Admin/Contents/List page. I will admit I am using Windows Authentication and the AD Authorization module. Is anyone getting this out of the box with Orchard authentication? See red text below and give it a try, if someone doesn't mind. I want to see if results are the same across other sites. If it fails to obtain this on the page you will get a popup that alerts you. Thanks in advance! :)

 

$(function () {
        var magicToken = $("input[name=__RequestVerificationToken]").first();
        if (magicToken.size() ===0 ) { alert("Failed To Get Token"); return; } // no sense in continuing if form POSTS will fail
        $("a[itemprop~=UnsafeUrl]").each(function () {
            var _this = $(this);
            var hrefParts = _this.attr("href").split("?");
            var form = $("<form action=\"" + hrefParts[0] + "\" method=\"POST\" />");
            form.append(magicToken.clone());
            if (hrefParts.length > 1) {
                var queryParts = hrefParts[1].split("&");
                for (var i = 0; i < queryParts.length; i++) {
                    var queryPartKVP = queryParts[i].split("=");
                    //trusting hrefs in the page here
                    form.append($("<input type=\"hidden\" name=\"" + decodeURIComponent(queryPartKVP[0]) + "\" value=\"" + decodeURIComponent(queryPartKVP[1]) + "\" />"));
                }
            }
            form.css({ "position": "absolute", "left": "-9999em" });
            $("body").append(form);
            _this.click(function () {
                if (_this.filter("[itemprop~='RemoveUrl']").length == 1) {
                    if (!confirm(confirmRemoveMessage)) {
                        return false;
                    }
                }

                form.submit();
                return false;
            });
        });
    });

 

Jan 14, 2013 at 10:42 PM

It's definitely not getting the token. FYI this is the error and stack trace it gives:

The required anti-forgery form field "__RequestVerificationToken" is not present. 
Description: An unhandled exception occurred during the execution of the current web request. Please review the stack trace for more information about the error and where it originated in the code. 

Exception Details: System.Web.Mvc.HttpAntiForgeryException: The required anti-forgery form field "__RequestVerificationToken" is not present.

Source Error: 

An unhandled exception was generated during the execution of the current web request. Information regarding the origin and location of the exception can be identified using the exception stack trace below.  

Stack Trace: 


[HttpAntiForgeryException (0x80004005): The required anti-forgery form field "__RequestVerificationToken" is not present.]
   System.Web.Helpers.AntiXsrf.TokenValidator.ValidateTokens(HttpContextBase httpContext, IIdentity identity, AntiForgeryToken sessionToken, AntiForgeryToken fieldToken) +757
   System.Web.Helpers.AntiXsrf.AntiForgeryWorker.Validate(HttpContextBase httpContext) +163
   Orchard.Mvc.AntiForgery.AntiForgeryAuthorizationFilter.OnAuthorization(AuthorizationContext filterContext) in c:\Users\sebros\My Projects\Orchard\src\Orchard\Mvc\AntiForgery\AntiForgeryAuthorizationFilter.cs:38
   System.Web.Mvc.ControllerActionInvoker.InvokeAuthorizationFilters(ControllerContext controllerContext, IList`1 filters, ActionDescriptor actionDescriptor) +156
   System.Web.Mvc.ControllerActionInvoker.InvokeAction(ControllerContext controllerContext, String actionName) +720
   System.Web.Mvc.<>c__DisplayClass1d.b__19() +40
   System.Web.Mvc.Async.<>c__DisplayClass1.b__0() +15
   System.Web.Mvc.Controller.EndExecuteCore(IAsyncResult asyncResult) +53
   System.Web.Mvc.Async.<>c__DisplayClass4.b__3(IAsyncResult ar) +15
   System.Web.Mvc.<>c__DisplayClass8.b__3(IAsyncResult asyncResult) +42
   System.Web.Mvc.Async.<>c__DisplayClass4.b__3(IAsyncResult ar) +15
   Orchard.Mvc.Routes.HttpAsyncHandler.EndProcessRequest(IAsyncResult result) in c:\Users\sebros\My Projects\Orchard\src\Orchard\Mvc\Routes\ShellRoute.cs:162
   System.Web.CallHandlerExecutionStep.System.Web.HttpApplication.IExecutionStep.Execute() +607
   System.Web.HttpApplication.ExecuteStep(IExecutionStep step, Boolean& completedSynchronously) +288
 


--------------------------------------------------------------------------------

Coordinator
Jan 15, 2013 at 12:26 AM

That script relies on the flawed assumption that there is going to be another form on the page that will have the token. Instead, you should get your own copy of the token. Look in Orchard.Mvc.Html.HtmlHelperExtensions to find the various Html.* helpers you can use to get the token.

Jan 15, 2013 at 2:35 AM

I will look into it tomorrow and post back. Thanks for pointing me in the right direction.

Jan 16, 2013 at 12:01 AM
Edited Jan 16, 2013 at 12:20 AM

Think I got everything I need, except how to get my current anti-forgery token to populate:

 

<form action="/Admin/Contents/Unpublish/{Content.Id}" method="POST"><input name="__RequestVerificationToken" type="hidden" value="NEED HELP HERE"><input type="submit" name="Submit" value="Submit"></form>

 

Let me explain a little better where and how I am using this. I am using a custom layout query that returns my requested information. One of the properties of this layout is a Custom Value, where I am forcing it to rewrite the output to archive (see above) returned item.

I have found some of the MVC helpers, as Bertrand suggested above. I tried found the following:

  • Html.AntiForgeryTokenValueOrchard()
  • Html.GetAntiForgeryTokenName()

What would be the proper syntax for my value field so that it gets and supplies the needed information when the form is submitted? When entered as simply Html.AntiForgeryTokenValueOrchard() or Html.GetAntiForgeryTokenName(), I get:

 

Server Error in '/' Application.
--------------------------------------------------------------------------------

The anti-forgery token could not be decrypted. If this application is hosted by a Web Farm or cluster, ensure that all machines are running the same version of ASP.NET Web Pages and that the <machineKey> configuration specifies explicit encryption and validation keys. AutoGenerate cannot be used in a cluster. 
Description: An unhandled exception occurred during the execution of the current web request. Please review the stack trace for more information about the error and where it originated in the code. 

Exception Details: System.Web.Mvc.HttpAntiForgeryException: The anti-forgery token could not be decrypted. If this application is hosted by a Web Farm or cluster, ensure that all machines are running the same version of ASP.NET Web Pages and that the <machineKey> configuration specifies explicit encryption and validation keys. AutoGenerate cannot be used in a cluster.

Source Error: 

An unhandled exception was generated during the execution of the current web request. Information regarding the origin and location of the exception can be identified using the exception stack trace below.  

Stack Trace: 


[HttpAntiForgeryException (0x80004005): The anti-forgery token could not be decrypted. If this application is hosted by a Web Farm or cluster, ensure that all machines are running the same version of ASP.NET Web Pages and that the <machineKey> configuration specifies explicit encryption and validation keys. AutoGenerate cannot be used in a cluster.]
   System.Web.Helpers.AntiXsrf.AntiForgeryTokenSerializer.Deserialize(String serializedToken) +261
   System.Web.Helpers.AntiXsrf.AntiForgeryWorker.Validate(HttpContextBase httpContext) +74
   Orchard.Mvc.AntiForgery.AntiForgeryAuthorizationFilter.OnAuthorization(AuthorizationContext filterContext) in c:\Users\sebros\My Projects\Orchard\src\Orchard\Mvc\AntiForgery\AntiForgeryAuthorizationFilter.cs:38
   System.Web.Mvc.ControllerActionInvoker.InvokeAuthorizationFilters(ControllerContext controllerContext, IList`1 filters, ActionDescriptor actionDescriptor) +156
   System.Web.Mvc.ControllerActionInvoker.InvokeAction(ControllerContext controllerContext, String actionName) +720
   System.Web.Mvc.<>c__DisplayClass1d.<BeginExecuteCore>b__19() +40
   System.Web.Mvc.Async.<>c__DisplayClass1.<MakeVoidDelegate>b__0() +15
   System.Web.Mvc.Controller.EndExecuteCore(IAsyncResult asyncResult) +53
   System.Web.Mvc.Async.<>c__DisplayClass4.<MakeVoidDelegate>b__3(IAsyncResult ar) +15
   System.Web.Mvc.<>c__DisplayClass8.<BeginProcessRequest>b__3(IAsyncResult asyncResult) +42
   System.Web.Mvc.Async.<>c__DisplayClass4.<MakeVoidDelegate>b__3(IAsyncResult ar) +15
   Orchard.Mvc.Routes.HttpAsyncHandler.EndProcessRequest(IAsyncResult result) in c:\Users\sebros\My Projects\Orchard\src\Orchard\Mvc\Routes\ShellRoute.cs:162
   System.Web.CallHandlerExecutionStep.System.Web.HttpApplication.IExecutionStep.Execute() +607
   System.Web.HttpApplication.ExecuteStep(IExecutionStep step, Boolean& completedSynchronously) +288
 


--------------------------------------------------------------------------------
Version Information: Microsoft .NET Framework Version:4.0.30319; ASP.NET Version:4.0.30319.17626 

I did look at my server's IIS and the Machine Key is set to autogenerate, I'm not sure if that is a problem. As always, thanks in advance for everyone's help.

Coordinator
Jan 16, 2013 at 1:24 AM

Ah, no that's the problem, you need to set the machine key to a fixed value. See: http://docs.orchardproject.net/Documentation/Setting-up-a-machine-key

Jan 16, 2013 at 4:03 AM

Wow, I had a feeling it was something simple....

Thanks again, I'll give it a try tomorrow when I get back to work.

Jan 16, 2013 at 6:01 PM
Edited Jan 16, 2013 at 6:29 PM

I followed the guide that you posted but I'm still getting the error above. I have checked the web.config for the site and it's decryption and validation keys match that of the site in IIS.

I found this post while looking up this error. Sounds like I am just using the wrong HTTP.* helper to generate the code. I'll do some more digging through the list of helpers, but in the meantime if someone knows exactly what I need let me know.

And here is my updated form that is submitting this:

<form action="/Admin/Contents/Unpublish/{Content.Id}" method="POST"><input name="__RequestVerificationToken" type="hidden" value="@Html.AntiForgeryTokenValueOrchard()"><input type="submit" name="Submit" value="Submit"></form>

Found this workitem in my searches too. Piedone states:

"

I'm sorry, but I think there's some misunderstanding, a POST indeed requires a token, but passing a token only works when Module.txt contains "AntiForgery: disabled".

When passing a completely valid token when Module.txt contains "AntiForgery: enabled", you receive the error "A required anti-forgery token was not supplied or was invalid."

"

Jan 16, 2013 at 7:18 PM
Edited Jan 16, 2013 at 11:27 PM

I've been looking at the media module and how it's passing the antiforgery key to perform actions. It appears it's using:

@using (Html.BeginFormAntiForgeryPost()) {
  *content*
}

Since this is just a custom value from a query/projection layout, I'm not really sure how to do this/ if its even possible. If this were in .cshtml, I'd think I could just do:

@using(Html.BeginFormAntiForgeryPost("/Admin/Contents/Unpublish/{Content.Id}")
{
<input name="__RequestVerificationToken" type="hidden" value="@Html.AntiForgeryTokenValueOrchard()">
<input name="Submit" type="submit" value="Submit">
}

Just for giggles I tried to paste the above into the Custom Value property's "Rewrite Text" field and it just puts the @using(Html.BeginFormAntiForgeryPost(stuff) into plain text. I know I am pushing the limits of the built-in features of the CMS and the Projections module. Anyone with any thoughts on something I could try?

Coordinator
Jan 17, 2013 at 2:14 AM

Wait, *where* are you trying to do that from?

Jan 17, 2013 at 6:16 AM
Edited Jan 17, 2013 at 6:29 AM

A projection page. I am returning all items of a certain content type. I am using a custom layout that returns only what information I need. One of the properties of this layout is a Custom Value, where I am forcing it to rewrite the output to create a button to essentially archive the item. This page is only accessible by certain users on the site; but for workflow reasons I would like to have something on the front-end to archive (or clear out) old entries, and not require them to have to go through the admin side of the site, and sort through pages of content items.

So to answer the question, this is being done on a projection page. The custom layout is of course designed within the query. My knowledge only extends to HTML and CSS, and I am forced to use the built-in features of Orchard, so I don't doubt I am pushing it a little bit. So far I have been able to make things work well without having to use many modules, which has been nice thus far. Thanks for your help.

Coordinator
Jan 17, 2013 at 5:59 PM

Right, you can't put server code in there. All that code you tried is only going to work from a cshtml file.

Jan 17, 2013 at 6:11 PM

Out of curiosity, what is the module that controls the publishing/unpublishing of content?

Coordinator
Jan 17, 2013 at 6:24 PM

Core.

Sep 5, 2013 at 10:11 PM
bertrandleroy wrote:
Ah, no that's the problem, you need to set the machine key to a fixed value. See: http://docs.orchardproject.net/Documentation/Setting-up-a-machine-key
Thanks, I just beat my head against this issue for hours and read many StackOverFlow and other posts regarding AntiForgery before I stumbled across this. This solved my problem immediately.
Coordinator
Sep 6, 2013 at 12:28 AM
Would it help if I told you it was in the release notes all along? ;)