Skip to content
This repository has been archived by the owner on Nov 1, 2022. It is now read-only.

Commit

Permalink
Knowledgebase improvements - breadcrumbs, search tweaks and more
Browse files Browse the repository at this point in the history
  • Loading branch information
JanKonopka committed Jul 12, 2018
1 parent bf7992d commit ebb4073
Show file tree
Hide file tree
Showing 16 changed files with 424 additions and 114 deletions.
5 changes: 3 additions & 2 deletions Grand.Core/Domain/Knowledgebase/KnowledgebaseCategory.cs
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
using Grand.Core.Domain.Localization;
using Grand.Core.Domain.Security;
using Grand.Core.Domain.Seo;
using Grand.Core.Domain.Stores;
using System;
using System.Collections.Generic;
using System.Text;

namespace Grand.Core.Domain.Knowledgebase
{
public class KnowledgebaseCategory : BaseEntity, ITreeNode, ILocalizedEntity, ISlugSupported
public class KnowledgebaseCategory : BaseEntity, ITreeNode, ILocalizedEntity, ISlugSupported, IAclSupported, IStoreMappingSupported
{
public KnowledgebaseCategory()
{
Expand Down
6 changes: 6 additions & 0 deletions Grand.Services/Knowledgebase/IKnowledgebaseService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,12 @@ public interface IKnowledgebaseService
/// <returns>List of public knowledgebase articles</returns>
List<KnowledgebaseArticle> GetPublicKnowledgebaseArticlesByKeyword(string keyword);

/// <summary>
/// Gets public(published etc) knowledgebase categories for keyword
/// </summary>
/// <returns>List of public knowledgebase categories</returns>
List<KnowledgebaseCategory> GetPublicKnowledgebaseCategoriesByKeyword(string keyword);

/// <summary>
/// Inserts knowledgebase article
/// </summary>
Expand Down
41 changes: 41 additions & 0 deletions Grand.Services/Knowledgebase/KnowledgebaseExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
using Grand.Services.Localization;
using System.Text;
using System.Linq;
using Grand.Services.Stores;

namespace Grand.Services.Knowledgebase
{
Expand Down Expand Up @@ -73,5 +74,45 @@ public static IList<KnowledgebaseCategory> GetCategoryBreadCrumb(this Knowledgeb
result.Reverse();
return result;
}

/// <summary>
/// Get category breadcrumb
/// </summary>
/// <param name="category">Category</param>
/// <param name="categoryService">Category service</param>
/// <param name="aclService">ACL service</param>
/// <param name="storeMappingService">Store mapping service</param>
/// <param name="showHidden">A value indicating whether to load hidden records</param>
/// <returns>Category breadcrumb </returns>
public static IList<KnowledgebaseCategory> GetCategoryBreadCrumb(this KnowledgebaseCategory category,
IKnowledgebaseService knowledgebaseService,
IAclService aclService,
IStoreMappingService storeMappingService,
bool showHidden = false)
{
if (category == null)
throw new ArgumentNullException("category");

var result = new List<KnowledgebaseCategory>();

//used to prevent circular references
var alreadyProcessedCategoryIds = new List<string>();

while (category != null && //not null
(showHidden || category.Published) && //published
(showHidden || aclService.Authorize(category)) && //ACL
(showHidden || storeMappingService.Authorize(category)) && //Store mapping
!alreadyProcessedCategoryIds.Contains(category.Id)) //prevent circular references
{
result.Add(category);

alreadyProcessedCategoryIds.Add(category.Id);

category = knowledgebaseService.GetKnowledgebaseCategory(category.ParentCategoryId);
}

result.Reverse();
return result;
}
}
}
56 changes: 56 additions & 0 deletions Grand.Services/Knowledgebase/KnowledgebaseService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,16 @@ public class KnowledgebaseService : IKnowledgebaseService
/// </remarks>
private const string ARTICLES_BY_KEYWORD = "Knowledgebase.article.keyword-{0}-{1}-{2}";

/// <summary>
/// Key for caching
/// </summary>
/// <remarks>
/// {0} : keyword
/// {1} : customer roles
/// {2} : store id
/// </remarks>
private const string CATEGORIES_BY_KEYWORD = "Knowledgebase.category.keyword-{0}-{1}-{2}";

/// <summary>
/// Key for caching
/// {0} : customer roles
Expand Down Expand Up @@ -463,6 +473,52 @@ public virtual List<KnowledgebaseArticle> GetPublicKnowledgebaseArticlesByKeywor
});
}

