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.