Question

Nous avons un site Web qui utilise un plan Sitemap par défaut conforme aux normes de la marque BOG avec le filtrage de sécurité, comme suit:

<siteMap defaultProvider="default" enabled="true">
  <providers>
    <add siteMapFile="~/Web.sitemap" securityTrimmingEnabled="true" name="default" type="System.Web.XmlSiteMapProvider, System.Web, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" />
  </providers>
</siteMap>

Très bien, mais une requête a été introduite pour modifier le Titre d'un nœud en fonction de certains critères d'arrière-plan. Cela semble simple, mais apparemment pas.

Tentative 1 : gestion de l'événement SiteMapResolve . Il semble que l'endroit où cet événement est géré n'a pas d'importance, je l'ai indiqué dans Global.asax simplement parce que c'est l'un des endroits où je l'ai essayé et que cela a fonctionné.

Public Class Global_asax
    Inherits System.Web.HttpApplication

    Sub Application_BeginRequest(ByVal sender As Object, ByVal e As EventArgs)
        AddHandler SiteMap.SiteMapResolve, AddressOf SiteMapResolve
    End Sub

    Sub Application_EndRequest(ByVal sender As Object, ByVal e As EventArgs)
        RemoveHandler SiteMap.SiteMapResolve, AddressOf SiteMapResolve
    End Sub

    Private Shared Function SiteMapResolve(ByVal sender As Object, ByVal e As SiteMapResolveEventArgs) As SiteMapNode

        Dim node As SiteMapNode = SiteMap.CurrentNode
        If IsThisTheNodeToChange(node) Then
            node = node.Clone()
            node.Title = GetNodeTitle()
        End If
        Return node

    End Function

End Class

Cela fonctionnait bien lorsque la page correspondante était consultée, mais malheureusement, une partie de la navigation du site implique une zone de liste déroulante liée aux données de la carte du site, comme suit:

<asp:SiteMapDataSource ID="siteMapDataSource" runat="Server" ShowStartingNode="false" StartFromCurrentNode="false" StartingNodeOffset="1" />
<asp:DropDownList ID="pageMenu" runat="Server" AutoPostBack="True" DataSourceID="siteMapDataSource" DataTextField="Title" DataValueField="Url" />

Lorsque ce menu est rendu, l'événement SiteMapResolve ne se déclenche pour aucun contenu car le nœud actuel est la page sur laquelle le menu est défini. En conséquence, le menu affiche le titre d’espace réservé non-sens du fichier de sitemap physique plutôt que le titre correct.

Tentative 2 : création de mon propre fournisseur de sitemap. Je ne voulais pas dupliquer tout le comportement par défaut. J'ai donc essayé de dériver du fournisseur par défaut comme suit.

Public Class DynamicXmlSiteMapProvider
    Inherits XmlSiteMapProvider

    Private _dataFixedUp As Boolean = False

    Public Overrides Function GetChildNodes(ByVal node As SiteMapNode) As SiteMapNodeCollection

        Dim result As SiteMapNodeCollection = MyBase.GetChildNodes(node)
        If Not _dataFixedUp Then
            For Each childNode As SiteMapNode In result
                FixUpNode(childNode)
            Next
        End If
        Return result

    End Function

    Private Sub FixUpNode(ByVal node As SiteMapNode)

        If IsThisTheNodeToChange(node) Then
            node.ReadOnly = False
            node.Title = GetNodeTitle()
            node.ReadOnly = True
            _dataFixedUp = True
        End If

    End Sub

End Class

Cela ne fonctionne pas car GetChildNodes ne semble pas être appelé très souvent lors de la navigation sur le site.

Tentative 3 : essayez de corriger les données immédiatement après leur chargement en mémoire, et non après leur accès.

Public Class DynamicXmlSiteMapProvider
    Inherits XmlSiteMapProvider

    Private _dataFixInProgress As Boolean = False
    Private _dataFixDone As Boolean = False

    Public Overrides Function BuildSiteMap() As SiteMapNode

        Dim result As SiteMapNode = MyBase.BuildSiteMap()
        If Not _dataFixInProgress AndAlso Not _dataFixDone Then
            _dataFixInProgress = True
            For Each childNode As SiteMapNode In result.GetAllNodes()
                FixUpNode(childNode)
            Next
            _dataFixInProgress = False
            _dataFixDone = True
        End If
        Return result

    End Function

    Private Sub FixUpNode(ByVal node As SiteMapNode)

        If IsThisTheNodeToChange(node) Then
            node.ReadOnly = False
            node.Title = GetNodeTitle()
            node.ReadOnly = True
        End If

    End Sub

End Class

