Непоследовательное поведение при отображении меню быстрого запуска в MOSS 2007 - PullRequest
1 голос
/ 18 сентября 2008

Я пытаюсь настроить меню быстрого запуска, чтобы отображать только узлы предков и потомков текущего выбранного узла. Меню также должно отображать все childern корневого узла. Проще говоря:

Учитывая карту сайта:

RootSite

--- SubSite1 = навигация установлена ​​на «Отображать текущий сайт, элементы навигации под текущим сайтом и братьев и сестер текущего сайта»

----- Заголовок1 = навигация установлена ​​на «Отображать те же элементы навигации, что и на родительском сайте»

------- Page1 = навигация установлена ​​на «Отображать те же элементы навигации, что и на родительском сайте»

------- Page2 = навигация установлена ​​на «Отображать те же элементы навигации, что и на родительском сайте»

----- Heading2 = навигация установлена ​​на «Отображать те же элементы навигации, что и на родительском сайте»

--- SubSite2 = навигация установлена ​​на «Отображать текущий сайт, элементы навигации под текущим сайтом и братьев и сестер текущего сайта»

----- Heading1 = навигация установлена ​​на "Отображать те же элементы навигации, что и на родительском сайте"

Конфигурация SiteMapProvider:

<PublishingNavigation:PortalSiteMapDataSource ID="SiteMapDS" Runat="server"
SiteMapProvider="CurrentNavSiteMapProvider" EnableViewState="true"
StartFromCurrentNode="true" ShowStartingNode="false"/>

Ожидаемое и фактическое поведение меню быстрого запуска, отображаемого на SubSite1:

--- SubSite1

----- Заголовок1

------- Page1

------- Page2

* * ----- тысяча сорок-девять * 1050 Заголовок 2 *

--- SubSite2

Ожидаемое поведение меню после перехода к заголовку 1 подсайта 2:

--- SubSite1

--- SubSite2

----- Заголовок1

Что я на самом деле вижу после перехода к заголовку 1 подсайта 2:

--- SubSite1

----- Заголовок1

------- Page1

------- Page2

----- * 1072 Заголовок 2 *

--- SubSite2

----- Заголовок1

Это не соответствует тому, что я ожидаю увидеть, если я установлю для навигации Heading1 значение «Показать те же элементы навигации, что и у родительского сайта », а для SubSite2 установлено« Отображать текущий сайт, элементы навигации под текущим сайтом и его братьев и сестер ». Я ожидаю Заголовок1 для наследования элемента навигации SubSite2 с элементами SubSite1, свернутыми из вида. Я также играл с различными Обрезать ... атрибуты без успеха. Любая помощь будет принята с благодарностью!

Ответы [ 3 ]

2 голосов
/ 24 сентября 2008

Я следовал указаниям @ Nat по темным веб-сайтам Sharepoint, чтобы добиться поведения, описанного выше. Мой подход состоял в том, чтобы развернуть мою собственную версию веб-части MossMenu , которую Microsoft выпустила через блог команды ECM. Этот код основан на нативном элементе управления AspMenu. Я использовал этот элемент управления для «перехвата» нативного SiteMapDataSource, внедренного через атрибут DataSourceId в разметке, и для создания нового источника данных XML, чтобы продемонстрировать желаемое поведение. Я включил окончательный исходный код в конце этого многословного ответа. Вот биты из разметки главной страницы:

<%@ Register TagPrefix="myCustom" Namespace="YourCompany.CustomWebParts"
   Assembly="YourCompany.CustomWebParts, Version=1.0.0.0, Culture=neutral,
   PublicKeyToken=9f4da00116c38ec5" %>

...

