1 //------------------------------------------------------------------------------
2 // <copyright file="XmlSiteMapProvider.cs" company="Microsoft">
3 // Copyright (c) Microsoft Corporation. All rights reserved.
5 //------------------------------------------------------------------------------
8 * XmlSiteMapProvider class definition
10 * Copyright (c) 2002 Microsoft Corporation
13 namespace System.Web {
16 using System.Collections;
17 using System.Collections.Specialized;
18 using System.ComponentModel;
19 using System.ComponentModel.Design;
20 using System.Configuration;
21 using System.Configuration.Provider;
22 using System.Diagnostics.CodeAnalysis;
23 using System.Globalization;
25 using System.Resources;
26 using System.Security;
27 using System.Security.Permissions;
28 using System.Web.Compilation;
29 using System.Web.Configuration;
30 using System.Web.Hosting;
32 using System.Web.Util;
35 // XmlMapProvider that generates sitemap tree from xml files
37 public class XmlSiteMapProvider : StaticSiteMapProvider, IDisposable {
39 private string _filename;
40 private VirtualPath _virtualPath;
41 private VirtualPath _normalizedVirtualPath;
42 private SiteMapNode _siteMapNode;
43 private XmlDocument _document;
44 private bool _initialized;
45 private FileChangeEventHandler _handler;
46 private StringCollection _parentSiteMapFileCollection;
48 private const string _providerAttribute = "provider";
49 private const string _siteMapFileAttribute = "siteMapFile";
50 private const string _siteMapNodeName = "siteMapNode";
51 private const string _xmlSiteMapFileExtension = ".sitemap";
52 private const string _resourcePrefix = "$resources:";
53 private const int _resourcePrefixLength = 10;
54 private const char _resourceKeySeparator = ',';
55 private static readonly char[] _seperators = new char[] { ';', ',' };
57 private ArrayList _childProviderList;
59 // table containing mappings from child providers to their root nodes.
60 private Hashtable _childProviderTable;
63 public XmlSiteMapProvider() {
66 private ArrayList ChildProviderList {
68 ArrayList returnList = _childProviderList;
69 if (returnList == null) {
71 if (_childProviderList == null) {
72 returnList = ArrayList.ReadOnly(new ArrayList(ChildProviderTable.Keys));
73 _childProviderList = returnList;
76 returnList = _childProviderList;
85 private Hashtable ChildProviderTable {
87 if (_childProviderTable == null) {
89 if (_childProviderTable == null) {
90 _childProviderTable = new Hashtable();
95 return _childProviderTable;
100 public override SiteMapNode RootNode {
103 SiteMapNode node = ReturnNodeIfAccessible(_siteMapNode);
104 return ApplyModifierIfExists(node);
108 public override SiteMapNode CurrentNode {
110 return ApplyModifierIfExists(base.CurrentNode);
114 public override SiteMapNode GetParentNode(SiteMapNode node) {
115 SiteMapNode parentNode = base.GetParentNode(node);
116 return ApplyModifierIfExists(parentNode);
119 public override SiteMapNodeCollection GetChildNodes(SiteMapNode node) {
120 SiteMapNodeCollection subNodes = base.GetChildNodes(node);
121 HttpContext context = HttpContext.Current;
123 // Do nothing if the modifier doesn't apply
124 if (context == null || !context.Response.UsePathModifier || subNodes.Count == 0) {
128 // Apply the modifier to the children nodes
129 SiteMapNodeCollection resultNodes = new SiteMapNodeCollection(subNodes.Count);
131 foreach (SiteMapNode n in subNodes) {
132 resultNodes.Add(ApplyModifierIfExists(n));
138 protected internal override void AddNode(SiteMapNode node, SiteMapNode parentNode) {
140 throw new ArgumentNullException("node");
143 if (parentNode == null) {
144 throw new ArgumentNullException("parentNode");
147 SiteMapProvider ownerProvider = node.Provider;
148 SiteMapProvider parentOwnerProvider = parentNode.Provider;
150 if (ownerProvider != this) {
151 throw new ArgumentException(SR.GetString(SR.XmlSiteMapProvider_cannot_add_node, node.ToString()), "node");
154 if (parentOwnerProvider != this) {
155 throw new ArgumentException(SR.GetString(SR.XmlSiteMapProvider_cannot_add_node, parentNode.ToString()), "parentNode");
159 // First remove it from its current location.
161 AddNodeInternal(node, parentNode, null);
165 private void AddNodeInternal(SiteMapNode node, SiteMapNode parentNode, XmlNode xmlNode) {
167 String url = node.Url;
168 String key = node.Key;
170 bool isValidUrl = false;
172 // Only add the node to the url table if it's a static node.
173 if (!String.IsNullOrEmpty(url)) {
174 if (UrlTable[url] != null) {
175 if (xmlNode != null) {
176 throw new ConfigurationErrorsException(
177 SR.GetString(SR.XmlSiteMapProvider_Multiple_Nodes_With_Identical_Url, url),
181 throw new InvalidOperationException(SR.GetString(
182 SR.XmlSiteMapProvider_Multiple_Nodes_With_Identical_Url, url));
189 if (KeyTable.Contains(key)) {
190 if (xmlNode != null) {
191 throw new ConfigurationErrorsException(
192 SR.GetString(SR.XmlSiteMapProvider_Multiple_Nodes_With_Identical_Key, key),
196 throw new InvalidOperationException(
197 SR.GetString(SR.XmlSiteMapProvider_Multiple_Nodes_With_Identical_Key, key));
202 UrlTable[url] = node;
205 KeyTable[key] = node;
207 // Add the new node into parentNode collection
208 if (parentNode != null) {
209 ParentNodeTable[node] = parentNode;
211 if (ChildNodeCollectionTable[parentNode] == null) {
212 ChildNodeCollectionTable[parentNode] = new SiteMapNodeCollection();
215 ((SiteMapNodeCollection)ChildNodeCollectionTable[parentNode]).Add(node);
220 protected virtual void AddProvider(string providerName, SiteMapNode parentNode) {
221 if (parentNode == null) {
222 throw new ArgumentNullException("parentNode");
225 if (parentNode.Provider != this) {
226 throw new ArgumentException(SR.GetString(SR.XmlSiteMapProvider_cannot_add_node, parentNode.ToString()), "parentNode");
229 SiteMapNode node = GetNodeFromProvider(providerName);
230 AddNodeInternal(node, parentNode, null);
234 [SuppressMessage("Microsoft.Security", "MSEC1205:DoNotAllowDtdOnXmlTextReader", Justification = "Legacy code that trusts our developer-controlled input.")]
235 [SuppressMessage("Microsoft.Security.Xml", "CA3054:DoNotAllowDtdOnXmlTextReader", Justification = "Legacy code that trusts our developer-controlled input.")]
236 public override SiteMapNode BuildSiteMap() {
238 SiteMapNode tempNode = _siteMapNode;
240 // If siteMap is already constructed, simply returns it.
241 // Child providers will only be updated when the parent providers need to access them.
242 if (tempNode != null) {
246 XmlDocument document = GetConfigDocument();
249 if (_siteMapNode != null) {
255 // Need to check if the sitemap file exists before opening it.
256 CheckSiteMapFileExists();
259 using (Stream stream = _normalizedVirtualPath.OpenFile()) {
260 XmlReader reader = new XmlTextReader(stream);
261 document.Load(reader);
264 catch (XmlException e) {
265 string sourceFile = _virtualPath.VirtualPathString;
266 string physicalDir = _normalizedVirtualPath.MapPathInternal();
267 if (physicalDir != null && HttpRuntime.HasPathDiscoveryPermission(physicalDir)) {
268 sourceFile = physicalDir;
271 throw new ConfigurationErrorsException(
272 SR.GetString(SR.XmlSiteMapProvider_Error_loading_Config_file, _virtualPath, e.Message),
273 e, sourceFile, e.LineNumber);
275 catch (Exception e) {
276 throw new ConfigurationErrorsException(
277 SR.GetString(SR.XmlSiteMapProvider_Error_loading_Config_file, _virtualPath, e.Message), e);
281 foreach (XmlNode siteMapMode in document.ChildNodes) {
282 if (String.Equals(siteMapMode.Name, "siteMap", StringComparison.Ordinal)) {
289 throw new ConfigurationErrorsException(
290 SR.GetString(SR.XmlSiteMapProvider_Top_Element_Must_Be_SiteMap),
293 bool enableLocalization = false;
294 HandlerBase.GetAndRemoveBooleanAttribute(node, "enableLocalization", ref enableLocalization);
295 EnableLocalization = enableLocalization;
297 XmlNode topElement = null;
298 foreach (XmlNode subNode in node.ChildNodes) {
299 if (subNode.NodeType == XmlNodeType.Element) {
300 if (!_siteMapNodeName.Equals(subNode.Name)) {
301 throw new ConfigurationErrorsException(
302 SR.GetString(SR.XmlSiteMapProvider_Only_SiteMapNode_Allowed),
306 if (topElement != null) {
307 throw new ConfigurationErrorsException(
308 SR.GetString(SR.XmlSiteMapProvider_Only_One_SiteMapNode_Required_At_Top),
312 topElement = subNode;
316 if (topElement == null) {
317 throw new ConfigurationErrorsException(
318 SR.GetString(SR.XmlSiteMapProvider_Only_One_SiteMapNode_Required_At_Top),
322 Queue queue = new Queue(50);
324 // The parentnode of the top node does not exist,
325 // simply add a null to satisfy the ConvertFromXmlNode condition.
327 queue.Enqueue(topElement);
328 _siteMapNode = ConvertFromXmlNode(queue);
334 private void CheckSiteMapFileExists() {
335 if (!System.Web.UI.Util.VirtualFileExistsWithAssert(_normalizedVirtualPath)) {
336 throw new InvalidOperationException(
337 SR.GetString(SR.XmlSiteMapProvider_FileName_does_not_exist, _virtualPath));
342 protected override void Clear() {
344 ChildProviderTable.Clear();
346 _childProviderList = null;
352 // helper method to convert an XmlNode to a SiteMapNode
353 private SiteMapNode ConvertFromXmlNode(Queue queue) {
355 SiteMapNode rootNode = null;
357 if (queue.Count == 0) {
361 SiteMapNode parentNode = (SiteMapNode)queue.Dequeue();
362 XmlNode xmlNode = (XmlNode)queue.Dequeue();
364 SiteMapNode node = null;
366 if (!_siteMapNodeName.Equals(xmlNode.Name)) {
367 throw new ConfigurationErrorsException(
368 SR.GetString(SR.XmlSiteMapProvider_Only_SiteMapNode_Allowed),
372 string providerName = null;
373 HandlerBase.GetAndRemoveNonEmptyStringAttribute(xmlNode, _providerAttribute, ref providerName);
375 // If the siteMapNode references another provider
376 if (providerName != null) {
377 node = GetNodeFromProvider(providerName);
379 // No other attributes or child nodes are allowed on a provider node.
380 HandlerBase.CheckForUnrecognizedAttributes(xmlNode);
381 HandlerBase.CheckForNonCommentChildNodes(xmlNode);
384 string siteMapFile = null;
385 HandlerBase.GetAndRemoveNonEmptyStringAttribute(xmlNode, _siteMapFileAttribute, ref siteMapFile);
387 if (siteMapFile != null) {
388 node = GetNodeFromSiteMapFile(xmlNode, VirtualPath.Create(siteMapFile));
391 node = GetNodeFromXmlNode(xmlNode, queue);
395 AddNodeInternal(node, parentNode, xmlNode);
397 if (rootNode == null) {
403 protected virtual void Dispose(bool disposing) {
404 if (_handler != null) {
405 Debug.Assert(_filename != null);
406 HttpRuntime.FileChangesMonitor.StopMonitoringFile(_filename, _handler);
410 public void Dispose() {
412 GC.SuppressFinalize(this);
415 private void EnsureChildSiteMapProviderUpToDate(SiteMapProvider childProvider) {
416 SiteMapNode oldNode = (SiteMapNode)ChildProviderTable[childProvider];
418 SiteMapNode newNode = childProvider.GetRootNodeCore();
419 if (newNode == null) {
420 throw new ProviderException(SR.GetString(SR.XmlSiteMapProvider_invalid_sitemapnode_returned, childProvider.Name));
423 // child providers have been updated.
424 if (!oldNode.Equals(newNode)) {
426 // If the child provider table has been updated, simply return null.
427 // This will happen when the current provider's sitemap file is changed or Clear() is called;
428 if (oldNode == null) {
433 oldNode = (SiteMapNode)ChildProviderTable[childProvider];
434 // If the child provider table has been updated, simply return null. See above.
435 if (oldNode == null) {
439 newNode = childProvider.GetRootNodeCore();
440 if (newNode == null) {
441 throw new ProviderException(SR.GetString(SR.XmlSiteMapProvider_invalid_sitemapnode_returned, childProvider.Name));
444 if (!oldNode.Equals(newNode)) {
446 // If the current provider does not contain any nodes but one child provider
447 // ie. _siteMapNode == oldNode
448 // the oldNode needs to be removed from Url table and the new node will be added.
449 if (_siteMapNode.Equals(oldNode)) {
450 UrlTable.Remove(oldNode.Url);
451 KeyTable.Remove(oldNode.Key);
453 UrlTable.Add(newNode.Url, newNode);
454 KeyTable.Add(newNode.Key, newNode);
456 _siteMapNode = newNode;
459 // First find the parent node
460 SiteMapNode parent = (SiteMapNode)ParentNodeTable[oldNode];
462 // parent is null when the provider does not contain any static nodes, ie.
463 // it only contains definition to include one child provider.
464 if (parent != null) {
465 // Update the child nodes table
466 SiteMapNodeCollection list = (SiteMapNodeCollection)ChildNodeCollectionTable[parent];
468 // Add the newNode to where the oldNode is within parent node's collection.
469 int index = list.IndexOf(oldNode);
471 list.Remove(oldNode);
472 list.Insert(index, newNode);
478 // Update the parent table
479 ParentNodeTable[newNode] = parent;
480 ParentNodeTable.Remove(oldNode);
482 // Update the Url table
483 UrlTable.Remove(oldNode.Url);
484 KeyTable.Remove(oldNode.Key);
486 UrlTable.Add(newNode.Url, newNode);
487 KeyTable.Add(newNode.Key, newNode);
490 // Notify the parent provider to update its child provider collection.
491 XmlSiteMapProvider provider = ParentProvider as XmlSiteMapProvider;
492 if (provider != null) {
493 provider.EnsureChildSiteMapProviderUpToDate(this);
497 // Update provider nodes;
498 ChildProviderTable[childProvider] = newNode;
499 _childProviderList = null;
505 // Returns sitemap node; Search recursively in child providers if not found.
507 public override SiteMapNode FindSiteMapNode(string rawUrl) {
508 SiteMapNode node = base.FindSiteMapNode(rawUrl);
511 foreach(SiteMapProvider provider in ChildProviderList) {
512 // First make sure the child provider is up-to-date.
513 EnsureChildSiteMapProviderUpToDate(provider);
515 node = provider.FindSiteMapNode(rawUrl);
525 // Returns sitemap node; Search recursively in child providers if not found.
526 public override SiteMapNode FindSiteMapNodeFromKey(string key) {
527 SiteMapNode node = base.FindSiteMapNodeFromKey(key);
530 foreach (SiteMapProvider provider in ChildProviderList) {
531 // First make sure the child provider is up-to-date.
532 EnsureChildSiteMapProviderUpToDate(provider);
534 node = provider.FindSiteMapNodeFromKey(key);
544 private XmlDocument GetConfigDocument() {
545 if (_document != null)
549 throw new InvalidOperationException(
550 SR.GetString(SR.XmlSiteMapProvider_Not_Initialized));
553 // Do the error checking here
554 if (_virtualPath == null) {
555 throw new ArgumentException(
556 SR.GetString(SR.XmlSiteMapProvider_missing_siteMapFile, _siteMapFileAttribute));
559 if (!_virtualPath.Extension.Equals(_xmlSiteMapFileExtension, StringComparison.OrdinalIgnoreCase)) {
560 throw new InvalidOperationException(
561 SR.GetString(SR.XmlSiteMapProvider_Invalid_Extension, _virtualPath));
564 _normalizedVirtualPath = _virtualPath.CombineWithAppRoot();
565 _normalizedVirtualPath.FailIfNotWithinAppRoot();
567 // Make sure the file exists
568 CheckSiteMapFileExists();
570 _parentSiteMapFileCollection = new StringCollection();
571 XmlSiteMapProvider xmlParentProvider = ParentProvider as XmlSiteMapProvider;
572 if (xmlParentProvider != null && xmlParentProvider._parentSiteMapFileCollection != null) {
573 if (xmlParentProvider._parentSiteMapFileCollection.Contains(_normalizedVirtualPath.VirtualPathString)) {
574 throw new InvalidOperationException(
575 SR.GetString(SR.XmlSiteMapProvider_FileName_already_in_use, _virtualPath));
578 // Copy the sitemapfiles in used from parent provider to current provider.
579 foreach (string filename in xmlParentProvider._parentSiteMapFileCollection) {
580 _parentSiteMapFileCollection.Add(filename);
584 // Add current sitemap file to the collection
585 _parentSiteMapFileCollection.Add(_normalizedVirtualPath.VirtualPathString);
587 _filename = HostingEnvironment.MapPathInternal(_normalizedVirtualPath);
589 if (!String.IsNullOrEmpty(_filename)) {
590 _handler = new FileChangeEventHandler(this.OnConfigFileChange);
591 HttpRuntime.FileChangesMonitor.StartMonitoringFile(_filename, _handler);
592 ResourceKey = (new FileInfo(_filename)).Name;
595 _document = new ConfigXmlDocument();
600 private SiteMapNode GetNodeFromProvider(string providerName) {
601 SiteMapProvider provider = GetProviderFromName(providerName);
602 SiteMapNode node = null;
604 // Check infinite recursive sitemap files
605 if (provider is XmlSiteMapProvider) {
606 XmlSiteMapProvider xmlProvider = (XmlSiteMapProvider)provider;
608 StringCollection parentSiteMapFileCollection = new StringCollection();
609 if (_parentSiteMapFileCollection != null) {
610 foreach (string filename in _parentSiteMapFileCollection) {
611 parentSiteMapFileCollection.Add(filename);
615 // Make sure the provider is initialized before adding to the collection.
616 xmlProvider.BuildSiteMap();
618 parentSiteMapFileCollection.Add(_normalizedVirtualPath.VirtualPathString);
619 if (parentSiteMapFileCollection.Contains(VirtualPath.GetVirtualPathString(xmlProvider._normalizedVirtualPath))) {
620 throw new InvalidOperationException(SR.GetString(SR.XmlSiteMapProvider_FileName_already_in_use, xmlProvider._virtualPath));
623 xmlProvider._parentSiteMapFileCollection = parentSiteMapFileCollection;
626 node = provider.GetRootNodeCore();
628 throw new InvalidOperationException(
629 SR.GetString(SR.XmlSiteMapProvider_invalid_GetRootNodeCore, ((ProviderBase)provider).Name));
632 ChildProviderTable.Add(provider, node);
633 _childProviderList = null;
635 provider.ParentProvider = this;
640 private SiteMapNode GetNodeFromSiteMapFile(XmlNode xmlNode, VirtualPath siteMapFile) {
642 SiteMapNode node = null;
644 // For external sitemap files, its secuity setting is inherited from parent provider
645 bool secuityTrimmingEnabled = SecurityTrimmingEnabled;
646 HandlerBase.GetAndRemoveBooleanAttribute(xmlNode, _securityTrimmingEnabledAttrName, ref secuityTrimmingEnabled);
648 // No other attributes or non-comment nodes are allowed on a siteMapFile node
649 HandlerBase.CheckForUnrecognizedAttributes(xmlNode);
650 HandlerBase.CheckForNonCommentChildNodes(xmlNode);
652 XmlSiteMapProvider childProvider = new XmlSiteMapProvider();
654 // siteMapFile was relative to the sitemap file where this xmlnode is defined, make it an application path.
655 siteMapFile = _normalizedVirtualPath.Parent.Combine(siteMapFile);
657 childProvider.ParentProvider = this;
658 childProvider.Initialize(siteMapFile, secuityTrimmingEnabled);
659 childProvider.BuildSiteMap();
661 node = childProvider._siteMapNode;
663 ChildProviderTable.Add(childProvider, node);
664 _childProviderList = null;
669 private void HandleResourceAttribute(XmlNode xmlNode, ref NameValueCollection collection,
670 string attrName, ref string text, bool allowImplicitResource) {
671 if (String.IsNullOrEmpty(text)) {
675 string resourceKey = null;
676 string temp = text.TrimStart(new char[] { ' ' });
678 if (temp != null && temp.Length > _resourcePrefixLength) {
679 if (temp.ToLower(CultureInfo.InvariantCulture).StartsWith(_resourcePrefix, StringComparison.Ordinal)) {
680 if (!allowImplicitResource) {
681 throw new ConfigurationErrorsException(
682 SR.GetString(SR.XmlSiteMapProvider_multiple_resource_definition, attrName), xmlNode);
685 resourceKey = temp.Substring(_resourcePrefixLength + 1);
687 if (resourceKey.Length == 0) {
688 throw new ConfigurationErrorsException(
689 SR.GetString(SR.XmlSiteMapProvider_resourceKey_cannot_be_empty), xmlNode);
692 // Retrieve className from attribute
693 string className = null;
695 int index = resourceKey.IndexOf(_resourceKeySeparator);
697 throw new ConfigurationErrorsException(
698 SR.GetString(SR.XmlSiteMapProvider_invalid_resource_key, resourceKey), xmlNode);
701 className = resourceKey.Substring(0, index);
702 key = resourceKey.Substring(index + 1);
704 // Retrieve resource key and default value from attribute
705 int defaultIndex = key.IndexOf(_resourceKeySeparator);
706 if (defaultIndex != -1) {
707 text = key.Substring(defaultIndex + 1);
708 key = key.Substring(0, defaultIndex);
714 if (collection == null) {
715 collection = new NameValueCollection();
718 collection.Add(attrName, className.Trim());
719 collection.Add(attrName, key.Trim());
724 private SiteMapNode GetNodeFromXmlNode(XmlNode xmlNode, Queue queue) {
725 SiteMapNode node = null;
727 string title = null, url = null, description = null, roles = null, resourceKey = null;
729 // Url attribute is NOT required for a xml node.
730 HandlerBase.GetAndRemoveStringAttribute(xmlNode, "url", ref url);
731 HandlerBase.GetAndRemoveStringAttribute(xmlNode, "title", ref title);
732 HandlerBase.GetAndRemoveStringAttribute(xmlNode, "description", ref description);
733 HandlerBase.GetAndRemoveStringAttribute(xmlNode, "roles", ref roles);
734 HandlerBase.GetAndRemoveStringAttribute(xmlNode, "resourceKey", ref resourceKey);
736 // Do not add the resourceKey if the resource is not valid.
737 if (!String.IsNullOrEmpty(resourceKey) &&
738 !ValidateResource(ResourceKey, resourceKey + ".title")) {
742 HandlerBase.CheckForbiddenAttribute(xmlNode, _securityTrimmingEnabledAttrName);
744 NameValueCollection resourceKeyCollection = null;
745 bool allowImplicitResourceAttribute = String.IsNullOrEmpty(resourceKey);
746 HandleResourceAttribute(xmlNode, ref resourceKeyCollection,
747 "title", ref title, allowImplicitResourceAttribute);
748 HandleResourceAttribute(xmlNode, ref resourceKeyCollection,
749 "description", ref description, allowImplicitResourceAttribute);
751 ArrayList roleList = new ArrayList();
753 int foundIndex = roles.IndexOf('?');
754 if (foundIndex != -1) {
755 throw new ConfigurationErrorsException(
756 SR.GetString(SR.Auth_rule_names_cant_contain_char,
757 roles[foundIndex].ToString(CultureInfo.InvariantCulture)), xmlNode);
760 foreach (string role in roles.Split(_seperators)) {
761 string trimmedRole = role.Trim();
762 if (trimmedRole.Length > 0) {
763 roleList.Add(trimmedRole);
767 roleList = ArrayList.ReadOnly(roleList);
771 // Make urls absolute.
772 if (!String.IsNullOrEmpty(url)) {
773 // URL needs to be trimmed. VSWhidbey 411041
776 if (!UrlPath.IsAbsolutePhysicalPath(url)) {
777 if (UrlPath.IsRelativeUrl(url)) {
778 url = UrlPath.Combine(HttpRuntime.AppDomainAppVirtualPathString, url);
782 // VSWhidbey 418056, Reject any suspicious or mal-formed Urls.
783 string decodedUrl = HttpUtility.UrlDecode(url);
784 if (!String.Equals(url, decodedUrl, StringComparison.Ordinal)) {
785 throw new ConfigurationErrorsException(
786 SR.GetString(SR.Property_Had_Malformed_Url, "url", url), xmlNode);
789 key = url.ToLowerInvariant();
792 key = Guid.NewGuid().ToString();
795 // attribute collection does not contain pre-defined properties like title, url, etc.
796 ReadOnlyNameValueCollection attributeCollection = new ReadOnlyNameValueCollection();
797 attributeCollection.SetReadOnly(false);
798 foreach (XmlAttribute attribute in xmlNode.Attributes) {
799 string value = attribute.Value;
800 HandleResourceAttribute(xmlNode, ref resourceKeyCollection, attribute.Name, ref value, allowImplicitResourceAttribute);
801 attributeCollection[attribute.Name] = value;
803 attributeCollection.SetReadOnly(true);
805 node = new SiteMapNode(this, key, url, title, description, roleList, attributeCollection, resourceKeyCollection, resourceKey);
806 node.ReadOnly = true;
808 foreach (XmlNode subNode in xmlNode.ChildNodes) {
809 if (subNode.NodeType != XmlNodeType.Element)
813 queue.Enqueue(subNode);
819 private SiteMapProvider GetProviderFromName(string providerName) {
820 Debug.Assert(providerName != null);
822 SiteMapProvider provider = SiteMap.Providers[providerName];
823 if (provider == null) {
824 throw new ProviderException(SR.GetString(SR.Provider_Not_Found, providerName));
830 protected internal override SiteMapNode GetRootNodeCore() {
836 public override void Initialize(string name, NameValueCollection attributes) {
838 throw new InvalidOperationException(
839 SR.GetString(SR.XmlSiteMapProvider_Cannot_Be_Inited_Twice));
842 if (attributes != null) {
843 if (string.IsNullOrEmpty(attributes["description"])) {
844 attributes.Remove("description");
845 attributes.Add("description", SR.GetString(SR.XmlSiteMapProvider_Description));
848 string siteMapFile = null;
849 ProviderUtil.GetAndRemoveStringAttribute(attributes, _siteMapFileAttribute, name, ref siteMapFile);
850 _virtualPath = VirtualPath.CreateAllowNull(siteMapFile);
853 base.Initialize(name, attributes);
855 if (attributes != null) {
856 ProviderUtil.CheckUnrecognizedAttributes(attributes, name);
862 private void Initialize(VirtualPath virtualPath, bool secuityTrimmingEnabled) {
863 NameValueCollection coll = new NameValueCollection();
864 coll.Add(_siteMapFileAttribute, virtualPath.VirtualPathString);
865 coll.Add(_securityTrimmingEnabledAttrName, System.Web.UI.Util.GetStringFromBool(secuityTrimmingEnabled));
867 // Use the siteMapFile virtual path as the provider name
868 Initialize(virtualPath.VirtualPathString, coll);
871 private void OnConfigFileChange(Object sender, FileChangeEvent e) {
872 // Notifiy the parent for the change.
873 XmlSiteMapProvider parentProvider = ParentProvider as XmlSiteMapProvider;
874 if (parentProvider != null) {
875 parentProvider.OnConfigFileChange(sender, e);
881 protected internal override void RemoveNode(SiteMapNode node) {
883 throw new ArgumentNullException("node");
886 SiteMapProvider ownerProvider = node.Provider;
888 if (ownerProvider != this) {
890 // Only nodes defined in this provider tree can be removed.
891 SiteMapProvider parentProvider = ownerProvider.ParentProvider;
892 while (parentProvider != this) {
893 if (parentProvider == null) {
894 // Cannot remove nodes defined in other providers
895 throw new InvalidOperationException(
896 SR.GetString(SR.XmlSiteMapProvider_cannot_remove_node, node.ToString(),
897 this.Name, ownerProvider.Name));
900 parentProvider = parentProvider.ParentProvider;
904 if (node.Equals(ownerProvider.GetRootNodeCore())) {
905 throw new InvalidOperationException(SR.GetString(SR.SiteMapProvider_cannot_remove_root_node));
908 if (ownerProvider != this) {
909 // Remove node from the owner provider.
910 ownerProvider.RemoveNode(node);
913 base.RemoveNode(node);
916 protected virtual void RemoveProvider(string providerName) {
917 if (providerName == null) {
918 throw new ArgumentNullException("providerName");
922 SiteMapProvider provider = GetProviderFromName(providerName);
923 SiteMapNode rootNode = (SiteMapNode)ChildProviderTable[provider];
925 if (rootNode == null) {
926 throw new InvalidOperationException(SR.GetString(SR.XmlSiteMapProvider_cannot_find_provider, provider.Name, this.Name));
929 provider.ParentProvider = null;
930 ChildProviderTable.Remove(provider);
931 _childProviderList = null;
933 base.RemoveNode(rootNode);
937 // VSWhidbey: 493981 Helper method to check if the valid resource type exists.
938 // Note that this only returns false if the classKey cannot be found, regardless of resourceKey.
939 private bool ValidateResource(string classKey, string resourceKey) {
941 HttpContext.GetGlobalResourceObject(classKey, resourceKey);
943 catch (MissingManifestResourceException) {
950 // Dev10# 923217 - SiteMapProvider URL Table Invalid Using Cookieless
951 // Don't keep the modifier inside the links table. Apply the modifier as approriate on demand
952 private static SiteMapNode ApplyModifierIfExists(SiteMapNode node) {
953 HttpContext context = HttpContext.Current;
955 // Do nothing if the modifier doesn't apply
956 if (node == null || context == null || !context.Response.UsePathModifier) {
960 // Set Url with the modifier applied
961 SiteMapNode resultNode = node.Clone();
962 resultNode.Url = context.Response.ApplyAppPathModifier(node.Url);
967 private class ReadOnlyNameValueCollection : NameValueCollection {
969 public ReadOnlyNameValueCollection() {
973 internal void SetReadOnly(bool isReadonly) {
974 IsReadOnly = isReadonly;