Posted 02/18/2015 by Akshay Sura
My original idea was to extend the ShowConfig.aspx to display other parts of the web.config. For instance to look at the handlers or webservice references in <system.web> and <system.webserver>.
What I found is that for each custom config section we need to specify a IConfigurationHandler class which handles the configuration for that section. Now if you try to cast that section to a different IConfigurationHandler than what is in the config, it will generate an error.
<configSections> <section name="sitecore" type="Sitecore.Configuration.ConfigReader, Sitecore.Kernel"/> <section name="log4net" type="log4net.Config.Log4NetConfigurationSectionHandler, Sitecore.Logging"/> <sectionGroup name="fiftyOne"> <section name="log" type="FiftyOne.Foundation.Mobile.Configuration.LogSection, FiftyOne.Foundation" requirePermission="false" allowDefinition="Everywhere" restartOnExternalChanges="false" allowExeDefinition="MachineToApplication"/> <section name="wurfl" type="FiftyOne.Foundation.Mobile.Detection.Wurfl.Configuration.WurflSection, FiftyOne.Foundation" requirePermission="false" allowDefinition="Everywhere" restartOnExternalChanges="false" allowExeDefinition="MachineToApplication"/> </sectionGroup> <sectionGroup name="applicationSettings" type="System.Configuration.ApplicationSettingsGroup, System, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"> </sectionGroup> </configSections>
That along with other concerns, I abandoned that idea. During the same time I also wanted to build a config file sequence page which will prove useful during debugging. Here is what I came up with:
1. A ConfigSequence.aspx file in /sitecore/admin directory
<%@ Page Language="C#" AutoEventWireup="true" Inherits="YOURNAMESPACE.ConfigSequence" %>
2. A Class to go through the configs and build the sequence
using Sitecore; using Sitecore.Collections; using Sitecore.Diagnostics; using Sitecore.Xml; using System; using System.Collections; using System.IO; using System.Xml; namespace YOURNAMESPACE { /// <summary> /// The Factory. /// </summary> public class FactoryEx { /// <summary> /// List of files in the sequence its loaded. /// </summary> private ArrayList filesList = new ArrayList(); /// <summary> /// Gets the configuration. /// </summary> /// <returns> /// Xml document containing the entire Sitecore configuration. /// </returns> public ArrayList GetConfigFileSequence() { if (filesList.Count > 0) return filesList; XmlNode configNode = Sitecore.Configuration.ConfigReader.GetConfigNode(); Assert.IsNotNull(configNode, "Could not read Sitecore configuration."); XmlDocument xmlDocument1 = new XmlDocument(); xmlDocument1.AppendChild(xmlDocument1.ImportNode(configNode, true)); filesList.Add("web.config"); ExpandIncludeFiles(xmlDocument1.DocumentElement, new Hashtable()); LoadAutoIncludeFiles(); ReplaceGlobalVariables(xmlDocument1.DocumentElement); return filesList; } /// <summary> /// Loads the auto include files. /// </summary> /// <param name="patcher"> /// The patcher. /// </param> /// <param name="folder"> /// The folder. /// </param> private void LoadAutoIncludeFiles( string folder) { Assert.ArgumentNotNull(folder, "folder"); try { if (Directory.Exists(folder)) { string[] files = Directory.GetFiles(folder, "*.config"); for (int i = 0; i < (int)files.Length; i++) { string str = files[i]; try { if ((int)(File.GetAttributes(str) & FileAttributes.Hidden) == 0) { filesList.Add(StripDriveInfo(str)); } } catch (Exception exception) { object[] objArray = new object[] { "Could not load configuration file: ", str, ": ", exception }; Log.Error(string.Concat(objArray), typeof(FactoryEx)); } } string[] directories = Directory.GetDirectories(folder); for (int j = 0; j < (int)directories.Length; j++) { string str1 = directories[j]; try { if ((int)(File.GetAttributes(str1) & FileAttributes.Hidden) == 0) { LoadAutoIncludeFiles(str1); } } catch (Exception exception1) { object[] objArray1 = new object[] { "Could not scan configuration folder ", str1, " for files: ", exception1 }; Log.Error(string.Concat(objArray1), typeof(FactoryEx)); } } } } catch (Exception exception2) { object[] objArray2 = new object[] { "Could not scan configuration folder ", folder, " for files: ", exception2 }; Log.Error(string.Concat(objArray2), typeof(FactoryEx)); } } /// <summary> /// Strips the drive info and converts to virtual path. /// </summary> /// <param name="filePath"> /// The file path which may or maynot have the drive information. /// </param> private string StripDriveInfo(string filePath) { string result = ""; int foundIndex = filePath.IndexOf(@"app_config", StringComparison.InvariantCultureIgnoreCase); if (foundIndex >= 0) result = filePath.Substring(foundIndex); else result = filePath; return result.Replace(@"", "/").ToLowerInvariant(); } /// <summary> /// Replaces global variables. /// </summary> /// <param name="rootNode"> /// The root node. /// </param> private void ReplaceGlobalVariables(XmlNode rootNode) { Assert.ArgumentNotNull(rootNode, "rootNode"); XmlNodeList xmlNodeLists = rootNode.SelectNodes(".//sc.variable"); StringDictionary stringDictionary = new StringDictionary(); foreach (XmlAttribute attribute in rootNode.Attributes) { string name = attribute.Name; string[] value = new string[] { attribute.Value }; string str = StringUtil.GetString(value); if (name.Length <= 0) { continue; } stringDictionary[string.Concat("$(", name, ")")] = str; } for (int i = 0; i < xmlNodeLists.Count; i++) { string attribute1 = XmlUtil.GetAttribute("name", xmlNodeLists[i]); string str1 = XmlUtil.GetAttribute("value", xmlNodeLists[i]); if (attribute1.Length > 0) { stringDictionary[string.Concat("$(", attribute1, ")")] = str1; } } if (stringDictionary.Count == 0) { return; } ReplaceGlobalVariables(rootNode, stringDictionary); } /// <summary> /// Replaces global variables. /// </summary> /// <param name="node"> /// The root node. /// </param> /// <param name="variables"> /// The variables. /// </param> private void ReplaceGlobalVariables(XmlNode node, StringDictionary variables) { Assert.ArgumentNotNull(node, "node"); Assert.ArgumentNotNull(variables, "variables"); foreach (XmlAttribute attribute in node.Attributes) { string value = attribute.Value; if (value.IndexOf('$') < 0) { continue; } foreach (string key in variables.Keys) { value = value.Replace(key, variables[key]); } attribute.Value = value; } foreach (XmlNode childNode in node.ChildNodes) { if (childNode.NodeType != XmlNodeType.Element) { continue; } ReplaceGlobalVariables(childNode, variables); } } /// <summary> /// Replaces the variables. /// </summary> /// <param name="value"> /// The value. /// </param> /// <param name="node"> /// The Xml node. /// </param> /// <param name="parameters"> /// The parameters. /// </param> /// <returns> /// The replace variables. /// </returns> private string ReplaceVariables(string value, XmlNode node, string[] parameters) { Assert.ArgumentNotNull(value, "value"); Assert.ArgumentNotNull(node, "node"); node = node.ParentNode; while (node != null && node.NodeType == XmlNodeType.Element && value.IndexOf("$(", StringComparison.InvariantCulture) >= 0) { foreach (XmlAttribute attribute in node.Attributes) { string str = string.Concat("$(", attribute.LocalName, ")"); value = value.Replace(str, attribute.Value); } value = value.Replace("$(name)", node.LocalName); node = node.ParentNode; } if (parameters != null) { for (int i = 0; i < (int)parameters.Length; i++) { value = value.Replace(string.Concat("$(", i, ")"), parameters[i]); } } return value; } /// <summary> /// Loads the auto include files. /// </summary> /// <param name="element"> /// The element. /// </param> private void LoadAutoIncludeFiles() { LoadAutoIncludeFiles(MainUtil.MapPath("/App_Config/Sitecore/Components")); LoadAutoIncludeFiles(MainUtil.MapPath("/App_Config/Include")); } /// <summary> /// Expands the include files embedded in the configuration document. /// </summary> /// <param name="rootNode"> /// The root node. /// </param> /// <param name="cycleDetector"> /// The cycle detector. /// </param> private void ExpandIncludeFiles(XmlNode rootNode, Hashtable cycleDetector) { Assert.ArgumentNotNull(rootNode, "rootNode"); Assert.ArgumentNotNull(cycleDetector, "cycleDetector"); if (rootNode.LocalName == "sc.include") { ExpandIncludeFile(rootNode, cycleDetector); return; } XmlNodeList xmlNodeLists = rootNode.SelectNodes(".//sc.include"); for (int i = 0; i < xmlNodeLists.Count; i++) { ExpandIncludeFile(xmlNodeLists[i], cycleDetector); } } /// <summary> /// Gets the attribute. /// </summary> /// <param name="name"> /// The attribute name. /// </param> /// <param name="node"> /// The Xml node. /// </param> /// <param name="parameters"> /// The parameters. /// </param> /// <returns> /// The attribute. /// </returns> public string GetAttribute(string name, XmlNode node, string[] parameters) { Assert.ArgumentNotNullOrEmpty(name, "name"); Assert.ArgumentNotNull(node, "node"); string attribute = XmlUtil.GetAttribute(name, node); return ReplaceVariables(attribute, node, parameters); } /// <summary> /// Expands the include file represented by a single configuration element (node). /// </summary> /// <param name="xmlNode"> /// The XML node. /// </param> /// <param name="cycleDetector"> /// The cycle detector. /// </param> private void ExpandIncludeFile(XmlNode xmlNode, Hashtable cycleDetector) { Assert.ArgumentNotNull(xmlNode, "xmlNode"); Assert.ArgumentNotNull(cycleDetector, "cycleDetector"); string lowerInvariant = GetAttribute("file", xmlNode, null).ToLowerInvariant(); if (lowerInvariant.Length == 0) { return; } object[] objArray = new object[] { lowerInvariant }; Assert.IsTrue(!cycleDetector.ContainsKey(lowerInvariant), "Cycle detected in configuration include files. The file '{0}' is being included directly or indirectly in a way that causes a cycle to form.", objArray); XmlDocument xmlDocument = XmlUtil.LoadXmlFile(lowerInvariant); if (xmlDocument.DocumentElement == null) { return; } filesList.Add(StripDriveInfo(lowerInvariant)); XmlNode parentNode = xmlNode.ParentNode; XmlNode xmlNodes = xmlNode.OwnerDocument.ImportNode(xmlDocument.DocumentElement, true); parentNode.ReplaceChild(xmlNodes, xmlNode); cycleDetector.Add(lowerInvariant, string.Empty); ExpandIncludeFiles(xmlNodes, cycleDetector); cycleDetector.Remove(lowerInvariant); while (xmlNodes.FirstChild != null) { parentNode.AppendChild(xmlNodes.FirstChild); } foreach (XmlNode childNode in xmlNodes.ChildNodes) { parentNode.AppendChild(childNode); } XmlUtil.TransferAttributes(xmlNodes, parentNode); parentNode.RemoveChild(xmlNodes); } } }
3. A Class for the code behind for the aspx file
using Sitecore.Configuration; using Sitecore.sitecore.admin; using System; using System.Collections; using System.Web; using System.Web.UI; using System.Xml; namespace YOURNAMESPACE { public class ConfigSequence : AdminPage { public ConfigSequence() { } /// <summary> /// Handles the Load event of the Page control. /// </summary> /// <param name="sender">The source of the event.</param> /// <param name="e">The <see cref="T:System.EventArgs" /> instance containing the event data.</param> protected void Page_Load(object sender, EventArgs e) { base.CheckSecurity(); FactoryEx factory = new FactoryEx(); ArrayList filesList = factory.GetConfigFileSequence(); for (int i = 0; i < filesList.Count; i++) { base.Response.Write(string.Format("{0}: {1}<BR>", (i+1).ToString(), filesList[i])); } } } }
And here is the output:
1: web.config 2: /app_config/prototypes.config 3: /app_config/prefetch/common.config 4: /app_config/prefetch/core.config 5: /app_config/prefetch/common.config 6: /app_config/prefetch/master.config 7: /app_config/prefetch/common.config 8: /app_config/prefetch/webdb.config 9: /app_config/fieldtypes.config 10: /app_config/xamlsharp.config 11: /app_config/languagedefinitions.config 12: /app_config/mimetypes.config 13: /app_config/commands.config 14: /app_config/icons.config 15: /app_config/portraits.config 16: /app_config/include/.sitecore.speak.important.config 17: /app_config/include/datafolder.config 18: /app_config/include/sitecore.analytics.automation.timeoutprocessing.config 19: /app_config/include/sitecore.analytics.compatibility.config 20: /app_config/include/sitecore.analytics.config 21: /app_config/include/sitecore.analytics.conversion.config 22: /app_config/include/sitecore.analytics.excluderobots.config 23: /app_config/include/sitecore.analytics.marketingtaxonomy.config 24: /app_config/include/sitecore.analytics.model.config 25: /app_config/include/sitecore.analytics.mongodb.config 26: /app_config/include/sitecore.analytics.outcome.config 27: /app_config/include/sitecore.analytics.processing.aggregation.config 28: /app_config/include/sitecore.analytics.processing.aggregation.processingpools.config 29: /app_config/include/sitecore.analytics.processing.aggregation.services.config 30: /app_config/include/sitecore.analytics.processing.config 31: /app_config/include/sitecore.analytics.processing.services.config 32: /app_config/include/sitecore.analytics.reporting.config 33: /app_config/include/sitecore.analytics.tracking.aggregation.config 34: /app_config/include/sitecore.analytics.tracking.config 35: /app_config/include/sitecore.analytics.tracking.database.config 36: /app_config/include/sitecore.analytics.tracking.robotdetection.config 37: /app_config/include/sitecore.anticsrf.config 38: /app_config/include/sitecore.apps.taginjection.config 39: /app_config/include/sitecore.buckets.config 40: /app_config/include/sitecore.contentsearch.analytics.config 41: /app_config/include/sitecore.contentsearch.config 42: /app_config/include/sitecore.contentsearch.defaultconfigurations.config 43: /app_config/include/sitecore.contentsearch.lucene.defaultindexconfiguration.config 44: /app_config/include/sitecore.contentsearch.lucene.index.analytics.config 45: /app_config/include/sitecore.contentsearch.lucene.index.core.config 46: /app_config/include/sitecore.contentsearch.lucene.index.master.config 47: /app_config/include/sitecore.contentsearch.lucene.index.web.config 48: /app_config/include/sitecore.experienceeditor.config 49: /app_config/include/sitecore.experienceeditor.speak.requests.config 50: /app_config/include/sitecore.experienceexplorer.config 51: /app_config/include/sitecore.experienceexplorer.speak.requests.config 52: /app_config/include/sitecore.itemwebapi.config 53: /app_config/include/sitecore.marketing.client.config 54: /app_config/include/sitecore.marketing.config 55: /app_config/include/sitecore.marketing.definitions.marketingassets.repositories.config 56: /app_config/include/sitecore.marketing.definitions.marketingassets.repositories.lucene.index.master.config 57: /app_config/include/sitecore.marketing.definitions.marketingassets.repositories.lucene.index.web.config 58: /app_config/include/sitecore.marketing.definitions.marketingassets.repositories.lucene.indexconfiguration.config 59: /app_config/include/sitecore.media.requestprotection.config 60: /app_config/include/sitecore.mvc.config 61: /app_config/include/sitecore.mvcanalytics.config 62: /app_config/include/sitecore.mvcexperienceeditor.config 63: /app_config/include/sitecore.mvcsimulator.config 64: /app_config/include/sitecore.pathanalyzer.client.config 65: /app_config/include/sitecore.pathanalyzer.config 66: /app_config/include/sitecore.pathanalyzer.processing.config 67: /app_config/include/sitecore.pathanalyzer.services.config 68: /app_config/include/sitecore.segmentbuilder.config 69: /app_config/include/sitecore.services.client.config 70: /app_config/include/sitecore.shell.marketingautomation.config 71: /app_config/include/sitecore.speak.anticsrf.sheerui.config 72: /app_config/include/sitecore.speak.applications.config 73: /app_config/include/sitecore.speak.config 74: /app_config/include/sitecore.speak.itemwebapi.config 75: /app_config/include/sitecore.speak.launchpad.config 76: /app_config/include/sitecore.speak.mvc.config 77: /app_config/include/sitecore.webdav.config 78: /app_config/include/synthesis.config 79: /app_config/include/channel/sitecore.analytics.channel.config 80: /app_config/include/contenttesting/sitecore.contenttesting.config 81: /app_config/include/contenttesting/sitecore.contenttesting.intelligence.config 82: /app_config/include/contenttesting/sitecore.contenttesting.lucene.indexconfiguration.config 83: /app_config/include/contenttesting/sitecore.contenttesting.mvc.config 84: /app_config/include/contenttesting/sitecore.contenttesting.processing.aggregation.config 85: /app_config/include/experienceanalytics/sitecore.experienceanalytics.aggregation.config 86: /app_config/include/experienceanalytics/sitecore.experienceanalytics.client.config 87: /app_config/include/experienceanalytics/sitecore.experienceanalytics.reduce.config 88: /app_config/include/experienceanalytics/sitecore.experienceanalytics.storageproviders.config 89: /app_config/include/experienceanalytics/sitecore.experienceanalytics.webapi.config 90: /app_config/include/experienceprofile/sitecore.experienceprofile.client.config 91: /app_config/include/experienceprofile/sitecore.experienceprofile.config 92: /app_config/include/experienceprofile/sitecore.experienceprofile.reporting.config 93: /app_config/include/fxm/sitecore.fxm.bundle.config 94: /app_config/include/fxm/sitecore.fxm.config 95: /app_config/include/fxm/sitecore.fxm.lucene.index.domainssearch.config 96: /app_config/include/fxm/sitecore.fxm.speak.config 97: /app_config/include/listmanagement/sitecore.listmanagement.client.config 98: /app_config/include/listmanagement/sitecore.listmanagement.config 99: /app_config/include/listmanagement/sitecore.listmanagement.lucene.index.list.config 100: /app_config/include/listmanagement/sitecore.listmanagement.lucene.indexconfiguration.config 101: /app_config/include/listmanagement/sitecore.listmanagement.services.config 102: /app_config/include/simpleinjector/simpleinjector.config 103: /app_config/include/social/sitecore.social.config 104: /app_config/include/social/sitecore.social.experienceprofile.config 105: /app_config/include/social/sitecore.social.facebook.config 106: /app_config/include/social/sitecore.social.googleplus.config 107: /app_config/include/social/sitecore.social.linkedin.config 108: /app_config/include/social/sitecore.social.lucene.index.master.config 109: /app_config/include/social/sitecore.social.lucene.index.web.config 110: /app_config/include/social/sitecore.social.lucene.indexconfiguration.config 111: /app_config/include/social/sitecore.social.profilemapping.facebook.config 112: /app_config/include/social/sitecore.social.profilemapping.googleplus.config 113: /app_config/include/social/sitecore.social.profilemapping.linkedin.config 114: /app_config/include/social/sitecore.social.profilemapping.twitter.config 115: /app_config/include/social/sitecore.social.socialmarketer.config 116: /app_config/include/social/sitecore.social.twitter.config
Let me know if you have any questions or comments.