/// <summary>
/// Gets public(published etc) knowledgebase categories for keyword
/// </summary>
/// <returns>List of public knowledgebase categories</returns>
public virtual List<KnowledgebaseCategory> GetPublicKnowledgebaseCategoriesByKeyword(string keyword)
{
var key = string.Format(CATEGORIES_BY_KEYWORD, keyword, string.Join(",", _workContext.CurrentCustomer.GetCustomerRoleIds()),
_storeContext.CurrentStore.Id);
return _cacheManager.Get(key, () =>
{
var builder = Builders<KnowledgebaseCategory>.Filter;
var filter = FilterDefinition<KnowledgebaseCategory>.Empty;
filter = filter & builder.Where(x => x.Published);
if (_commonSettings.UseFullTextSearch)
{
keyword = "\"" + keyword + "\"";
keyword = keyword.Replace("+", "\" \"");
keyword = keyword.Replace(" ", "\" \"");
filter = filter & builder.Text(keyword);
}
else
{
filter = filter & builder.Where(p => p.Locales.Any(x => x.LocaleValue != null && x.LocaleValue.ToLower().Contains(keyword.ToLower()))
|| p.Name.ToLower().Contains(keyword.ToLower()) || p.Description.ToLower().Contains(keyword.ToLower()));
}
if (!_catalogSettings.IgnoreAcl)
{
var allowedCustomerRolesIds = _workContext.CurrentCustomer.GetCustomerRoleIds();
filter = filter & (builder.AnyIn(x => x.CustomerRoles, allowedCustomerRolesIds) | builder.Where(x => !x.SubjectToAcl));
}
if (!_catalogSettings.IgnoreStoreLimitations)
{
//Store mapping
var currentStoreId = new List<string> { _storeContext.CurrentStore.Id };
filter = filter & (builder.AnyIn(x => x.Stores, currentStoreId) | builder.Where(x => !x.LimitedToStores));
}
var builderSort = Builders<KnowledgebaseCategory>.Sort.Ascending(x => x.DisplayOrder);
var toReturn = _knowledgebaseCategoryRepository.Collection.Find(filter).Sort(builderSort);
return toReturn.ToList();
});
}

/// <summary>
/// Gets homepage knowledgebase articles
/// </summary>
Expand Down
4 changes: 2 additions & 2 deletions Grand.Web/App_Data/Localization/Upgrade/EN_410_420.nopres.xml
Original file line number Diff line number Diff line change
Expand Up @@ -269,8 +269,8 @@
<LocaleResource Name="Knowledgebase.SearchPlaceholder">
<Value>Search in knowledgebase...</Value>
</LocaleResource>
<LocaleResource Name="Knowledgebase.Backtohomepage">
<Value>Back to knowledgebase homepage</Value>
<LocaleResource Name="Knowledgebase.ResultsFor">
<Value>Results for</Value>
</LocaleResource>
<LocaleResource Name="Knowledgebase.Notfound">
<Value>No articles were found that matched your criteria</Value>
Expand Down
6 changes: 3 additions & 3 deletions Grand.Web/App_Data/Localization/defaultResources.grandres.xml
Original file line number Diff line number Diff line change
Expand Up @@ -16605,9 +16605,6 @@
<LocaleResource Name="Knowledgebase.Article.RelatedArticles">
<Value>Related articles</Value>
</LocaleResource>
<LocaleResource Name="Knowledgebase.Backtohomepage">
<Value>Back to knowledgebase homepage</Value>
</LocaleResource>
<LocaleResource Name="Knowledgebase.Categories">
<Value>Categories</Value>
</LocaleResource>
Expand All @@ -16619,6 +16616,9 @@
</LocaleResource>
<LocaleResource Name="Knowledgebase.SearchPlaceholder">
<Value>Search in knowledgebase...</Value>
</LocaleResource>
<LocaleResource Name="Knowledgebase.ResultsFor">
<Value>Results for</Value>
</LocaleResource>
<LocaleResource Name="Languages">
<Value>Languages</Value>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -669,7 +669,7 @@ public IActionResult EditArticle(KnowledgebaseArticleModel model, bool continueE
.ToList();

SuccessNotification(_localizationService.GetResource("Admin.ContentManagement.Knowledgebase.KnowledgebaseArticle.Updated"));
return continueEditing ? RedirectToAction("EditArticle", new { id = knowledgebaseArticle.Id }) : RedirectToAction("List");
return continueEditing ? RedirectToAction("EditArticle", new { knowledgebaseArticle.Id }) : RedirectToAction("EditCategory", new { id = model.ParentCategoryId });
}