<myCustom:MossMenu ID="CurrentNav" runat="server" datasourceID="SiteMapDS"
    orientation="Vertical" UseCompactMenus="true" StaticDisplayLevels="6"
    MaximumDynamicDisplayLevels="0" StaticSubMenuIndent="5" ItemWrap="false"
    AccessKey="3" CssClass="leftNav"
    SkipLinkText="<%$Resources:cms,masterpages_skiplinktext%>">
    <LevelMenuItemStyles>
        <asp:MenuItemStyle CssClass="Nav" />
        <asp:MenuItemStyle CssClass="SecNav" />
    </LevelMenuItemStyles>
    <StaticHoverStyle CssClass="leftNavHover"/>
    <StaticSelectedStyle CssClass="leftNavSelected"/>
    <DynamicMenuStyle CssClass="leftNavFlyOuts" />
    <DynamicMenuItemStyle CssClass="leftNavFlyOutsItem"/>
    <DynamicHoverStyle CssClass="leftNavFlyOutsHover"/>
</myCustom:MossMenu>

<PublishingNavigation:PortalSiteMapDataSource ID="SiteMapDS" Runat="server"
     SiteMapProvider="CurrentNavSiteMapProvider" EnableViewState="true"
     StartFromCurrentNode="true" ShowStartingNode="false"/>

...

Я следовал превосходным пошаговым инструкциям по созданию своей пользовательской веб-части в разделе комментариев веб-части MossMenu на «Среда, 19 сентября 2007 г., 7:20, автор Roel». В своем поиске я также нашел что-то, чтобы настроить сайт Sharepoint для отображения исключений так же, как это делает ASP.NET, внеся изменения web.config здесь .

Я решил назвать свое пользовательское поведение «компактным меню», поэтому я создал свойство UseCompactMenus для элемента управления. Если вы не установите для этого атрибута в разметке значение true, элемент управления будет вести себя идентично элементу управления AspMenu.

В моем приложении пользователь всегда начинается с домашней страницы в корне карты сайта. Я могу сделать так, чтобы пользовательский элемент управления сохранял исходную (полную) карту сайта при отображении корневой страницы. Это хранится в статической строке для использования в настройке поведения. Если ваше приложение не соответствует этому предположению, элемент управления не будет работать должным образом.

На начальной странице приложения в меню отображаются только прямые дочерние страницы на корневую страницу. Нажатие на эти узлы меню откроет все дочерние узлы под ним, но сделает узлы-братья «закрытыми». Если вы щелкнете по одному из других родственных узлов, он свернет текущий узел и откроет вновь выбранный узел. Вот и все, наслаждайтесь!

using System;
using System.Text;
using System.ComponentModel;
using System.Collections.Generic;
using System.Security.Permissions;
using System.Xml;
using System.Xml.Serialization;

using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Web.UI.Design.WebControls;

using Microsoft.SharePoint;
using Microsoft.SharePoint.Utilities;
using Microsoft.SharePoint.Security;

namespace YourCompany.CustomWebParts
{
    [AspNetHostingPermission(SecurityAction.LinkDemand, Level = AspNetHostingPermissionLevel.Minimal)]
    [AspNetHostingPermission(SecurityAction.InheritanceDemand, Level = AspNetHostingPermissionLevel.Minimal)]
    [SharePointPermission(SecurityAction.LinkDemand, ObjectModel = true)]
    [SharePointPermission(SecurityAction.InheritanceDemand, ObjectModel = true)]
    [Designer(typeof(MossMenuDesigner))]
    [ToolboxData("<{0}:MossMenu runat=\"server\" />")]
    public class MossMenu : System.Web.UI.WebControls.Menu
    {
        private string idPrefix;

        // a url->menuItem dictionary
        private Dictionary<string, System.Web.UI.WebControls.MenuItem> menuItemDictionary =
            new Dictionary<string, System.Web.UI.WebControls.MenuItem>(StringComparer.OrdinalIgnoreCase);

        private bool customSelectionEnabled = true;
        private bool selectStaticItemsOnly = true;

        private bool performTargetBinding = true;

        //** Variables used for compact menu behavior **//
        private bool useCompactMenus = false;
        private static bool showStartingNode;
        private static string originalSiteMap;

        /// <summary>
        /// Controls whether or not the control performs compacting of the site map to display only ancestor and child nodes of the selected and first level root childern.
        /// </summary>
        [Category("Behavior")]
        public bool UseCompactMenus
        {
            get
            {
                return this.useCompactMenus;
            }
            set
            {
                this.useCompactMenus = value;
            }
        }

