Merge remote-tracking branch 'upstream/1.9-int' into 1.9-int

This commit is contained in:
Kees Damen
2015-04-25 22:06:15 +02:00
9 changed files with 127 additions and 104 deletions

View File

@@ -124,7 +124,7 @@ namespace Orchard.Alias.Implementation {
return _aliasStorage.List(sourceStartsWith).Select(item => Tuple.Create(item.Item1, item.Item3.ToRouteValueDictionary(), item.Item4)); return _aliasStorage.List(sourceStartsWith).Select(item => Tuple.Create(item.Item1, item.Item3.ToRouteValueDictionary(), item.Item4));
} }
public IEnumerable<VirtualPathData> LookupVirtualPaths(RouteValueDictionary routeValues,HttpContextBase httpContext) { public IEnumerable<VirtualPathData> LookupVirtualPaths(RouteValueDictionary routeValues, HttpContextBase httpContext) {
return Utils.LookupVirtualPaths(httpContext, _routeDescriptors.Value, routeValues); return Utils.LookupVirtualPaths(httpContext, _routeDescriptors.Value, routeValues);
} }

View File

@@ -6,11 +6,13 @@ using System.Xml.Linq;
using Orchard.Alias.Records; using Orchard.Alias.Records;
using Orchard.Data; using Orchard.Data;
using Orchard.Alias.Implementation.Holder; using Orchard.Alias.Implementation.Holder;
using Orchard.Validation;
namespace Orchard.Alias.Implementation.Storage { namespace Orchard.Alias.Implementation.Storage {
public interface IAliasStorage : IDependency { public interface IAliasStorage : IDependency {
void Set(string path, IDictionary<string, string> routeValues, string source); void Set(string path, IDictionary<string, string> routeValues, string source);
IDictionary<string, string> Get(string aliasPath); IDictionary<string, string> Get(string aliasPath);
void Remove(Expression<Func<AliasRecord, bool>> filter);
void Remove(string path); void Remove(string path);
void Remove(string path, string aliasSource); void Remove(string path, string aliasSource);
void RemoveBySource(string aliasSource); void RemoveBySource(string aliasSource);
@@ -88,38 +90,23 @@ namespace Orchard.Alias.Implementation.Storage {
} }
public void Remove(string path) { public void Remove(string path) {
Remove(x => x.Path == path && x.Source == path);
if (path == null) {
throw new ArgumentNullException("path");
}
foreach (var aliasRecord in _aliasRepository.Fetch(r => r.Path == path)) {
_aliasRepository.Delete(aliasRecord);
// Bulk updates might go wrong if we don't flush
_aliasRepository.Flush();
var dict = ToDictionary(aliasRecord);
_aliasHolder.RemoveAlias(new AliasInfo() { Path = dict.Item1, Area = dict.Item2, RouteValues = dict.Item3 });
}
} }
public void Remove(string path, string aliasSource) { public void Remove(string path, string aliasSource) {
Remove(x => x.Path == path && x.Source == aliasSource);
if (path == null) {
throw new ArgumentNullException("path");
}
foreach (var aliasRecord in _aliasRepository.Fetch(r => r.Path == path && r.Source == aliasSource)) {
_aliasRepository.Delete(aliasRecord);
// Bulk updates might go wrong if we don't flush
_aliasRepository.Flush();
var dict = ToDictionary(aliasRecord);
_aliasHolder.RemoveAlias(new AliasInfo() { Path = dict.Item1, Area = dict.Item2, RouteValues = dict.Item3 });
}
} }
public void RemoveBySource(string aliasSource) { public void RemoveBySource(string aliasSource) {
foreach (var aliasRecord in _aliasRepository.Fetch(r => r.Source == aliasSource)) { Remove(x => x.Source == aliasSource);
}
public void Remove(Expression<Func<AliasRecord, bool>> filter) {
Argument.ThrowIfNull(filter, "filter");
foreach (var aliasRecord in _aliasRepository.Fetch(filter)) {
_aliasRepository.Delete(aliasRecord); _aliasRepository.Delete(aliasRecord);
// Bulk updates might go wrong if we don't flush // Bulk updates might go wrong if we don't flush.
_aliasRepository.Flush(); _aliasRepository.Flush();
var dict = ToDictionary(aliasRecord); var dict = ToDictionary(aliasRecord);
_aliasHolder.RemoveAlias(new AliasInfo() { Path = dict.Item1, Area = dict.Item2, RouteValues = dict.Item3 }); _aliasHolder.RemoveAlias(new AliasInfo() { Path = dict.Item1, Area = dict.Item2, RouteValues = dict.Item3 });
@@ -127,7 +114,7 @@ namespace Orchard.Alias.Implementation.Storage {
} }
public IEnumerable<Tuple<string, string, IDictionary<string, string>, string, int>> List() { public IEnumerable<Tuple<string, string, IDictionary<string, string>, string, int>> List() {
return List((Expression<Func<AliasRecord, bool>>) null); return List((Expression<Func<AliasRecord, bool>>)null);
} }
public IEnumerable<Tuple<string, string, IDictionary<string, string>, string, int>> List(Expression<Func<AliasRecord, bool>> predicate) { public IEnumerable<Tuple<string, string, IDictionary<string, string>, string, int>> List(Expression<Func<AliasRecord, bool>> predicate) {

View File

@@ -3,23 +3,23 @@ using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Text.RegularExpressions; using System.Text.RegularExpressions;
using Orchard.Alias; using Orchard.Alias;
using Orchard.Alias.Implementation.Storage;
using Orchard.Autoroute.Models; using Orchard.Autoroute.Models;
using Orchard.Autoroute.Settings; using Orchard.Autoroute.Settings;
using Orchard.ContentManagement; using Orchard.ContentManagement;
using Orchard.ContentManagement.MetaData; using Orchard.ContentManagement.MetaData;
using Orchard.ContentManagement.MetaData.Models; using Orchard.ContentManagement.MetaData.Models;
using Orchard.Localization;
using Orchard.Logging;
using Orchard.Tokens; using Orchard.Tokens;
namespace Orchard.Autoroute.Services { namespace Orchard.Autoroute.Services {
public class AutorouteService : IAutorouteService { public class AutorouteService : Component, IAutorouteService {
private readonly IAliasService _aliasService; private readonly IAliasService _aliasService;
private readonly ITokenizer _tokenizer; private readonly ITokenizer _tokenizer;
private readonly IContentDefinitionManager _contentDefinitionManager; private readonly IContentDefinitionManager _contentDefinitionManager;
private readonly IContentManager _contentManager; private readonly IContentManager _contentManager;
private readonly IRouteEvents _routeEvents; private readonly IRouteEvents _routeEvents;
private readonly IAliasStorage _aliasStorage;
private const string AliasSource = "Autoroute:View"; private const string AliasSource = "Autoroute:View";
public AutorouteService( public AutorouteService(
@@ -27,39 +27,36 @@ namespace Orchard.Autoroute.Services {
ITokenizer tokenizer, ITokenizer tokenizer,
IContentDefinitionManager contentDefinitionManager, IContentDefinitionManager contentDefinitionManager,
IContentManager contentManager, IContentManager contentManager,
IRouteEvents routeEvents) { IRouteEvents routeEvents,
_aliasService = aliasService; IAliasStorage aliasStorage) {
_tokenizer = tokenizer;
_contentDefinitionManager = contentDefinitionManager; _aliasService = aliasService;
_tokenizer = tokenizer;
_contentDefinitionManager = contentDefinitionManager;
_contentManager = contentManager; _contentManager = contentManager;
_routeEvents = routeEvents; _routeEvents = routeEvents;
_aliasStorage = aliasStorage;
Logger = NullLogger.Instance;
T = NullLocalizer.Instance;
} }
public ILogger Logger { get; set; }
public Localizer T { get; set; }
public string GenerateAlias(AutoroutePart part) { public string GenerateAlias(AutoroutePart part) {
if (part == null) { if (part == null) {
throw new ArgumentNullException("part"); throw new ArgumentNullException("part");
} }
string pattern = GetDefaultPattern(part.ContentItem.ContentType).Pattern; var pattern = GetDefaultPattern(part.ContentItem.ContentType).Pattern;
// String.Empty forces pattern based generation. "/" forces homepage // String.Empty forces pattern based generation. "/" forces homepage.
if(part.UseCustomPattern if (part.UseCustomPattern
&& (!String.IsNullOrWhiteSpace(part.CustomPattern) || String.Equals(part.CustomPattern, "/"))) { && (!String.IsNullOrWhiteSpace(part.CustomPattern) || String.Equals(part.CustomPattern, "/"))) {
pattern = part.CustomPattern; pattern = part.CustomPattern;
} }
// Convert the pattern and route values via tokens // Convert the pattern and route values via tokens.
var path = _tokenizer.Replace(pattern, BuildTokenContext(part.ContentItem), new ReplaceOptions { Encoding = ReplaceOptions.NoEncode }); var path = _tokenizer.Replace(pattern, BuildTokenContext(part.ContentItem), new ReplaceOptions { Encoding = ReplaceOptions.NoEncode });
// removing trailing slashes in case the container is empty, and tokens are base on it (e.g. home page) // Removing trailing slashes in case the container is empty, and tokens are base on it (e.g. home page).
while(path.StartsWith("/")) { while (path.StartsWith("/")) {
path = path.Substring(1); path = path.Substring(1);
} }
@@ -68,9 +65,7 @@ namespace Orchard.Autoroute.Services {
public void PublishAlias(AutoroutePart part) { public void PublishAlias(AutoroutePart part) {
var displayRouteValues = _contentManager.GetItemMetadata(part).DisplayRouteValues; var displayRouteValues = _contentManager.GetItemMetadata(part).DisplayRouteValues;
_aliasService.Replace(part.DisplayAlias, displayRouteValues, AliasSource); _aliasService.Replace(part.DisplayAlias, displayRouteValues, AliasSource);
_routeEvents.Routed(part, part.DisplayAlias); _routeEvents.Routed(part, part.DisplayAlias);
} }
@@ -97,7 +92,7 @@ namespace Orchard.Autoroute.Services {
patterns.Add(routePattern); patterns.Add(routePattern);
settings.Patterns = patterns; settings.Patterns = patterns;
// define which pattern is the default // Define which pattern is the default.
if (makeDefault || settings.Patterns.Count == 1) { if (makeDefault || settings.Patterns.Count == 1) {
settings.DefaultPatternIndex = settings.Patterns.IndexOf(routePattern); settings.DefaultPatternIndex = settings.Patterns.IndexOf(routePattern);
} }
@@ -113,26 +108,33 @@ namespace Orchard.Autoroute.Services {
public RoutePattern GetDefaultPattern(string contentType) { public RoutePattern GetDefaultPattern(string contentType) {
var settings = GetTypePartSettings(contentType).GetModel<AutorouteSettings>(); var settings = GetTypePartSettings(contentType).GetModel<AutorouteSettings>();
// return a default pattern if none is defined // Return a default pattern if none is defined.
if(settings.DefaultPatternIndex < settings.Patterns.Count) { if (settings.DefaultPatternIndex < settings.Patterns.Count) {
return settings.Patterns.ElementAt(settings.DefaultPatternIndex); return settings.Patterns.ElementAt(settings.DefaultPatternIndex);
} }
return new RoutePattern {Name = "Title", Description = "my-title", Pattern = "{Content.Slug}"}; return new RoutePattern { Name = "Title", Description = "my-title", Pattern = "{Content.Slug}" };
} }
public void RemoveAliases(AutoroutePart part) { public void RemoveAliases(AutoroutePart part) {
_aliasService.Delete(part.Path, AliasSource); // https://github.com/OrchardCMS/Orchard/issues/5137
} // If the alias of the specified part is empty while not being the homepage,
// we need to make sure we are not removing all empty aliases in order to prevent losing the homepage content item being the homepage.
if (String.IsNullOrWhiteSpace(part.Path)) {
if (!IsHomePage(part)) {
// The item being removed is NOT the homepage, so we need to make sure we're not removing the alias for the homepage.
var aliasRecordId = GetHomePageAliasRecordId();
private SettingsDictionary GetTypePartSettings(string contentType) { // Remove all aliases EXCEPT for the alias of the homepage.
var contentDefinition = _contentDefinitionManager.GetTypeDefinition(contentType); _aliasStorage.Remove(x => x.Path == part.Path && x.Source == AliasSource && x.Id != aliasRecordId);
if (contentDefinition == null) { // Done.
throw new OrchardException(T("Unknown content type: {0}", contentType)); return;
}
} }
return contentDefinition.Parts.First(x => x.PartDefinition.Name == "AutoroutePart").Settings; // Safe to delete all aliases for the specified part since it is definitely not the homepage.
_aliasService.Delete(part.Path, AliasSource);
} }
public string GenerateUniqueSlug(AutoroutePart part, IEnumerable<string> existingPaths) { public string GenerateUniqueSlug(AutoroutePart part, IEnumerable<string> existingPaths) {
@@ -142,22 +144,10 @@ namespace Orchard.Autoroute.Services {
int? version = existingPaths.Select(s => GetSlugVersion(part.Path, s)).OrderBy(i => i).LastOrDefault(); int? version = existingPaths.Select(s => GetSlugVersion(part.Path, s)).OrderBy(i => i).LastOrDefault();
return version != null return version != null
? string.Format("{0}-{1}", part.Path, version) ? String.Format("{0}-{1}", part.Path, version)
: part.Path; : part.Path;
} }
private static int? GetSlugVersion(string path, string potentialConflictingPath) {
int v;
string[] slugParts = potentialConflictingPath.Split(new[] { path }, StringSplitOptions.RemoveEmptyEntries);
if (slugParts.Length == 0)
return 2;
return int.TryParse(slugParts[0].TrimStart('-'), out v)
? (int?)++v
: null;
}
public IEnumerable<AutoroutePart> GetSimilarPaths(string path) { public IEnumerable<AutoroutePart> GetSimilarPaths(string path) {
return return
_contentManager.Query<AutoroutePart, AutoroutePartRecord>() _contentManager.Query<AutoroutePart, AutoroutePartRecord>()
@@ -170,11 +160,10 @@ namespace Orchard.Autoroute.Services {
} }
public bool ProcessPath(AutoroutePart part) { public bool ProcessPath(AutoroutePart part) {
var pathsLikeThis = GetSimilarPaths(part.Path).ToArray(); var pathsLikeThis = GetSimilarPaths(part.Path).ToArray();
// Don't include *this* part in the list // Don't include *this* part in the list
// of slugs to consider for conflict detection // of slugs to consider for conflict detection.
pathsLikeThis = pathsLikeThis.Where(p => p.ContentItem.Id != part.ContentItem.Id).ToArray(); pathsLikeThis = pathsLikeThis.Where(p => p.ContentItem.Id != part.ContentItem.Id).ToArray();
if (pathsLikeThis.Any()) { if (pathsLikeThis.Any()) {
@@ -188,5 +177,37 @@ namespace Orchard.Autoroute.Services {
return true; return true;
} }
private bool IsHomePage(IContent content) {
var homePageRoute = _aliasService.Get("");
var homePageId = homePageRoute.ContainsKey("id") ? XmlHelper.Parse<int>((string)homePageRoute["id"]) : default(int?);
return content.Id == homePageId;
}
private int GetHomePageAliasRecordId() {
return _aliasStorage.List(x => x.Path == "").First().Item5;
}
private SettingsDictionary GetTypePartSettings(string contentType) {
var contentDefinition = _contentDefinitionManager.GetTypeDefinition(contentType);
if (contentDefinition == null) {
throw new OrchardException(T("Unknown content type: {0}", contentType));
}
return contentDefinition.Parts.First(x => x.PartDefinition.Name == "AutoroutePart").Settings;
}
private static int? GetSlugVersion(string path, string potentialConflictingPath) {
int v;
var slugParts = potentialConflictingPath.Split(new[] { path }, StringSplitOptions.RemoveEmptyEntries);
if (slugParts.Length == 0)
return 2;
return Int32.TryParse(slugParts[0].TrimStart('-'), out v)
? (int?)++v
: null;
}
} }
} }

View File

@@ -1,8 +1,7 @@
@model Orchard.Autoroute.ViewModels.AutoroutePartEditViewModel @using Orchard.Autoroute
@using Orchard.Autoroute
@using Orchard.Mvc.Extensions
@using Orchard.Utility.Extensions;
@using Orchard.Environment.Configuration @using Orchard.Environment.Configuration
@using Orchard.Mvc.Extensions
@model Orchard.Autoroute.ViewModels.AutoroutePartEditViewModel
@if(Model.Settings.DefaultPatternIndex == -1) { @if(Model.Settings.DefaultPatternIndex == -1) {
<div class="message message-Error">@T("The current Content Type does not have a default Autoroute Pattern. Please edit the settings first.")</div> <div class="message message-Error">@T("The current Content Type does not have a default Autoroute Pattern. Please edit the settings first.")</div>

View File

@@ -57,7 +57,7 @@ namespace Orchard.Recipes.Services {
_appDataFolder.DeleteFile(stepPath); _appDataFolder.DeleteFile(stepPath);
} }
if (stepIndex < 1) { if (stepIndex < 0) {
_appDataFolder.DeleteFile(Path.Combine(_recipeQueueFolder, executionId)); _appDataFolder.DeleteFile(Path.Combine(_recipeQueueFolder, executionId));
} }

View File

@@ -91,7 +91,8 @@ namespace Orchard.Taxonomies.Handlers {
// Fires off a processing engine task to run the count processing after the request so it's non-blocking. // Fires off a processing engine task to run the count processing after the request so it's non-blocking.
private void RecalculateCount(IProcessingEngine processingEngine, ShellSettings shellSettings, IShellDescriptorManager shellDescriptorManager, TermsPart part) { private void RecalculateCount(IProcessingEngine processingEngine, ShellSettings shellSettings, IShellDescriptorManager shellDescriptorManager, TermsPart part) {
processingEngine.AddTask(shellSettings, shellDescriptorManager.GetShellDescriptor(), "ITermCountProcessor.Process", new Dictionary<string, object> { { "termsPartId", part.ContentItem.Id } }); var termPartRecordIds = part.Terms.Select(t => t.TermRecord.Id).ToArray();
processingEngine.AddTask(shellSettings, shellDescriptorManager.GetShellDescriptor(), "ITermCountProcessor.Process", new Dictionary<string, object> { { "termPartRecordIds", termPartRecordIds } });
} }

View File

@@ -7,6 +7,6 @@ using Orchard.Events;
namespace Orchard.Taxonomies.Services { namespace Orchard.Taxonomies.Services {
public interface ITermCountProcessor : IEventHandler { public interface ITermCountProcessor : IEventHandler {
void Process(int termsPartId); void Process(params int[] termPartRecordIds);
} }
} }

View File

@@ -9,6 +9,9 @@ using Orchard.ContentManagement.MetaData;
using Orchard.Core.Common.Models; using Orchard.Core.Common.Models;
using Orchard.Core.Title.Models; using Orchard.Core.Title.Models;
using Orchard.Data; using Orchard.Data;
using Orchard.Environment.Configuration;
using Orchard.Environment.Descriptor;
using Orchard.Environment.State;
using Orchard.Localization; using Orchard.Localization;
using Orchard.Logging; using Orchard.Logging;
using Orchard.Security; using Orchard.Security;
@@ -23,6 +26,10 @@ namespace Orchard.Taxonomies.Services {
private readonly IAuthorizationService _authorizationService; private readonly IAuthorizationService _authorizationService;
private readonly IContentDefinitionManager _contentDefinitionManager; private readonly IContentDefinitionManager _contentDefinitionManager;
private readonly IOrchardServices _services; private readonly IOrchardServices _services;
private readonly IProcessingEngine _processingEngine;
private readonly ShellSettings _shellSettings;
private readonly IShellDescriptorManager _shellDescriptorManager;
public TaxonomyService( public TaxonomyService(
IRepository<TermContentItem> termContentItemRepository, IRepository<TermContentItem> termContentItemRepository,
@@ -30,13 +37,20 @@ namespace Orchard.Taxonomies.Services {
INotifier notifier, INotifier notifier,
IContentDefinitionManager contentDefinitionManager, IContentDefinitionManager contentDefinitionManager,
IAuthorizationService authorizationService, IAuthorizationService authorizationService,
IOrchardServices services) { IOrchardServices services,
IProcessingEngine processingEngine,
ShellSettings shellSettings,
IShellDescriptorManager shellDescriptorManager)
{
_termContentItemRepository = termContentItemRepository; _termContentItemRepository = termContentItemRepository;
_contentManager = contentManager; _contentManager = contentManager;
_notifier = notifier; _notifier = notifier;
_authorizationService = authorizationService; _authorizationService = authorizationService;
_contentDefinitionManager = contentDefinitionManager; _contentDefinitionManager = contentDefinitionManager;
_services = services; _services = services;
_processingEngine = processingEngine;
_shellSettings = shellSettings;
_shellDescriptorManager = shellDescriptorManager;
Logger = NullLogger.Instance; Logger = NullLogger.Instance;
T = NullLocalizer.Instance; T = NullLocalizer.Instance;
@@ -230,24 +244,31 @@ namespace Orchard.Taxonomies.Services {
var termsPart = contentItem.As<TermsPart>(); var termsPart = contentItem.As<TermsPart>();
// removing current terms for specific field // removing current terms for specific field
var fieldIndexes = termsPart.Terms.Select((t, i) => new {Term = t, Index = i}) var termList = termsPart.Terms.Select((t, i) => new {Term = t, Index = i})
.Where(x => x.Term.Field == field) .Where(x => x.Term.Field == field)
.Select(x => x.Index) .Select(x => x)
.OrderByDescending(i => i) .OrderByDescending(i => i.Index)
.ToList(); .ToList();
foreach(var x in fieldIndexes) { foreach (var x in termList) {
termsPart.Terms.RemoveAt(x); termsPart.Terms.RemoveAt(x.Index);
} }
// adding new terms list // adding new terms list
foreach(var term in terms) { foreach(var term in terms) {
// Remove the newly added terms because they will get processed by the Published-Event
termList.RemoveAll(t => t.Term.Id == term.Id);
termsPart.Terms.Add( termsPart.Terms.Add(
new TermContentItem { new TermContentItem {
TermsPartRecord = termsPart.Record, TermsPartRecord = termsPart.Record,
TermRecord = term.Record, Field = field TermRecord = term.Record, Field = field
}); });
} }
var termPartRecordIds = termList.Select(t => t.Term.TermRecord.Id).ToArray();
_processingEngine.AddTask(_shellSettings, _shellDescriptorManager.GetShellDescriptor(), "ITermCountProcessor.Process", new Dictionary<string, object> { { "termPartRecordIds", termPartRecordIds } });
} }
public IContentQuery<TermsPart, TermsPartRecord> GetContentItemsQuery(TermPart term, string fieldName = null) { public IContentQuery<TermsPart, TermsPartRecord> GetContentItemsQuery(TermPart term, string fieldName = null) {

View File

@@ -15,16 +15,10 @@ namespace Orchard.Taxonomies.Services {
_taxonomyService = taxonomyService; _taxonomyService = taxonomyService;
} }
public void Process(int termsPartId) { public void Process(params int[] termPartRecordIds) {
var termsPart = _contentManager.Get<TermsPart>(termsPartId);
if (termsPart == null) { foreach (var id in termPartRecordIds) {
return; var termPart = _taxonomyService.GetTerm(id);
}
// Retrieve the number of associated content items, for the whole hierarchy
foreach (var term in termsPart.Terms) {
var termPart = _taxonomyService.GetTerm(term.TermRecord.Id);
while (termPart != null) { while (termPart != null) {
termPart.Count = (int)_taxonomyService.GetContentItemsCount(termPart); termPart.Count = (int)_taxonomyService.GetContentItemsCount(termPart);