Fixing taxonomy navigation

This commit is contained in:
Sebastien Ros 2013-09-23 16:33:15 -07:00
parent 2f45a02a1f
commit a45d4b29d9
9 changed files with 97 additions and 45 deletions

View File

@ -149,7 +149,7 @@ namespace Orchard.Taxonomies.Controllers {
var taxonomy = _taxonomyService.GetTaxonomy(taxonomyId); var taxonomy = _taxonomyService.GetTaxonomy(taxonomyId);
var parentTerm = _taxonomyService.GetTerm(parentTermId); var parentTerm = _taxonomyService.GetTerm(parentTermId);
var term = _taxonomyService.NewTerm(taxonomy); var term = _taxonomyService.NewTerm(taxonomy);
// assign a container to show the full route while editing // assign a container to show the full route while editing
term.Container = parentTerm == null ? taxonomy : (IContent)parentTerm; term.Container = parentTerm == null ? taxonomy : (IContent)parentTerm;

View File

@ -3,6 +3,7 @@ using System.Linq;
using System.Web.Mvc; using System.Web.Mvc;
using Orchard.ContentManagement; using Orchard.ContentManagement;
using Orchard.ContentManagement.Drivers; using Orchard.ContentManagement.Drivers;
using Orchard.Localization;
using Orchard.Taxonomies.Models; using Orchard.Taxonomies.Models;
using Orchard.Taxonomies.Services; using Orchard.Taxonomies.Services;
using Orchard.Taxonomies.ViewModels; using Orchard.Taxonomies.ViewModels;
@ -15,6 +16,8 @@ namespace Orchard.Taxonomies.Drivers {
_taxonomyService = taxonomyService; _taxonomyService = taxonomyService;
} }
public Localizer T { get; set; }
protected override string Prefix { get { return "TaxonomyNavigationPart"; } } protected override string Prefix { get { return "TaxonomyNavigationPart"; } }
protected override DriverResult Editor(TaxonomyNavigationPart part, dynamic shapeHelper) { protected override DriverResult Editor(TaxonomyNavigationPart part, dynamic shapeHelper) {
@ -30,17 +33,25 @@ namespace Orchard.Taxonomies.Drivers {
DisplayContentCount = part.DisplayContentCount, DisplayContentCount = part.DisplayContentCount,
DisplayTopMenuItem = part.DisplayRootTerm, DisplayTopMenuItem = part.DisplayRootTerm,
HideEmptyTerms = part.HideEmptyTerms, HideEmptyTerms = part.HideEmptyTerms,
LevelsToDisplay = part.LevelsToDisplay,
}; };
if (updater != null) { if (updater != null) {
if (updater.TryUpdateModel(model, Prefix, null, null)) { if (updater.TryUpdateModel(model, Prefix, null, null)) {
// taxonomy to render
part.TaxonomyId = model.SelectedTaxonomyId; if (model.LevelsToDisplay < 0) {
// root term (can be null) updater.AddModelError("LevelsToDisplay", T("The levels to display must be a positive number"));
part.TermId = model.SelectedTermId; }
part.DisplayContentCount = model.DisplayContentCount; else {
part.DisplayRootTerm = model.DisplayTopMenuItem; // taxonomy to render
part.HideEmptyTerms = model.HideEmptyTerms; part.TaxonomyId = model.SelectedTaxonomyId;
// root term (can be null)
part.TermId = model.SelectedTermId;
part.DisplayContentCount = model.DisplayContentCount;
part.DisplayRootTerm = model.DisplayTopMenuItem;
part.HideEmptyTerms = model.HideEmptyTerms;
part.LevelsToDisplay = model.LevelsToDisplay;
}
} }
} }

View File

@ -1,4 +1,5 @@
using System; using System;
using System.Collections.Generic;
using System.Linq; using System.Linq;
using Orchard.Taxonomies.Fields; using Orchard.Taxonomies.Fields;
using Orchard.Taxonomies.Models; using Orchard.Taxonomies.Models;
@ -59,10 +60,19 @@ namespace Orchard.Taxonomies.Handlers {
)); ));
} }
// Retrieve the number of associated content items, for the whole hierarchy
private static void RecalculateCount(ITaxonomyService taxonomyService, TermsPart part) { private static void RecalculateCount(ITaxonomyService taxonomyService, TermsPart part) {
foreach (var term in part.Terms) { foreach (var term in part.Terms) {
var termPart = taxonomyService.GetTerm(term.TermRecord.Id); var termPart = taxonomyService.GetTerm(term.TermRecord.Id);
term.TermRecord.Count = (int)taxonomyService.GetContentItemsCount(termPart); while (termPart != null) {
termPart.Count = (int)taxonomyService.GetContentItemsCount(termPart);
// compute count for the hierarchy too
if (termPart.Container != null) {
var parentTerm = termPart.Container.As<TermPart>();
termPart = parentTerm;
}
}
} }
} }

