--HG--
branch : 1.x
This commit is contained in:
Bertrand Le Roy
2013-02-10 13:08:09 -08:00
37 changed files with 551 additions and 120 deletions

View File

@@ -639,5 +639,24 @@ namespace Orchard.Tests.Modules.Indexing {
.WithField("field3", 3).Mandatory().AsFilter()
.Count(), Is.EqualTo(1));
}
[Test]
public void ShouldReturnFacetedResults() {
_provider.CreateIndex("default");
_provider.Store("default", _provider.New(1).Add("body", "michael is in the kitchen").Analyze());
_provider.Store("default", _provider.New(2).Add("body", "michael has a cousin named michel").Analyze());
_provider.Store("default", _provider.New(3).Add("body", "speak inside the mic").Analyze());
_provider.Store("default", _provider.New(4).Add("body", "a dog is pursuing a cat").Analyze());
_provider.Store("default", _provider.New(5).Add("body", "michael speaks to elephants").Analyze());
var michael = SearchBuilder.WithField("body", "michael").GetBits();
var speak = SearchBuilder.WithField("body", "speak").GetBits();
Assert.That(michael.Count(), Is.EqualTo(3));
Assert.That(speak.Count(), Is.EqualTo(2));
Assert.That(speak.And(michael).Count(), Is.EqualTo(1));
Assert.That(speak.Or(michael).Count(), Is.EqualTo(4));
Assert.That(speak.Xor(michael).Count(), Is.EqualTo(3));
}
}
}

View File

@@ -37,5 +37,11 @@ namespace Orchard.Tests.DisplayManagement {
dynamic nil = Nil.Instance;
Assert.That(nil.Foo.Bar.ToString(), Is.EqualTo(""));
}
[Test]
public void ConvertingToStringShouldReturnNullString() {
dynamic nil = Nil.Instance;
Assert.That((string)nil == null, Is.True);
}
}
}

View File

