This is the story of stupid SharePoint problem and an ugly, kludgy, and embarrasing solution. On our project we have the need for an application that looks and feels like a SharePoint subsite. Specifically, we need it to inherit from SharePoint's masterpage. But we also need it to be a separate IIS application.
Problem: SharePoint dislikes Applications
We get the following error when we try to dynamically set the MasterPageFile in code:
System.ArgumentException: The virtual path '/_layouts/application.master' maps to another application, which is not allowed.
Several sites say we just can't do what we're trying to do. We considered a Virtual Path Provider, but decided not to even go down that path because SharePoint's master page undoubtedly has hooks into web.config and httpmodules and such, so we came up with the following hack:
Solution: An Ugly SharePoint Text Manipulation Hack
Deploy a nearly blank page in to /_layouts/ (we do this in our WebAppManifest.xml & WebAppSolution.ddf files). In PageBase (that all pages inherit from) we override Render to retrieve the blank page and do text manipulation to insert our content into it. Here's the code:
protected override void
Render(HtmlTextWriter writer) {
HttpWebRequest request = (HttpWebRequest)WebRequest.Create(GetUrlToBlankPage());
request.Credentials =
CredentialCache.DefaultCredentials;
using
(WebResponse webResponse =
request.GetResponse()) {
StreamReader streamReader =
new StreamReader(webResponse.GetResponseStream());
string pageHostHtml = streamReader.ReadToEnd();
using (StringWriter
stringWriter = new
StringWriter())
using (HtmlTextWriter
htmlTextWriter = new
HtmlTextWriter(stringWriter)) {
base.Render(htmlTextWriter);
string renderPageHtml = stringWriter.ToString();
string title = GetTitleFromHtml(renderPageHtml);
pageHostHtml.Replace("PAGE TITLE", title);
string content =
GetContentFromHtml(renderPageHtml);
pageHostHtml = RemoveFormData(pageHostHtml);
pageHostHtml = pageHostHtml.Replace("DO NOT MODIFY
THIS PAGE", content);
}
writer.Write(pageHostHtml);
}
}
private static
string GetContentFromHtml(string html) {
Match
match = Regex.Match(html,
"<body>(.*)</body>",
RegexOptions.IgnoreCase | RegexOptions.Singleline);
return
match.Success ? match.Groups[1].Captures[0].Value :
"";
}
private static
string GetTitleFromHtml(string html) {
Match
match = Regex.Match(html,
"<title>(.*)</title>",
RegexOptions.IgnoreCase);
return
match.Success ? match.Groups[1].Captures[0].Value :
"";
}
private static
string RemoveFormData(string
html) {
string
replaced = Regex.Replace(html,
"<form[^>]*>", "",,
RegexOptions.IgnoreCase | RegexOptions.Singleline);
replaced = replaced.Replace("</form>", "");
return
replaced;
}
Conclusion
I’m sure there are plenty of optimizations that we can do (e.g. caching), but that’s the basic idea. But this solution makes me feel so dirty. So please, please, dear reader, tell me there is a better way. I want to feel clean again.
Comments