Adding Content Picker filters

--HG--
branch : 1.x
This commit is contained in:
Sebastien Ros
2013-02-10 07:38:34 -08:00
parent fef0a46c0c
commit 9673ea7aea
18 changed files with 302 additions and 38 deletions

View File

@@ -2,13 +2,16 @@
using System.Collections.Generic;
using System.Linq;
using System.Web.Mvc;
using System.Web.Routing;
using Orchard.ContentManagement;
using Orchard.ContentManagement.MetaData;
using Orchard.ContentManagement.MetaData.Models;
using Orchard.ContentPicker.Settings;
using Orchard.Core.Common.Models;
using Orchard.Core.Contents.Settings;
using Orchard.Core.Contents.ViewModels;
using Orchard.DisplayManagement;
using Orchard.Localization;
using Orchard.Mvc;
using Orchard.Settings;
using Orchard.Themes;
@@ -18,23 +21,71 @@ namespace Orchard.ContentPicker.Controllers {
public class AdminController : Controller {
private readonly ISiteService _siteService;
private readonly IContentDefinitionManager _contentDefinitionManager;
private readonly INavigationManager _navigationManager;
public AdminController(
IOrchardServices orchardServices,
ISiteService siteService,
IContentDefinitionManager contentDefinitionManager) {
IContentDefinitionManager contentDefinitionManager,
INavigationManager navigationManager) {
_siteService = siteService;
_contentDefinitionManager = contentDefinitionManager;
_navigationManager = navigationManager;
Services = orchardServices;
T = NullLocalizer.Instance;
}
public IOrchardServices Services { get; set; }
public Localizer T { get; set; }
[Themed(false)]
public ActionResult Index(ListContentsViewModel model, PagerParameters pagerParameters) {
public ActionResult Index(ListContentsViewModel model, PagerParameters pagerParameters, string part, string field) {
IEnumerable<MenuItem> menuItems = _navigationManager.BuildMenu("content-picker").ToList();
var contentPickerMenuItem = menuItems.FirstOrDefault();
if (contentPickerMenuItem == null) {
return HttpNotFound();
}
if (contentPickerMenuItem.Items.All(x => x.Text.ToString() != T("Recent Content").Text)) {
// the default tab should not be displayed, redirect to the next one
var routeData = new RouteValueDictionary(menuItems.First().RouteValues);
var queryString = Request.QueryString;
foreach (var key in queryString.AllKeys) {
routeData[key] = queryString[key];
}
return RedirectToRoute(routeData);
}
ContentPickerFieldSettings settings = null;
// if the picker is loaded for a specific field, apply custom settings
if (!String.IsNullOrEmpty(part) && !String.IsNullOrEmpty(field)) {
var definition = _contentDefinitionManager.GetPartDefinition(part).Fields.FirstOrDefault(x => x.Name == field);
if (definition != null) {
settings = definition.Settings.GetModel<ContentPickerFieldSettings>();
}
}
IEnumerable<ContentTypeDefinition> contentTypes;
if (settings != null && !String.IsNullOrEmpty(settings.DisplayedContentTypes)) {
var rawTypes = settings.DisplayedContentTypes.Split(new[] {',', ' '}, StringSplitOptions.RemoveEmptyEntries).ToList();
contentTypes = _contentDefinitionManager
.ListTypeDefinitions()
.Where(x => x.Parts.Any(p => rawTypes.Contains(p.PartDefinition.Name)))
.ToArray();
}
else {
contentTypes = GetCreatableTypes(false).ToList();
}
var pager = new Pager(_siteService.GetSiteSettings(), pagerParameters);
var query = Services.ContentManager.Query(VersionOptions.Latest, GetCreatableTypes(false).Select(ctd => ctd.Name).ToArray());
var query = Services.ContentManager.Query(VersionOptions.Latest, contentTypes.Select(ctd => ctd.Name).ToArray());
if (!string.IsNullOrEmpty(model.Options.SelectedFilter)) {
var contentTypeDefinition = _contentDefinitionManager.GetTypeDefinition(model.Options.SelectedFilter);
@@ -60,7 +111,7 @@ namespace Orchard.ContentPicker.Controllers {
break;
}
model.Options.FilterOptions = GetCreatableTypes(false)
model.Options.FilterOptions = contentTypes
.Select(ctd => new KeyValuePair<string, string>(ctd.Name, ctd.DisplayName))
.ToList().OrderBy(kvp => kvp.Value);

View File

@@ -39,6 +39,7 @@ namespace Orchard.ContentPicker.Drivers {
() => {
var model = new ContentPickerFieldViewModel {
Field = field,
Part = part,
ContentItems = _contentManager.GetMany<ContentItem>(field.Ids, VersionOptions.Published, QueryHints.Empty).ToList(),
};

View File

@@ -20,11 +20,14 @@
// remove trailing slash if any
if (baseUrl.substr(-1) == '/')
baseUrl = baseUrl.substr(0, baseUrl.length - 1);
var url = baseUrl
+ "/Admin/Orchard.ContentPicker?"
+ "callback=" + callbackName
+ "&" + (new Date() - 0);
+ "&" + (new Date() - 0)
+ "&part=" + encodeURIComponent(data.part)
+ "&field=" + encodeURIComponent(data.field);
var w = window.open(url, "_blank", data.windowFeatures || "width=685,height=700,status=no,toolbar=no,location=no,menubar=no,resizable=no,scrollbars=yes");
});
});

View File

@@ -1,9 +1,20 @@
using Orchard.Localization;
using System;
using System.Linq;
using Orchard.ContentManagement.MetaData;
using Orchard.ContentPicker.Settings;
using Orchard.Localization;
using Orchard.UI.Navigation;
namespace Orchard.ContentPicker.Services {
public class ContentPickerNavigationProvider : INavigationProvider {
public ContentPickerNavigationProvider() {
private readonly IContentDefinitionManager _contentDefinitionManager;
private readonly IWorkContextAccessor _workContextAccessor;
public ContentPickerNavigationProvider(
IContentDefinitionManager contentDefinitionManager,
IWorkContextAccessor workContextAccessor) {
_contentDefinitionManager = contentDefinitionManager;
_workContextAccessor = workContextAccessor;
T = NullLocalizer.Instance;
}
@@ -14,9 +25,36 @@ namespace Orchard.ContentPicker.Services {
}
public void GetNavigation(NavigationBuilder builder) {
var workContext = _workContextAccessor.GetContext();
var httpContext = workContext.HttpContext;
if (httpContext == null) {
return;
}
var queryString = workContext.HttpContext.Request.QueryString;
string part = queryString["part"];
string field = queryString["field"];
ContentPickerFieldSettings settings = null;
// if the picker is loaded for a specific field, apply custom settings
if (!String.IsNullOrEmpty(part) && !String.IsNullOrEmpty(field)) {
var definition = _contentDefinitionManager.GetPartDefinition(part).Fields.FirstOrDefault(x => x.Name == field);
if (definition != null) {
settings = definition.Settings.GetModel<ContentPickerFieldSettings>();
}
}
if (settings != null && !settings.ShowContentTab) {
return;
}
builder.Add(T("Content Picker"),
menu => menu
.Add(T("Recent Content"), "5", item => item.Action("Index", "Admin", new {area = "Orchard.ContentPicker"}).LocalNav()));
.Add(T("Recent Content"), "5", item => item.Action("Index", "Admin", new { area = "Orchard.ContentPicker" }).LocalNav()));
}
}
}

View File

@@ -1,4 +1,5 @@
using System.Collections.Generic;
using System.Linq;
using System.Web;
using Orchard.ContentManagement;
using Orchard.DisplayManagement;

View File

@@ -26,6 +26,8 @@ namespace Orchard.ContentPicker.Settings {
builder.WithSetting("ContentPickerFieldSettings.Hint", model.Hint);
builder.WithSetting("ContentPickerFieldSettings.Required", model.Required.ToString(CultureInfo.InvariantCulture));
builder.WithSetting("ContentPickerFieldSettings.Multiple", model.Multiple.ToString(CultureInfo.InvariantCulture));
builder.WithSetting("ContentPickerFieldSettings.ShowContentTab", model.ShowContentTab.ToString(CultureInfo.InvariantCulture));
builder.WithSetting("ContentPickerFieldSettings.DisplayedContentTypes", model.DisplayedContentTypes);
}
yield return DefinitionTemplate(model);

View File

@@ -1,7 +1,14 @@
namespace Orchard.ContentPicker.Settings {
public class ContentPickerFieldSettings {
public ContentPickerFieldSettings() {
ShowContentTab = true;
}
public string Hint { get; set; }
public bool Required { get; set; }
public bool Multiple { get; set; }
public bool ShowContentTab { get; set; }
public string DisplayedContentTypes { get; set; }
}
}

View File

@@ -9,5 +9,6 @@ namespace Orchard.ContentPicker.ViewModels {
public ICollection<ContentItem> ContentItems { get; set; }
public string SelectedIds { get; set; }
public ContentPickerField Field { get; set; }
public ContentPart Part { get; set; }
}
}

View File

@@ -18,3 +18,16 @@
<span class="hint">@T("The help text is written under the field when authors are selecting content items.")</span>
@Html.ValidationMessageFor(m => m.Hint)
</fieldset>
<fieldset>
<div>
@Html.CheckBoxFor(m => m.ShowContentTab) <label for="@Html.FieldIdFor(m => m.ShowContentTab)" class="forcheckbox">@T("Show Content tab")</label>
<span class="hint">@T("Uncheck to hide the Content tab from the picker window.")</span>
</div>
</fieldset>
<fieldset>
<div>
<label for="@Html.FieldIdFor(m => m.DisplayedContentTypes)">@T("Content Types and Parts")</label>
@Html.TextBoxFor(m => m.DisplayedContentTypes)
<span class="hint">@T("A comma separated value of all the content types or content parts to display.")</span>
</div>
</fieldset>

View File

@@ -87,7 +87,9 @@
refreshIds();
$('#save-message-@Html.FieldIdFor(m => m.Field.Ids)').show();
},
baseUrl: '@Url.Content("~/")'
baseUrl: '@Url.Content("~/")',
part: '@HttpUtility.JavaScriptStringEncode(Model.Part.PartDefinition.Name)',
field: '@HttpUtility.JavaScriptStringEncode(Model.Field.PartFieldDefinition.Name)'
});
});