        /// <summary>
        /// Controls whether or not the control performs custom selection/highlighting.
        /// </summary>
        [Category("Behavior")]
        public bool CustomSelectionEnabled
        {
            get
            {
                return this.customSelectionEnabled;
            }
            set
            {
                this.customSelectionEnabled = value;
            }
        }

        /// <summary>
        /// Controls whether only static items may be selected or if
        /// dynamic (fly-out) items may be selected too.
        /// </summary>
        [Category("Behavior")]
        public bool SelectStaticItemsOnly
        {
            get
            {
                return this.selectStaticItemsOnly;
            }
            set
            {
                this.selectStaticItemsOnly = value;
            }
        }

        /// <summary>
        /// Controls whether or not to bind the Target property of any menu
        /// items to the Target property in the SiteMapNode's Attributes
        /// collection.
        /// </summary>
        [Category("Behavior")]
        public bool PerformTargetBinding
        {
            get
            {
                return this.performTargetBinding;
            }
            set
            {
                this.performTargetBinding = value;
            }
        }

        /// <summary>
        /// Gets the ClientID of this control. 
        /// </summary>
        public override string ClientID
        {
            [SharePointPermission(SecurityAction.Demand, ObjectModel = true)]
            get
            {
                if (this.idPrefix == null)
                {
                    this.idPrefix = SPUtility.GetNewIdPrefix(this.Context);
                }

                return SPUtility.GetShortId(this.idPrefix, this);
            }
        }

        [SharePointPermission(SecurityAction.Demand, ObjectModel = true)]
        protected override void OnMenuItemDataBound(MenuEventArgs e)
        {
            base.OnMenuItemDataBound(e);

            if (this.customSelectionEnabled)
            {
                // store in the url->item dictionary
                this.menuItemDictionary[e.Item.NavigateUrl] = e.Item;
            }

            if (this.performTargetBinding)
            {
                // try to bind to the Target property if the data item is a SiteMapNode
                SiteMapNode smn = e.Item.DataItem as SiteMapNode;
                if (smn != null)
                {
                    string target = smn["Target"];
                    if (!string.IsNullOrEmpty(target))
                    {
                        e.Item.Target = target;
                    }
                }
            }
        }

