tisdag 7 augusti 2012

Komma runt bugg med hierarkiska menyer i Orchard CMS 1.5.1

Hej!

Idag stötte jag på en bugg i Orchard CMS 1.5.1 (http://orchard.codeplex.com/workitem/18896)

Problemet kan kort beskrivas som följande:
När man har en orchard-site med hierarkiska menyer (3 nivåer+) så hämtas inte hierarkin korrekt. Utan modellen returnerar sub-meny-items både på den inre och den yttersta nivån inuti objektet. Följdaktligen får man dubletter.

I mitt fall har jag en toppmeny där översta menyhierarkin visas och en vänstermeny som visar övriga nivåer, och buggen artar sig såhär i vänstermenyn:

















Eftersom jag inte hinner sätta mig in i källkoden för Orchard så blir det ett redigt fulhack för att komma runt problemet:

1. Kopiera meny view till aktuell Theme-katalog
Leta upp filen Menu.cshtml (under <sökvägtillorchard>\Core\Shapes\Views) och kopiera in den i din view foldern under din theme-katalog (i mitt ex. <sökvägtillorchard>\Themes\<namnpåtema>\Views\).

2. Editera och ersätt hela innehållet i filen med följande:


@functions {
public void RecurseItems(IList<dynamic> items, ref Dictionary<string, string> dict) {
if (items == null)
return;

foreach(var item in items) {
if (item.Items != null && item.Items.Count > 0)
RecurseItems(item.Items, ref dict);

if (dict.ContainsKey(item.Content.MenuPosition))
return;
else
dict.Add(item.Content.MenuPosition, string.Empty);
}
}
}

@{
    // Model is Model.Menu from the layout (Layout.Menu)
    var tag = Tag(Model, "ul");

    var items = (IList<dynamic>)Enumerable.Cast<dynamic>(Model.Items);

    if (items.Any()) {
        items[0].Classes.Add("first");
        items[items.Count - 1].Classes.Add("last");
    }  

/* Remove duplicate entries (bug: http://orchard.codeplex.com/workitem/18896) */
var alreadyAdded = new Dictionary<string, string>();
var filteredItems = new List<dynamic>();

foreach(var item in items) {
if (alreadyAdded.ContainsKey(item.Content.MenuPosition)) {
continue;
} else {
filteredItems.Add(item);
alreadyAdded.Add(item.Content.MenuPosition, string.Empty);
// Recurse inner items if any
if (item.Items != null && item.Items.Count > 0) {
RecurseItems(item.Items, ref alreadyAdded);
}
}
}
}

<nav>
    @tag.StartElement
        @* see MenuItem shape template *@
        @DisplayChildren(filteredItems)
    @tag.EndElement
</nav>


Kort och gött en rekursiv funktion som fyller en dictionary med redan tillagda poster. För varje nytt menyitem kollar vi mot dictionaryn innan vi lägger till den i den filtrerade listan. Slutligen skickar vi in den filtrerade listan som argument till DisplayChildren (istället för som tidigare Model).

Nu visas istället:















Success!

Som synes alltså riktigt fulhack med inlinekod, jag löser inte grundproblemet, och det kan ge prestandaproblem på en större site. Men det funkar för mig i detta fall eftersom jag bara bygger en prototyp.

Följer upp inlägget när Orchard-teamet löst felet, ska bli kul å se hur lång tid det tar :)

Inga kommentarer:

Skicka en kommentar