//If we got this far, something failed, redisplay form
Expand Down
7 changes: 4 additions & 3 deletions Grand.Web/Components/KnowledgebaseHomepageArticles.cs
Original file line number Diff line number Diff line change
Expand Up @@ -30,14 +30,15 @@ public IViewComponentResult Invoke(KnowledgebaseHomePageModel model)

foreach (var article in articles)
{
var a = new KnowledgebaseArticleModel
var a = new KnowledgebaseItemModel
{
Id = article.Id,
Name = article.GetLocalized(y => y.Name),
SeName = article.GetLocalized(y => y.SeName)
SeName = article.GetLocalized(y => y.SeName),
IsArticle = true
};

model.Articles.Add(a);
model.Items.Add(a);
}

return View(model);
Expand Down
94 changes: 81 additions & 13 deletions Grand.Web/Controllers/KnowledgebaseController.cs
Original file line number Diff line number Diff line change
@@ -1,22 +1,39 @@
using Grand.Core.Domain.Knowledgebase;
using Grand.Framework.Mvc.Filters;
using Grand.Framework.Security;
using Grand.Services.Knowledgebase;
using Grand.Web.Models.Knowledgebase;
using Microsoft.AspNetCore.Mvc;
using Grand.Services.Localization;
using Grand.Web.Infrastructure.Cache;
using Grand.Core;
using Grand.Services.Customers;
using Grand.Core.Caching;
using Grand.Services.Security;
using Grand.Services.Stores;
using System.Linq;
using Grand.Services.Seo;