        /// <id guid="08e034e7-5872-4a31-a771-84cac1dcd53d" />
        /// <owner alias="MarkWal">
        /// </owner>
        [SharePointPermission(SecurityAction.Demand, ObjectModel = true)]
        protected override void OnPreRender(System.EventArgs e)
        {

            SiteMapDataSource dataSource = this.GetDataSource() as SiteMapDataSource;
            SiteMapProvider provider = (dataSource != null) ? dataSource.Provider : null;

            if (useCompactMenus && dataSource != null && provider != null)
            {
                showStartingNode = dataSource.ShowStartingNode;

                SiteMapNodeCollection rootChildNodes = provider.RootNode.ChildNodes;

                if (provider.CurrentNode.Equals(provider.RootNode))
                {
                    //** Store original site map for future use in compacting menus **//
                    if (originalSiteMap == null)
                    {
                        //Store original SiteMapXML for future adjustments:
                        XmlDocument newSiteMapDoc = new XmlDocument();
                        newSiteMapDoc.LoadXml("<?xml version='1.0' ?>"
                            + "<siteMapNode title='" + provider.RootNode.Title
                            + "' url='" + provider.RootNode.Url
                            + "' />");

                        foreach (SiteMapNode node in rootChildNodes)
                        {
                            XmlNode newNode = GetXmlSiteMapNode(newSiteMapDoc.DocumentElement, node);

                            newSiteMapDoc.DocumentElement.AppendChild(newNode);

                            //Create XML for all the child nodes for selected menu item:
                            NavigateSiteMap(newNode, node);
                        }

                        originalSiteMap = newSiteMapDoc.OuterXml;
                    }

                    //This is set to only display the child nodes of the root node on first view:
                    this.StaticDisplayLevels = 1;
                }
                else
                {
                    //
                    //Adjust site map for this page
                    //
                    XmlDocument newSiteMapDoc = InitializeNewSiteMapXml(provider, rootChildNodes);

                    //Clear the current default site map:
                    this.DataSourceID = null;

                    //Create the new site map data source
                    XmlDataSource newSiteMap = new XmlDataSource();
                    newSiteMap.ID = "XmlDataSource1";
                    newSiteMap.EnableCaching = false; //Required to prevent redisplay of the previous menu

                    //Add bindings for dynamic site map:
                    MenuItemBindingCollection bindings = this.DataBindings;
                    bindings.Clear();

                    MenuItemBinding binding = new MenuItemBinding();
                    binding.DataMember = "siteMapNode";
                    binding.TextField = "title";
                    binding.Text = "title";
                    binding.NavigateUrlField = "url";
                    binding.NavigateUrl = "url";
                    binding.ValueField = "url";
                    binding.Value = "url";

                    bindings.Add(binding);

                    //Bind menu to new site map:
                    this.DataSource = newSiteMap;

                    //Assign the newly created dynamic site map:
                    ((XmlDataSource)this.DataSource).Data = newSiteMapDoc.OuterXml;

                    /** this expression removes the root if initialized: **/
                    if (!showStartingNode)
                        ((XmlDataSource)this.DataSource).XPath = "/siteMapNode/siteMapNode";

                    /** Re-initialize menu data source with new site map: **/
                    this.DataBind();

                    /** Find depth of current node: **/
                    int depth = 0;
                    SiteMapNode currNode = provider.CurrentNode;
                    do
                    {
                        depth++;
                        currNode = currNode.ParentNode;
                    }
                    while (currNode != null);

                    //Set the StaticDisplayLevels to match the current depth:
                    if (depth >= this.StaticDisplayLevels)
                        this.StaticDisplayLevels = depth;
                }
            }

            base.OnPreRender(e);

            // output some script to override the default menu flyout behaviour; this helps to avoid
            // intermittent "Operation Aborted" errors
            Page.ClientScript.RegisterStartupScript(
                typeof(MossMenu),
                "overrideMenu_HoverStatic",
                "if (typeof(overrideMenu_HoverStatic) == 'function' && typeof(Menu_HoverStatic) == 'function')\n" +
                "{\n" +
                    "_spBodyOnLoadFunctionNames.push('enableFlyoutsAfterDelay');\n" +
                    "Menu_HoverStatic = overrideMenu_HoverStatic;\n" +
                "}\n",
                true);

            // output some script to avoid a known issue with SSL Termination and the ASP.NET
            // Menu implementation. http://support.microsoft.com/?id=910444
            Page.ClientScript.RegisterStartupScript(
                typeof(MossMenu),
                "MenuHttpsWorkaround_" + this.ClientID,
                this.ClientID + "_Data.iframeUrl='/_layouts/images/blank.gif';",
                true);

            // adjust the fly-out indicator arrow direction for locale if not already set
            if (this.Orientation == System.Web.UI.WebControls.Orientation.Vertical &&
                ((string.IsNullOrEmpty(this.StaticPopOutImageUrl) && this.StaticEnableDefaultPopOutImage) ||
                    (string.IsNullOrEmpty(this.DynamicPopOutImageUrl) && this.DynamicEnableDefaultPopOutImage)))
            {
                SPWeb currentWeb = SPContext.Current.Web;
                if (currentWeb != null)
                {
                    uint localeId = currentWeb.Language;

                    bool isBidiWeb = SPUtility.IsRightToLeft(currentWeb, currentWeb.Language);

                    string arrowUrl = "/_layouts/images/" + (isBidiWeb ? "largearrowleft.gif" : "largearrowright.gif");

                    if (string.IsNullOrEmpty(this.StaticPopOutImageUrl) && this.StaticEnableDefaultPopOutImage)
                    {
                        this.StaticPopOutImageUrl = arrowUrl;
                    }
                    if (string.IsNullOrEmpty(this.DynamicPopOutImageUrl) && this.DynamicEnableDefaultPopOutImage)
                    {
                        this.DynamicPopOutImageUrl = arrowUrl;
                    }
                }
            }

            if (provider == null)
            {
                // if we're not attached to a SiteMapDataSource we'll just leave everything alone
                return;
            }
            else if (this.customSelectionEnabled)
            {
                MenuItem selectedMenuItem = this.SelectedItem;
                SiteMapNode currentNode = provider.CurrentNode;

                // if no menu item is presently selected, we need to work our way up from the current 
                // node until we can find a node in the menu item dictionary
                while (selectedMenuItem == null && currentNode != null)
                {
                    this.menuItemDictionary.TryGetValue(currentNode.Url, out selectedMenuItem);

                    currentNode = currentNode.ParentNode;
                }

                if (this.selectStaticItemsOnly)
                {
                    // only static items may be selected, keep moving up until we find an item
                    // that falls within the static range
                    while (selectedMenuItem != null && selectedMenuItem.Depth >= this.StaticDisplayLevels)
                    {
                        selectedMenuItem = selectedMenuItem.Parent;
                    }

                    // if we found an item to select, go ahead and select (highlight) it
                    if (selectedMenuItem != null && selectedMenuItem.Selectable)
                    {
                        selectedMenuItem.Selected = true;
                    }
                }
            }
        }