Cela semble fonctionner. Cependant, l'appel à GetAllNodes dans la méthode BuildSiteMap m'inquiète. Il me semble juste de ne pas rentrer de manière récursive toutes les données en mémoire simplement pour corriger une valeur. De plus, je n’ai aucun contrôle sur le moment où BuildSiteMap est appelé. Je préférerais quelque chose de plus similaire à Attempt 1, appelé à la demande lorsque les données du nœud sont nécessaires pour la première fois.

Tentative 4 (NOUVEAU) : similaire à Tentative 2, mais remplace tous les membres virtuels liés à la lecture de données ( CurrentNode , < code> FindSiteMapNode , FindSiteMapNodeFromKey , GetChildNodes , GetCurrentNodeAndHintAncestorNodes , GetCurrentNodeAndHintNearthhoodNodes> code de sécurité>, GetParentNodeRelativeToCurrentNodeAndHintDownFromParent , quelque part.

Cela n'a pas fonctionné. Je mets des instructions de débogage dans tous les membres remplacés, et il semble qu'aucune d'elles ne soit appelée lors de la liaison de données à la liste déroulante. La seule explication à laquelle je puisse penser est que tous les nœuds sont lus en mémoire en une seule fois lors de l'appel BuildSiteMap , de sorte que le SiteMapNode ne frappe pas la classe fournisseur en énumérant nœuds enfants.

Quelqu'un a-t-il de meilleures suggestions?

Était-ce utile?

La solution

Dans notre site personnalisé SiteMapProvider, nous substituons la méthode BuildSiteMap et construisons le SiteMapNodes manuellement. Pour modifier et / ou ajouter des propriétés personnalisées, nous ajoutons des attributs personnalisés à SiteMapNodes en créant un NameValueCollection et en le transmettant au constructeur SiteMapNode.

Autres conseils

Vous êtes assez proche de la tentative n ° 2. Vous devez également remplacer GetParentNode et FindSiteMapNode également.

Merci à Mark et Rex pour les suggestions. Ce que j’ai fini par faire, c’est de laisser le fournisseur de sitemap seul et de corriger le nœud de la page principale, par exemple:

Protected Sub Page_Init(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Init

    ' Do this as early as possible in the page lifecycle so that it happens before any
    ' automatic data binding or control initialisation is done.
    Dim node As SiteMapNode = GetNodeToEdit()
    Dim nodeReadOnly As Boolean = node.ReadOnly
    node.ReadOnly = False
    node.Title = GetNodeTitle()
    node.ReadOnly = nodeReadOnly

End Sub

Toutefois, j’ai accepté la réponse de Mark, car c’est ce que je ferai s’il s’avère que des modifications plus importantes devront être apportées à l’avenir.

Merci Christian pour vos recherches. En utilisant vos résultats, j'ai mis au point les éléments suivants qui pourraient aider les autres:

' Dynamically update some menu items.
'
' Since the correct Medical Center ID is not known until runtime, need to
' append "&MEDICAL_CENTER_ID=xxx" to all of the report's URLs in Web.sitemap.
Public Shared Sub UpdateMenu(ByVal MedicalCenterID As String)
  Dim CurrentNodeTitle As String = ""
  Dim NodeReadOnlyProperty As Boolean

  ' Home menu item
  CurrentNodeTitle = SiteMap.CurrentNode.Title

  ' Determines if the current node has child nodes.
  If (SiteMap.CurrentNode.HasChildNodes) Then
    ' Loop through top level 1 menu items (looking for Reports)
    For Each ChildNodesEnumerator1 As SiteMapNode In SiteMap.CurrentNode.ChildNodes
      CurrentNodeTitle = ChildNodesEnumerator1.Title
      If CurrentNodeTitle = "Reports" Then
        ' Loop through level 2 menu items (looking for specfic reports)
        For Each ChildNodesEnumerator2 As SiteMapNode In ChildNodesEnumerator1.ChildNodes
          CurrentNodeTitle = ChildNodesEnumerator2.Title
          If CurrentNodeTitle = "Multi-Day Vehicle Requests" Or _
           CurrentNodeTitle = "XXXXXXXXXXXXXXXXX" Then
            ' First check if the URL has not been modified already
            If InStr(ChildNodesEnumerator2.Url, "MEDICAL_CENTER_ID") = 0 Then
              NodeReadOnlyProperty = ChildNodesEnumerator2.ReadOnly
              ChildNodesEnumerator2.ReadOnly = False
              ChildNodesEnumerator2.Url = ChildNodesEnumerator2.Url & "&MEDICAL_CENTER_ID=" & MedicalCenterID
              ChildNodesEnumerator2.ReadOnly = NodeReadOnlyProperty
            End If
          End If
        Next
      End If
    Next
  End If
End Sub
Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top