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 { /// /// Holds methods for parsing and building umbraco templates /// /// 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"; } } 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("") > 0 || templateOutput.ToString().ToLower().IndexOf("") > 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(""); int aspnetFormTagLength = 14; int aspnetFormTagEnd = tempOutput.ToString().ToLower().IndexOf("") + 15; // check if we should disable the script manager if (aspnetFormTagBegin == -1) { aspnetFormTagBegin = templateOutput.ToString().ToLower().IndexOf(""); 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("")) { StringBuilder beforeHeader = new StringBuilder(header.ToString().Substring(0, header.ToString().ToLower().IndexOf(""))); header.Remove(0, header.ToString().ToLower().IndexOf("") + 14); StringBuilder afterHeader = new StringBuilder(header.ToString().Substring(header.ToString().ToLower().IndexOf("") + 15, header.Length - header.ToString().ToLower().IndexOf("") - 15)); header.Remove(header.ToString().ToLower().IndexOf(""), header.Length - header.ToString().ToLower().IndexOf("")); // Find the title from head MatchCollection matches = Regex.Matches(header.ToString(), @"(.*?)", 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(""); 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(" -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 with children are only used when a macro is inserted by the umbraco-editor, in the // following format: "", 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("
")); // 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.ReturnFromAlias(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("
")); } 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 = "
" + tempElementContent + "
"; } 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; } /// /// 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" /// /// public void Parse(page umbPage) { System.Web.HttpContext.Current.Trace.Write("umbracoTemplate", "Start parsing"); // First parse for known umbraco tags // - macros // - print item from page, level, or recursive MatchCollection tags = Regex.Matches(templateOutput.ToString(), "<\\?UMBRACO_MACRO[^>]*/>|<\\?UMBRACO_GETITEM[^>]*/>|<\\?(?[\\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(?[^>]*)>]*><\\/\\?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 new macro(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) { template t = (template)templateCache["template" + templateID]; if (t == null) t = new template(templateID); if (t != null) 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); } } } checkForMaster(templateID); } private void checkForMaster(int templateID) { // Get template design if (_masterTemplate != 0 && _masterTemplate != templateID) { template masterTemplateDesign = new template(_masterTemplate); if (masterTemplateDesign.TemplateContent.IndexOf("") > -1 || masterTemplateDesign.TemplateContent.IndexOf("") > -1) { templateOutput.Append( masterTemplateDesign.TemplateContent.Replace("", _templateDesign).Replace("", _templateDesign) ); } else templateOutput.Append(_templateDesign); } else { if (_masterTemplate == templateID) System.Web.HttpContext.Current.Trace.Warn("template", "Master template is the same as the current template. It would course an endless loop!"); 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 } }