From 94d5703816e397e9416b9399c267658f05564d58 Mon Sep 17 00:00:00 2001 From: Sebastien Ros Date: Thu, 24 Feb 2011 16:58:18 -0800 Subject: [PATCH 1/5] Fixing an issue on indexing - When Creating and Removing a content in the same transaction, previous tasks were not removed as Flush was not called on the repository - Adding unit tests to IndexingTaskExecutor --HG-- branch : dev --- .../Indexing/IndexingTaskExecutorTests.cs | 306 ++++++++++++++++++ .../Orchard.Tests.Modules.csproj | 1 + .../Services/IndexingTaskExecutor.cs | 17 +- .../Services/IndexingTaskManager.cs | 2 + .../Orchard.Indexing/Settings/EditorEvents.cs | 3 +- 5 files changed, 318 insertions(+), 11 deletions(-) create mode 100644 src/Orchard.Tests.Modules/Indexing/IndexingTaskExecutorTests.cs diff --git a/src/Orchard.Tests.Modules/Indexing/IndexingTaskExecutorTests.cs b/src/Orchard.Tests.Modules/Indexing/IndexingTaskExecutorTests.cs new file mode 100644 index 000000000..664b10f06 --- /dev/null +++ b/src/Orchard.Tests.Modules/Indexing/IndexingTaskExecutorTests.cs @@ -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 _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(); + _appDataFolder = AppDataFolderTests.CreateAppDataFolder(_basePath); + + builder.RegisterType().As(); + builder.RegisterInstance(_appDataFolder).As(); + builder.RegisterType().As(); + builder.RegisterType().As(); + builder.RegisterType().As(); + builder.RegisterType().As(); + builder.RegisterType().As(); + builder.RegisterType().As(); + builder.RegisterInstance(_contentDefinitionManager.Object); + builder.RegisterInstance(new Mock().Object); + + builder.RegisterType().As(); + builder.RegisterInstance(new Mock().Object); + builder.RegisterInstance(new Mock().Object); + builder.RegisterType().As(); + + builder.RegisterType().As(); + builder.RegisterType().As(); + + builder.RegisterType().As(); + builder.RegisterType().As(); + builder.RegisterType().As(); + + // setting up a ShellSettings instance + _shellSettings = new ShellSettings { Name = "My Site" }; + builder.RegisterInstance(_shellSettings).As(); + } + + protected override IEnumerable 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(); + _indexNotifier = _container.Resolve(); + _contentManager = _container.Resolve(); + ((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(entry => entry.LogFormat == "Rebuild index started")); + Assert.That(_logger.LogEntries, Has.Some.Matches(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(entry => entry.LogFormat == "Rebuild index started")); + Assert.That(_logger.LogEntries, Has.Some.Matches(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(entry => entry.LogFormat == "Rebuild index started")); + Assert.That(_logger.LogEntries, Has.Some.Matches(entry => entry.LogFormat == "Index update requested, nothing to do")); + } + + [Test] + public void ShouldIndexContentIfSettingsIsSetAndHandlerIsProvided() { + var content = _contentManager.Create(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(entry => entry.LogFormat == "Rebuild index started")); + Assert.That(_logger.LogEntries, Has.Some.Matches(entry => entry.LogFormat == "Processing {0} indexing tasks")); + Assert.That(_logger.LogEntries, Has.Some.Matches(entry => entry.LogFormat == "Added content items to index: {0}")); + } + + [Test] + public void ShouldUpdateTheIndexWhenContentIsPublished() { + _contentManager.Create(ThingDriver.ContentTypeName).Text = "Lorem ipsum"; + _indexNotifier.UpdateIndex(IndexName); + Assert.That(_provider.NumDocs(IndexName), Is.EqualTo(1)); + Assert.That(_logger.LogEntries, Has.Some.Matches(entry => entry.LogFormat == "Rebuild index started")); + _logger.Clear(); + + _contentManager.Create(ThingDriver.ContentTypeName).Text = "Lorem ipsum"; + _indexNotifier.UpdateIndex(IndexName); + Assert.That(_provider.NumDocs(IndexName), Is.EqualTo(2)); + Assert.That(_logger.LogEntries, Has.None.Matches(entry => entry.LogFormat == "Rebuild index started")); + } + + [Test] + public void ShouldUpdateTheIndexWhenContentIsUnPublished() { + _contentManager.Create(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(entry => entry.LogFormat == "Rebuild index started")); + _logger.Clear(); + + var content = _contentManager.Create(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(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(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(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(entry => entry.LogFormat == "Rebuild index started")); + _logger.Clear(); + + var content = _contentManager.Create(ThingDriver.ContentTypeName); + content.Text = "Lorem ipsum"; + + _indexNotifier.UpdateIndex(IndexName); + Assert.That(_provider.NumDocs(IndexName), Is.EqualTo(2)); + Assert.That(_logger.LogEntries, Has.None.Matches(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(entry => entry.LogFormat == "Rebuild index started")); + } + + #region Stubs + public class ThingHandler : ContentHandler { + public ThingHandler() { + Filters.Add(new ActivatingFilter(ThingDriver.ContentTypeName)); + Filters.Add(new ActivatingFilter>(ThingDriver.ContentTypeName)); + Filters.Add(new ActivatingFilter(ThingDriver.ContentTypeName)); + Filters.Add(new ActivatingFilter(ThingDriver.ContentTypeName)); + } + } + + public class Thing : ContentPart { + public string Text { + get { return this.As().Text; } + set { this.As().Text = value; } + } + } + + public class ThingDriver : ContentPartDriver { + 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 LogEntries { get; set; } + + public StubLogger() { + LogEntries = new List(); + } + + 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 + } +} diff --git a/src/Orchard.Tests.Modules/Orchard.Tests.Modules.csproj b/src/Orchard.Tests.Modules/Orchard.Tests.Modules.csproj index 2ac699418..3c39d5ebc 100644 --- a/src/Orchard.Tests.Modules/Orchard.Tests.Modules.csproj +++ b/src/Orchard.Tests.Modules/Orchard.Tests.Modules.csproj @@ -140,6 +140,7 @@ + diff --git a/src/Orchard.Web/Modules/Orchard.Indexing/Services/IndexingTaskExecutor.cs b/src/Orchard.Web/Modules/Orchard.Indexing/Services/IndexingTaskExecutor.cs index e3dc20952..3747fe997 100644 --- a/src/Orchard.Web/Modules/Orchard.Indexing/Services/IndexingTaskExecutor.cs +++ b/src/Orchard.Web/Modules/Orchard.Indexing/Services/IndexingTaskExecutor.cs @@ -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) { diff --git a/src/Orchard.Web/Modules/Orchard.Indexing/Services/IndexingTaskManager.cs b/src/Orchard.Web/Modules/Orchard.Indexing/Services/IndexingTaskManager.cs index 75c30f699..ccb4ff385 100644 --- a/src/Orchard.Web/Modules/Orchard.Indexing/Services/IndexingTaskManager.cs +++ b/src/Orchard.Web/Modules/Orchard.Indexing/Services/IndexingTaskManager.cs @@ -68,6 +68,8 @@ namespace Orchard.Indexing.Services { foreach (var task in tasks) { _repository.Delete(task); } + + _repository.Flush(); } } } diff --git a/src/Orchard.Web/Modules/Orchard.Indexing/Settings/EditorEvents.cs b/src/Orchard.Web/Modules/Orchard.Indexing/Settings/EditorEvents.cs index 9c5f99fea..69f37315a 100644 --- a/src/Orchard.Web/Modules/Orchard.Indexing/Settings/EditorEvents.cs +++ b/src/Orchard.Web/Modules/Orchard.Indexing/Settings/EditorEvents.cs @@ -40,8 +40,7 @@ namespace Orchard.Indexing.Settings { /// private void CreateIndexingTasks() { - if (!_tasksCreated) - { + if (!_tasksCreated) { CreateTasksForType(_contentTypeName); _tasksCreated = true; } From 455df51df96aa89537fa328ca5692f32829c8c97 Mon Sep 17 00:00:00 2001 From: Sebastien Ros Date: Thu, 24 Feb 2011 16:59:01 -0800 Subject: [PATCH 2/5] Updating shape tracing UI - Tabs view - Sticky shape selection --HG-- branch : dev --- .../orchard-designertools-shapetracing.js | 40 +++++++- .../orchard-designertools-shapetracing.css | 81 +++++++++++----- .../Views/ShapeTracing.Wrapper.cshtml | 92 +++++++++++++------ 3 files changed, 161 insertions(+), 52 deletions(-) diff --git a/src/Orchard.Web/Modules/Orchard.DesignerTools/Scripts/orchard-designertools-shapetracing.js b/src/Orchard.Web/Modules/Orchard.DesignerTools/Scripts/orchard-designertools-shapetracing.js index 2d44b3e9d..ee36b1f5d 100644 --- a/src/Orchard.Web/Modules/Orchard.DesignerTools/Scripts/orchard-designertools-shapetracing.js +++ b/src/Orchard.Web/Modules/Orchard.DesignerTools/Scripts/orchard-designertools-shapetracing.js @@ -20,7 +20,7 @@ for (i = 0; i < classes.length; i++) { if (classes[i].indexOf("zone-") === 0) { - $(this).append('
' + classes[i].substr(classes[i].indexOf("-")+1) + '
'); + $(this).append('
' + classes[i].substr(classes[i].indexOf("-") + 1) + '
'); } } @@ -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); diff --git a/src/Orchard.Web/Modules/Orchard.DesignerTools/Styles/orchard-designertools-shapetracing.css b/src/Orchard.Web/Modules/Orchard.DesignerTools/Styles/orchard-designertools-shapetracing.css index 1f66e6c95..91168a8a9 100644 --- a/src/Orchard.Web/Modules/Orchard.DesignerTools/Styles/orchard-designertools-shapetracing.css +++ b/src/Orchard.Web/Modules/Orchard.DesignerTools/Styles/orchard-designertools-shapetracing.css @@ -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; -} \ No newline at end of file +} + +.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; +} diff --git a/src/Orchard.Web/Modules/Orchard.DesignerTools/Views/ShapeTracing.Wrapper.cshtml b/src/Orchard.Web/Modules/Orchard.DesignerTools/Views/ShapeTracing.Wrapper.cshtml index 761b1face..2ac94ade1 100644 --- a/src/Orchard.Web/Modules/Orchard.DesignerTools/Views/ShapeTracing.Wrapper.cshtml +++ b/src/Orchard.Web/Modules/Orchard.DesignerTools/Views/ShapeTracing.Wrapper.cshtml @@ -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().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 @@ } } -
- @Display(Model.Metadata.ChildContent) +
+ @Display(Model.Metadata.ChildContent) -
-
- Shape: @Model.Metadata.Type
- Definition: @descriptor.BindingSource
- Display Type: @(Model.Metadata.DisplayType ?? "n/a")
- Position: @(Model.Metadata.Position ?? "n/a")
- Placement Source: @(Model.Metadata.PlacementSource ?? "n/a")
-
+ +
+
+ Shape: @Model.Metadata.Type
+ Definition: @descriptor.BindingSource
+ Display Type: @(Model.Metadata.DisplayType ?? "n/a")
+ Position: @(Model.Metadata.Position ?? "n/a")
+ Placement Source: @(Model.Metadata.PlacementSource ?? "n/a")
+
+ + + + + + + + + + +
+
From ad786fc53382b5b2746687de060d01a445b42a16 Mon Sep 17 00:00:00 2001 From: Sebastien Ros Date: Thu, 24 Feb 2011 17:28:07 -0800 Subject: [PATCH 3/5] Allowing placement files to Match the content type on the stereotype too --HG-- branch : dev --- src/Orchard/ContentManagement/DefaultContentDisplay.cs | 9 +++++---- .../Descriptors/ShapeAlterationBuilder.cs | 1 + .../ShapePlacementParsingStrategy.cs | 4 ++-- 3 files changed, 8 insertions(+), 6 deletions(-) diff --git a/src/Orchard/ContentManagement/DefaultContentDisplay.cs b/src/Orchard/ContentManagement/DefaultContentDisplay.cs index f71b80ae5..a74321789 100644 --- a/src/Orchard/ContentManagement/DefaultContentDisplay.cs +++ b/src/Orchard/ContentManagement/DefaultContentDisplay.cs @@ -48,7 +48,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; @@ -66,7 +66,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); @@ -85,7 +85,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); @@ -97,7 +97,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); @@ -108,6 +108,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 diff --git a/src/Orchard/DisplayManagement/Descriptors/ShapeAlterationBuilder.cs b/src/Orchard/DisplayManagement/Descriptors/ShapeAlterationBuilder.cs index eff4fb95b..ab4040a98 100644 --- a/src/Orchard/DisplayManagement/Descriptors/ShapeAlterationBuilder.cs +++ b/src/Orchard/DisplayManagement/Descriptors/ShapeAlterationBuilder.cs @@ -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; } diff --git a/src/Orchard/DisplayManagement/Descriptors/ShapePlacementStrategy/ShapePlacementParsingStrategy.cs b/src/Orchard/DisplayManagement/Descriptors/ShapePlacementStrategy/ShapePlacementParsingStrategy.cs index a9fdeaff0..fb077d712 100644 --- a/src/Orchard/DisplayManagement/Descriptors/ShapePlacementStrategy/ShapePlacementParsingStrategy.cs +++ b/src/Orchard/DisplayManagement/Descriptors/ShapePlacementStrategy/ShapePlacementParsingStrategy.cs @@ -98,9 +98,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); From 1e78caba4dd2151861601329aae311de6330585a Mon Sep 17 00:00:00 2001 From: Dave Reed Date: Fri, 25 Feb 2011 12:56:29 -0800 Subject: [PATCH 4/5] Working around an IE9 bug wrt accessing the window of a popup. New code is cleaner anyway. --HG-- branch : dev --- .../Scripts/MediaBrowser.js | 24 +++++++------------ .../Scripts/MediaPicker.js | 7 +----- 2 files changed, 9 insertions(+), 22 deletions(-) diff --git a/src/Orchard.Web/Modules/Orchard.MediaPicker/Scripts/MediaBrowser.js b/src/Orchard.Web/Modules/Orchard.MediaPicker/Scripts/MediaBrowser.js index e442fa9e2..9043420df 100644 --- a/src/Orchard.Web/Modules/Orchard.MediaPicker/Scripts/MediaBrowser.js +++ b/src/Orchard.Web/Modules/Orchard.MediaPicker/Scripts/MediaBrowser.js @@ -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"); } }); diff --git a/src/Orchard.Web/Modules/Orchard.MediaPicker/Scripts/MediaPicker.js b/src/Orchard.Web/Modules/Orchard.MediaPicker/Scripts/MediaPicker.js index 493fc7139..90a4b72db 100644 --- a/src/Orchard.Web/Modules/Orchard.MediaPicker/Scripts/MediaPicker.js +++ b/src/Orchard.Web/Modules/Orchard.MediaPicker/Scripts/MediaPicker.js @@ -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; - } }); }); From ba5d831a97436e99f5d79e51e070e922eeeae925 Mon Sep 17 00:00:00 2001 From: jowall Date: Fri, 25 Feb 2011 13:02:52 -0800 Subject: [PATCH 5/5] Added preview image to media picker and updated CSS. --HG-- branch : dev --- .../Orchard.MediaPicker.csproj | 3 ++- .../Styles/images/imagepreview.png | Bin 0 -> 5412 bytes .../{Content => Styles/images}/synchronizing.gif | Bin .../Orchard.MediaPicker/Styles/mediapicker.css | 6 ++++-- .../Views/Admin/Tab_Gallery.cshtml | 9 ++++----- .../Views/Admin/Tab_Url.cshtml | 6 +++--- 6 files changed, 13 insertions(+), 11 deletions(-) create mode 100644 src/Orchard.Web/Modules/Orchard.MediaPicker/Styles/images/imagepreview.png rename src/Orchard.Web/Modules/Orchard.MediaPicker/{Content => Styles/images}/synchronizing.gif (100%) diff --git a/src/Orchard.Web/Modules/Orchard.MediaPicker/Orchard.MediaPicker.csproj b/src/Orchard.Web/Modules/Orchard.MediaPicker/Orchard.MediaPicker.csproj index 67d25862f..a9afb457c 100644 --- a/src/Orchard.Web/Modules/Orchard.MediaPicker/Orchard.MediaPicker.csproj +++ b/src/Orchard.Web/Modules/Orchard.MediaPicker/Orchard.MediaPicker.csproj @@ -58,9 +58,10 @@ - + + diff --git a/src/Orchard.Web/Modules/Orchard.MediaPicker/Styles/images/imagepreview.png b/src/Orchard.Web/Modules/Orchard.MediaPicker/Styles/images/imagepreview.png new file mode 100644 index 0000000000000000000000000000000000000000..27b08034362d9342d42687bcf3a3f56cebf59ebb GIT binary patch literal 5412 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 literal 0 HcmV?d00001 diff --git a/src/Orchard.Web/Modules/Orchard.MediaPicker/Content/synchronizing.gif b/src/Orchard.Web/Modules/Orchard.MediaPicker/Styles/images/synchronizing.gif similarity index 100% rename from src/Orchard.Web/Modules/Orchard.MediaPicker/Content/synchronizing.gif rename to src/Orchard.Web/Modules/Orchard.MediaPicker/Styles/images/synchronizing.gif diff --git a/src/Orchard.Web/Modules/Orchard.MediaPicker/Styles/mediapicker.css b/src/Orchard.Web/Modules/Orchard.MediaPicker/Styles/mediapicker.css index db7f096dc..6a13c5043 100644 --- a/src/Orchard.Web/Modules/Orchard.MediaPicker/Styles/mediapicker.css +++ b/src/Orchard.Web/Modules/Orchard.MediaPicker/Styles/mediapicker.css @@ -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 { diff --git a/src/Orchard.Web/Modules/Orchard.MediaPicker/Views/Admin/Tab_Gallery.cshtml b/src/Orchard.Web/Modules/Orchard.MediaPicker/Views/Admin/Tab_Gallery.cshtml index b456a51c4..75f0c505a 100644 --- a/src/Orchard.Web/Modules/Orchard.MediaPicker/Views/Admin/Tab_Gallery.cshtml +++ b/src/Orchard.Web/Modules/Orchard.MediaPicker/Views/Admin/Tab_Gallery.cshtml @@ -27,8 +27,8 @@

- - + +
@@ -66,11 +66,10 @@ - + } } - - +
diff --git a/src/Orchard.Web/Modules/Orchard.MediaPicker/Views/Admin/Tab_Url.cshtml b/src/Orchard.Web/Modules/Orchard.MediaPicker/Views/Admin/Tab_Url.cshtml index 5840aeb70..ae32d3d06 100644 --- a/src/Orchard.Web/Modules/Orchard.MediaPicker/Views/Admin/Tab_Url.cshtml +++ b/src/Orchard.Web/Modules/Orchard.MediaPicker/Views/Admin/Tab_Url.cshtml @@ -15,7 +15,7 @@
- @T( + @T(
@@ -26,9 +26,9 @@ - + } - +