View File

@@ -63,13 +63,14 @@ namespace Orchard.Projections.Drivers {
var pageKey = String.IsNullOrWhiteSpace(part.Record.PagerSuffix) ? "page" : "page-" + part.Record.PagerSuffix;
var page = 0;
if(queryString.AllKeys.Contains(pageKey)) {
// default page size
int pageSize = part.Record.Items;
// don't try to page if not necessary
if (part.Record.DisplayPager && queryString.AllKeys.Contains(pageKey)) {
Int32.TryParse(queryString[pageKey], out page);
}
// default page size
int pageSize = part.Record.Items;
// if 0, then assume "All"
if (pageSize == 0) {
pageSize = Int32.MaxValue;

View File

@@ -66,7 +66,7 @@ namespace Orchard.Projections.Providers.SortCriteria {
}
public void ApplySortCriterion(SortCriterionContext context, IFieldTypeEditor fieldTypeEditor, string storageName, Type storageType, ContentPartDefinition part, ContentPartFieldDefinition field) {
bool ascending = Convert.ToBoolean(context.State.Sort);
bool ascending = (bool)context.State.Sort;
var propertyName = String.Join(".", part.Name, field.Name, storageName ?? "");
// use an alias with the join so that two filters on the same Field Type wont collide
@@ -87,7 +87,7 @@ namespace Orchard.Projections.Providers.SortCriteria {
}
public LocalizedString DisplaySortCriterion(SortCriterionContext context, ContentPartDefinition part, ContentPartFieldDefinition fieldDefinition) {
bool ascending = Convert.ToBoolean(context.State.Sort);
bool ascending = (bool)context.State.Sort;
return ascending
? T("Ordered by field {0}, ascending", fieldDefinition.Name)

View File

@@ -1,11 +1,23 @@
using Orchard.Environment.Extensions;
using System;
using System.Linq;
using Orchard.ContentManagement.MetaData;
using Orchard.Environment.Extensions;
using Orchard.Localization;
using Orchard.Search.Settings;
using Orchard.UI.Navigation;
namespace Orchard.Search {
[OrchardFeature("Orchard.Search.ContentPicker")]
public class ContentPickerNavigationProvider : INavigationProvider {
public ContentPickerNavigationProvider() {
private readonly IWorkContextAccessor _workContextAccessor;
private readonly IContentDefinitionManager _contentDefinitionManager;
public ContentPickerNavigationProvider(
IWorkContextAccessor workContextAccessor,
IContentDefinitionManager contentDefinitionManager
) {
_workContextAccessor = workContextAccessor;
_contentDefinitionManager = contentDefinitionManager;
T = NullLocalizer.Instance;
}
@@ -16,6 +28,33 @@ namespace Orchard.Search {
}
public void GetNavigation(NavigationBuilder builder) {
var workContext = _workContextAccessor.GetContext();
var httpContext = workContext.HttpContext;
if (httpContext == null) {
return;
}
var queryString = workContext.HttpContext.Request.QueryString;
string part = queryString["part"];
string field = queryString["field"];
ContentPickerSearchFieldSettings settings = null;
// if the picker is loaded for a specific field, apply custom settings
if (!String.IsNullOrEmpty(part) && !String.IsNullOrEmpty(field)) {
var definition = _contentDefinitionManager.GetPartDefinition(part).Fields.FirstOrDefault(x => x.Name == field);
if (definition != null) {
settings = definition.Settings.GetModel<ContentPickerSearchFieldSettings>();
}
}
if (settings != null && !settings.ShowSearchTab) {
return;
}
builder.Add(T("Content Picker"),
menu => menu
.Add(T("Search Content"), "5", item => item.Action("Index", "ContentPicker", new {area = "Orchard.Search"}).LocalNav()));

View File

@@ -1,8 +1,8 @@
using System;
using System.Linq;
using System.Web.Mvc;
using Orchard.Collections;
using Orchard.ContentManagement;
using Orchard.ContentManagement.MetaData;
using Orchard.DisplayManagement;
using Orchard.Environment.Extensions;
using Orchard.Indexing;
@@ -10,7 +10,7 @@ using Orchard.Localization;
using Orchard.Logging;
using Orchard.Mvc;
using Orchard.Search.Models;
using Orchard.Search.Services;
using Orchard.Search.Settings;
using Orchard.Settings;
using Orchard.Themes;
using Orchard.UI.Admin;
@@ -21,15 +21,18 @@ namespace Orchard.Search.Controllers {
[Admin]
[OrchardFeature("Orchard.Search.ContentPicker")]
public class ContentPickerController : Controller {
private readonly ISearchService _searchService;
private readonly ISiteService _siteService;
private readonly IContentDefinitionManager _contentDefinitionManager;
private readonly IIndexManager _indexManager;
public ContentPickerController(
IOrchardServices orchardServices,
ISearchService searchService,
ISiteService siteService) {
_searchService = searchService;
ISiteService siteService,
IContentDefinitionManager contentDefinitionManager,
IIndexManager indexManager) {
_siteService = siteService;
_contentDefinitionManager = contentDefinitionManager;
_indexManager = indexManager;
Services = orchardServices;
T = NullLocalizer.Instance;
Logger = NullLogger.Instance;
@@ -40,35 +43,68 @@ namespace Orchard.Search.Controllers {
public Localizer T { get; set; }
[Themed(false)]
public ActionResult Index(PagerParameters pagerParameters, string searchText = "") {
public ActionResult Index(PagerParameters pagerParameters, string part, string field, string searchText = "") {
Pager pager = new Pager(_siteService.GetSiteSettings(), pagerParameters);
var searchFields = Services.WorkContext.CurrentSite.As<SearchSettingsPart>().SearchedFields;
IPageOfItems<ISearchHit> searchHits = new PageOfItems<ISearchHit>(new ISearchHit[] { });
try {
int totalCount = 0;
int[] foundIds = new int[0];
searchHits = _searchService.Query(searchText, pager.Page, pager.PageSize,
Services.WorkContext.CurrentSite.As<SearchSettingsPart>().Record.FilterCulture,
searchFields,
searchHit => searchHit);
}
catch (Exception exception) {
Logger.Error(T("Invalid search query: {0}", exception.Message).Text);
Services.Notifier.Error(T("Invalid search query: {0}", exception.Message));
if (!String.IsNullOrWhiteSpace(searchText)) {
ContentPickerSearchFieldSettings settings = null;
// if the picker is loaded for a specific field, apply custom settings
if (!String.IsNullOrEmpty(part) && !String.IsNullOrEmpty(field)) {
var definition = _contentDefinitionManager.GetPartDefinition(part).Fields.FirstOrDefault(x => x.Name == field);
if (definition != null) {
settings = definition.Settings.GetModel<ContentPickerSearchFieldSettings>();
}
}
if (!_indexManager.HasIndexProvider()) {
return HttpNotFound();
}
var builder = _indexManager.GetSearchIndexProvider().CreateSearchBuilder("Search");
try {
builder.Parse(searchFields, searchText);
if (settings != null && !String.IsNullOrEmpty(settings.DisplayedContentTypes)) {
var rawTypes = settings.DisplayedContentTypes.Split(new[] { ',', ' ' }, StringSplitOptions.RemoveEmptyEntries).ToList();
var contentTypes = _contentDefinitionManager
.ListTypeDefinitions()
.Where(x => x.Parts.Any(p => rawTypes.Contains(p.PartDefinition.Name)))
.ToArray();
foreach (string type in contentTypes.Select(x => x.Name)) {
builder.WithField("type", type).AsFilter();
}
}
totalCount = builder.Count();
builder = builder.Slice((pager.Page > 0 ? pager.Page - 1 : 0) * pager.PageSize, pager.PageSize);
var searchResults = builder.Search();
foundIds = searchResults.Select(searchHit => searchHit.ContentItemId).ToArray();
}
catch (Exception exception) {
Logger.Error(T("Invalid search query: {0}", exception.Message).Text);
Services.Notifier.Error(T("Invalid search query: {0}", exception.Message));
}
}
var list = Services.New.List();
foreach (var contentItem in Services.ContentManager.GetMany<IContent>(searchHits.Select(x => x.ContentItemId), VersionOptions.Published, QueryHints.Empty)) {
foreach (var contentItem in Services.ContentManager.GetMany<IContent>(foundIds, VersionOptions.Published, QueryHints.Empty)) {
// ignore search results which content item has been removed or unpublished
if (contentItem == null) {
searchHits.TotalItemCount--;
totalCount--;
continue;
}
list.Add(Services.ContentManager.BuildDisplay(contentItem, "SummaryAdmin"));
}
var pagerShape = Services.New.Pager(pager).TotalItemCount(searchHits.TotalItemCount);
var pagerShape = Services.New.Pager(pager).TotalItemCount(totalCount);
foreach(IShape item in list.Items) {

View File

@@ -21,6 +21,10 @@
<UpgradeBackupLocation>
</UpgradeBackupLocation>
<OldToolsVersion>4.0</OldToolsVersion>
<IISExpressSSLPort />
<IISExpressAnonymousAuthentication />
<IISExpressWindowsAuthentication />
<IISExpressUseClassicPipelineMode />
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<DebugSymbols>true</DebugSymbols>
@@ -68,6 +72,8 @@
<Compile Include="Handlers\SearchSettingsPartHandler.cs" />
<Compile Include="Services\ISearchService.cs" />
<Compile Include="Services\SearchService.cs" />
<Compile Include="Settings\ContentPickerFieldEditorEvents.cs" />
<Compile Include="Settings\ContentPickerFieldSettings.cs" />
<Compile Include="ViewModels\SearchSettingsViewModel.cs" />
<Compile Include="ViewModels\SearchResultViewModel.cs" />
<Compile Include="ViewModels\SearchViewModel.cs" />
@@ -109,6 +115,9 @@
<ItemGroup>
<Content Include="Views\Admin\Index.cshtml" />
</ItemGroup>
<ItemGroup>
<Content Include="Views\DefinitionTemplates\ContentPickerSearchFieldSettings.cshtml" />
</ItemGroup>
<PropertyGroup>
<VisualStudioVersion Condition="'$(VisualStudioVersion)' == ''">10.0</VisualStudioVersion>
<VSToolsPath Condition="'$(VSToolsPath)' == ''">$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)</VSToolsPath>

View File

@@ -0,0 +1,35 @@
using System.Collections.Generic;
using System.Globalization;
using Orchard.ContentManagement;
using Orchard.ContentManagement.MetaData;
using Orchard.ContentManagement.MetaData.Builders;
using Orchard.ContentManagement.MetaData.Models;
using Orchard.ContentManagement.ViewModels;
using Orchard.Environment.Extensions;
namespace Orchard.Search.Settings {
[OrchardFeature("Orchard.Search.ContentPicker")]
public class ContentPickerFieldEditorEvents : ContentDefinitionEditorEventsBase {
public override IEnumerable<TemplateViewModel> PartFieldEditor(ContentPartFieldDefinition definition) {
if (definition.FieldDefinition.Name == "ContentPickerField") {
var model = definition.Settings.GetModel<ContentPickerSearchFieldSettings>();
yield return DefinitionTemplate(model);
}
}
public override IEnumerable<TemplateViewModel> PartFieldEditorUpdate(ContentPartFieldDefinitionBuilder builder, IUpdateModel updateModel) {
if (builder.FieldType != "ContentPickerField") {
yield break;
}
var model = new ContentPickerSearchFieldSettings();
if (updateModel.TryUpdateModel(model, "ContentPickerSearchFieldSettings", null, null)) {
builder.WithSetting("ContentPickerSearchFieldSettings.ShowSearchTab", model.ShowSearchTab.ToString(CultureInfo.InvariantCulture));
builder.WithSetting("ContentPickerSearchFieldSettings.DisplayedContentTypes", model.DisplayedContentTypes);
}
yield return DefinitionTemplate(model);
}
}
}

View File

@@ -0,0 +1,10 @@
namespace Orchard.Search.Settings {
public class ContentPickerSearchFieldSettings {
public ContentPickerSearchFieldSettings() {
ShowSearchTab = true;
}
public bool ShowSearchTab { get; set; }
public string DisplayedContentTypes { get; set; }
}
}

View File

@@ -0,0 +1,15 @@
@model Orchard.Search.Settings.ContentPickerSearchFieldSettings
<fieldset>
<div>
@Html.CheckBoxFor(m => m.ShowSearchTab) <label for="@Html.FieldIdFor(m => m.ShowSearchTab)" class="forcheckbox">@T("Show Content tab")</label>
<span class="hint">@T("Uncheck to hide the Search tab from the picker window.")</span>
</div>
</fieldset>
<fieldset>
<div>
<label for="@Html.FieldIdFor(m => m.DisplayedContentTypes)">@T("Content Types and Parts")</label>
@Html.TextBoxFor(m => m.DisplayedContentTypes)
<span class="hint">@T("A comma separated value of all the content types or content parts to display.")</span>
</div>
</fieldset>