Files
Umbraco-CMS/umbraco/presentation/template.cs
2012-02-11 01:13:41 +05:00

629 lines
28 KiB
C#

using System;
using System.Xml;
using System.Web.Caching;
using System.Text;
using System.IO;
using System.Text.RegularExpressions;
using System.Data;
using System.Web.UI;
using System.Collections;
using System.Collections.Generic;
using umbraco.DataLayer;
using umbraco.BusinessLogic;
using umbraco.IO;
using System.Web;
namespace umbraco
{
/// <summary>
/// Holds methods for parsing and building umbraco templates
/// </summary>
///
public class template
{
#region private variables
StringBuilder templateOutput = new StringBuilder();
// Cache
static System.Web.Caching.Cache templateCache = System.Web.HttpRuntime.Cache;
private string _templateDesign = "";
int _masterTemplate = -1;
private string _templateName = "";
private string _templateAlias = "";
#endregion
#region public properties
public String TemplateContent
{
set
{
templateOutput.Append(value);
}
get
{
return templateOutput.ToString();
}
}
public int MasterTemplate
{
get { return _masterTemplate; }
}
//added fallback to the default template to avoid nasty .net errors.
//This is referenced in /default.aspx.cs during page rendering.
public string MasterPageFile
{
get
{
string file = TemplateAlias.Replace(" ", "") + ".master";
string path = SystemDirectories.Masterpages + "/" + file;
if (System.IO.File.Exists(IOHelper.MapPath(VirtualPathUtility.ToAbsolute(path))))
return path;
else
return SystemDirectories.Umbraco + "/masterPages/default.master";
}
}
//Support for template folders, if a alternative skin folder is requested
//we will try to look for template files in another folder
public string AlternateMasterPageFile(string templateFolder)
{
string file = TemplateAlias.Replace(" ", "") + ".master";
string path = SystemDirectories.Masterpages + "/" + templateFolder + "/" + file;
//if it doesn't exists then we return the normal file
if (!System.IO.File.Exists(IOHelper.MapPath(VirtualPathUtility.ToAbsolute(path))))
{
string originalPath = IOHelper.MapPath(VirtualPathUtility.ToAbsolute(MasterPageFile));
string copyPath = IOHelper.MapPath(VirtualPathUtility.ToAbsolute(path));
FileStream fs = new FileStream(originalPath, FileMode.Open, FileAccess.ReadWrite);
StreamReader f = new StreamReader(fs);
String newfile = f.ReadToEnd();
f.Close();
fs.Close();
newfile = newfile.Replace("MasterPageFile=\"~/masterpages/", "MasterPageFile=\"");
fs = new FileStream(copyPath, FileMode.Create, FileAccess.Write);
StreamWriter replacement = new StreamWriter(fs);
replacement.Write(newfile);
replacement.Close();
}
return path;
}
public string TemplateAlias
{
get { return _templateAlias; }
}
#endregion
#region public methods
public override string ToString()
{
return this._templateName;
}
public Control ParseWithControls(page umbPage)
{
System.Web.HttpContext.Current.Trace.Write("umbracoTemplate", "Start parsing");
if (System.Web.HttpContext.Current.Items["macrosAdded"] == null)
System.Web.HttpContext.Current.Items.Add("macrosAdded", 0);
StringBuilder tempOutput = templateOutput;
Control pageLayout = new Control();
Control pageHeader = new Control();
Control pageFooter = new Control();
Control pageContent = new Control();
System.Web.UI.HtmlControls.HtmlForm pageForm = new System.Web.UI.HtmlControls.HtmlForm();
System.Web.UI.HtmlControls.HtmlHead pageAspNetHead = new System.Web.UI.HtmlControls.HtmlHead();
// Find header and footer of page if there is an aspnet-form on page
if (templateOutput.ToString().ToLower().IndexOf("<?aspnet_form>") > 0 ||
templateOutput.ToString().ToLower().IndexOf("<?aspnet_form disablescriptmanager=\"true\">") > 0)
{
pageForm.Attributes.Add("method", "post");
pageForm.Attributes.Add("action", Convert.ToString(System.Web.HttpContext.Current.Items["VirtualUrl"]));
// Find header and footer from tempOutput
int aspnetFormTagBegin = tempOutput.ToString().ToLower().IndexOf("<?aspnet_form>");
int aspnetFormTagLength = 14;
int aspnetFormTagEnd = tempOutput.ToString().ToLower().IndexOf("</?aspnet_form>") + 15;
// check if we should disable the script manager
if (aspnetFormTagBegin == -1)
{
aspnetFormTagBegin =
templateOutput.ToString().ToLower().IndexOf("<?aspnet_form disablescriptmanager=\"true\">");
aspnetFormTagLength = 42;
}
else
{
ScriptManager sm = new ScriptManager();
sm.ID = "umbracoScriptManager";
pageForm.Controls.Add(sm);
}
StringBuilder header = new StringBuilder(tempOutput.ToString().Substring(0, aspnetFormTagBegin));
// Check if there's an asp.net head element in the header
if (header.ToString().ToLower().Contains("<?aspnet_head>"))
{
StringBuilder beforeHeader = new StringBuilder(header.ToString().Substring(0, header.ToString().ToLower().IndexOf("<?aspnet_head>")));
header.Remove(0, header.ToString().ToLower().IndexOf("<?aspnet_head>") + 14);
StringBuilder afterHeader = new StringBuilder(header.ToString().Substring(header.ToString().ToLower().IndexOf("</?aspnet_head>") + 15, header.Length - header.ToString().ToLower().IndexOf("</?aspnet_head>") - 15));
header.Remove(header.ToString().ToLower().IndexOf("</?aspnet_head>"), header.Length - header.ToString().ToLower().IndexOf("</?aspnet_head>"));
// Find the title from head
MatchCollection matches = Regex.Matches(header.ToString(), @"<title>(.*?)</title>", RegexOptions.IgnoreCase | RegexOptions.Multiline);
if (matches.Count > 0)
{
StringBuilder titleText = new StringBuilder();
HtmlTextWriter titleTextTw = new HtmlTextWriter(new System.IO.StringWriter(titleText));
parseStringBuilder(new StringBuilder(matches[0].Groups[1].Value), umbPage).RenderControl(titleTextTw);
pageAspNetHead.Title = titleText.ToString();
header = new StringBuilder(header.ToString().Replace(matches[0].Value, ""));
}
pageAspNetHead.Controls.Add(parseStringBuilder(header, umbPage));
pageAspNetHead.ID = "head1";
// build the whole header part
pageHeader.Controls.Add(parseStringBuilder(beforeHeader, umbPage));
pageHeader.Controls.Add(pageAspNetHead);
pageHeader.Controls.Add(parseStringBuilder(afterHeader, umbPage));
}
else
pageHeader.Controls.Add(parseStringBuilder(header, umbPage));
pageFooter.Controls.Add(parseStringBuilder(new StringBuilder(tempOutput.ToString().Substring(aspnetFormTagEnd, tempOutput.Length - aspnetFormTagEnd)), umbPage));
tempOutput.Remove(0, aspnetFormTagBegin + aspnetFormTagLength);
aspnetFormTagEnd = tempOutput.ToString().ToLower().IndexOf("</?aspnet_form>");
tempOutput.Remove(aspnetFormTagEnd, tempOutput.Length - aspnetFormTagEnd);
//throw new ArgumentException(tempOutput.ToString());
pageForm.Controls.Add(parseStringBuilder(tempOutput, umbPage));
pageContent.Controls.Add(pageHeader);
pageContent.Controls.Add(pageForm);
pageContent.Controls.Add(pageFooter);
return pageContent;
}
else
return parseStringBuilder(tempOutput, umbPage);
}
public Control parseStringBuilder(StringBuilder tempOutput, page umbPage)
{
Control pageContent = new Control();
bool stop = false;
bool debugMode = umbraco.presentation.UmbracoContext.Current.Request.IsDebug;
while (!stop)
{
System.Web.HttpContext.Current.Trace.Write("template", "Begining of parsing rutine...");
int tagIndex = tempOutput.ToString().ToLower().IndexOf("<?umbraco");
if (tagIndex > -1)
{
String tempElementContent = "";
pageContent.Controls.Add(new LiteralControl(tempOutput.ToString().Substring(0, tagIndex)));
tempOutput.Remove(0, tagIndex);
String tag = tempOutput.ToString().Substring(0, tempOutput.ToString().IndexOf(">") + 1);
Hashtable attributes = helper.ReturnAttributes(tag);
// Check whether it's a single tag (<?.../>) or a tag with children (<?..>...</?...>)
if (tag.Substring(tag.Length - 2, 1) != "/" && tag.IndexOf(" ") > -1)
{
String closingTag = "</" + (tag.Substring(1, tag.IndexOf(" ") - 1)) + ">";
// Tag with children are only used when a macro is inserted by the umbraco-editor, in the
// following format: "<?UMBRACO_MACRO ...><IMG SRC="..."..></?UMBRACO_MACRO>", so we
// need to delete extra information inserted which is the image-tag and the closing
// umbraco_macro tag
if (tempOutput.ToString().IndexOf(closingTag) > -1)
{
tempOutput.Remove(0, tempOutput.ToString().IndexOf(closingTag));
}
}
System.Web.HttpContext.Current.Trace.Write("umbTemplate", "Outputting item: " + tag);
// Handle umbraco macro tags
if (tag.ToString().ToLower().IndexOf("umbraco_macro") > -1)
{
if (debugMode)
pageContent.Controls.Add(new LiteralControl("<div title=\"Macro Tag: '" + System.Web.HttpContext.Current.Server.HtmlEncode(tag) + "'\" style=\"border: 1px solid #009;\">"));
// NH: Switching to custom controls for macros
if (UmbracoSettings.UseAspNetMasterPages)
{
umbraco.presentation.templateControls.Macro macroControl = new umbraco.presentation.templateControls.Macro();
macroControl.Alias = helper.FindAttribute(attributes, "macroalias");
IDictionaryEnumerator ide = attributes.GetEnumerator();
while (ide.MoveNext())
if (macroControl.Attributes[ide.Key.ToString()] == null)
macroControl.Attributes.Add(ide.Key.ToString(), ide.Value.ToString());
pageContent.Controls.Add(macroControl);
}
else
{
macro tempMacro;
String macroID = helper.FindAttribute(attributes, "macroid");
if (macroID != String.Empty)
tempMacro = getMacro(macroID);
else
tempMacro = macro.GetMacro(helper.FindAttribute(attributes, "macroalias"));
if (tempMacro != null)
{
try
{
Control c = tempMacro.renderMacro(attributes, umbPage.Elements, umbPage.PageID);
if (c != null)
pageContent.Controls.Add(c);
else
System.Web.HttpContext.Current.Trace.Warn("Template", "Result of macro " + tempMacro.Name + " is null");
}
catch (Exception e)
{
System.Web.HttpContext.Current.Trace.Warn("Template", "Error adding macro " + tempMacro.Name, e);
}
}
}
if (debugMode)
pageContent.Controls.Add(new LiteralControl("</div>"));
}
else
{
if (tag.ToLower().IndexOf("umbraco_getitem") > -1)
{
// NH: Switching to custom controls for items
if (UmbracoSettings.UseAspNetMasterPages)
{
umbraco.presentation.templateControls.Item itemControl = new umbraco.presentation.templateControls.Item();
itemControl.Field = helper.FindAttribute(attributes, "field");
IDictionaryEnumerator ide = attributes.GetEnumerator();
while (ide.MoveNext())
if (itemControl.Attributes[ide.Key.ToString()] == null)
itemControl.Attributes.Add(ide.Key.ToString(), ide.Value.ToString());
pageContent.Controls.Add(itemControl);
}
else
{
try
{
if (helper.FindAttribute(attributes, "nodeId") != "" && int.Parse(helper.FindAttribute(attributes, "nodeId")) != 0)
{
cms.businesslogic.Content c = new umbraco.cms.businesslogic.Content(int.Parse(helper.FindAttribute(attributes, "nodeId")));
item umbItem = new item(c.getProperty(helper.FindAttribute(attributes, "field")).Value.ToString(), attributes);
tempElementContent = umbItem.FieldContent;
// Check if the content is published
if (c.nodeObjectType == cms.businesslogic.web.Document._objectType)
{
try
{
cms.businesslogic.web.Document d = (cms.businesslogic.web.Document)c;
if (!d.Published)
tempElementContent = "";
}
catch { }
}
}
else
{
// NH adds Live Editing test stuff
item umbItem = new item(umbPage.Elements, attributes);
// item umbItem = new item(umbPage.PageElements[helper.FindAttribute(attributes, "field")].ToString(), attributes);
tempElementContent = umbItem.FieldContent;
}
if (debugMode)
tempElementContent =
"<div title=\"Field Tag: '" + System.Web.HttpContext.Current.Server.HtmlEncode(tag) + "'\" style=\"border: 1px solid #fc6;\">" + tempElementContent + "</div>";
}
catch (Exception e)
{
System.Web.HttpContext.Current.Trace.Warn("umbracoTemplate", "Error reading element (" + helper.FindAttribute(attributes, "field") + ")", e);
}
}
}
}
tempOutput.Remove(0, tempOutput.ToString().IndexOf(">") + 1);
tempOutput.Insert(0, tempElementContent);
}
else
{
pageContent.Controls.Add(new LiteralControl(tempOutput.ToString()));
break;
}
}
return pageContent;
}
public static string ParseInternalLinks(string pageContents)
{
// Parse internal links
MatchCollection tags = Regex.Matches(pageContents, @"href=""[/]?(?:\{|\%7B)localLink:([0-9]+)(?:\}|\%7D)", RegexOptions.IgnoreCase | RegexOptions.IgnorePatternWhitespace);
foreach (Match tag in tags)
if (tag.Groups.Count > 0)
{
string id = tag.Groups[1].Value; //.Remove(tag.Groups[1].Value.Length - 1, 1);
string newLink = library.NiceUrl(int.Parse(id));
pageContents = pageContents.Replace(tag.Value.ToString(), "href=\"" + newLink);
}
return pageContents;
}
/// <summary>
/// Parses the content of the templateOutput stringbuilder, and matches any tags given in the
/// XML-file /umbraco/config/umbracoTemplateTags.xml.
/// Replaces the found tags in the StringBuilder object, with "real content"
/// </summary>
/// <param name="umbPage"></param>
public void Parse(page umbPage)
{
System.Web.HttpContext.Current.Trace.Write("umbracoTemplate", "Start parsing");
// First parse for known umbraco tags
// <?UMBRACO_MACRO/> - macros
// <?UMBRACO_GETITEM/> - print item from page, level, or recursive
MatchCollection tags = Regex.Matches(templateOutput.ToString(), "<\\?UMBRACO_MACRO[^>]*/>|<\\?UMBRACO_GETITEM[^>]*/>|<\\?(?<tagName>[\\S]*)[^>]*/>", RegexOptions.IgnoreCase | RegexOptions.IgnorePatternWhitespace);
foreach (Match tag in tags)
{
Hashtable attributes = helper.ReturnAttributes(tag.Value.ToString());
if (tag.ToString().ToLower().IndexOf("umbraco_macro") > -1)
{
String macroID = helper.FindAttribute(attributes, "macroid");
if (macroID != "")
{
macro tempMacro = getMacro(macroID);
templateOutput.Replace(tag.Value.ToString(), tempMacro.MacroContent.ToString());
}
}
else
{
if (tag.ToString().ToLower().IndexOf("umbraco_getitem") > -1)
{
try
{
String tempElementContent = umbPage.Elements[helper.FindAttribute(attributes, "field")].ToString();
MatchCollection tempMacros = Regex.Matches(tempElementContent, "<\\?UMBRACO_MACRO(?<attributes>[^>]*)><img[^>]*><\\/\\?UMBRACO_MACRO>", RegexOptions.IgnoreCase | RegexOptions.IgnorePatternWhitespace);
foreach (Match tempMacro in tempMacros)
{
Hashtable tempAttributes = helper.ReturnAttributes(tempMacro.Groups["attributes"].Value.ToString());
String macroID = helper.FindAttribute(tempAttributes, "macroid");
if (Convert.ToInt32(macroID) > 0)
{
macro tempContentMacro = getMacro(macroID);
templateOutput.Replace(tag.Value.ToString(), tempContentMacro.MacroContent.ToString());
}
}
templateOutput.Replace(tag.Value.ToString(), tempElementContent);
}
catch (Exception e)
{
System.Web.HttpContext.Current.Trace.Warn("umbracoTemplate", "Error reading element (" + helper.FindAttribute(attributes, "field") + ")", e);
}
}
}
}
System.Web.HttpContext.Current.Trace.Write("umbracoTemplate", "Done parsing");
}
#endregion
#region private methods
private macro getMacro(String macroID)
{
System.Web.HttpContext.Current.Trace.Write("umbracoTemplate", "Starting macro (" + macroID.ToString() + ")");
return macro.GetMacro(Convert.ToInt16(macroID));
}
private String FindAttribute(Hashtable attributes, String key)
{
if (attributes[key] != null)
return attributes[key].ToString();
else
return "";
}
#endregion
protected static ISqlHelper SqlHelper
{
get { return Application.SqlHelper; }
}
#region constructors
public static string GetMasterPageName(int templateID)
{
return GetMasterPageName(templateID, null);
}
public static string GetMasterPageName(int templateID, string templateFolder)
{
template t = (template)templateCache["template" + templateID];
if (t == null)
t = new template(templateID);
if (t != null)
if (!string.IsNullOrEmpty(templateFolder))
return t.AlternateMasterPageFile(templateFolder);
else
return t.MasterPageFile;
else
throw new ArgumentException(String.Format("Template with id '{0}' not found", templateID));
}
public template(int templateID)
{
if (templateCache["template" + templateID.ToString()] != null)
{
template t = (template)templateCache["template" + templateID];
this._masterTemplate = t._masterTemplate;
this._templateAlias = t._templateAlias;
this._templateDesign = t._templateDesign;
this._masterTemplate = t._masterTemplate;
this._templateName = t._templateName;
}
else
{
using (IRecordsReader templateData = SqlHelper.ExecuteReader("select nodeId, alias, master, text, design from cmsTemplate inner join umbracoNode node on node.id = cmsTemplate.nodeId where nodeId = @templateID", SqlHelper.CreateParameter("@templateID", templateID)))
{
if (templateData.Read())
{
// Get template master and replace content where the template
if (!templateData.IsNull("master"))
_masterTemplate = templateData.GetInt("master");
if (!templateData.IsNull("alias"))
_templateAlias = templateData.GetString("alias");
if (!templateData.IsNull("text"))
_templateName = templateData.GetString("text");
if (!templateData.IsNull("design"))
_templateDesign = templateData.GetString("design");
templateData.Close();
templateCache.Insert("template" + templateID.ToString(), this);
}
}
}
// Only check for master on legacy templates - can show error when using master pages.
if(!UmbracoSettings.UseAspNetMasterPages)
checkForMaster(templateID);
}
private void checkForMaster(int templateID) {
// Get template design
if (_masterTemplate != 0 && _masterTemplate != templateID) {
template masterTemplateDesign = new template(_masterTemplate);
if (masterTemplateDesign.TemplateContent.IndexOf("<?UMBRACO_TEMPLATE_LOAD_CHILD/>") > -1
|| masterTemplateDesign.TemplateContent.IndexOf("<?UMBRACO_TEMPLATE_LOAD_CHILD />") > -1) {
templateOutput.Append(
masterTemplateDesign.TemplateContent.Replace("<?UMBRACO_TEMPLATE_LOAD_CHILD/>",
_templateDesign).Replace("<?UMBRACO_TEMPLATE_LOAD_CHILD />", _templateDesign)
);
} else
templateOutput.Append(_templateDesign);
} else {
if (_masterTemplate == templateID)
{
cms.businesslogic.template.Template t = cms.businesslogic.template.Template.GetTemplate(templateID);
string templateName = (t != null) ? t.Text : string.Format("'Template with id: '{0}", templateID);
System.Web.HttpContext.Current.Trace.Warn("template",
String.Format("Master template is the same as the current template. It would cause an endless loop! Make sure that the current template '{0}' has another Master Template than itself. You can change this in the template editor under 'Settings'", templateName));
templateOutput.Append(_templateDesign);
}
}
}
public static void ClearCachedTemplate(int templateID)
{
if (templateCache["template" + templateID.ToString()] != null)
templateCache.Remove("template" + templateID.ToString());
}
public template(String templateContent)
{
templateOutput.Append(templateContent);
_masterTemplate = 0;
}
#endregion
}
public class templateCacheRefresh : interfaces.ICacheRefresher
{
#region ICacheRefresher Members
public string Name
{
get
{
// TODO: Add templateCacheRefresh.Name getter implementation
return "Template cache refresher";
}
}
public Guid UniqueIdentifier
{
get
{
// TODO: Add templateCacheRefresh.UniqueIdentifier getter implementation
return new Guid("DD12B6A0-14B9-46e8-8800-C154F74047C8");
}
}
public void RefreshAll()
{
// Doesn't do anything
}
public void Refresh(Guid Id)
{
// Doesn't do anything
}
void umbraco.interfaces.ICacheRefresher.Refresh(int Id)
{
template.ClearCachedTemplate(Id);
}
//PPH remove tamplete from cache
public void Remove(int Id)
{
template.ClearCachedTemplate(Id);
}
#endregion
}
}