diff --git a/build/NuSpecs/UmbracoCms.nuspec b/build/NuSpecs/UmbracoCms.nuspec index 4a587e8a07..4a56a6c9cf 100644 --- a/build/NuSpecs/UmbracoCms.nuspec +++ b/build/NuSpecs/UmbracoCms.nuspec @@ -17,7 +17,7 @@ - + diff --git a/src/Umbraco.Core/Services/ContentTypeService.cs b/src/Umbraco.Core/Services/ContentTypeService.cs index 0d088deb6b..1b6735153d 100644 --- a/src/Umbraco.Core/Services/ContentTypeService.cs +++ b/src/Umbraco.Core/Services/ContentTypeService.cs @@ -19,14 +19,15 @@ namespace Umbraco.Core.Services /// public class ContentTypeService : ContentTypeServiceBase, IContentTypeService { - private readonly IContentService _contentService; + private readonly IContentService _contentService; private readonly IMediaService _mediaService; //Support recursive locks because some of the methods that require locking call other methods that require locking. //for example, the Move method needs to be locked but this calls the Save method which also needs to be locked. private static readonly ReaderWriterLockSlim Locker = new ReaderWriterLockSlim(LockRecursionPolicy.SupportsRecursion); - public ContentTypeService(IDatabaseUnitOfWorkProvider provider, RepositoryFactory repositoryFactory, ILogger logger, IEventMessagesFactory eventMessagesFactory, IContentService contentService, IMediaService mediaService) + public ContentTypeService(IDatabaseUnitOfWorkProvider provider, RepositoryFactory repositoryFactory, ILogger logger, IEventMessagesFactory eventMessagesFactory, IContentService contentService, + IMediaService mediaService) : base(provider, repositoryFactory, logger, eventMessagesFactory) { if (contentService == null) throw new ArgumentNullException("contentService"); @@ -210,7 +211,7 @@ namespace Umbraco.Core.Services public IEnumerable GetMediaTypeContainers(IMediaType mediaType) { - var ancestorIds = mediaType.Path.Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries) + var ancestorIds = mediaType.Path.Split(new[] {','}, StringSplitOptions.RemoveEmptyEntries) .Select(x => { var asInt = x.TryConvertTo(); @@ -425,7 +426,7 @@ namespace Umbraco.Core.Services clone.Name = name; - var compositionAliases = clone.CompositionAliases().Except(new[] { alias }).ToList(); + var compositionAliases = clone.CompositionAliases().Except(new[] {alias}).ToList(); //remove all composition that is not it's current alias foreach (var a in compositionAliases) { @@ -712,7 +713,7 @@ namespace Umbraco.Core.Services var comparer = new DelegateEqualityComparer((x, y) => x.Id == y.Id, x => x.Id); var dependencies = new HashSet(compositions, comparer); var stack = new Stack(); - indirectReferences.ForEach(stack.Push);//Push indirect references to a stack, so we can add recursively + indirectReferences.ForEach(stack.Push); //Push indirect references to a stack, so we can add recursively while (stack.Count > 0) { var indirectReference = stack.Pop(); @@ -770,6 +771,8 @@ namespace Umbraco.Core.Services ValidateLocked(contentType); // throws if invalid contentType.CreatorId = userId; + if (contentType.Description == string.Empty) + contentType.Description = null; repository.AddOrUpdate(contentType); uow.Commit(); @@ -812,6 +815,8 @@ namespace Umbraco.Core.Services foreach (var contentType in asArray) { contentType.CreatorId = userId; + if (contentType.Description == string.Empty) + contentType.Description = null; repository.AddOrUpdate(contentType); } @@ -1233,6 +1238,8 @@ namespace Umbraco.Core.Services ValidateLocked(mediaType); // throws if invalid mediaType.CreatorId = userId; + if (mediaType.Description == string.Empty) + mediaType.Description = null; repository.AddOrUpdate(mediaType); uow.Commit(); @@ -1274,6 +1281,8 @@ namespace Umbraco.Core.Services foreach (var mediaType in asArray) { mediaType.CreatorId = userId; + if (mediaType.Description == string.Empty) + mediaType.Description = null; repository.AddOrUpdate(mediaType); } @@ -1452,40 +1461,40 @@ namespace Umbraco.Core.Services /// public static event TypedEventHandler> DeletingContentType; - /// - /// Occurs after Delete - /// - public static event TypedEventHandler> DeletedContentType; + /// + /// Occurs after Delete + /// + public static event TypedEventHandler> DeletedContentType; - /// - /// Occurs before Delete - /// - public static event TypedEventHandler> DeletingMediaType; + /// + /// Occurs before Delete + /// + public static event TypedEventHandler> DeletingMediaType; - /// - /// Occurs after Delete - /// - public static event TypedEventHandler> DeletedMediaType; + /// + /// Occurs after Delete + /// + public static event TypedEventHandler> DeletedMediaType; /// /// Occurs before Save /// - public static event TypedEventHandler> SavingContentType; + public static event TypedEventHandler> SavingContentType; /// /// Occurs after Save /// - public static event TypedEventHandler> SavedContentType; + public static event TypedEventHandler> SavedContentType; - /// - /// Occurs before Save - /// - public static event TypedEventHandler> SavingMediaType; + /// + /// Occurs before Save + /// + public static event TypedEventHandler> SavingMediaType; - /// - /// Occurs after Save - /// - public static event TypedEventHandler> SavedMediaType; + /// + /// Occurs after Save + /// + public static event TypedEventHandler> SavedMediaType; /// /// Occurs before Move diff --git a/src/Umbraco.Core/Services/MemberTypeService.cs b/src/Umbraco.Core/Services/MemberTypeService.cs index 1cf4748f32..9ba34df10a 100644 --- a/src/Umbraco.Core/Services/MemberTypeService.cs +++ b/src/Umbraco.Core/Services/MemberTypeService.cs @@ -93,6 +93,8 @@ namespace Umbraco.Core.Services var repository = RepositoryFactory.CreateMemberTypeRepository(uow); memberType.CreatorId = userId; + if (memberType.Description == string.Empty) + memberType.Description = null; repository.AddOrUpdate(memberType); uow.Commit(); // flush, so that the db contains the saved value @@ -121,6 +123,8 @@ namespace Umbraco.Core.Services foreach (var memberType in asArray) { memberType.CreatorId = userId; + if (memberType.Description == string.Empty) + memberType.Description = null; repository.AddOrUpdate(memberType); } uow.Commit(); // flush, so that the db contains the saved values diff --git a/src/Umbraco.Tests/Services/ContentTypeServiceTests.cs b/src/Umbraco.Tests/Services/ContentTypeServiceTests.cs index 3841f637e5..9c3bbab42c 100644 --- a/src/Umbraco.Tests/Services/ContentTypeServiceTests.cs +++ b/src/Umbraco.Tests/Services/ContentTypeServiceTests.cs @@ -1,4 +1,3 @@ -using System.Runtime.Remoting; using NUnit.Framework; using System; using System.Collections.Generic; @@ -9,7 +8,6 @@ using Umbraco.Core.Exceptions; using Umbraco.Core.Models; using Umbraco.Core.Models.Rdbms; using Umbraco.Core.Services; -using Umbraco.Tests.CodeFirst.TestModels.Composition; using Umbraco.Tests.TestHelpers; using Umbraco.Tests.TestHelpers.Entities; @@ -1644,6 +1642,38 @@ namespace Umbraco.Tests.Services Assert.That(descriptionPropertyTypeReloaded.PropertyGroupId.IsValueCreated, Is.False); } + [Test] + public void Empty_Description_Is_Always_Null_After_Saving_Content_Type() + { + var service = ServiceContext.ContentTypeService; + var contentType = MockedContentTypes.CreateBasicContentType(); + contentType.Description = null; + service.Save(contentType); + + var contentType2 = MockedContentTypes.CreateBasicContentType("basePage2", "Base Page 2"); + contentType2.Description = string.Empty; + service.Save(contentType2); + + Assert.IsNull(contentType.Description); + Assert.IsNull(contentType2.Description); + } + + [Test] + public void Empty_Description_Is_Always_Null_After_Saving_Media_Type() + { + var service = ServiceContext.ContentTypeService; + var mediaType = MockedContentTypes.CreateSimpleMediaType("mediaType", "Media Type"); + mediaType.Description = null; + service.Save(mediaType); + + var mediaType2 = MockedContentTypes.CreateSimpleMediaType("mediaType2", "Media Type 2"); + mediaType2.Description = string.Empty; + service.Save(mediaType2); + + Assert.IsNull(mediaType.Description); + Assert.IsNull(mediaType2.Description); + } + private ContentType CreateComponent() { var component = new ContentType(-1) diff --git a/src/Umbraco.Tests/Services/MemberTypeServiceTests.cs b/src/Umbraco.Tests/Services/MemberTypeServiceTests.cs index 8dd18b8b50..373551a524 100644 --- a/src/Umbraco.Tests/Services/MemberTypeServiceTests.cs +++ b/src/Umbraco.Tests/Services/MemberTypeServiceTests.cs @@ -1,8 +1,6 @@ using System; -using System.Collections.Generic; using System.Linq; using NUnit.Framework; -using umbraco.cms.presentation.create.controls; using Umbraco.Core; using Umbraco.Core.Models; using Umbraco.Core.Models.Rdbms; @@ -183,6 +181,22 @@ namespace Umbraco.Tests.Services Assert.Throws(() => ServiceContext.MemberTypeService.Save(memberType)); } + [Test] + public void Empty_Description_Is_Always_Null_After_Saving_Member_Type() + { + var service = ServiceContext.MemberTypeService; + var memberType = MockedContentTypes.CreateSimpleMemberType(); + memberType.Description = null; + service.Save(memberType); + + var memberType2 = MockedContentTypes.CreateSimpleMemberType("memberType2", "Member Type 2"); + memberType2.Description = string.Empty; + service.Save(memberType2); + + Assert.IsNull(memberType.Description); + Assert.IsNull(memberType2.Description); + } + //[Test] //public void Can_Save_MemberType_Structure_And_Create_A_Member_Based_On_It() //{ diff --git a/src/Umbraco.Tests/TestHelpers/Entities/MockedContentTypes.cs b/src/Umbraco.Tests/TestHelpers/Entities/MockedContentTypes.cs index 30daccd0fc..4d421e9bdf 100644 --- a/src/Umbraco.Tests/TestHelpers/Entities/MockedContentTypes.cs +++ b/src/Umbraco.Tests/TestHelpers/Entities/MockedContentTypes.cs @@ -1,5 +1,4 @@ using System; -using System.Linq; using Umbraco.Core; using Umbraco.Core.Models; diff --git a/src/Umbraco.Web.UI.Client/src/views/dashboard/developer/examinemanagement.html b/src/Umbraco.Web.UI.Client/src/views/dashboard/developer/examinemanagement.html index 912cdbfcc1..e80e7775da 100644 --- a/src/Umbraco.Web.UI.Client/src/views/dashboard/developer/examinemanagement.html +++ b/src/Umbraco.Web.UI.Client/src/views/dashboard/developer/examinemanagement.html @@ -3,232 +3,285 @@