        private XmlDocument InitializeNewSiteMapXml(SiteMapProvider provider, SiteMapNodeCollection rootChildNodes)
        {
            /** Find the level 1 ancestor node of the current node: **/
            SiteMapNode levelOneAncestorOfSelectedNode = null;
            SiteMapNode currNode = provider.CurrentNode;
            do
            {
                levelOneAncestorOfSelectedNode = (currNode.ParentNode == null ? levelOneAncestorOfSelectedNode : currNode);
                currNode = currNode.ParentNode;
            }
            while (currNode != null);

            /** Initialize base SiteMapXML **/
            XmlDocument newSiteMapDoc = new XmlDocument();
            newSiteMapDoc.LoadXml(originalSiteMap);

            /** Prune out the childern nodes that shouldn't display: **/
            currNode = provider.CurrentNode;
            do
            {
                if (currNode.ParentNode != null)
                {
                    SiteMapNodeCollection currNodeSiblings = currNode.ParentNode.ChildNodes;
                    foreach (SiteMapNode siblingNode in currNodeSiblings)
                    {
                        if (siblingNode.HasChildNodes)
                        {
                            if (provider.CurrentNode.Equals(siblingNode))
                            {
                                //Remove all the childerns child nodes from display:
                                SiteMapNodeCollection currNodesChildren = siblingNode.ChildNodes;
                                foreach (SiteMapNode childNode in currNodesChildren)
                                {
                                    XmlNode currentXmNode = GetCurrentXmlNode(newSiteMapDoc, childNode);

                                    DeleteChildNodes(currentXmNode);
                                }
                            }
                            else if (!provider.CurrentNode.IsDescendantOf(siblingNode)
                                && !levelOneAncestorOfSelectedNode.Equals(siblingNode))
                            {
                                XmlNode currentXmNode = GetCurrentXmlNode(newSiteMapDoc, siblingNode);

                                DeleteChildNodes(currentXmNode);
                            }
                        }
                    }
                }

                currNode = currNode.ParentNode;
            }
            while (currNode != null);

            return newSiteMapDoc;
        }

        private XmlNode GetCurrentXmlNode(XmlDocument newSiteMapDoc, SiteMapNode node)
        {
            //Find this node in the original site map:
            XmlNode currentXmNode = newSiteMapDoc.DocumentElement.SelectSingleNode(
                "//siteMapNode[@url='"
                + node.Url
                + "']");
            return currentXmNode;
        }

