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 parentTerm = _taxonomyService.GetTerm(parentTermId);
var term = _taxonomyService.NewTerm(taxonomy);
// assign a container to show the full route while editing
term.Container = parentTerm == null ? taxonomy : (IContent)parentTerm;

View File

@ -3,6 +3,7 @@ using System.Linq;
using System.Web.Mvc;
using Orchard.ContentManagement;
using Orchard.ContentManagement.Drivers;
using Orchard.Localization;
using Orchard.Taxonomies.Models;
using Orchard.Taxonomies.Services;
using Orchard.Taxonomies.ViewModels;
@ -15,6 +16,8 @@ namespace Orchard.Taxonomies.Drivers {
_taxonomyService = taxonomyService;
}
public Localizer T { get; set; }
protected override string Prefix { get { return "TaxonomyNavigationPart"; } }
protected override DriverResult Editor(TaxonomyNavigationPart part, dynamic shapeHelper) {
@ -30,17 +33,25 @@ namespace Orchard.Taxonomies.Drivers {
DisplayContentCount = part.DisplayContentCount,
DisplayTopMenuItem = part.DisplayRootTerm,
HideEmptyTerms = part.HideEmptyTerms,
LevelsToDisplay = part.LevelsToDisplay,
};
if (updater != null) {
if (updater.TryUpdateModel(model, Prefix, null, null)) {
// taxonomy to render
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;
if (model.LevelsToDisplay < 0) {
updater.AddModelError("LevelsToDisplay", T("The levels to display must be a positive number"));
}
else {
// taxonomy to render
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.Collections.Generic;
using System.Linq;
using Orchard.Taxonomies.Fields;
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) {
foreach (var term in part.Terms) {
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.ComponentModel.DataAnnotations;
using System.Globalization;
using Orchard.ContentManagement;
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)); }
}
/// <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>
/// Whether to display the number of content items
/// associated with this term, in the generated menu item text

View File

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

View File

@ -28,6 +28,7 @@ namespace Orchard.Taxonomies.Services {
void UpdateTerms(ContentItem contentItem, IEnumerable<TermPart> terms, string field);
IEnumerable<TermPart> GetParents(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);
long GetContentItemsCount(TermPart term, string fieldName = null);
IContentQuery<TermsPart, TermsPartRecord> GetContentItemsQuery(TermPart term, string fieldName = null);

View File

@ -1,7 +1,6 @@
using System;
using System.Collections.Generic;
using System.Linq;
using Orchard.Taxonomies.Fields;
using Orchard.Taxonomies.Models;
using Orchard.Autoroute.Models;
using Orchard.ContentManagement;
@ -267,12 +266,20 @@ namespace Orchard.Taxonomies.Services {
}
public IEnumerable<TermPart> GetChildren(TermPart term) {
return GetChildren(term, false);
}
public IEnumerable<TermPart> GetChildren(TermPart term, bool includeParent) {
var rootPath = term.FullPath + "/";
var result = _contentManager.Query<TermPart, TermPartRecord>()
.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);
}

View File

@ -12,5 +12,6 @@ namespace Orchard.Taxonomies.ViewModels {
public bool DisplayTopMenuItem { get; set; }
public bool DisplayContentCount { 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>
</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>
@Html.CheckBoxFor(m => m.HideEmptyTerms)
<label for="@Html.FieldIdFor(m => m.HideEmptyTerms)" class="forcheckbox">@T("Hide empty entries")</label>