Examine Management

- Loading... +
+
-

Indexers

+
-
    -
  • +
    +
    Indexers
    +
    - - {{indexer.name}} - +
    +
    +
    +
    Manage Examine's indexes
    +
    Allows you to view the details of each index and provides some tools for managing the indexes
    +
    -
      +
      -
    • +
      + +
      - Index info & tools - -
      -
      -
      -
      - - - +
      +
      +
      + {{indexer.name}}
      - -
      -
      -
      - -
      - The process is taking longer than expected, check the umbraco log to see if there have been any errors during this operation + + {{indexer.name}} + +
      + The index cannot be read and will need to be rebuilt
      - - - - - - - - - - - - - - - - - - -
       
      Documents in index{{indexer.documentCount}}
      Fields in index{{indexer.fieldCount}}
      Has deletions? - {{indexer.deletionCount > 0}} - ({{indexer.deletionCount}}) -
      Optimized? - {{indexer.isOptimized}} -
      -
      -
    • - -
    • - Node types - - - - - - - - - - - - - -
      Include node types{{indexer.indexCriteria.IncludeNodeTypes | json}}
      Exclude node types{{indexer.indexCriteria.ExcludeNodeTypes | json}}
      Parent node id{{indexer.indexCriteria.ParentNodeId}}
      -
    • - -
    • - System fields - - - - - - - - - - - - - - - - -
       
      NameEnable sortingType
      {{field.Name}}{{field.EnableSorting}}{{field.Type}}
      -
    • - -
    • - User fields - - - - - - - - - - - - - - - - -
       
      NameEnable sortingType
      {{field.Name}}{{field.EnableSorting}}{{field.Type}}
      -
    • - -
    • - Provider properties - - - - - - -
       
      {{key}}{{val}}
      -
    • -
    -
  • - - - -
