views:

464

answers:

5

I have a page that has a bunch of user controls on it. I want to be able to have "macros" or "placeholders" directly in the content that will get replaced in my code. It shouldn't really matter, but I'm using Ektron as my CMS.

Are there any page events that I can hook into to do a string replace on the entire rendered page content, right before it's sent to the client?

UPDATE

Here is the code that I am currently using to accomplish this:

protected override void Render(HtmlTextWriter writer)
{
    string content = string.Empty;

    using (var stringWriter = new StringWriter())
    using (var htmlWriter = new HtmlTextWriter(stringWriter))
    {
        // render the current page content to our temp writer
        base.Render(htmlWriter);
        htmlWriter.Close();

        // get the content
        content = stringWriter.ToString();
    }

    // replace our placeholders
    string newContent = content.Replace("$placeholder1$", "placeholder1 data").Replace("$placeholder2$", "placeholder2 data");

    // write the new html to the page
    writer.Write(newContent);
}
+3  A: 

Have you tried overriding the render method?

protected override void Render(HtmlTextWriter writer)
{
   StringBuilder htmlString = new StringBuilder(); // this will hold the string
   StringWriter stringWriter = new StringWriter(htmlString);
   HtmlTextWriter htmlWriter = new HtmlTextWriter(stringWriter);
   Page.Render(tmpWriter);
   tmpWriter.Flush();

   writer.Write(DoReplaceLogic(htmlString.ToString()););
}
JustLoren
Yes, this is the right place but is it possible (and wise) to do reset and read the stream?
Henk Holterman
you're never "resetting" the stream since it hasn't been sent yet. I'm not certain if its wise, no, I just answered the question lol
JustLoren
I am using this method right now, but will wait to accept the answer to see if there is a better solution. Basically I...Let the page write to a temp buffer.Modify the buffer.Replace the original buffer with the modified one.
SkippyFire
I posted my code that is basically doing what you're doing.
SkippyFire
+1  A: 

Have you looked at the PreRender event in the life-cycle?

Before this event occurs:

• The Page object calls EnsureChildControls for each control and for the page.

• Each data bound control whose DataSourceID property is set calls its DataBind method.

• The PreRender event occurs for each control on the page. Use the event to make final changes to the contents of the page or its controls.

I believe this is the last place you could do something like this. The next event is SaveStateComplete, which according to the documentation has this behavior:

Before this event occurs, ViewState has been saved for the page and for all controls. Any changes to the page or controls at this point will be ignored. Use this event perform tasks that require view state to be saved, but that do not make any changes to controls.

Aaron Daniels
A: 

The simplistic answer that comes to mind is to use asp:Literal controls for your "placeholders". You can set their content during page load, or you can hook into the PreRender event and set them then.

Cylon Cat
Unfortunately, I can't do that because these "placeholders" will be inside content that get rendered inside a user control, that I can't touch. So by the time it gets to me, the only thing I could hope to do is look at the rendered content.
SkippyFire
Sorry, I missed the "user control", my bad.User controls have PreRender events as well. You should be able to put public properties on your user controls that the page can set, then have the user controls do the substitution.
Cylon Cat
A: 

It sounds like you might want to have HTML literals within your page and then you can simply replace them with the appropriate content on the Page_Load event.

This will require you to write out HTML code, as opposed to some simple text, but it sounds like you may be injecting your own JavaScript code or the like in there, which this will work great for.

Dillie-O
+1  A: 

I know that this answer is not going to help since you have already solved this prob and moved on. This is just for people who will face a similar problem in the future ;)

There are two approaches you could use.

  1. This is similar to the accepted answer. But I would recommend overriding the render method in a BasePage and deriving all your templates from this.

  2. Use a HttpModule or the Global.asax and attach a Filter to the Response object. To me this make more aesthetic sense because the "Filter" property is supposed to help you filter the output which is exactly what you want!

BTW, how is it going with Ektron so far? They are driving me crazy for sure!

LightX
Thanks for the answer. Up-vote for effort!The BasePage thing isn't really a different solution, but more of an addition to the accepted one. But good idea. I think it's always a good idea to create a BasePage class that all of your page derive from. Or maybe 2-3 depending on the types of pages you have.As for the HttpModule and filter, that is a great! Again, similar to the accepted solution, but the logic for the replacements would be extracted out of the page. This would be great if you're doing replacements in more than one place! Nice!
SkippyFire
Oh yeah... Ektron... I personally don't like it. The whole product is ass-backwards. Their API is ugly, and very convoluted. The whole product is buggy, but the code is obfuscated so you have no hope of trying to figure out what they were trying to do. Their support forums aren't very good. If you're looking for a new CMS, I would recommend Kentico. For the most part, you don't have to write any code. But when you do write code, their API is awesome! The only gripe I have with Kentico is that their caching system is confusing, and by default, not setup in an intelligent way.
SkippyFire