View File

@ -1,4 +1,5 @@
using System; using System;
using System.ComponentModel.DataAnnotations;
using System.Globalization; using System.Globalization;
using Orchard.ContentManagement; using Orchard.ContentManagement;
using Orchard.ContentManagement.FieldStorage.InfosetStorage; using Orchard.ContentManagement.FieldStorage.InfosetStorage;
@ -32,6 +33,14 @@ namespace Orchard.Taxonomies.Models {
set { this.As<InfosetPart>().Set("TaxonomyNavigationPart", "DisplayRootTerm", null, Convert.ToString(value, CultureInfo.InvariantCulture)); } set { this.As<InfosetPart>().Set("TaxonomyNavigationPart", "DisplayRootTerm", null, Convert.ToString(value, CultureInfo.InvariantCulture)); }
} }
/// <summary>
/// Number of levels to render. If <value>0</value> all levels are rendered.
/// </summary>
public int LevelsToDisplay {
get { return Convert.ToInt32(this.As<InfosetPart>().Get("TaxonomyNavigationPart", "LevelsToDisplay"), CultureInfo.InvariantCulture); }
set { this.As<InfosetPart>().Set("TaxonomyNavigationPart", "LevelsToDisplay", Convert.ToString(value, CultureInfo.InvariantCulture)); }
}
/// <summary> /// <summary>
/// Whether to display the number of content items /// Whether to display the number of content items
/// associated with this term, in the generated menu item text /// associated with this term, in the generated menu item text

View File

