mluker
mluker

Reputation: 702

Get Rendered HTML From Sitecore Item

I need to get the rendered HTML output from a given Sitecore item, assuming it has a layout. I need it to be the latest version of the rendered content whether it's published or not. Using a web request approach like WebClient or HtmlAgility pack will not work because they make the request as an anonymous user which will only render the latest published version (and I need the latest version no matter the state.) Any thoughts? I have everything working I just cant find a way to impersonate or elevate the rights while I execute the page requests.

Upvotes: 1

Views: 1417

Answers (2)

Derek Dysart
Derek Dysart

Reputation: 1396

You could go the WebClient or HtmlAgility pack, but silently login the user based on a token in the query string:

public static class UserExtensions
{
    public const string TokenKey = "UserToken";
    public const string TokenDateKey = "UserTokenDate";

    public static ID CreateUserToken(this User user)
    {
        if (user.IsAuthenticated)
        {
            var token = ID.NewID;
            user.Profile.SetCustomProperty(TokenKey, token.ToString());
            user.Profile.SetCustomProperty(TokenDateKey, DateTime.Now.ToString());
            user.Profile.Save();
            return token;
        }
        else
            return ID.Null;
    }

    public static bool IsTokenValid(this User user, string token, TimeSpan maxAge)
    {
        var tokenId = ID.Null;
        if (ID.TryParse(token, out tokenId))
        {
            var minDate = DateTime.Now.Add(-maxAge);
            var tokenDateString = user.Profile.GetCustomProperty(TokenDateKey);
            var tokenDate = DateTime.MinValue;

            DateTime.TryParse(tokenDateString, out tokenDate);

            if (tokenDate < minDate)
                return false;

            var storedToken = user.Profile.GetCustomProperty(TokenKey);
            var storedTokenId = ID.NewID;
            if (ID.TryParse(storedToken, out storedTokenId))
                return storedTokenId == tokenId;
        }

        return false;
    }
}

Then patch in a HttpRequestProcessor to look for the token:

public class SilentUserLogin : HttpRequestProcessor
{
    public TimeSpan MaximumAge
    {
        get;
        set;
    }

    public override void Process(HttpRequestArgs args)
    {
        var userValue = args.Context.Request.QueryString["user"];
        var tokenValue = args.Context.Request.QueryString["token"];

        if (!string.IsNullOrEmpty(userValue) && !string.IsNullOrEmpty(tokenValue))
        {
            // find user
            var user = User.FromName(userValue, AccountType.User);
            if (user != null)
            {
                // Check token is valid
                if ((user as User).IsTokenValid(tokenValue, MaximumAge))
                {
                    // log user in
                    AuthenticationManager.Login(user as User);
                }
                else
                    Log.Audit("User token has expired for user: '{0}'".FormatWith(user.Name), this);
            }
            else
                Log.Audit("Failed to locate auto login user " + userValue, this);
        }
    }

Patch this in with a config file:

<configuration xmlns:patch="http://www.sitecore.net/xmlconfig/">
    <sitecore>
        <pipelines>
            <httpRequestBegin>
                <processor type="Namespace.SilentUserLogin,Assembly" patch:after="*[@type='Sitecore.Pipelines.HttpRequest.StartMeasurements, Sitecore.Kernel']">
                    <MaximumAge>00:02:00</MaximumAge>
                </processor>
            </httpRequestBegin>
        </pipelines>
    </sitecore>
</configuration>

Finally, call the page via WebClient or HtmlAgility:

var token = Sitecore.Context.User.CreateUserToken();

var url = new UrlString();
url.HostName = HttpContext.Current.Request.Url.Host;
url.Protocol = HttpContext.Current.Request.IsSecureConnection ? "https" : "http";
url.Path = "/";

url["sc_itemid"] = myItem.ID.ToString();
url["sc_lang"] = myItem.Language.ToString();

// Add parameters to allow accessing the master DB
url["user"] = Sitecore.Context.User.Name;
url["token"] = token.ToString();

// Call the url here

This code was cribbed from a similar situation where I needed a URL to feed to a PDF generation library, which behind the scenes fired up IE and hit the site as an anonymous user. This way we could pass a limited time security token via the query string.

Upvotes: 4

Mark Ursino
Mark Ursino

Reputation: 31435

You can setup a "preview" site that shows content from the master database as opposed to the public-facing published content. This article will help setting that up: How to Setup a Sitecore Preview Site to Review Content Before Publishing

Once you have this setup on a unique URL, you can then make a WebRequest to pages or use HtmlAgilityPack.

Upvotes: 0

Related Questions