- -

Searchers

- -
    -
  • - - {{searcher.name}} - - -
      - -
    • - - Search tools - -
      - Hide search results - -
      - -
      - - -
      - - +
      +
      +
      +
      +
      +
        +
      • + Index info & tools -
      • +
        +
        +
        -
        +
        + +
        -
        -
        -
        +
        + The process is taking longer than expected, check the umbraco log to see if there have been any errors during this operation +
        +
        + + + + + + + + + + + + + + + + + + -
         
        Documents in index{{indexer.documentCount}}
        Fields in index{{indexer.fieldCount}}
        Has deletions? + {{indexer.deletionCount > 0}} + ({{indexer.deletionCount}}) +
        Optimized? + {{indexer.isOptimized}} +
        - - - - - - - - - - - - - - -
        ScoreIdValues
        {{result.Score}}{{result.Id}} - - {{key}}: - {{val}} - -
        + +
        + +
      • + Node types + + + + + + + + + + + + + +
        Include node types{{indexer.indexCriteria.IncludeNodeTypes | json}}
        Exclude node types{{indexer.indexCriteria.ExcludeNodeTypes | json}}
        Parent node id{{indexer.indexCriteria.ParentNodeId}}
        +
      • +
      • + System fields + + + + + + + + + + + + + + + + +
         
        NameEnable sortingType
        {{field.Name}}{{field.EnableSorting}}{{field.Type}}
        +
      • +
      • + User fields + + + + + + + + + + + + + + + + +
         
        NameEnable sortingType
        {{field.Name}}{{field.EnableSorting}}{{field.Type}}
        +
      • +
      • + Provider properties + + + + + + +
         
        {{key}}{{val}}
        +
      • +
      -
    • -
    • - Provider properties - - - - - - -
       
      {{key}}{{val}}
      -
    • +
      +
      + +
      +