@@ -1,7 +1,6 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Web;
@@ -28,20 +27,24 @@ namespace Orchard.Core.Shapes {
private readonly Work<WorkContext> _workContext;
private readonly Work<IResourceManager> _resourceManager;
private readonly Work<IHttpContextAccessor> _httpContextAccessor;
private readonly Work<IShapeFactory> _shapeFactory;
public CoreShapes(
Work<WorkContext> workContext,
Work<IResourceManager> resourceManager,
Work<IHttpContextAccessor> httpContextAccessor
Work<IHttpContextAccessor> httpContextAccessor,
Work<IShapeFactory> shapeFactory
) {
_workContext = workContext;
_resourceManager = resourceManager;
_httpContextAccessor = httpContextAccessor;
_shapeFactory = shapeFactory;
T = NullLocalizer.Instance;
}
public Localizer T { get; set; }
public dynamic New { get { return _shapeFactory.Value; } }
public void Discover(ShapeTableBuilder builder) {
// the root page shape named 'Layout' is wrapped with 'Document'
@@ -490,49 +493,49 @@ namespace Orchard.Core.Shapes {
routeData.Remove(pageKey); // to keep from having "page=1" in the query string
}
// first
Shape.Add(Display.Pager_First(Value: firstText, RouteValues: routeData, Pager: Shape));
Shape.Add(New.Pager_First(Value: firstText, RouteValues: new RouteValueDictionary(routeData), Pager: Shape));
// previous
if (currentPage > 2) { // also to keep from having "page=1" in the query string
routeData[pageKey] = currentPage - 1;
}
Shape.Add(Display.Pager_Previous(Value: previousText, RouteValues: routeData, Pager: Shape));
Shape.Add(New.Pager_Previous(Value: previousText, RouteValues: new RouteValueDictionary(routeData), Pager: Shape));
}
// gap at the beginning of the pager
if (firstPage > 1 && numberOfPagesToShow > 0) {
Shape.Add(Display.Pager_Gap(Value: gapText, Pager: Shape));
Shape.Add(New.Pager_Gap(Value: gapText, Pager: Shape));
}
// page numbers
if (numberOfPagesToShow > 0 && firstPage != lastPage) {
if (numberOfPagesToShow > 0) {
for (var p = firstPage; p <= lastPage; p++) {
if (p == currentPage) {
Shape.Add(Display.Pager_CurrentPage(Value: p, RouteValues: routeData, Pager: Shape));
Shape.Add(New.Pager_CurrentPage(Value: p, RouteValues: new RouteValueDictionary(routeData), Pager: Shape));
}
else {
if (p == 1)
routeData.Remove(pageKey);
else
routeData[pageKey] = p;
Shape.Add(Display.Pager_Link(Value: p, RouteValues: routeData, Pager: Shape));
Shape.Add(New.Pager_Link(Value: p, RouteValues: new RouteValueDictionary(routeData), Pager: Shape));
}
}
}
// gap at the end of the pager
if (lastPage < totalPageCount && numberOfPagesToShow > 0) {
Shape.Add(Display.Pager_Gap(Value: gapText, Pager: Shape));
Shape.Add(New.Pager_Gap(Value: gapText, Pager: Shape));
}
// next and last pages
if (Page < totalPageCount) {
// next
routeData[pageKey] = Page + 1;
Shape.Add(Display.Pager_Next(Value: nextText, RouteValues: routeData, Pager: Shape));
Shape.Add(New.Pager_Next(Value: nextText, RouteValues: new RouteValueDictionary(routeData), Pager: Shape));
// last
routeData[pageKey] = totalPageCount;
Shape.Add(Display.Pager_Last(Value: lastText, RouteValues: routeData, Pager: Shape));
Shape.Add(New.Pager_Last(Value: lastText, RouteValues: new RouteValueDictionary(routeData), Pager: Shape));
}
return Display(Shape);
@@ -562,7 +565,7 @@ namespace Orchard.Core.Shapes {
[Shape]
public IHtmlString Pager_CurrentPage(HtmlHelper Html, dynamic Display, object Value) {
var tagBuilder = new TagBuilder("span");
tagBuilder.InnerHtml = Html.Encode(Value is string ? (string)Value : Display(Value));
tagBuilder.InnerHtml = EncodeOrDisplay(Value, Display, Html).ToString();
return MvcHtmlString.Create(tagBuilder.ToString());
}
@@ -583,21 +586,13 @@ namespace Orchard.Core.Shapes {
[Shape]
public IHtmlString Pager_Link(HtmlHelper Html, dynamic Shape, dynamic Display, object Value) {
var RouteValues = (object)Shape.RouteValues;
RouteValueDictionary rvd;
if (RouteValues == null) {
rvd = new RouteValueDictionary();
}
else {
rvd = RouteValues is RouteValueDictionary ? (RouteValueDictionary)RouteValues : new RouteValueDictionary(RouteValues);
}
string value = Html.Encode(Value is string ? (string)Value : Display(Value));
return @Html.ActionLink(value, (string)rvd["action"], (string)rvd["controller"], rvd, null);
Shape.Metadata.Alternates.Clear();
Shape.Metadata.Type = "ActionLink";
return Display(Shape);
}
[Shape]
public IHtmlString ActionLink(HtmlHelper Html, dynamic Shape, dynamic Display, object Value) {
public IHtmlString ActionLink(HtmlHelper Html, UrlHelper Url, dynamic Shape, dynamic Display, object Value) {
var RouteValues = (object)Shape.RouteValues;
RouteValueDictionary rvd;
if (RouteValues == null) {
@@ -606,15 +601,23 @@ namespace Orchard.Core.Shapes {
else {
rvd = RouteValues is RouteValueDictionary ? (RouteValueDictionary)RouteValues : new RouteValueDictionary(RouteValues);
}
string value = Html.Encode(Value is string ? (string)Value : Display(Value));
return @Html.ActionLink(value, (string)rvd["action"], (string)rvd["controller"], rvd, null);
var action = Url.Action((string)rvd["action"], (string)rvd["controller"], rvd);
IEnumerable<string> classes = Shape.Classes;
IDictionary<string, string> attributes = Shape.Attributes;
attributes.Add("href", action);
string id = Shape.Id;
var tag = GetTagBuilder("a", id, classes, attributes);
tag.InnerHtml = EncodeOrDisplay(Value, Display, Html).ToString();
return Html.Raw(tag.ToString());
}
[Shape]
public IHtmlString Pager_Gap(HtmlHelper Html, dynamic Display, object Value) {
var tagBuilder = new TagBuilder("span");
tagBuilder.InnerHtml = Html.Encode(Value is string ? (string)Value : Display(Value));
tagBuilder.InnerHtml = EncodeOrDisplay(Value, Display, Html).ToString();
return MvcHtmlString.Create(tagBuilder.ToString());
}
@@ -628,37 +631,88 @@ namespace Orchard.Core.Shapes {
string Id,
IEnumerable<string> Classes,
IDictionary<string, string> Attributes,
string ItemTag,
IEnumerable<string> ItemClasses,
IDictionary<string, string> ItemAttributes) {
if (Items == null)
return;
// prevent multiple enumerations
var items = Items.ToList();
var itemDisplayOutputs = Items.Select(item => Display(item)).Where(output => !string.IsNullOrWhiteSpace(output.ToHtmlString())).ToList();
var count = itemDisplayOutputs.Count();
// var itemDisplayOutputs = Items.Select(item => Display(item)).Where(output => !string.IsNullOrWhiteSpace(output.ToHtmlString())).ToList();
var count = items.Count();
if (count < 1)
return;
var listTagName = string.IsNullOrEmpty(Tag) ? "ul" : Tag;
const string itemTagName = "li";
string listTagName = null;
if (Tag != "-") {
listTagName = string.IsNullOrEmpty(Tag) ? "ul" : Tag;
}
var listTag = GetTagBuilder(listTagName, Id, Classes, Attributes);
Output.Write(listTag.ToString(TagRenderMode.StartTag));
var listTag = String.IsNullOrEmpty(listTagName) ? null : GetTagBuilder(listTagName, Id, Classes, Attributes);
string itemTagName = null;
if (ItemTag != "-") {
itemTagName = string.IsNullOrEmpty(ItemTag) ? "li" : ItemTag;
}
if (listTag != null) {
Output.Write(listTag.ToString(TagRenderMode.StartTag));
}
var itemTags = new List<TagBuilder>();
var itemOutputs = new List<string>();
// give the item shape the possibility to alter its container tag
var index = 0;
foreach (var itemDisplayOutput in itemDisplayOutputs) {
var itemTag = GetTagBuilder(itemTagName, null, ItemClasses, ItemAttributes);
if (index == 0)
itemTag.AddCssClass("first");
if (index == count - 1)
itemTag.AddCssClass("last");
Output.Write(itemTag.ToString(TagRenderMode.StartTag));
Output.Write(itemDisplayOutput);
Output.Write(itemTag.ToString(TagRenderMode.EndTag));
foreach (var item in items) {
var itemTag = String.IsNullOrEmpty(itemTagName) ? null : GetTagBuilder(itemTagName, null, ItemClasses, ItemAttributes);
if (item is IShape) {
item.Tag = itemTag;
}
var itemOutput = Display(item).ToHtmlString();
if (!String.IsNullOrWhiteSpace(itemOutput)) {
itemTags.Add(itemTag);
itemOutputs.Add(itemOutput);
}
else {
count--;
}
++index;
}
Output.Write(listTag.ToString(TagRenderMode.EndTag));
index = 0;
foreach(var itemOutput in itemOutputs) {
var itemTag = itemTags[index];
if (itemTag != null) {
if (index == 0)
itemTag.AddCssClass("first");
if (index == count - 1)
itemTag.AddCssClass("last");
Output.Write(itemTag.ToString(TagRenderMode.StartTag));
}
Output.Write(itemOutput);
if (itemTag != null) {
Output.Write(itemTag.ToString(TagRenderMode.EndTag));
}
++index;
}
if (listTag != null) {
Output.Write(listTag.ToString(TagRenderMode.EndTag));
}
}
[Shape]
@@ -718,5 +772,20 @@ namespace Orchard.Core.Shapes {
private string EncodeAlternateElement(string alternateElement) {
return alternateElement.Replace("-", "__").Replace(".", "_");
}
/// <summary>
/// Encode a value if it's a string, or render it if it's a Shape
/// </summary>
private IHtmlString EncodeOrDisplay(dynamic Value, dynamic Display, HtmlHelper Html) {
if (Value is IHtmlString) {
return Value;
}
if (Value is IShape) {
return Display(Value).ToString();
}
return Html.Raw(Html.Encode(Value.ToString()));
}
}
}

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>
@@ -66,6 +70,7 @@
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="Services\LuceneIndexProvider.cs" />
<Compile Include="Services\LuceneSearchBuilder.cs" />
<Compile Include="Services\SearchBits.cs" />
</ItemGroup>
<ItemGroup>
<Content Include="Module.txt" />

View File

@@ -1,4 +1,5 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
@@ -6,6 +7,7 @@ using Lucene.Models;
using Lucene.Net.Index;
using Lucene.Net.Search;
using Lucene.Net.Store;
using Lucene.Net.Util;
using Orchard.Indexing;
using Orchard.Logging;
using Lucene.Net.Documents;
@@ -355,6 +357,30 @@ namespace Lucene.Services {
}
public ISearchBits GetBits() {
var query = CreateQuery();
IndexSearcher searcher;
try {
searcher = new IndexSearcher(_directory, true);
}
catch {
// index might not exist if it has been rebuilt
Logger.Information("Attempt to read a none existing index");
return null;
}
try {
var filter = new QueryWrapperFilter(query);
var bits = filter.GetDocIdSet(searcher.GetIndexReader());
var disi = new OpenBitSetDISI(bits.Iterator(), searcher.MaxDoc());
return new SearchBits(disi);
}
finally {
searcher.Close();
}
}
public ISearchHit Get(int documentId) {
var query = new TermQuery(new Term("id", documentId.ToString(CultureInfo.InvariantCulture)));

View File

@@ -0,0 +1,43 @@
using System;
using Lucene.Net.Util;
using Orchard.Indexing;
namespace Lucene.Services {
public class SearchBits : ISearchBits {
internal readonly OpenBitSet _openBitSet;
public SearchBits(OpenBitSet openBitSet) {
_openBitSet = openBitSet;
}
public ISearchBits And(ISearchBits other) {
return Apply(other, (x, y) => x.And(y));
}
public ISearchBits Or(ISearchBits other) {
return Apply(other, (x, y) => x.Or(y));
}
public ISearchBits Xor(ISearchBits other) {
return Apply(other, (x, y) => x.Xor(y));
}
public long Count() {
return _openBitSet.Cardinality();
}
private ISearchBits Apply(ISearchBits other, Action<OpenBitSet, OpenBitSet> operation) {
var bitset = (OpenBitSet)_openBitSet.Clone();
var otherBitSet = other as SearchBits;
if (otherBitSet == null) {
throw new InvalidOperationException("The other bitset must be of type OpenBitSet");
}
operation(bitset, otherBitSet._openBitSet);
return new SearchBits(bitset);
}
}
}

View File

@@ -1,5 +1,5 @@
@{
Html.AddTitleParts(T("Manage Blog").ToString());
Layout.Title = T("Manage Blog").ToString();
}
@* Model is a Shape, calling Display() so that it is rendered using the most specific template for its Shape type *@
@Display(Model)

View File

@@ -1,7 +1,8 @@
@using Orchard.Blogs.Extensions;
@using Orchard.Blogs.Models;
@using Orchard.ContentManagement
@{ Layout.Title = (string)Model.Title; }
@{ Layout.Title = T("Manage {0}", Html.ItemDisplayText((IContent)Model.ContentItem)); }
@Display(Model.Header)
@Display(Model.Actions)

View File

@@ -1,6 +1,6 @@
@using Orchard.Mvc.Html;
@{
Html.AddTitleParts((string)Model.Title);
Layout.Title = T("New Blog");
}
<div class="edit-item">
<div class="edit-item-primary">

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>

View File

@@ -1,32 +1,8 @@
using Orchard.ContentManagement;
using Orchard.ContentManagement.MetaData;
using Orchard.ContentManagement.MetaData;
using Orchard.Core.Contents.Extensions;
using Orchard.Data.Migration;
using Orchard.Widgets.Models;
namespace Orchard.Widgets {
public interface IDefaultLayersInitializer : IDependency {
void CreateDefaultLayers();
}
public class DefaultLayersInitializer : IDefaultLayersInitializer {
private readonly IContentManager _contentManager;
public DefaultLayersInitializer(IContentManager contentManager) {
_contentManager = contentManager;
}
public void CreateDefaultLayers() {
IContent defaultLayer = _contentManager.Create<LayerPart>("Layer", t => { t.Record.Name = "Default"; t.Record.LayerRule = "true"; });
_contentManager.Publish(defaultLayer.ContentItem);
IContent authenticatedLayer = _contentManager.Create<LayerPart>("Layer", t => { t.Record.Name = "Authenticated"; t.Record.LayerRule = "authenticated"; });
_contentManager.Publish(authenticatedLayer.ContentItem);
IContent anonymousLayer = _contentManager.Create<LayerPart>("Layer", t => { t.Record.Name = "Anonymous"; t.Record.LayerRule = "not authenticated"; });
_contentManager.Publish(anonymousLayer.ContentItem);
IContent disabledLayer = _contentManager.Create<LayerPart>("Layer", t => { t.Record.Name = "Disabled"; t.Record.LayerRule = "false"; });
_contentManager.Publish(disabledLayer.ContentItem);
}
}
public class WidgetsDataMigration : DataMigrationImpl {
public int Create() {

View File

@@ -1,3 +0,0 @@
Ceci est une actualite parlant de :
@Model.ContentItem.Actualite.Sommaire.Value

View File

@@ -1,5 +1,7 @@
@{
// number of page number links to show, 0 means no link, 1 means only the current page, or more accepted.
Model.Quantity = 0;
Model.PreviousText = T("Newer");
Model.NextText = T("Older");
Model.Classes.Add("group");

View File

@@ -6,14 +6,9 @@ namespace Orchard.DisplayManagement {
/// This may be used directly, or through the IShapeHelperFactory.
/// </summary>
public interface IShapeFactory : IDependency {
IShape Create(string shapeType);
IShape Create(string shapeType, INamedEnumerable<object> parameters);
IShape Create(string shapeType, INamedEnumerable<object> parameters, Func<dynamic> createShape);
}
public static class ShapeFactoryExtensions {
public static IShape Create(this IShapeFactory factory, string shapeType) {
return factory.Create(shapeType, Arguments.Empty());
}
}
}

View File

@@ -23,6 +23,10 @@ namespace Orchard.DisplayManagement.Implementation {
return true;
}
public IShape Create(string shapeType) {
return Create(shapeType, Arguments.Empty(), () => new Shape());
}
public IShape Create(string shapeType, INamedEnumerable<object> parameters) {
return Create(shapeType, parameters, () => new Shape());
}

View File

@@ -0,0 +1,8 @@
namespace Orchard.Indexing {
public interface ISearchBits {
ISearchBits And(ISearchBits other);
ISearchBits Or(ISearchBits other);
ISearchBits Xor(ISearchBits other);
long Count();
}
}

View File

@@ -57,6 +57,7 @@ namespace Orchard.Indexing {
ISearchBuilder Slice(int skip, int count);
IEnumerable<ISearchHit> Search();
ISearchHit Get(int documentId);
ISearchBits GetBits();
int Count();
}

View File

@@ -127,6 +127,11 @@ namespace Orchard.Indexing {
public ISearchHit Get(int documentId) {
return null;
}
public ISearchBits GetBits() {
throw new NotImplementedException();
}
public int Count() {
return 0;
}

View File

@@ -238,6 +238,7 @@
<Compile Include="FileSystems\LockFile\LockFile.cs" />
<Compile Include="FileSystems\LockFile\DefaultLockFileManager.cs" />
<Compile Include="FileSystems\Media\FileSystemStorageProvider.cs" />
<Compile Include="Indexing\ISearchBits.cs" />
<Compile Include="Localization\Services\CurrentCultureWorkContext.cs" />
<Compile Include="Localization\Services\DefaultLocalizedStringManager.cs" />
<Compile Include="Localization\Services\ILocalizedStringManager.cs" />

View File

@@ -139,7 +139,16 @@ namespace Orchard.UI.Zones {
}
public override bool TryConvert(System.Dynamic.ConvertBinder binder, out object result) {
result = Nil.Instance;
if (binder.ReturnType == typeof (string)) {
result = null;
}
else if (binder.ReturnType.IsValueType) {
result = Activator.CreateInstance(binder.ReturnType);
}
else {
result = null;
}
return true;
}