mirror of
https://github.com/OrchardCMS/Orchard.git
synced 2025-12-03 03:58:13 +08:00
Merge
--HG-- branch : dev
This commit is contained in:
306
src/Orchard.Tests.Modules/Indexing/IndexingTaskExecutorTests.cs
Normal file
306
src/Orchard.Tests.Modules/Indexing/IndexingTaskExecutorTests.cs
Normal file
@@ -0,0 +1,306 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using Autofac;
|
||||
using Lucene.Services;
|
||||
using Moq;
|
||||
using NUnit.Framework;
|
||||
using Orchard.ContentManagement;
|
||||
using Orchard.ContentManagement.Drivers;
|
||||
using Orchard.ContentManagement.Handlers;
|
||||
using Orchard.ContentManagement.MetaData;
|
||||
using Orchard.ContentManagement.MetaData.Builders;
|
||||
using Orchard.ContentManagement.Records;
|
||||
using Orchard.Core.Common.Handlers;
|
||||
using Orchard.Core.Common.Models;
|
||||
using Orchard.Data;
|
||||
using Orchard.Environment;
|
||||
using Orchard.Environment.Configuration;
|
||||
using Orchard.Environment.Extensions;
|
||||
using Orchard.FileSystems.AppData;
|
||||
using Orchard.Indexing;
|
||||
using Orchard.Indexing.Handlers;
|
||||
using Orchard.Indexing.Models;
|
||||
using Orchard.Indexing.Services;
|
||||
using Orchard.Logging;
|
||||
using Orchard.Security;
|
||||
using Orchard.Tasks.Indexing;
|
||||
using Orchard.Tests.FileSystems.AppData;
|
||||
using Orchard.Tests.Stubs;
|
||||
|
||||
namespace Orchard.Tests.Modules.Indexing {
|
||||
public class IndexingTaskExecutorTests : DatabaseEnabledTestsBase {
|
||||
private IIndexProvider _provider;
|
||||
private IAppDataFolder _appDataFolder;
|
||||
private ShellSettings _shellSettings;
|
||||
private IIndexNotifierHandler _indexNotifier;
|
||||
private IContentManager _contentManager;
|
||||
private Mock<IContentDefinitionManager> _contentDefinitionManager;
|
||||
private StubLogger _logger;
|
||||
private const string IndexName = "Search";
|
||||
|
||||
private readonly string _basePath = Path.Combine(Path.GetTempPath(), Path.GetRandomFileName());
|
||||
|
||||
[TestFixtureTearDown]
|
||||
public void Clean() {
|
||||
if (Directory.Exists(_basePath)) {
|
||||
Directory.Delete(_basePath, true);
|
||||
}
|
||||
}
|
||||
|
||||
public override void Register(ContainerBuilder builder) {
|
||||
if (Directory.Exists(_basePath)) {
|
||||
Directory.Delete(_basePath, true);
|
||||
}
|
||||
Directory.CreateDirectory(_basePath);
|
||||
_contentDefinitionManager = new Mock<IContentDefinitionManager>();
|
||||
_appDataFolder = AppDataFolderTests.CreateAppDataFolder(_basePath);
|
||||
|
||||
builder.RegisterType<LuceneIndexProvider>().As<IIndexProvider>();
|
||||
builder.RegisterInstance(_appDataFolder).As<IAppDataFolder>();
|
||||
builder.RegisterType<IndexingTaskExecutor>().As<IIndexNotifierHandler>();
|
||||
builder.RegisterType<DefaultIndexManager>().As<IIndexManager>();
|
||||
builder.RegisterType<IndexingTaskManager>().As<IIndexingTaskManager>();
|
||||
builder.RegisterType<IndexSynLock>().As<IIndexSynLock>();
|
||||
builder.RegisterType<DefaultContentManager>().As<IContentManager>();
|
||||
builder.RegisterType<DefaultContentManagerSession>().As<IContentManagerSession>();
|
||||
builder.RegisterInstance(_contentDefinitionManager.Object);
|
||||
builder.RegisterInstance(new Mock<IContentDisplay>().Object);
|
||||
|
||||
builder.RegisterType<DefaultContentManagerSession>().As<IContentManagerSession>();
|
||||
builder.RegisterInstance(new Mock<ITransactionManager>().Object);
|
||||
builder.RegisterInstance(new Mock<IAuthorizer>().Object);
|
||||
builder.RegisterType<OrchardServices>().As<IOrchardServices>();
|
||||
|
||||
builder.RegisterType<ThingHandler>().As<IContentHandler>();
|
||||
builder.RegisterType<CreateIndexingTaskHandler>().As<IContentHandler>();
|
||||
|
||||
builder.RegisterType<DefaultContentQuery>().As<IContentQuery>();
|
||||
builder.RegisterType<BodyPartHandler>().As<IContentHandler>();
|
||||
builder.RegisterType<StubExtensionManager>().As<IExtensionManager>();
|
||||
|
||||
// setting up a ShellSettings instance
|
||||
_shellSettings = new ShellSettings { Name = "My Site" };
|
||||
builder.RegisterInstance(_shellSettings).As<ShellSettings>();
|
||||
}
|
||||
|
||||
protected override IEnumerable<Type> DatabaseTypes {
|
||||
get {
|
||||
return new[] { typeof(IndexingTaskRecord),
|
||||
typeof(ContentTypeRecord),
|
||||
typeof(ContentItemRecord),
|
||||
typeof(ContentItemVersionRecord),
|
||||
typeof(BodyPartRecord),
|
||||
typeof(CommonPartRecord),
|
||||
typeof(CommonPartVersionRecord),
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
public override void Init() {
|
||||
base.Init();
|
||||
|
||||
_provider = _container.Resolve<IIndexProvider>();
|
||||
_indexNotifier = _container.Resolve<IIndexNotifierHandler>();
|
||||
_contentManager = _container.Resolve<IContentManager>();
|
||||
((IndexingTaskExecutor)_indexNotifier).Logger = _logger = new StubLogger();
|
||||
|
||||
var thingType = new ContentTypeDefinitionBuilder()
|
||||
.Named(ThingDriver.ContentTypeName)
|
||||
.WithSetting("TypeIndexing.Included", "true")
|
||||
.Build();
|
||||
|
||||
_contentDefinitionManager
|
||||
.Setup(x => x.GetTypeDefinition(ThingDriver.ContentTypeName))
|
||||
.Returns(thingType);
|
||||
}
|
||||
|
||||
private string[] Indexes() {
|
||||
return new DirectoryInfo(Path.Combine(_basePath, "Sites", "My Site", "Indexes")).GetDirectories().Select(d => d.Name).ToArray();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void IndexShouldBeEmptyWhenThereIsNoContent() {
|
||||
_indexNotifier.UpdateIndex(IndexName);
|
||||
Assert.That(_provider.NumDocs(IndexName), Is.EqualTo(0));
|
||||
Assert.That(_logger.LogEntries.Count(), Is.EqualTo(2));
|
||||
Assert.That(_logger.LogEntries, Has.Some.Matches<LogEntry>(entry => entry.LogFormat == "Rebuild index started"));
|
||||
Assert.That(_logger.LogEntries, Has.Some.Matches<LogEntry>(entry => entry.LogFormat == "Index update requested, nothing to do"));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ShouldIngoreNonIndexableContentWhenRebuildingTheIndex() {
|
||||
var alphaType = new ContentTypeDefinitionBuilder()
|
||||
.Named("alpha")
|
||||
.Build();
|
||||
|
||||
_contentDefinitionManager
|
||||
.Setup(x => x.GetTypeDefinition("alpha"))
|
||||
.Returns(alphaType);
|
||||
|
||||
_contentManager.Create("alpha");
|
||||
|
||||
_indexNotifier.UpdateIndex(IndexName);
|
||||
Assert.That(_provider.NumDocs(IndexName), Is.EqualTo(0));
|
||||
Assert.That(_logger.LogEntries.Count(), Is.EqualTo(2));
|
||||
Assert.That(_logger.LogEntries, Has.Some.Matches<LogEntry>(entry => entry.LogFormat == "Rebuild index started"));
|
||||
Assert.That(_logger.LogEntries, Has.Some.Matches<LogEntry>(entry => entry.LogFormat == "Index update requested, nothing to do"));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ShouldNotIndexContentIfIndexDocumentIsEmpty() {
|
||||
var alphaType = new ContentTypeDefinitionBuilder()
|
||||
.Named("alpha")
|
||||
.WithSetting("TypeIndexing.Included", "true") // the content types should be indexed, but there is no content at all
|
||||
.Build();
|
||||
|
||||
_contentDefinitionManager
|
||||
.Setup(x => x.GetTypeDefinition("alpha"))
|
||||
.Returns(alphaType);
|
||||
|
||||
_contentManager.Create("alpha");
|
||||
|
||||
_indexNotifier.UpdateIndex(IndexName);
|
||||
Assert.That(_provider.NumDocs(IndexName), Is.EqualTo(0));
|
||||
Assert.That(_logger.LogEntries.Count(), Is.EqualTo(2));
|
||||
Assert.That(_logger.LogEntries, Has.Some.Matches<LogEntry>(entry => entry.LogFormat == "Rebuild index started"));
|
||||
Assert.That(_logger.LogEntries, Has.Some.Matches<LogEntry>(entry => entry.LogFormat == "Index update requested, nothing to do"));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ShouldIndexContentIfSettingsIsSetAndHandlerIsProvided() {
|
||||
var content = _contentManager.Create<Thing>(ThingDriver.ContentTypeName);
|
||||
content.Text = "Lorem ipsum";
|
||||
|
||||
_indexNotifier.UpdateIndex(IndexName);
|
||||
Assert.That(_provider.NumDocs(IndexName), Is.EqualTo(1));
|
||||
Assert.That(_logger.LogEntries.Count(), Is.EqualTo(3));
|
||||
Assert.That(_logger.LogEntries, Has.Some.Matches<LogEntry>(entry => entry.LogFormat == "Rebuild index started"));
|
||||
Assert.That(_logger.LogEntries, Has.Some.Matches<LogEntry>(entry => entry.LogFormat == "Processing {0} indexing tasks"));
|
||||
Assert.That(_logger.LogEntries, Has.Some.Matches<LogEntry>(entry => entry.LogFormat == "Added content items to index: {0}"));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ShouldUpdateTheIndexWhenContentIsPublished() {
|
||||
_contentManager.Create<Thing>(ThingDriver.ContentTypeName).Text = "Lorem ipsum";
|
||||
_indexNotifier.UpdateIndex(IndexName);
|
||||
Assert.That(_provider.NumDocs(IndexName), Is.EqualTo(1));
|
||||
Assert.That(_logger.LogEntries, Has.Some.Matches<LogEntry>(entry => entry.LogFormat == "Rebuild index started"));
|
||||
_logger.Clear();
|
||||
|
||||
_contentManager.Create<Thing>(ThingDriver.ContentTypeName).Text = "Lorem ipsum";
|
||||
_indexNotifier.UpdateIndex(IndexName);
|
||||
Assert.That(_provider.NumDocs(IndexName), Is.EqualTo(2));
|
||||
Assert.That(_logger.LogEntries, Has.None.Matches<LogEntry>(entry => entry.LogFormat == "Rebuild index started"));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ShouldUpdateTheIndexWhenContentIsUnPublished() {
|
||||
_contentManager.Create<Thing>(ThingDriver.ContentTypeName).Text = "Lorem ipsum";
|
||||
_clock.Advance(TimeSpan.FromSeconds(1));
|
||||
|
||||
_indexNotifier.UpdateIndex(IndexName);
|
||||
Assert.That(_provider.NumDocs(IndexName), Is.EqualTo(1));
|
||||
Assert.That(_logger.LogEntries, Has.Some.Matches<LogEntry>(entry => entry.LogFormat == "Rebuild index started"));
|
||||
_logger.Clear();
|
||||
|
||||
var content = _contentManager.Create<Thing>(ThingDriver.ContentTypeName);
|
||||
content.Text = "Lorem ipsum";
|
||||
_clock.Advance(TimeSpan.FromSeconds(1));
|
||||
|
||||
_indexNotifier.UpdateIndex(IndexName);
|
||||
Assert.That(_provider.NumDocs(IndexName), Is.EqualTo(2));
|
||||
Assert.That(_logger.LogEntries, Has.None.Matches<LogEntry>(entry => entry.LogFormat == "Rebuild index started"));
|
||||
_clock.Advance(TimeSpan.FromSeconds(1));
|
||||
|
||||
_contentManager.Unpublish(content.ContentItem);
|
||||
_clock.Advance(TimeSpan.FromSeconds(1));
|
||||
|
||||
_indexNotifier.UpdateIndex(IndexName);
|
||||
Assert.That(_provider.NumDocs(IndexName), Is.EqualTo(1));
|
||||
Assert.That(_logger.LogEntries, Has.None.Matches<LogEntry>(entry => entry.LogFormat == "Rebuild index started"));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ShouldRemoveFromIndexEvenIfPublishedAndUnpublishedInTheSameSecond() {
|
||||
// This test is to ensure that when a task is created, all previous tasks for the same content item
|
||||
// are also removed, and thus that multiple tasks don't conflict while updating the index
|
||||
|
||||
_contentManager.Create<Thing>(ThingDriver.ContentTypeName).Text = "Lorem ipsum";
|
||||
_clock.Advance(TimeSpan.FromSeconds(1));
|
||||
|
||||
_indexNotifier.UpdateIndex(IndexName);
|
||||
Assert.That(_provider.NumDocs(IndexName), Is.EqualTo(1));
|
||||
Assert.That(_logger.LogEntries, Has.Some.Matches<LogEntry>(entry => entry.LogFormat == "Rebuild index started"));
|
||||
_logger.Clear();
|
||||
|
||||
var content = _contentManager.Create<Thing>(ThingDriver.ContentTypeName);
|
||||
content.Text = "Lorem ipsum";
|
||||
|
||||
_indexNotifier.UpdateIndex(IndexName);
|
||||
Assert.That(_provider.NumDocs(IndexName), Is.EqualTo(2));
|
||||
Assert.That(_logger.LogEntries, Has.None.Matches<LogEntry>(entry => entry.LogFormat == "Rebuild index started"));
|
||||
|
||||
_contentManager.Unpublish(content.ContentItem);
|
||||
|
||||
_indexNotifier.UpdateIndex(IndexName);
|
||||
Assert.That(_provider.NumDocs(IndexName), Is.EqualTo(1));
|
||||
Assert.That(_logger.LogEntries, Has.None.Matches<LogEntry>(entry => entry.LogFormat == "Rebuild index started"));
|
||||
}
|
||||
|
||||
#region Stubs
|
||||
public class ThingHandler : ContentHandler {
|
||||
public ThingHandler() {
|
||||
Filters.Add(new ActivatingFilter<Thing>(ThingDriver.ContentTypeName));
|
||||
Filters.Add(new ActivatingFilter<ContentPart<CommonPartVersionRecord>>(ThingDriver.ContentTypeName));
|
||||
Filters.Add(new ActivatingFilter<CommonPart>(ThingDriver.ContentTypeName));
|
||||
Filters.Add(new ActivatingFilter<BodyPart>(ThingDriver.ContentTypeName));
|
||||
}
|
||||
}
|
||||
|
||||
public class Thing : ContentPart {
|
||||
public string Text {
|
||||
get { return this.As<BodyPart>().Text; }
|
||||
set { this.As<BodyPart>().Text = value; }
|
||||
}
|
||||
}
|
||||
|
||||
public class ThingDriver : ContentPartDriver<Thing> {
|
||||
public static readonly string ContentTypeName = "thing";
|
||||
}
|
||||
|
||||
public class LogEntry {
|
||||
public Exception LogException { get; set; }
|
||||
public string LogFormat { get; set; }
|
||||
public object[] LogArgs { get; set; }
|
||||
public LogLevel LogLevel { get; set; }
|
||||
}
|
||||
|
||||
public class StubLogger : ILogger {
|
||||
public List<LogEntry> LogEntries { get; set; }
|
||||
|
||||
public StubLogger() {
|
||||
LogEntries = new List<LogEntry>();
|
||||
}
|
||||
|
||||
public void Clear() {
|
||||
LogEntries.Clear();
|
||||
}
|
||||
|
||||
public bool IsEnabled(LogLevel level) {
|
||||
return true;
|
||||
}
|
||||
|
||||
public void Log(LogLevel level, Exception exception, string format, params object[] args) {
|
||||
LogEntries.Add(new LogEntry() {
|
||||
LogArgs = args,
|
||||
LogException = exception,
|
||||
LogFormat = format,
|
||||
LogLevel = level
|
||||
});
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -140,6 +140,7 @@
|
||||
<ItemGroup>
|
||||
<Compile Include="CodeGeneration\Commands\CodeGenerationCommandsTests.cs" />
|
||||
<Compile Include="Comments\Services\CommentServiceTests.cs" />
|
||||
<Compile Include="Indexing\IndexingTaskExecutorTests.cs" />
|
||||
<Compile Include="Indexing\LuceneIndexProviderTests.cs" />
|
||||
<Compile Include="Indexing\LuceneSearchBuilderTests.cs" />
|
||||
<Compile Include="Media\Services\MediaServiceTests.cs" />
|
||||
|
||||
@@ -20,7 +20,7 @@
|
||||
|
||||
for (i = 0; i < classes.length; i++) {
|
||||
if (classes[i].indexOf("zone-") === 0) {
|
||||
$(this).append('<div class="zone-name">' + classes[i].substr(classes[i].indexOf("-")+1) + '</div>');
|
||||
$(this).append('<div class="zone-name">' + classes[i].substr(classes[i].indexOf("-") + 1) + '</div>');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -39,5 +39,43 @@
|
||||
_this.prev().show("fast", function () { _this.addClass(open).html("«"); });
|
||||
}
|
||||
});
|
||||
|
||||
$("div.shape-tracing.wrapper").click(function (e) {
|
||||
var _this = $(this);
|
||||
var classes = $(this).attr("class").split(' ');
|
||||
e.stopPropagation();
|
||||
for (i = 0; i < classes.length; i++) {
|
||||
if (classes[i].indexOf("shapeId-") === 0) {
|
||||
var shapeId = classes[i].substr(classes[i].indexOf("-") + 1);
|
||||
$("div.shape-tracing.wrapper").toggleClass('selected', false);
|
||||
_this.toggleClass('selected', true);
|
||||
$("div.shape-tracing.meta").toggle(false);
|
||||
$("div.shape-tracing.meta.shapeId-" + shapeId).toggle(true);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
/* tabs */
|
||||
function bindTab(selector) {
|
||||
$('li' + selector).click(function () {
|
||||
var _this = $(this);
|
||||
|
||||
// toggle the selected class on the tab li
|
||||
_this.parent().children('li').toggleClass('selected', false);
|
||||
_this.toggleClass('selected', true);
|
||||
|
||||
// hide all tabs and display the selected one
|
||||
var wrapper = _this.parent().parent().first();
|
||||
wrapper.children('.content').children().toggle(false);
|
||||
wrapper.children('.content').children('div' + selector).toggle(true);
|
||||
});
|
||||
}
|
||||
|
||||
bindTab('.shape');
|
||||
bindTab('.model');
|
||||
bindTab('.placement');
|
||||
bindTab('.templates');
|
||||
bindTab('.source');
|
||||
bindTab('.html');
|
||||
});
|
||||
})(jQuery);
|
||||
|
||||
@@ -12,9 +12,6 @@
|
||||
position:fixed;
|
||||
top:0;
|
||||
}
|
||||
#debug-control :hover {
|
||||
color:yellow;
|
||||
}
|
||||
#debug-control .debug-active {
|
||||
color:Lime;
|
||||
}
|
||||
@@ -48,30 +45,27 @@
|
||||
min-width:20px;
|
||||
min-height:20px;
|
||||
}
|
||||
.debug-shapes body
|
||||
{
|
||||
|
||||
.debug-shapes body {
|
||||
margin-top:25px;
|
||||
}
|
||||
|
||||
.debug-shapes .shape-tracing .wrapper :hover {
|
||||
background:#E0E9EE;
|
||||
}
|
||||
|
||||
.shape-tracing .meta {
|
||||
display:none;
|
||||
}
|
||||
|
||||
.debug-shapes .shape-tracing .meta {
|
||||
.debug-shapes div.shape-tracing.meta {
|
||||
clear:both;
|
||||
display:block;
|
||||
background:#cfc;
|
||||
background:#ccc;
|
||||
font-size:9pt;
|
||||
font-family:Segoe;
|
||||
color:black;
|
||||
margin-top:5px;
|
||||
position:fixed;
|
||||
left: 0px;
|
||||
bottom: 0px;
|
||||
width:100%;
|
||||
z-index:999;
|
||||
}
|
||||
|
||||
.debug-zones .Zone {
|
||||
.debug-zones .shapeType-Zone {
|
||||
border:1px dashed grey;
|
||||
margin:5px;
|
||||
position: relative;
|
||||
@@ -84,10 +78,6 @@
|
||||
opacity: 0.7;
|
||||
}
|
||||
|
||||
.debug-shapes .Zone :hover {
|
||||
background:#E0E9EE;
|
||||
}
|
||||
|
||||
.meta .shape {
|
||||
color:black;
|
||||
}
|
||||
@@ -98,4 +88,53 @@
|
||||
|
||||
.meta .wrappers {
|
||||
color:green;
|
||||
}
|
||||
}
|
||||
|
||||
.debug-shapes div.wrapper.selected {
|
||||
background-color:#BAFFC3;
|
||||
}
|
||||
|
||||
/* tabs */
|
||||
|
||||
ul.debuggerMenu { margin:8px 0 -8px 0; }
|
||||
|
||||
.debuggerMenu li {
|
||||
color:#4053DE;
|
||||
display: inline;
|
||||
font-size:15px;
|
||||
line-height:32px;
|
||||
}
|
||||
.debuggerMenu li {
|
||||
margin:0 2px;
|
||||
padding: 8px 18px 7px 18px;
|
||||
}
|
||||
.debuggerMenu li.middle, .debuggerMenu li.first, .debuggerMenu li.last {
|
||||
border:1px solid #E4E5E6;
|
||||
border-bottom:none;
|
||||
background-color: #F5F5F5;
|
||||
}
|
||||
.debuggerMenu li.first {
|
||||
margin-left:16px;
|
||||
}
|
||||
.debuggerMenu li.selected {
|
||||
background-color: #fff;
|
||||
}
|
||||
.debuggerMenu li.selected, .debuggerMenu li:hover {
|
||||
background-color: #fff;
|
||||
}
|
||||
.debuggerMenu li.selected a {
|
||||
color: #3A822E;
|
||||
}
|
||||
|
||||
.debug-shapes div.shape-tracing.meta .content {
|
||||
background-color: #fff;
|
||||
padding: 20px;
|
||||
margin:8px 0 0 0;
|
||||
border: 1px solid #e4e5e6;
|
||||
height:10em;
|
||||
overflow:scroll;
|
||||
/*CSS3 properties*/
|
||||
border-radius: 3px;
|
||||
-webkit-border-radius: 3px;
|
||||
-moz-border-radius: 3px;
|
||||
}
|
||||
|
||||
@@ -4,8 +4,7 @@
|
||||
@using System.Xml;
|
||||
|
||||
@{
|
||||
Script.Require("jQuery");
|
||||
Script.Include("tooltip.min.js");
|
||||
Script.Require("jQueryUI_Tabs");
|
||||
Script.Include("orchard-designertools-shapetracing.js");
|
||||
Style.Include("orchard-designertools-shapetracing.css");
|
||||
|
||||
@@ -13,6 +12,7 @@
|
||||
var shapeTable = workContext.Resolve<IShapeTableManager>().GetShapeTable(workContext.CurrentTheme.Id);
|
||||
var descriptor = shapeTable.Descriptors[Model.Metadata.Type];
|
||||
}
|
||||
|
||||
@functions {
|
||||
string FormatShapeFilename(string type, string themeId) {
|
||||
return "~/Themes/" + themeId + "/Views/" + type.Replace("__", "-").Replace("_", ".") + ".cshtml";
|
||||
@@ -28,34 +28,66 @@
|
||||
}
|
||||
}
|
||||
|
||||
<div class="shape-tracing wrapper @Model.Metadata.Type">
|
||||
@Display(Model.Metadata.ChildContent)
|
||||
<div class="shape-tracing wrapper shapeType-@Model.Metadata.Type shapeId-@Model.GetHashCode() " title="@Model.Metadata.Type">
|
||||
@Display(Model.Metadata.ChildContent)
|
||||
|
||||
<div class="shape-tracing meta">
|
||||
<div class="shape">
|
||||
Shape: @Model.Metadata.Type <br />
|
||||
Definition: @descriptor.BindingSource <br />
|
||||
Display Type: @(Model.Metadata.DisplayType ?? "n/a")<br />
|
||||
Position: @(Model.Metadata.Position ?? "n/a") <br />
|
||||
Placement Source: @(Model.Metadata.PlacementSource ?? "n/a") <br />
|
||||
</div>
|
||||
<div class="shape-tracing meta shapeId-@Model.GetHashCode()" style="display:none">
|
||||
<ul class="debuggerMenu">
|
||||
<li class="shape selected first"><a href="#">Shape</a></li>
|
||||
<li class="model middle"><a href="#">Model</a></li>
|
||||
<li class="placement middle"><a href="#">Placement</a></li>
|
||||
<li class="templates middle"><a href="#">Templates</a></li>
|
||||
<li class="source middle"><a href="#">Source</a></li>
|
||||
<li class="html last"><a href="#">HTML</a></li>
|
||||
</ul>
|
||||
|
||||
<div class="alternates">
|
||||
@foreach(var alternate in Model.Metadata.Alternates) {
|
||||
var formatted = @FormatShapeFilename(alternate, workContext.CurrentTheme.Id);
|
||||
<div>@formatted</div>
|
||||
}
|
||||
</div>
|
||||
<div class="wrappers">
|
||||
@foreach(var wrapper in Model.Metadata.Wrappers) {
|
||||
if(wrapper != "ShapeTracing_Wrapper") {
|
||||
var formatted = @FormatShapeFilename(wrapper, workContext.CurrentTheme.Id);
|
||||
<div>@formatted</div>
|
||||
}
|
||||
}
|
||||
</div>
|
||||
<div class="dump" style="display:none">
|
||||
<pre>@DumpObject((object)Model)</pre>
|
||||
</div>
|
||||
</div>
|
||||
<div class="content">
|
||||
<div class="shape">
|
||||
Shape: @Model.Metadata.Type <br />
|
||||
Definition: @descriptor.BindingSource <br />
|
||||
Display Type: @(Model.Metadata.DisplayType ?? "n/a")<br />
|
||||
Position: @(Model.Metadata.Position ?? "n/a") <br />
|
||||
Placement Source: @(Model.Metadata.PlacementSource ?? "n/a") <br />
|
||||
</div>
|
||||
|
||||
<div class="model" style="display:none">
|
||||
<pre>@DumpObject((object)Model)</pre>
|
||||
</div>
|
||||
|
||||
<div class="placement" style="display:none">
|
||||
@if(Model.Metadata.PlacementSource != null) {
|
||||
// System.IO.File.ReadAllText(VirtualPathUtility.GetFileName(Model.Metadata.PlacementSource))
|
||||
}
|
||||
else {
|
||||
<div>n/a</div>
|
||||
}
|
||||
</div>
|
||||
|
||||
<div class="templates" style="display:none">
|
||||
<div class="alternates">
|
||||
<span>Alternates</span>
|
||||
@foreach(var alternate in Model.Metadata.Alternates) {
|
||||
var formatted = @FormatShapeFilename(alternate, workContext.CurrentTheme.Id);
|
||||
<div>@formatted</div>
|
||||
}
|
||||
</div>
|
||||
<div class="wrappers">
|
||||
<span >Wrappers</span>
|
||||
@foreach(var wrapper in Model.Metadata.Wrappers) {
|
||||
if(wrapper != "ShapeTracing_Wrapper") {
|
||||
var formatted = @FormatShapeFilename(wrapper, workContext.CurrentTheme.Id);
|
||||
<div>@formatted</div>
|
||||
}
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="source" style="display:none">
|
||||
</div>
|
||||
|
||||
<div class="html" style="display:none">
|
||||
<pre>@HttpUtility.HtmlEncode(Display(Model.Metadata.ChildContent))</pre>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -23,7 +23,6 @@ namespace Orchard.Indexing.Services {
|
||||
private readonly IIndexingTaskManager _indexingTaskManager;
|
||||
private readonly IContentManager _contentManager;
|
||||
private readonly IIndexSynLock _indexSynLock;
|
||||
private const string SearchIndexName = "Search";
|
||||
|
||||
public IndexingTaskExecutor(
|
||||
IClock clock,
|
||||
@@ -44,7 +43,7 @@ namespace Orchard.Indexing.Services {
|
||||
public ILogger Logger { get; set; }
|
||||
|
||||
public void UpdateIndex(string indexName) {
|
||||
var synLock = _indexSynLock.GetSynLock(SearchIndexName);
|
||||
var synLock = _indexSynLock.GetSynLock(indexName);
|
||||
|
||||
if (!System.Threading.Monitor.TryEnter(synLock)) {
|
||||
Logger.Information("Index was requested but was already running");
|
||||
@@ -63,7 +62,7 @@ namespace Orchard.Indexing.Services {
|
||||
DateTime? lastIndexUtc;
|
||||
|
||||
// Do we need to rebuild the full index (first time module is used, or rebuild index requested) ?
|
||||
if (_indexProvider.IsEmpty(SearchIndexName)) {
|
||||
if (_indexProvider.IsEmpty(indexName)) {
|
||||
Logger.Information("Rebuild index started");
|
||||
|
||||
// mark current last task, as we should process older ones (in case of rebuild index only)
|
||||
@@ -93,10 +92,10 @@ namespace Orchard.Indexing.Services {
|
||||
}
|
||||
else {
|
||||
// retrieve last processed index time
|
||||
lastIndexUtc = _indexProvider.GetLastIndexUtc(SearchIndexName);
|
||||
lastIndexUtc = _indexProvider.GetLastIndexUtc(indexName);
|
||||
}
|
||||
|
||||
_indexProvider.SetLastIndexUtc(SearchIndexName, _clock.UtcNow);
|
||||
_indexProvider.SetLastIndexUtc(indexName, _clock.UtcNow);
|
||||
|
||||
// retrieve not yet processed tasks
|
||||
var taskRecords = lastIndexUtc == null
|
||||
@@ -111,15 +110,15 @@ namespace Orchard.Indexing.Services {
|
||||
|
||||
Logger.Information("Processing {0} indexing tasks", taskRecords.Length);
|
||||
|
||||
if (!_indexProvider.Exists(SearchIndexName)) {
|
||||
_indexProvider.CreateIndex(SearchIndexName);
|
||||
if (!_indexProvider.Exists(indexName)) {
|
||||
_indexProvider.CreateIndex(indexName);
|
||||
}
|
||||
|
||||
// process Delete tasks
|
||||
try {
|
||||
var deleteIds = taskRecords.Where(t => t.Action == IndexingTaskRecord.Delete).Select(t => t.ContentItemRecord.Id).ToArray();
|
||||
if (deleteIds.Length > 0) {
|
||||
_indexProvider.Delete(SearchIndexName, deleteIds);
|
||||
_indexProvider.Delete(indexName, deleteIds);
|
||||
Logger.Information("Deleted content items from index: {0}", String.Join(", ", deleteIds));
|
||||
}
|
||||
}
|
||||
@@ -151,7 +150,7 @@ namespace Orchard.Indexing.Services {
|
||||
|
||||
if (updateIndexDocuments.Count > 0) {
|
||||
try {
|
||||
_indexProvider.Store(SearchIndexName, updateIndexDocuments);
|
||||
_indexProvider.Store(indexName, updateIndexDocuments);
|
||||
Logger.Information("Added content items to index: {0}", String.Join(", ", addedContentItemIds));
|
||||
}
|
||||
catch (Exception ex) {
|
||||
|
||||
@@ -68,6 +68,8 @@ namespace Orchard.Indexing.Services {
|
||||
foreach (var task in tasks) {
|
||||
_repository.Delete(task);
|
||||
}
|
||||
|
||||
_repository.Flush();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -40,8 +40,7 @@ namespace Orchard.Indexing.Settings {
|
||||
/// </summary>
|
||||
private void CreateIndexingTasks()
|
||||
{
|
||||
if (!_tasksCreated)
|
||||
{
|
||||
if (!_tasksCreated) {
|
||||
CreateTasksForType(_contentTypeName);
|
||||
_tasksCreated = true;
|
||||
}
|
||||
|
||||
@@ -58,9 +58,10 @@
|
||||
<Reference Include="System.Xml.Linq" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Content Include="Content\synchronizing.gif" />
|
||||
<Content Include="Scripts\MediaBrowser.js" />
|
||||
<Content Include="Scripts\MediaPicker.js" />
|
||||
<Content Include="Styles\images\imagepreview.png" />
|
||||
<Content Include="Styles\images\synchronizing.gif" />
|
||||
<Content Include="Styles\mediapicker.css" />
|
||||
<Content Include="Views\Web.config" />
|
||||
<Content Include="Scripts\Web.config" />
|
||||
|
||||
@@ -5,18 +5,6 @@
|
||||
|
||||
$.extend({
|
||||
mediaPicker: {
|
||||
init: function (data) {
|
||||
// called by the opener to initiate dialog with existing image data.
|
||||
// todo: data.img may contain existing image data, populate the fields
|
||||
var img = data.img;
|
||||
if (img) {
|
||||
for (var name in img) {
|
||||
$("#img-" + name).val(img[name]);
|
||||
}
|
||||
suppressResize = true;
|
||||
$("#img-src").trigger("change");
|
||||
}
|
||||
},
|
||||
uploadMedia: uploadMedia,
|
||||
scalePreview: scalePreview
|
||||
}
|
||||
@@ -113,10 +101,14 @@
|
||||
});
|
||||
}
|
||||
|
||||
var data = window.mediaPickerData;
|
||||
if (data) {
|
||||
window.mediaPickerData = null;
|
||||
$.mediaPicker.init(data);
|
||||
var data = window.opener.jQuery[query("callback")].data,
|
||||
img = data ? data.img : null;
|
||||
if (img) {
|
||||
for (var name in img) {
|
||||
$("#img-" + name).val(img[name]);
|
||||
}
|
||||
suppressResize = true;
|
||||
$("#img-src").trigger("change");
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
@@ -12,6 +12,7 @@
|
||||
delete $[callbackName];
|
||||
data.callback(returnData);
|
||||
};
|
||||
$[callbackName].data = data;
|
||||
|
||||
var adminIndex = location.href.toLowerCase().indexOf("/admin/");
|
||||
if (adminIndex === -1) return;
|
||||
@@ -21,11 +22,5 @@
|
||||
+ "&editmode=" + (!!(data.img && data.img.src))
|
||||
+ "&" + (new Date() - 0);
|
||||
var w = window.open(url, "_blank", data.windowFeatures || "width=685,height=540,status=no,toolbar=no,location=no,menubar=no,resizable=no");
|
||||
if (w.jQuery && w.jQuery.mediaPicker) {
|
||||
w.jQuery.mediaPicker.init(data);
|
||||
}
|
||||
else {
|
||||
w.mediaPickerData = data;
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 5.3 KiB |
|
Before Width: | Height: | Size: 1.2 KiB After Width: | Height: | Size: 1.2 KiB |
@@ -13,7 +13,7 @@ body {
|
||||
|
||||
#orchardmediapicker * { font:100% normal Segoe UI,Trebuchet,Arial,Sans-Serif; }
|
||||
|
||||
#orchardmediapicker #tabs { padding: 0 0 24px 0; }
|
||||
#orchardmediapicker #tabs { padding: 0 0 12px 0; }
|
||||
|
||||
#orchardmediapicker a, #orchardmediapicker a:link, #orchardmediapicker a:visited, #orchardmediapicker a:hover, #orchardmediapicker a:active, #orchardmediapicker a:focus {
|
||||
color:#1e5d7d;
|
||||
@@ -111,9 +111,11 @@ fieldset { padding:0; margin: 0; border: 0px solid #dbdbdb; }
|
||||
#orchardmediapicker label.forcheckbox { display: inline; margin: 10px 0 0 4px; }
|
||||
#orchardmediapicker .actions { margin:12px 0 0 0; clear:both; text-align:left; }
|
||||
|
||||
#orchardmediapicker .text-box {width:90%;}
|
||||
|
||||
.image-width, .image-height {float:left; width:90px;}
|
||||
|
||||
.image-width .text-box, .image-height .text-box {width:60%;}
|
||||
#orchardmediapicker .image-width .text-box, #orchardmediapicker .image-height .text-box, #orchardmediapicker #folderName {width:60%;}
|
||||
|
||||
|
||||
.clearboth {
|
||||
|
||||
@@ -27,8 +27,8 @@
|
||||
</p>
|
||||
<fieldset>
|
||||
<input id="__requesttoken" type="hidden" value="@Html.AntiForgeryTokenValueOrchard()" />
|
||||
<input id="folderName" type="text" />
|
||||
<input type="button" id="createFolder" value="@T("Create Folder")" />
|
||||
<input class="text-box" id="folderName" type="text" />
|
||||
<input class="button" type="button" id="createFolder" value="@T("Create Folder")" />
|
||||
</fieldset>
|
||||
</div>
|
||||
<fieldset>
|
||||
@@ -66,11 +66,10 @@
|
||||
<input type="hidden" name="MediaPath" value="@Model.MediaPath" />
|
||||
<label for="fileUpload">@T("Upload an image from your computer")</label>
|
||||
<input type="file" name="fileUpload" id="fileUpload" />
|
||||
<input type="submit" id="upload" value="Upload" />
|
||||
<input class="button" type="submit" id="upload" value="Upload" />
|
||||
}
|
||||
}
|
||||
<img id="lib-indicator" src="@Url.Content("~/modules/orchard.mediapicker/content/synchronizing.gif")" alt="" class="throbber" />
|
||||
|
||||
<img id="img-indicator" src="@Url.Content("~/modules/orchard.mediapicker/Styles/images/synchronizing.gif")" alt="" class="throbber" />
|
||||
|
||||
</fieldset>
|
||||
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
<div id="image-preview">
|
||||
<img alt="" id="img-loader" style="display:none" src="" />
|
||||
<div class="media-largepreview">
|
||||
<img alt="@T("Preview of Image")" id="img-preview" src="TODO: some placeholder image" onload="jQuery.mediaPicker.scalePreview(this)" />
|
||||
<img alt="@T("Preview of Image")" id="img-preview" src="@Url.Content("~/modules/orchard.mediapicker/Styles/images/imagepreview.png")" onload="jQuery.mediaPicker.scalePreview(this)" />
|
||||
</div>
|
||||
<div>
|
||||
<label for="img-src">@T("URL for the image resource")</label>
|
||||
@@ -26,9 +26,9 @@
|
||||
<input type="hidden" name="MediaPath" value="@mediaPath" />
|
||||
<label for="fileUpload">@T("Upload an image from your computer")</label>
|
||||
<input type="file" name="fileUpload" id="fileUpload" />
|
||||
<input type="submit" id="upload" value="Upload" />
|
||||
<input class="button" type="submit" id="upload" value="Upload" />
|
||||
}
|
||||
<img id="img-indicator" src="@Url.Content("~/modules/orchard.mediapicker/content/synchronizing.gif")" alt="" class="throbber" />
|
||||
<img id="img-indicator" src="@Url.Content("~/modules/orchard.mediapicker/Styles/images/synchronizing.gif")" alt="" class="throbber" />
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
@@ -49,7 +49,7 @@ namespace Orchard.ContentManagement {
|
||||
itemShape.Metadata.DisplayType = actualDisplayType;
|
||||
|
||||
var context = new BuildDisplayContext(itemShape, content, actualDisplayType, _shapeFactory);
|
||||
BindPlacement(context, actualDisplayType);
|
||||
BindPlacement(context, actualDisplayType, stereotype);
|
||||
|
||||
_handlers.Value.Invoke(handler => handler.BuildDisplay(context), Logger);
|
||||
return context.Shape;
|
||||
@@ -67,7 +67,7 @@ namespace Orchard.ContentManagement {
|
||||
itemShape.ContentItem = content.ContentItem;
|
||||
|
||||
var context = new BuildEditorContext(itemShape, content, groupInfoId, _shapeFactory);
|
||||
BindPlacement(context, null);
|
||||
BindPlacement(context, null, stereotype);
|
||||
|
||||
_handlers.Value.Invoke(handler => handler.BuildEditor(context), Logger);
|
||||
|
||||
@@ -86,7 +86,7 @@ namespace Orchard.ContentManagement {
|
||||
itemShape.ContentItem = content.ContentItem;
|
||||
|
||||
var context = new UpdateEditorContext(itemShape, content, updater, groupInfoId, _shapeFactory);
|
||||
BindPlacement(context, null);
|
||||
BindPlacement(context, null, stereotype);
|
||||
|
||||
_handlers.Value.Invoke(handler => handler.UpdateEditor(context), Logger);
|
||||
|
||||
@@ -98,7 +98,7 @@ namespace Orchard.ContentManagement {
|
||||
return _shapeFactory.Create(actualShapeType, Arguments.Empty(), new[] { zoneHoldingBehavior });
|
||||
}
|
||||
|
||||
private void BindPlacement(BuildShapeContext context, string displayType) {
|
||||
private void BindPlacement(BuildShapeContext context, string displayType, string stereotype) {
|
||||
context.FindPlacement = (partShapeType, differentiator, defaultLocation) => {
|
||||
|
||||
var theme = _themeService.Value.GetRequestTheme(_requestContext);
|
||||
@@ -109,6 +109,7 @@ namespace Orchard.ContentManagement {
|
||||
if (shapeTable.Descriptors.TryGetValue(partShapeType, out descriptor)) {
|
||||
var placementContext = new ShapePlacementContext {
|
||||
ContentType = context.ContentItem.ContentType,
|
||||
Stereotype = stereotype,
|
||||
DisplayType = displayType,
|
||||
Differentiator = differentiator,
|
||||
Path = VirtualPathUtility.AppendTrailingSlash(VirtualPathUtility.ToAppRelative(request.Path)) // get the current app-relative path, i.e. ~/my-blog/foo
|
||||
|
||||
@@ -109,6 +109,7 @@ namespace Orchard.DisplayManagement.Descriptors {
|
||||
|
||||
public class ShapePlacementContext {
|
||||
public string ContentType { get; set; }
|
||||
public string Stereotype { get; set; }
|
||||
public string DisplayType { get; set; }
|
||||
public string Differentiator { get; set; }
|
||||
public string Path { get; set; }
|
||||
|
||||
@@ -127,9 +127,9 @@ namespace Orchard.DisplayManagement.Descriptors.ShapePlacementStrategy {
|
||||
case "ContentType":
|
||||
if (expression.EndsWith("*")) {
|
||||
var prefix = expression.Substring(0, expression.Length - 1);
|
||||
return ctx => (ctx.ContentType ?? "").StartsWith(prefix) && predicate(ctx);
|
||||
return ctx => ((ctx.ContentType ?? "").StartsWith(prefix) || (ctx.Stereotype ?? "").StartsWith(prefix)) && predicate(ctx);
|
||||
}
|
||||
return ctx => (ctx.ContentType == expression) && predicate(ctx);
|
||||
return ctx => ((ctx.ContentType == expression) || (ctx.Stereotype == expression)) && predicate(ctx);
|
||||
case "DisplayType":
|
||||
if (expression.EndsWith("*")) {
|
||||
var prefix = expression.Substring(0, expression.Length - 1);
|
||||
|
||||
Reference in New Issue
Block a user