+ + + +
+ +
+
Searchers
+
+ +
+
+
+
Search indexes
+
Allows you to search the indexes and view the searcher properties
+
+ +
+ +
+ +
+ +
+ + +
+
    +
  • + + Search tools + +
    + Hide search results + +
    + +
    + + +
    + + +
    + + +
    + +
    + +
    +
    +
    + + + + + + + + + + + + + + + + +
    ScoreIdValues
    {{result.Score}}{{result.Id}} + + {{key}}: + {{val}} + +
    +
    +
    +
  • +
  • + Provider properties + + + + + + +
     
    {{key}}{{val}}
    +
  • +
+
+
+
+
+
+ + + - - - diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/listview/listview.html b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/listview/listview.html index 3969483734..92436ee218 100644 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/listview/listview.html +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/listview/listview.html @@ -17,7 +17,7 @@
internal static class ExamineExtensions { + /// + /// Checks if the index can be read/opened + /// + /// + /// The exception returned if there was an error + /// + public static bool IsHealthy(this LuceneIndexer indexer, out Exception ex) + { + try + { + using (indexer.GetIndexWriter().GetReader()) + { + ex = null; + return true; + } + } + catch (Exception e) + { + ex = e; + return false; + } + } /// /// Return the number of indexed documents in Lucene diff --git a/src/Umbraco.Web/WebServices/ExamineManagementApiController.cs b/src/Umbraco.Web/WebServices/ExamineManagementApiController.cs index 79e7fe8f53..1a4f5d4b5d 100644 --- a/src/Umbraco.Web/WebServices/ExamineManagementApiController.cs +++ b/src/Umbraco.Web/WebServices/ExamineManagementApiController.cs @@ -86,7 +86,7 @@ namespace Umbraco.Web.WebServices var model = new List( ExamineManager.Instance.SearchProviderCollection.Cast().Select(searcher => { - var indexerModel = new ExamineIndexerModel() + var indexerModel = new ExamineSearcherModel() { Name = searcher.Name }; @@ -260,6 +260,7 @@ namespace Umbraco.Web.WebServices IndexCriteria = indexer.IndexerData, Name = indexer.Name }; + var props = TypeHelper.CachedDiscoverableProperties(indexer.GetType(), mustWrite: false) //ignore these properties .Where(x => new[] {"IndexerData", "Description", "WorkingFolder"}.InvariantContains(x.Name) == false) @@ -281,11 +282,21 @@ namespace Umbraco.Web.WebServices var luceneIndexer = indexer as LuceneIndexer; if (luceneIndexer != null) - { + { indexerModel.IsLuceneIndex = true; if (luceneIndexer.IndexExists()) { + Exception indexError; + indexerModel.IsHealthy = luceneIndexer.IsHealthy(out indexError); + + if (indexerModel.IsHealthy == false) + { + //we cannot continue at this point + indexerModel.Error = indexError.ToString(); + return indexerModel; + } + indexerModel.DocumentCount = luceneIndexer.GetIndexDocumentCount(); indexerModel.FieldCount = luceneIndexer.GetIndexFieldCount(); indexerModel.IsOptimized = luceneIndexer.IsIndexOptimized(); diff --git a/src/Umbraco.Web/umbraco.presentation/content.cs b/src/Umbraco.Web/umbraco.presentation/content.cs index 34446c41de..a96863ea09 100644 --- a/src/Umbraco.Web/umbraco.presentation/content.cs +++ b/src/Umbraco.Web/umbraco.presentation/content.cs @@ -823,8 +823,15 @@ namespace umbraco catch (Exception e) { // if something goes wrong remove the file - DeleteXmlFile(); - + try + { + DeleteXmlFile(); + } + catch + { + // don't make it worse: could be that we failed to write because we cannot + // access the file, in which case we won't be able to delete it either + } LogHelper.Error("Failed to save Xml to file.", e); } } @@ -886,7 +893,15 @@ namespace umbraco catch (Exception e) { LogHelper.Error("Failed to load Xml from file.", e); - DeleteXmlFile(); + try + { + DeleteXmlFile(); + } + catch + { + // don't make it worse: could be that we failed to read because we cannot + // access the file, in which case we won't be able to delete it either + } return null; } }