        private void DeleteChildNodes(XmlNode currentXmNode)
        {
            if (currentXmNode != null && currentXmNode.HasChildNodes)
            {
                //Remove child nodes:
                XmlNodeList xmlNodes = currentXmNode.ChildNodes;
                int lastNodeIndex = xmlNodes.Count - 1;
                for (int i = lastNodeIndex; i >= 0; i--)
                {
                    currentXmNode.RemoveChild(xmlNodes[i]);
                }
            }
        }
        private XmlNode GetXmlSiteMapNode(XmlNode currentDocumentNode, SiteMapNode currentNode)
        {
            XmlElement newNode = currentDocumentNode.OwnerDocument.CreateElement("siteMapNode");

            XmlAttribute newAttr = currentDocumentNode.OwnerDocument.CreateAttribute("title");
            newAttr.InnerText = currentNode.Title;
            newNode.Attributes.Append(newAttr);

            newAttr = currentDocumentNode.OwnerDocument.CreateAttribute("url");
            newAttr.InnerText = currentNode.Url;
            newNode.Attributes.Append(newAttr);

            return newNode;
        }

        private void NavigateSiteMap(XmlNode currentDocumentNode, SiteMapNode currentNode)
        {
            foreach (SiteMapNode node in currentNode.ChildNodes)
            {
                //Add this node to structure:
                XmlNode newNode = GetXmlSiteMapNode(currentDocumentNode, node);
                currentDocumentNode.AppendChild(newNode);

                if (node.HasChildNodes)
                {
                    //Make a recursive call to add any child nodes:
                    NavigateSiteMap(newNode, node);
                }
            }
        }
    }

    [PermissionSet(SecurityAction.LinkDemand, Name = "FullTrust")]
    [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Security", "CA2117:AptcaTypesShouldOnlyExtendAptcaBaseTypes")]
    public sealed class MossMenuDesigner : MenuDesigner
    {
        [PermissionSet(SecurityAction.Demand, Name = "FullTrust")]
        protected override void DataBind(BaseDataBoundControl dataBoundControl)
        {
            try
            {
                dataBoundControl.DataBind();
            }
            catch
            {
                base.DataBind(dataBoundControl);
            }
        }

        [PermissionSet(SecurityAction.Demand, Name = "FullTrust")]
        public override string GetDesignTimeHtml()
        {
            System.Web.UI.WebControls.Menu menu = (System.Web.UI.WebControls.Menu)ViewControl;
            int oldDisplayLevels = menu.MaximumDynamicDisplayLevels;
            string designTimeHtml = string.Empty;

            try
            {
                menu.MaximumDynamicDisplayLevels = 0;

                // ASP.NET MenuDesigner has some dynamic/static item trick in design time
                // to show dynamic item in design time. We only want to show preview without
                // dynamic menu items.
                designTimeHtml = base.GetDesignTimeHtml();
            }
            catch (Exception e)
            {
                designTimeHtml = GetErrorDesignTimeHtml(e);
            }
            finally
            {
                menu.MaximumDynamicDisplayLevels = oldDisplayLevels;
            }

            return designTimeHtml;
        }
    }
}
1 голос
/ 19 сентября 2008

Мне лично не нравится HTML, который предоставляет меню по умолчанию (макет на основе таблицы). К счастью, команда SharePoint выпустила код для этого элемента управления .

Что мы сделали, так это включили этот код в проект и переопределили метод рендеринга, чтобы делать все, что мы хотим. Это дает вам гибкость в определении точных отношений между родителями, которые должны отображаться, а также в настройке стилей для всех созданных вами div-ов.

С другой стороны, вы сейчас кодируете, а не настраиваете, и необходимо внести изменения в главную страницу, которую вы используете для использования элемента управления.

Стоит, по моему мнению. Теперь это стандартное изменение, которое мы вносим для любого сайта.

0 голосов
/ 23 октября 2008

Подход, который мы использовали для достижения желаемого эффекта, заключался в использовании Адаптеров дружественного управления CSS . Адаптеры изменяют отображаемый HTML-код без изменения элементов управления, используемых на ваших страницах. Возможно, вам придется немного настроить адаптер меню, чтобы получить нужный макет. Нам потребовалось всего несколько строк кода. Как только вы это заработаете, вы можете использовать CSS для получения описанного вами поведения.

...