namespace Grand.Web.Controllers
{
public class KnowledgebaseController : BasePublicController
{
private readonly KnowledgebaseSettings _knowledgebaseSettings;
private readonly IKnowledgebaseService _knowledgebaseService;

public KnowledgebaseController(KnowledgebaseSettings knowledgebaseSettings, IKnowledgebaseService knowledgebaseService)
private readonly IWorkContext _workContext;
private readonly IStoreContext _storeContext;
private readonly ICacheManager _cacheManager;
private readonly IAclService _aclService;
private readonly IStoreMappingService _storeMappingService;

public KnowledgebaseController(KnowledgebaseSettings knowledgebaseSettings, IKnowledgebaseService knowledgebaseService, IWorkContext workContext,
IStoreContext storeContext, ICacheManager cacheManager, IAclService aclService, IStoreMappingService storeMappingService)
{
this._knowledgebaseSettings = knowledgebaseSettings;
this._knowledgebaseService = knowledgebaseService;
this._workContext = workContext;
this._storeContext = storeContext;
this._cacheManager = cacheManager;
this._aclService = aclService;
this._storeMappingService = storeMappingService;
}

public IActionResult List()
Expand All @@ -40,13 +57,13 @@ public IActionResult ArticlesByCategory(string categoryId)

var model = new KnowledgebaseHomePageModel();
var articles = _knowledgebaseService.GetPublicKnowledgebaseArticlesByCategory(categoryId);
articles.ForEach(x => model.Articles.Add(new KnowledgebaseArticleModel
var allCategories = _knowledgebaseService.GetPublicKnowledgebaseCategories();
articles.ForEach(x => model.Items.Add(new KnowledgebaseItemModel
{
Content = x.GetLocalized(y => y.Content),
Name = x.GetLocalized(y => y.Name),
Id = x.Id,
ParentCategoryId = x.ParentCategoryId,
SeName = x.GetLocalized(y => y.SeName)
SeName = x.GetLocalized(y => y.SeName),
IsArticle = true
}));

model.CurrentCategoryId = categoryId;
Expand All @@ -58,10 +75,27 @@ public IActionResult ArticlesByCategory(string categoryId)
model.CurrentCategoryName = category.GetLocalized(y => y.Name);
model.CurrentCategorySeName = category.GetLocalized(y => y.SeName);

string breadcrumbCacheKey = string.Format(ModelCacheEventConsumer.KNOWLEDGEBASE_CATEGORY_BREADCRUMB_KEY,
category.Id,
string.Join(",", _workContext.CurrentCustomer.GetCustomerRoleIds()),
_storeContext.CurrentStore.Id,
_workContext.WorkingLanguage.Id);
model.CategoryBreadcrumb = _cacheManager.Get(breadcrumbCacheKey, () =>
category
.GetCategoryBreadCrumb(_knowledgebaseService, _aclService, _storeMappingService)
.Select(catBr => new KnowledgebaseCategoryModel
{
Id = catBr.Id,
Name = catBr.GetLocalized(x => x.Name, _workContext.WorkingLanguage.Id),
SeName = catBr.GetSeName(_workContext.WorkingLanguage.Id)
})
.ToList()
);

return View("List", model);
}

public IActionResult ArticlesByKeyword(string keyword)
public IActionResult ItemsByKeyword(string keyword)
{
if (!_knowledgebaseSettings.Enabled)
return RedirectToRoute("HomePage");
Expand All @@ -70,18 +104,31 @@ public IActionResult ArticlesByKeyword(string keyword)

if (!string.IsNullOrEmpty(keyword))
{
var categories = _knowledgebaseService.GetPublicKnowledgebaseCategoriesByKeyword(keyword);
var allCategories = _knowledgebaseService.GetPublicKnowledgebaseCategories();
categories.ForEach(x => model.Items.Add(new KnowledgebaseItemModel
{
Name = x.GetLocalized(y => y.Name),
Id = x.Id,
SeName = x.GetLocalized(y => y.SeName),
IsArticle = false,
FormattedBreadcrumbs = x.GetFormattedBreadCrumb(allCategories, ">")
}));

var articles = _knowledgebaseService.GetPublicKnowledgebaseArticlesByKeyword(keyword);
articles.ForEach(x => model.Articles.Add(new KnowledgebaseArticleModel
articles.ForEach(x => model.Items.Add(new KnowledgebaseItemModel
{
Content = x.GetLocalized(y => y.Content),
Name = x.GetLocalized(y => y.Name),
Id = x.Id,
ParentCategoryId = x.ParentCategoryId,
SeName = x.GetLocalized(y => y.SeName)
SeName = x.GetLocalized(y => y.SeName),
IsArticle = true,
FormattedBreadcrumbs = _knowledgebaseService.GetPublicKnowledgebaseCategory(x.ParentCategoryId)?.GetFormattedBreadCrumb(allCategories, ">") +
" > " + x.GetLocalized(y => y.Name)
}));
}

model.CurrentCategoryId = "[NONE]";
model.SearchKeyword = keyword;

return View("List", model);
}
Expand Down Expand Up @@ -114,6 +161,27 @@ public IActionResult KnowledgebaseArticle(string articleId)
});
}

var category = _knowledgebaseService.GetKnowledgebaseCategory(article.ParentCategoryId);
if (category != null)
{
string breadcrumbCacheKey = string.Format(ModelCacheEventConsumer.KNOWLEDGEBASE_CATEGORY_BREADCRUMB_KEY,
article.ParentCategoryId,
string.Join(",", _workContext.CurrentCustomer.GetCustomerRoleIds()),
_storeContext.CurrentStore.Id,
_workContext.WorkingLanguage.Id);
model.CategoryBreadcrumb = _cacheManager.Get(breadcrumbCacheKey, () =>
category
.GetCategoryBreadCrumb(_knowledgebaseService, _aclService, _storeMappingService)
.Select(catBr => new KnowledgebaseCategoryModel
{
Id = catBr.Id,
Name = catBr.GetLocalized(x => x.Name, _workContext.WorkingLanguage.Id),
SeName = catBr.GetSeName(_workContext.WorkingLanguage.Id)
})
.ToList()
);
}

return View("Article", model);
}
}
Expand Down
Loading

0 comments on commit ebb4073

Please sign in to comment.