@ -1,17 +1,17 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Globalization;
using System.Linq; using System.Linq;
using Orchard.ContentManagement; using Orchard.ContentManagement;
using Orchard.Localization; using Orchard.Localization;
using Orchard.Taxonomies.Helpers;
using Orchard.Taxonomies.Models; using Orchard.Taxonomies.Models;
using Orchard.Taxonomies.Services; using Orchard.Taxonomies.Services;
using Orchard.UI.Navigation; using Orchard.UI.Navigation;
using Orchard.Taxonomies.Helpers;
namespace Orchard.Taxonomies.Navigation { namespace Orchard.Taxonomies.Navigation {
/// <summary> /// <summary>
/// Dynamically injects query results as menu items on NavigationQueryMenuItem elements /// Dynamically injects taxonomy items as menu items on TaxonomyNavigationMenuItem elements
/// </summary> /// </summary>
public class TaxonomyNavigationProvider : INavigationFilter { public class TaxonomyNavigationProvider : INavigationFilter {
private readonly IContentManager _contentManager; private readonly IContentManager _contentManager;
@ -28,60 +28,66 @@ namespace Orchard.Taxonomies.Navigation {
foreach (var item in items) { foreach (var item in items) {
if (item.Content != null && item.Content.ContentItem.ContentType == "TaxonomyNavigationMenuItem") { if (item.Content != null && item.Content.ContentItem.ContentType == "TaxonomyNavigationMenuItem") {
// expand query
var taxonomyNavigationPart = item.Content.As<TaxonomyNavigationPart>(); var taxonomyNavigationPart = item.Content.As<TaxonomyNavigationPart>();
var rootTerm = _taxonomyService.GetTerm(taxonomyNavigationPart.TermId); var rootTerm = _taxonomyService.GetTerm(taxonomyNavigationPart.TermId);
List<int> positionList = new List<int>(); TermPart[] allTerms;
var allTerms = rootTerm != null if (rootTerm != null) {
? _taxonomyService.GetChildren(rootTerm).ToArray() // if DisplayRootTerm is specified add it to the menu items to render
: _taxonomyService.GetTerms(taxonomyNavigationPart.TaxonomyId).ToArray(); allTerms = _taxonomyService.GetChildren(rootTerm, taxonomyNavigationPart.DisplayRootTerm).ToArray();
}
else {
allTerms = _taxonomyService.GetTerms(taxonomyNavigationPart.TaxonomyId).ToArray();
}
var rootlevel = rootTerm == null ? 0 : rootTerm.GetLevels(); var rootLevel = rootTerm != null
? rootTerm.GetLevels()
: 0;
positionList.Add(0);
var menuPosition = item.Position; var menuPosition = item.Position;
int parentLevel = rootlevel; var rootPath = rootTerm == null || taxonomyNavigationPart.DisplayRootTerm ? "" : rootTerm.FullPath;
var startLevel = rootLevel + 1;
if (rootTerm == null || taxonomyNavigationPart.DisplayRootTerm) {
startLevel = rootLevel;
}
var endLevel = Int32.MaxValue;
if (taxonomyNavigationPart.LevelsToDisplay > 0) {
endLevel = startLevel + taxonomyNavigationPart.LevelsToDisplay - 1;
}
foreach (var contentItem in allTerms) { foreach (var contentItem in allTerms) {
if (contentItem != null) { if (contentItem != null) {
var part = contentItem; var part = contentItem;
var level = part.GetLevels();
if (taxonomyNavigationPart.HideEmptyTerms == true && part.Count == 0) { // filter levels ?
if (level < startLevel || level > endLevel) {
continue; continue;
} }
string termPosition = "";
if (part.GetLevels() - rootlevel > parentLevel) {
positionList.Add(0);
parentLevel = positionList.Count - 1;
}
else
if ((part.GetLevels() - rootlevel) == parentLevel) {
positionList[parentLevel]++;
}
else {
positionList.RemoveRange(1, positionList.Count - 1);
parentLevel = positionList.Count - 1;
positionList[parentLevel]++;
}
termPosition = positionList.First().ToString(); // ignore menu item if there are no content items associated to the term
foreach (var position in positionList.Skip(1)) { if (taxonomyNavigationPart.HideEmptyTerms && part.Count == 0) {
termPosition = termPosition + "." + position.ToString(); continue;
} }
var menuText = _contentManager.GetItemMetadata(part).DisplayText; var menuText = _contentManager.GetItemMetadata(part).DisplayText;
var routes = _contentManager.GetItemMetadata(part).DisplayRouteValues; var routes = _contentManager.GetItemMetadata(part).DisplayRouteValues;
if (taxonomyNavigationPart.DisplayContentCount) { if (taxonomyNavigationPart.DisplayContentCount) {
menuText = String.Format(menuText + " ({0})", part.Count.ToString()); menuText += " (" + part.Count + ")";
} }
// create
var positions = contentItem.FullPath.Substring(rootPath.Length)
.Split(new[] { '/' }, StringSplitOptions.RemoveEmptyEntries)
.Select(p => Array.FindIndex(allTerms, t => t.Id == Int32.Parse(p)))
.ToArray();
var inserted = new MenuItem { var inserted = new MenuItem {
Text = new LocalizedString(menuText), Text = new LocalizedString(menuText),
IdHint = item.IdHint, IdHint = item.IdHint,
@ -92,7 +98,7 @@ namespace Orchard.Taxonomies.Navigation {
RouteValues = routes, RouteValues = routes,
LocalNav = item.LocalNav, LocalNav = item.LocalNav,
Items = new MenuItem[0], Items = new MenuItem[0],
Position = menuPosition + ":" + termPosition, Position = menuPosition + ":" + String.Join(".", positions.Select(x => x.ToString(CultureInfo.InvariantCulture)).ToArray()),
Permissions = item.Permissions, Permissions = item.Permissions,
Content = part Content = part
}; };

View File

@ -28,6 +28,7 @@ namespace Orchard.Taxonomies.Services {
void UpdateTerms(ContentItem contentItem, IEnumerable<TermPart> terms, string field); void UpdateTerms(ContentItem contentItem, IEnumerable<TermPart> terms, string field);
IEnumerable<TermPart> GetParents(TermPart term); IEnumerable<TermPart> GetParents(TermPart term);
IEnumerable<TermPart> GetChildren(TermPart term); IEnumerable<TermPart> GetChildren(TermPart term);
IEnumerable<TermPart> GetChildren(TermPart term, bool includeParent);
IEnumerable<IContent> GetContentItems(TermPart term, int skip = 0, int count = 0, string fieldName = null); IEnumerable<IContent> GetContentItems(TermPart term, int skip = 0, int count = 0, string fieldName = null);
long GetContentItemsCount(TermPart term, string fieldName = null); long GetContentItemsCount(TermPart term, string fieldName = null);
IContentQuery<TermsPart, TermsPartRecord> GetContentItemsQuery(TermPart term, string fieldName = null); IContentQuery<TermsPart, TermsPartRecord> GetContentItemsQuery(TermPart term, string fieldName = null);

View File

@ -1,7 +1,6 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using Orchard.Taxonomies.Fields;
using Orchard.Taxonomies.Models; using Orchard.Taxonomies.Models;
using Orchard.Autoroute.Models; using Orchard.Autoroute.Models;
using Orchard.ContentManagement; using Orchard.ContentManagement;
@ -267,12 +266,20 @@ namespace Orchard.Taxonomies.Services {
} }
public IEnumerable<TermPart> GetChildren(TermPart term) { public IEnumerable<TermPart> GetChildren(TermPart term) {
return GetChildren(term, false);
}
public IEnumerable<TermPart> GetChildren(TermPart term, bool includeParent) {
var rootPath = term.FullPath + "/"; var rootPath = term.FullPath + "/";
var result = _contentManager.Query<TermPart, TermPartRecord>() var result = _contentManager.Query<TermPart, TermPartRecord>()
.WithQueryHints(new QueryHints().ExpandRecords<AutoroutePartRecord, TitlePartRecord, CommonPartRecord>()) .WithQueryHints(new QueryHints().ExpandRecords<AutoroutePartRecord, TitlePartRecord, CommonPartRecord>())
.List() .Where(x => x.Path.StartsWith(rootPath))
.Where(x => x.Path.StartsWith(rootPath)); .List();
if (includeParent) {
result = result.Concat(new [] {term});
}
return TermPart.Sort(result); return TermPart.Sort(result);
} }

View File

@ -12,5 +12,6 @@ namespace Orchard.Taxonomies.ViewModels {
public bool DisplayTopMenuItem { get; set; } public bool DisplayTopMenuItem { get; set; }
public bool DisplayContentCount { get; set; } public bool DisplayContentCount { get; set; }
public bool HideEmptyTerms { get; set; } public bool HideEmptyTerms { get; set; }
public int LevelsToDisplay { get; set; }
} }
} }

View File

@ -24,6 +24,13 @@
<span class="hint">@T("When checked, the selected term to display will be rendered as a root element in the menu.")</span> <span class="hint">@T("When checked, the selected term to display will be rendered as a root element in the menu.")</span>
</fieldset> </fieldset>
<fieldset>
<label for="@Html.FieldIdFor( m => m.LevelsToDisplay)">@T("Levels to display")</label>
@Html.TextBoxFor(m => m.LevelsToDisplay, new { @class = "text small" } )
@Html.ValidationMessage("LevelsToDisplay", "*")
<span class="hint">@T("The number of levels to display in the hierarchy. 0 to display all levels.")</span>
</fieldset>
<fieldset> <fieldset>
@Html.CheckBoxFor(m => m.HideEmptyTerms) @Html.CheckBoxFor(m => m.HideEmptyTerms)
<label for="@Html.FieldIdFor(m => m.HideEmptyTerms)" class="forcheckbox">@T("Hide empty entries")</label> <label for="@Html.FieldIdFor(m => m.HideEmptyTerms)" class="forcheckbox">@T("Hide empty entries")</label>