diff --git a/src/Orchard.Tests.Modules/Users/Controllers/AccountControllerTests.cs b/src/Orchard.Tests.Modules/Users/Controllers/AccountControllerTests.cs index cea0c7fe4..37a82affd 100644 --- a/src/Orchard.Tests.Modules/Users/Controllers/AccountControllerTests.cs +++ b/src/Orchard.Tests.Modules/Users/Controllers/AccountControllerTests.cs @@ -205,7 +205,8 @@ namespace Orchard.Tests.Modules.Users.Controllers { @"Ima.Fool@example.com", @"""Ima.Fool""@example.com", @"""Ima Fool""@example.com", - "2xxx1414@i.ua" + "2xxx1414@i.ua", + "Dreißig-öffentliche-Ämter-in-Übersee@Beispiel.de" )] string email) { diff --git a/src/Orchard.Web/Modules/Orchard.Autoroute/Migrations.cs b/src/Orchard.Web/Modules/Orchard.Autoroute/Migrations.cs index 545e7d35d..b31392396 100644 --- a/src/Orchard.Web/Modules/Orchard.Autoroute/Migrations.cs +++ b/src/Orchard.Web/Modules/Orchard.Autoroute/Migrations.cs @@ -1,10 +1,20 @@ -using Orchard.ContentManagement.MetaData; +using System; +using System.Collections.Generic; +using System.Linq; +using Orchard.Autoroute.Models; +using Orchard.Autoroute.Settings; +using Orchard.ContentManagement.MetaData; using Orchard.Core.Contents.Extensions; using Orchard.Data.Migration; +using Orchard.Localization.Services; -namespace Orchard.Autoroute -{ - public class Migrations : DataMigrationImpl { +namespace Orchard.Autoroute { + public class Migrations : DataMigrationImpl { + private readonly ICultureManager _cultureManager; + + public Migrations(ICultureManager cultureManager) { + _cultureManager = cultureManager; + } public int Create() { SchemaBuilder.CreateTable("AutoroutePartRecord", @@ -49,5 +59,46 @@ namespace Orchard.Autoroute return 4; } + + public int UpdateFrom4() { + // Adding some culture neutral patterns if they don't exist + var autoroutePartDefinitions = ContentDefinitionManager.ListTypeDefinitions() + .Where(t => t.Parts.Any(p => p.PartDefinition.Name.Equals(typeof(AutoroutePart).Name))) + .Select(s => new { contentTypeName = s.Name, autoroutePart = s.Parts.First(x => x.PartDefinition.Name == "AutoroutePart") }); + + foreach (var partDefinition in autoroutePartDefinitions) { + var settingsDictionary = partDefinition.autoroutePart.Settings; + var settings = settingsDictionary.GetModel(); + + if (!settings.Patterns.Any(x => String.IsNullOrWhiteSpace(x.Culture))) { + string siteCulture = _cultureManager.GetSiteCulture(); + List newPatterns = new List(); + + if (settings.Patterns.Any(x => String.Equals(x.Culture, siteCulture, StringComparison.OrdinalIgnoreCase))) { + var siteCulturePatterns = settings.Patterns.Where(x => String.Equals(x.Culture, siteCulture, StringComparison.OrdinalIgnoreCase)).ToList(); + + foreach (RoutePattern pattern in siteCulturePatterns) { + newPatterns.Add(String.Format("{{\"Name\":\"{0}\",\"Pattern\":\"{1}\",\"Description\":\"{2}\"}}", pattern.Name, pattern.Pattern, pattern.Description)); + } + } + else { + newPatterns.Add(String.Format("{{\"Name\":\"{0}\",\"Pattern\":\"{1}\",\"Description\":\"{2}\"}}", "Title", "{Content.Slug}", "my-title")); + } + + if (settingsDictionary.ContainsKey("AutorouteSettings.PatternDefinitions")) { + string oldPatterns = settingsDictionary["AutorouteSettings.PatternDefinitions"]; + if (oldPatterns.StartsWith("[") && oldPatterns.EndsWith("]")) + newPatterns.Add(oldPatterns.Substring(1, oldPatterns.Length - 2)); + } + + ContentDefinitionManager.AlterTypeDefinition(partDefinition.contentTypeName, cfg => cfg + .WithPart("AutoroutePart", builder => builder + .WithSetting("AutorouteSettings.PatternDefinitions", "[" + String.Join(",", newPatterns) + "]") + )); + } + } + + return 5; + } } } \ No newline at end of file diff --git a/src/Orchard.Web/Modules/Orchard.Autoroute/Providers/ContentDefinition/ContentDefinitionEventHandler.cs b/src/Orchard.Web/Modules/Orchard.Autoroute/Providers/ContentDefinition/ContentDefinitionEventHandler.cs index 97d61324f..6c8e02a7f 100644 --- a/src/Orchard.Web/Modules/Orchard.Autoroute/Providers/ContentDefinition/ContentDefinitionEventHandler.cs +++ b/src/Orchard.Web/Modules/Orchard.Autoroute/Providers/ContentDefinition/ContentDefinitionEventHandler.cs @@ -50,19 +50,24 @@ namespace Orchard.Autoroute.Providers.ContentDefinition { public void ContentPartAttached(ContentPartAttachedContext context) { if (context.ContentPartName == "AutoroutePart") { - //Create pattern and default pattern for each culture installed + // Create pattern and default pattern for each culture installed and for the neutral culture - //get cultures + // Get cultures var SiteCultures = _cultureManager.ListCultures().ToList(); - //Create Patterns and DefaultPatterns + // Adding a null culture for the culture neutral pattern + List cultures = new List(); + cultures.Add(null); + cultures.AddRange(SiteCultures); + + // Create Patterns and DefaultPatterns var settings = new AutorouteSettings { Patterns = new List() }; List newPatterns = new List(); List newDefaultPatterns = new List(); - foreach (string culture in SiteCultures) { + foreach (string culture in cultures) { newPatterns.Add(new RoutePattern { Name = "Title", Description = "my-title", diff --git a/src/Orchard.Web/Modules/Orchard.Autoroute/Services/AutorouteService.cs b/src/Orchard.Web/Modules/Orchard.Autoroute/Services/AutorouteService.cs index e015e7c91..ff52a64e7 100644 --- a/src/Orchard.Web/Modules/Orchard.Autoroute/Services/AutorouteService.cs +++ b/src/Orchard.Web/Modules/Orchard.Autoroute/Services/AutorouteService.cs @@ -135,35 +135,47 @@ namespace Orchard.Autoroute.Services { public RoutePattern GetDefaultPattern(string contentType, string culture) { var settings = GetTypePartSettings(contentType).GetModel(); - var defaultPattern = settings.DefaultPatterns.FirstOrDefault(x => x.Culture == culture); - var defaultPatternIndex = defaultPattern != null ? defaultPattern.PatternIndex : "0"; - if (String.IsNullOrWhiteSpace(defaultPatternIndex)) - defaultPatternIndex = "0"; + if (settings.UseCulturePattern) { + var defaultPatternIndex = "0"; - if (!settings.DefaultPatterns.Any(x => String.Equals(x.Culture, culture, StringComparison.OrdinalIgnoreCase))) { - var patternIndex = String.IsNullOrWhiteSpace(settings.DefaultPatternIndex) ? "0" : settings.DefaultPatternIndex; - // Lazy updating from old setting. - if (String.Equals(culture, _cultureManager.GetSiteCulture(), StringComparison.OrdinalIgnoreCase)) { - settings.DefaultPatterns.Add(new DefaultPattern { PatternIndex = patternIndex, Culture = culture }); - return settings.Patterns.Where(x => x.Culture == null).ElementAt(Convert.ToInt32(defaultPatternIndex)); + if (!settings.DefaultPatterns.Any(x => String.Equals(x.Culture, culture, StringComparison.OrdinalIgnoreCase))) { + // If no default pattern exists for the language return the default culture neutral pattern if it exists, else a generic pattern + if (settings.Patterns.Any(x => String.IsNullOrEmpty(x.Culture))) { + defaultPatternIndex = GetDefaultPatternIndex(contentType, null); + return settings.Patterns.Where(x => String.IsNullOrEmpty(x.Culture)).ElementAt(Convert.ToInt32(defaultPatternIndex)); + } + else { + return new RoutePattern { Name = "Title", Description = "my-title", Pattern = "{Content.Slug}", Culture = culture }; + } } - else { - settings.DefaultPatterns.Add(new DefaultPattern { PatternIndex = "0", Culture = culture }); - return new RoutePattern { Name = "Title", Description = "my-title", Pattern = "{Content.Slug}", Culture = culture }; + + // If patterns for the specified culture exist search one of them, else search a culture neutral pattern + var patternCultureSearch = settings.Patterns.Any(x => String.Equals(x.Culture, culture, StringComparison.OrdinalIgnoreCase)) ? culture : null; + defaultPatternIndex = GetDefaultPatternIndex(contentType, patternCultureSearch); + + if (settings.Patterns.Any()) { + if (settings.Patterns.Where(x => x.Culture == patternCultureSearch).ElementAt(Convert.ToInt32(defaultPatternIndex)) != null) { + return settings.Patterns.Where(x => x.Culture == patternCultureSearch).ElementAt(Convert.ToInt32(defaultPatternIndex)); + } + } + } + else { + // Using the culture neutral pattern + if (settings.Patterns.Any(x => String.IsNullOrEmpty(x.Culture))) { + var defaultPatternIndex = GetDefaultPatternIndex(contentType, null); + + // If no default culture neutral pattern exist use the default pattern + if (!settings.DefaultPatterns.Any(x => String.IsNullOrEmpty(x.Culture))) { + var patternIndex = String.IsNullOrWhiteSpace(settings.DefaultPatternIndex) ? "0" : settings.DefaultPatternIndex; + settings.DefaultPatterns.Add(new DefaultPattern { PatternIndex = patternIndex, Culture = null }); + } + + return settings.Patterns.Where(x => String.IsNullOrEmpty(x.Culture)).ElementAt(Convert.ToInt32(defaultPatternIndex)); } } - // Return a default pattern if set. - var patternCultureSearch = settings.Patterns.Any(x => String.Equals(x.Culture, culture, StringComparison.OrdinalIgnoreCase)) ? culture : null; - - if (settings.Patterns.Any()) { - if (settings.Patterns.Where(x => x.Culture == patternCultureSearch).ElementAt(Convert.ToInt32(defaultPatternIndex)) != null) { - return settings.Patterns.Where(x => x.Culture == patternCultureSearch).ElementAt(Convert.ToInt32(defaultPatternIndex)); - }; - } - - // Return a default pattern if none is defined. + // Return a default pattern if none is defined return new RoutePattern { Name = "Title", Description = "my-title", Pattern = "{Content.Slug}", Culture = culture }; } @@ -222,6 +234,23 @@ namespace Orchard.Autoroute.Services { return contentDefinition.Parts.First(x => x.PartDefinition.Name == "AutoroutePart").Settings; } + private string GetDefaultPatternIndex(string contentType, string culture) { + var settings = GetTypePartSettings(contentType).GetModel(); + + DefaultPattern defaultPattern = null; + if (String.IsNullOrEmpty(culture)) + defaultPattern = settings.DefaultPatterns.FirstOrDefault(x => String.IsNullOrEmpty(x.Culture)); + else + defaultPattern = settings.DefaultPatterns.FirstOrDefault(x => String.Equals(x.Culture, culture, StringComparison.OrdinalIgnoreCase)); + + var defaultPatternIndex = defaultPattern != null ? defaultPattern.PatternIndex : "0"; + + if (String.IsNullOrWhiteSpace(defaultPatternIndex)) + defaultPatternIndex = "0"; + + return defaultPatternIndex; + } + private static int? GetSlugVersion(string path, string potentialConflictingPath) { int v; var slugParts = potentialConflictingPath.Split(new[] { path }, StringSplitOptions.RemoveEmptyEntries); diff --git a/src/Orchard.Web/Modules/Orchard.Autoroute/Settings/AutorouteSettingsEvents.cs b/src/Orchard.Web/Modules/Orchard.Autoroute/Settings/AutorouteSettingsEvents.cs index fb15c691e..fa369575f 100644 --- a/src/Orchard.Web/Modules/Orchard.Autoroute/Settings/AutorouteSettingsEvents.cs +++ b/src/Orchard.Web/Modules/Orchard.Autoroute/Settings/AutorouteSettingsEvents.cs @@ -28,56 +28,55 @@ namespace Orchard.Autoroute.Settings { var settings = definition.Settings.GetModel(); - //get cultures + // Get cultures settings.SiteCultures = _cultureManager.ListCultures().ToList(); - //get default site culture + // Get default site culture settings.DefaultSiteCulture = _cultureManager.GetSiteCulture(); - //if a culture is not set on the pattern we set it to the default site culture for backward compatibility - if (!settings.Patterns.Any(x => String.Equals(x.Culture, settings.DefaultSiteCulture, StringComparison.OrdinalIgnoreCase))) { - foreach (RoutePattern pattern in settings.Patterns.Where(x => String.IsNullOrWhiteSpace(x.Culture))) { - settings.Patterns.Where(x => x.GetHashCode() == pattern.GetHashCode()).FirstOrDefault().Culture = settings.DefaultSiteCulture; - } - } - - //Adding Patterns for the UI + // Adding Patterns for the UI List newPatterns = new List(); + + // Adding a null culture for the culture neutral pattern + List cultures = new List(); + cultures.Add(null); + cultures.AddRange(settings.SiteCultures); + int current = 0; - foreach (string culture in settings.SiteCultures) { + foreach (string culture in cultures) { + // Adding all existing patterns for the culture foreach (RoutePattern routePattern in settings.Patterns.Where(x => String.Equals(x.Culture, culture, StringComparison.OrdinalIgnoreCase))) { - if (settings.Patterns.Any(x => String.Equals(x.Culture, culture, StringComparison.OrdinalIgnoreCase))) { - newPatterns.Add(settings.Patterns[current]); - } else { - newPatterns.Add(new RoutePattern { - Name = "Title", - Description = "my-title", - Pattern = "{Content.Slug}", - Culture = settings.DefaultSiteCulture - }); - } + newPatterns.Add(settings.Patterns[current]); current++; } - //We add a pattern for each culture if there is none + // Adding a pattern for each culture if there is none if (!settings.Patterns.Where(x => String.Equals(x.Culture, culture, StringComparison.OrdinalIgnoreCase)).Any()) { newPatterns.Add(new RoutePattern { Culture = culture, Name = "Title", Description = "my-title", Pattern = "{Content.Slug}" }); } - //we add a new empty line for each culture + // Adding a new empty line for each culture newPatterns.Add(new RoutePattern { Culture = culture, Name = null, Description = null, Pattern = null }); - // if the content type has no defaultPattern for autoroute, then assign one - if (!settings.DefaultPatterns.Any(x => String.Equals(x.Culture, culture, StringComparison.OrdinalIgnoreCase))) { - //if we are in the default culture check the old setting + // If the content type has no defaultPattern for autoroute, assign one + bool defaultPatternExists = false; + if (String.IsNullOrEmpty(culture)) + defaultPatternExists = settings.DefaultPatterns.Any(x => String.IsNullOrEmpty(x.Culture)); + else + defaultPatternExists = settings.DefaultPatterns.Any(x => String.Equals(x.Culture, culture, StringComparison.OrdinalIgnoreCase)); + + if (!defaultPatternExists) { + // If in the default culture check the old setting if (String.Equals(culture, _cultureManager.GetSiteCulture(), StringComparison.OrdinalIgnoreCase)) { var defaultPatternIndex = settings.DefaultPatternIndex; if (!String.IsNullOrWhiteSpace(defaultPatternIndex)) { var patternIndex = defaultPatternIndex; settings.DefaultPatterns.Add(new DefaultPattern { Culture = settings.DefaultSiteCulture, PatternIndex = patternIndex }); - } else { + } + else { settings.DefaultPatterns.Add(new DefaultPattern { PatternIndex = "0", Culture = culture }); } - } else { + } + else { settings.DefaultPatterns.Add(new DefaultPattern { PatternIndex = "0", Culture = culture }); } } @@ -96,25 +95,51 @@ namespace Orchard.Autoroute.Settings { Patterns = new List() }; - //get cultures + // Get cultures settings.SiteCultures = _cultureManager.ListCultures().ToList(); if (updateModel.TryUpdateModel(settings, "AutorouteSettings", null, null)) { //TODO need to add validations client and/or server side here - // remove empty patterns + + // If some default pattern is an empty pattern set it to the first pattern for the language + List newDefaultPatterns = new List(); + + foreach (var defaultPattern in settings.DefaultPatterns) { + RoutePattern correspondingPattern = null; + + if (string.IsNullOrEmpty(defaultPattern.Culture)) + correspondingPattern = settings.Patterns.Where(x => String.IsNullOrEmpty(x.Culture)).ElementAt(Convert.ToInt32(defaultPattern.PatternIndex)); + else + correspondingPattern = settings.Patterns.Where(x => String.Equals(x.Culture, defaultPattern.Culture, StringComparison.OrdinalIgnoreCase)).ElementAt(Convert.ToInt32(defaultPattern.PatternIndex)); + + if (String.IsNullOrWhiteSpace(correspondingPattern.Name) && String.IsNullOrWhiteSpace(correspondingPattern.Pattern) && String.IsNullOrWhiteSpace(correspondingPattern.Description)) + newDefaultPatterns.Add(new DefaultPattern { Culture = defaultPattern.Culture, PatternIndex = "0" }); + else + newDefaultPatterns.Add(defaultPattern); + } + + settings.DefaultPatterns = newDefaultPatterns; + + // Remove empty patterns var patterns = settings.Patterns; patterns.RemoveAll(p => String.IsNullOrWhiteSpace(p.Name) && String.IsNullOrWhiteSpace(p.Pattern) && String.IsNullOrWhiteSpace(p.Description)); - //If there is no default pattern for each culture we set default ones + // Adding a null culture for the culture neutral pattern + List cultures = new List(); + cultures.Add(null); + cultures.AddRange(settings.SiteCultures); + + //If there is no pattern for some culture create a default one List newPatterns = new List(); int current = 0; - foreach (string culture in settings.SiteCultures) { + foreach (string culture in cultures) { if (settings.Patterns.Any(x => String.Equals(x.Culture, culture, StringComparison.OrdinalIgnoreCase))) { foreach (RoutePattern routePattern in settings.Patterns.Where(x => String.Equals(x.Culture, culture, StringComparison.OrdinalIgnoreCase))) { newPatterns.Add(settings.Patterns[current]); current++; } - } else { + } + else { newPatterns.Add(new RoutePattern { Name = "Title", Description = "my-title", @@ -128,7 +153,7 @@ namespace Orchard.Autoroute.Settings { settings.Patterns = newPatterns; - // update the settings builder + // Update the settings builder settings.Build(builder); } diff --git a/src/Orchard.Web/Modules/Orchard.Autoroute/Views/DefinitionTemplates/AutorouteSettings.cshtml b/src/Orchard.Web/Modules/Orchard.Autoroute/Views/DefinitionTemplates/AutorouteSettings.cshtml index b128b071d..ebc345920 100644 --- a/src/Orchard.Web/Modules/Orchard.Autoroute/Views/DefinitionTemplates/AutorouteSettings.cshtml +++ b/src/Orchard.Web/Modules/Orchard.Autoroute/Views/DefinitionTemplates/AutorouteSettings.cshtml @@ -1,11 +1,15 @@ @model Orchard.Autoroute.Settings.AutorouteSettings -@using Orchard.Utility.Extensions; @{ Script.Require("AutorouteBrowser"); Style.Require("AutorouteSettings"); + int patternCount = 0; int patternCultureCount = 0; + + List cultures = new List(); + cultures.Add(null); + cultures.AddRange(Model.SiteCultures); }
@@ -40,10 +44,10 @@ int i = 1; string cssClass = ""; } -
  • @Model.DefaultSiteCulture
  • - @foreach (var culture in Model.SiteCultures) { - if (culture != Model.DefaultSiteCulture) { - cssClass = i == Model.SiteCultures.Count - 1 ? "last" : "middle"; +
  • @T("All cultures")
  • + @foreach (var culture in cultures) { + if (!String.IsNullOrEmpty(culture)) { + cssClass = i == cultures.Count - 1 ? "last" : "middle";
  • @culture
  • i++; } @@ -51,8 +55,8 @@
    - @foreach (var culture in Model.SiteCultures) { -
    + @foreach (var culture in cultures) { +
    @@ -63,7 +67,7 @@ @for (int index = 0; index < Model.Patterns.Where(x => x.Culture == culture).Count(); index++) { - + diff --git a/src/Orchard.Web/Modules/Orchard.ImportExport/Commands/ImportExportCommands.cs b/src/Orchard.Web/Modules/Orchard.ImportExport/Commands/ImportExportCommands.cs index 061637d4c..9a5763d3b 100644 --- a/src/Orchard.Web/Modules/Orchard.ImportExport/Commands/ImportExportCommands.cs +++ b/src/Orchard.Web/Modules/Orchard.ImportExport/Commands/ImportExportCommands.cs @@ -165,6 +165,13 @@ namespace Orchard.ImportExport.Commands { } if (!String.IsNullOrEmpty(customSteps)) { + var customStepsList = customSteps.Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries); + + foreach (var customStepName in customStepsList) { + GetOrCreateElement(stepsElement, customStepName); + } + + //Still need CustomStepsStep to support older export steps created by users var customStepsElement = GetOrCreateElement(stepsElement, "CustomSteps"); customStepsElement.Attr("Steps", customSteps); } diff --git a/src/Orchard.Web/Modules/Orchard.ImportExport/Providers/ExportActions/BuildRecipeAction.cs b/src/Orchard.Web/Modules/Orchard.ImportExport/Providers/ExportActions/BuildRecipeAction.cs index a02e0dbca..996ccd7e0 100644 --- a/src/Orchard.Web/Modules/Orchard.ImportExport/Providers/ExportActions/BuildRecipeAction.cs +++ b/src/Orchard.Web/Modules/Orchard.ImportExport/Providers/ExportActions/BuildRecipeAction.cs @@ -14,11 +14,13 @@ using Orchard.Recipes.Services; namespace Orchard.ImportExport.Providers.ExportActions { public class BuildRecipeAction : ExportAction { private readonly IEnumerable _recipeBuilderSteps; + private readonly IRecipeBuilderStepResolver _recipeBuilderStepResolver; private readonly IRecipeBuilder _recipeBuilder; private readonly IOrchardServices _orchardServices; - public BuildRecipeAction(IEnumerable recipeBuilderSteps, IRecipeBuilder recipeBuilder, IOrchardServices orchardServices) { + public BuildRecipeAction(IRecipeBuilderStepResolver recipeBuilderStepResolver, IEnumerable recipeBuilderSteps, IRecipeBuilder recipeBuilder, IOrchardServices orchardServices) { _recipeBuilderSteps = recipeBuilderSteps; + _recipeBuilderStepResolver = recipeBuilderStepResolver; _recipeBuilder = recipeBuilder; _orchardServices = orchardServices; @@ -60,10 +62,7 @@ namespace Orchard.ImportExport.Providers.ExportActions { } else { var exportStepNames = viewModel.Steps.Where(x => x.IsSelected).Select(x => x.Name); - var stepsQuery = from name in exportStepNames - let provider = _recipeBuilderSteps.SingleOrDefault(x => x.Name == name) - where provider != null - select provider; + var stepsQuery = _recipeBuilderStepResolver.Resolve(exportStepNames); var steps = stepsQuery.ToArray(); var stepUpdater = new Updater(updater, secondHalf => String.Format("{0}.{1}", Prefix, secondHalf)); foreach (var exportStep in steps) { @@ -86,7 +85,7 @@ namespace Orchard.ImportExport.Providers.ExportActions { return; foreach (var stepElement in recipeBuilderStepsElement.Elements()) { - var step = _recipeBuilderSteps.SingleOrDefault(x => x.Name == stepElement.Name.LocalName); + var step = _recipeBuilderStepResolver.Resolve(stepElement.Name.LocalName); if (step != null) { var stepContext = new RecipeBuilderStepConfigurationContext(stepElement); diff --git a/src/Orchard.Web/Modules/Orchard.JobsQueue/AdminMenu.cs b/src/Orchard.Web/Modules/Orchard.JobsQueue/AdminMenu.cs index 60ce93e28..6c498c1af 100644 --- a/src/Orchard.Web/Modules/Orchard.JobsQueue/AdminMenu.cs +++ b/src/Orchard.Web/Modules/Orchard.JobsQueue/AdminMenu.cs @@ -1,4 +1,5 @@ using Orchard.Environment.Extensions; +using Orchard.Security; using Orchard.UI.Navigation; namespace Orchard.JobsQueue { @@ -11,6 +12,7 @@ namespace Orchard.JobsQueue { builder .AddImageSet("jobsqueue") .Add(T("Jobs Queue"), "15.0", item => { + item.Permission(StandardPermissions.SiteOwner); item.Action("List", "Admin", new { area = "Orchard.JobsQueue" }); item.LinkToFirstChild(false); }); diff --git a/src/Orchard.Web/Modules/Orchard.MediaLibrary/Implementation/DefaultMediaUsername.cs b/src/Orchard.Web/Modules/Orchard.MediaLibrary/Implementation/DefaultMediaUsername.cs new file mode 100644 index 000000000..a068dc318 --- /dev/null +++ b/src/Orchard.Web/Modules/Orchard.MediaLibrary/Implementation/DefaultMediaUsername.cs @@ -0,0 +1,20 @@ +using System; +using Orchard.ContentManagement; +using Orchard.MediaLibrary.Providers; +using Orchard.Security; + +namespace Orchard.MediaLibrary.Implementation { + public class DefaultMediaUsername : IMediaFolderProvider { + public virtual string GetFolderName(IUser content) { + string folder = ""; + foreach (char c in content.UserName) { + if (char.IsLetterOrDigit(c)) { + folder += c; + } + else + folder += "_" + String.Format("{0:X}", Convert.ToInt32(c)); + } + return folder; + } + } +} \ No newline at end of file diff --git a/src/Orchard.Web/Modules/Orchard.MediaLibrary/Orchard.MediaLibrary.csproj b/src/Orchard.Web/Modules/Orchard.MediaLibrary/Orchard.MediaLibrary.csproj index 0a42a0917..d07548bfa 100644 --- a/src/Orchard.Web/Modules/Orchard.MediaLibrary/Orchard.MediaLibrary.csproj +++ b/src/Orchard.Web/Modules/Orchard.MediaLibrary/Orchard.MediaLibrary.csproj @@ -128,6 +128,7 @@ + @@ -200,6 +201,7 @@ + diff --git a/src/Orchard.Web/Modules/Orchard.MediaLibrary/Providers/IMediaFolderProvider.cs b/src/Orchard.Web/Modules/Orchard.MediaLibrary/Providers/IMediaFolderProvider.cs new file mode 100644 index 000000000..07a69b4e8 --- /dev/null +++ b/src/Orchard.Web/Modules/Orchard.MediaLibrary/Providers/IMediaFolderProvider.cs @@ -0,0 +1,7 @@ +using Orchard.Security; + +namespace Orchard.MediaLibrary.Providers { + public interface IMediaFolderProvider : IDependency { + string GetFolderName(IUser content); + } +} \ No newline at end of file diff --git a/src/Orchard.Web/Modules/Orchard.MediaLibrary/Services/MediaLibraryService.cs b/src/Orchard.Web/Modules/Orchard.MediaLibrary/Services/MediaLibraryService.cs index 01aec9503..af39b97fd 100644 --- a/src/Orchard.Web/Modules/Orchard.MediaLibrary/Services/MediaLibraryService.cs +++ b/src/Orchard.Web/Modules/Orchard.MediaLibrary/Services/MediaLibraryService.cs @@ -12,6 +12,7 @@ using Orchard.MediaLibrary.Factories; using Orchard.MediaLibrary.Models; using Orchard.Core.Title.Models; using Orchard.Validation; +using Orchard.MediaLibrary.Providers; namespace Orchard.MediaLibrary.Services { public class MediaLibraryService : IMediaLibraryService { @@ -19,18 +20,20 @@ namespace Orchard.MediaLibrary.Services { private readonly IMimeTypeProvider _mimeTypeProvider; private readonly IStorageProvider _storageProvider; private readonly IEnumerable _mediaFactorySelectors; - + private readonly IMediaFolderProvider _mediaFolderProvider; private static char[] HttpUnallowed = new char[] { '<', '>', '*', '%', '&', ':', '\\', '?', '#' }; public MediaLibraryService( IOrchardServices orchardServices, IMimeTypeProvider mimeTypeProvider, IStorageProvider storageProvider, - IEnumerable mediaFactorySelectors) { + IEnumerable mediaFactorySelectors, + IMediaFolderProvider mediaFolderProvider) { _orchardServices = orchardServices; _mimeTypeProvider = mimeTypeProvider; _storageProvider = storageProvider; _mediaFactorySelectors = mediaFactorySelectors; + _mediaFolderProvider = mediaFolderProvider; T = NullLocalizer.Instance; } @@ -229,8 +232,7 @@ namespace Orchard.MediaLibrary.Services { if (_orchardServices.Authorizer.Authorize(Permissions.ManageOwnMedia)) { var currentUser = _orchardServices.WorkContext.CurrentUser; - var userPath = _storageProvider.Combine("Users", currentUser.UserName); - + var userPath = _storageProvider.Combine("Users", _mediaFolderProvider.GetFolderName(currentUser)); return new MediaFolder() { Name = currentUser.UserName, MediaPath = userPath diff --git a/src/Orchard.Web/Modules/Orchard.Projections/Drivers/ProjectionPartDriver.cs b/src/Orchard.Web/Modules/Orchard.Projections/Drivers/ProjectionPartDriver.cs index 91f7ef174..8ab22df97 100644 --- a/src/Orchard.Web/Modules/Orchard.Projections/Drivers/ProjectionPartDriver.cs +++ b/src/Orchard.Web/Modules/Orchard.Projections/Drivers/ProjectionPartDriver.cs @@ -24,7 +24,7 @@ using Orchard.Utility.Extensions; namespace Orchard.Projections.Drivers { public class ProjectionPartDriver : ContentPartDriver { private readonly IRepository _queryRepository; - private readonly IProjectionManager _projectionManager; + private readonly IProjectionManagerExtension _projectionManager; private readonly IFeedManager _feedManager; private readonly ITokenizer _tokenizer; private readonly IDisplayHelperFactory _displayHelperFactory; @@ -34,7 +34,7 @@ namespace Orchard.Projections.Drivers { public ProjectionPartDriver( IOrchardServices services, IRepository queryRepository, - IProjectionManager projectionManager, + IProjectionManagerExtension projectionManager, IFeedManager feedManager, ITokenizer tokenizer, IDisplayHelperFactory displayHelperFactory, @@ -110,7 +110,7 @@ namespace Orchard.Projections.Drivers { _feedManager.Register(metaData.DisplayText, "rss", new RouteValueDictionary { { "projection", part.Id } }); // execute the query - var contentItems = _projectionManager.GetContentItems(query.Id, pager.GetStartIndex() + part.Record.Skip, pager.PageSize).ToList(); + var contentItems = _projectionManager.GetContentItems(query.Id, part, pager.GetStartIndex() + part.Record.Skip, pager.PageSize).ToList(); // sanity check so that content items with ProjectionPart can't be added here, or it will result in an infinite loop contentItems = contentItems.Where(x => !x.Has()).ToList(); @@ -122,7 +122,7 @@ namespace Orchard.Projections.Drivers { // create pager shape if (part.Record.DisplayPager) { - var contentItemsCount = _projectionManager.GetCount(query.Id) - part.Record.Skip; + var contentItemsCount = _projectionManager.GetCount(query.Id, part) - part.Record.Skip; contentItemsCount = Math.Max(0, contentItemsCount); pagerShape.TotalItemCount(contentItemsCount); } diff --git a/src/Orchard.Web/Modules/Orchard.Projections/Orchard.Projections.csproj b/src/Orchard.Web/Modules/Orchard.Projections/Orchard.Projections.csproj index 8eaa2ccfc..003e87bd9 100644 --- a/src/Orchard.Web/Modules/Orchard.Projections/Orchard.Projections.csproj +++ b/src/Orchard.Web/Modules/Orchard.Projections/Orchard.Projections.csproj @@ -175,6 +175,7 @@ + diff --git a/src/Orchard.Web/Modules/Orchard.Projections/Services/IProjectionManagerExtension.cs b/src/Orchard.Web/Modules/Orchard.Projections/Services/IProjectionManagerExtension.cs new file mode 100644 index 000000000..cad06b62e --- /dev/null +++ b/src/Orchard.Web/Modules/Orchard.Projections/Services/IProjectionManagerExtension.cs @@ -0,0 +1,16 @@ +using System.Collections.Generic; +using Orchard.ContentManagement; +using Orchard.Projections.Descriptors; +using Orchard.Projections.Descriptors.Property; +using Orchard.Projections.Descriptors.Filter; +using Orchard.Projections.Descriptors.Layout; +using Orchard.Projections.Descriptors.SortCriterion; + +namespace Orchard.Projections.Services { + public interface IProjectionManagerExtension : IProjectionManager { + + IEnumerable GetContentItems(int queryId, ContentPart part, int skip = 0, int count = 0); + int GetCount(int queryId, ContentPart part); + } + +} \ No newline at end of file diff --git a/src/Orchard.Web/Modules/Orchard.Projections/Services/ProjectionManager.cs b/src/Orchard.Web/Modules/Orchard.Projections/Services/ProjectionManager.cs index 665197b45..69e343132 100644 --- a/src/Orchard.Web/Modules/Orchard.Projections/Services/ProjectionManager.cs +++ b/src/Orchard.Web/Modules/Orchard.Projections/Services/ProjectionManager.cs @@ -14,7 +14,7 @@ using Orchard.Projections.Models; using Orchard.Tokens; namespace Orchard.Projections.Services { - public class ProjectionManager : IProjectionManager{ + public class ProjectionManager : IProjectionManagerExtension { private readonly ITokenizer _tokenizer; private readonly IEnumerable _filterProviders; private readonly IEnumerable _sortCriterionProviders; @@ -104,6 +104,33 @@ namespace Orchard.Projections.Services { } public int GetCount(int queryId) { + return GetCount(queryId, null); + } + + public int GetCount(int queryId, ContentPart part) { + var queryRecord = _queryRepository.Get(queryId); + + if (queryRecord == null) { + throw new ArgumentException("queryId"); + } + + // prepares tokens + Dictionary tokens = new Dictionary(); + if (part != null) { + tokens.Add("Content", part.ContentItem); + } + + // aggregate the result for each group query + return GetContentQueries(queryRecord, Enumerable.Empty(), tokens) + .Sum(contentQuery => contentQuery.Count()); + } + + public IEnumerable GetContentItems(int queryId, int skip = 0, int count = 0) { + return GetContentItems(queryId, null, skip, count); + } + + public IEnumerable GetContentItems(int queryId, ContentPart part, int skip = 0, int count = 0) { + var availableSortCriteria = DescribeSortCriteria().ToList(); var queryRecord = _queryRepository.Get(queryId); @@ -111,36 +138,27 @@ namespace Orchard.Projections.Services { throw new ArgumentException("queryId"); } - // aggregate the result for each group query - - return GetContentQueries(queryRecord, Enumerable.Empty()) - .Sum(contentQuery => contentQuery.Count()); - } - - public IEnumerable GetContentItems(int queryId, int skip = 0, int count = 0) { - var availableSortCriteria = DescribeSortCriteria().ToList(); - - var queryRecord = _queryRepository.Get(queryId); - - if(queryRecord == null) { - throw new ArgumentException("queryId"); - } - var contentItems = new List(); + // prepares tokens + Dictionary tokens = new Dictionary(); + if (part != null) { + tokens.Add("Content", part.ContentItem); + } + // aggregate the result for each group query - foreach(var contentQuery in GetContentQueries(queryRecord, queryRecord.SortCriteria.OrderBy(sc => sc.Position))) { + foreach (var contentQuery in GetContentQueries(queryRecord, queryRecord.SortCriteria.OrderBy(sc => sc.Position), tokens)) { contentItems.AddRange(contentQuery.Slice(skip, count)); } - if(queryRecord.FilterGroups.Count <= 1) { + if (queryRecord.FilterGroups.Count <= 1) { return contentItems; } // re-executing the sorting with the cumulated groups var ids = contentItems.Select(c => c.Id).ToArray(); - if(ids.Length == 0) { + if (ids.Length == 0) { return Enumerable.Empty(); } @@ -173,9 +191,13 @@ namespace Orchard.Projections.Services { return groupQuery.Slice(skip, count); } - public IEnumerable GetContentQueries(QueryPartRecord queryRecord, IEnumerable sortCriteria) { + public IEnumerable GetContentQueries(QueryPartRecord queryRecord, IEnumerable sortCriteria, Dictionary tokens) { + var availableFilters = DescribeFilters().ToList(); var availableSortCriteria = DescribeSortCriteria().ToList(); + if (tokens == null) { + tokens = new Dictionary(); + } // pre-executing all groups foreach (var group in queryRecord.FilterGroups) { @@ -184,7 +206,7 @@ namespace Orchard.Projections.Services { // iterate over each filter to apply the alterations to the query object foreach (var filter in group.Filters) { - var tokenizedState = _tokenizer.Replace(filter.State, new Dictionary()); + var tokenizedState = _tokenizer.Replace(filter.State, tokens); var filterContext = new FilterContext { Query = contentQuery, State = FormParametersHelper.ToDynamic(tokenizedState) @@ -237,7 +259,7 @@ namespace Orchard.Projections.Services { yield return contentQuery; - } + } } } } diff --git a/src/Orchard.Web/Modules/Orchard.Recipes/Providers/RecipeHandlers/RecipeExecutionStepHandler.cs b/src/Orchard.Web/Modules/Orchard.Recipes/Providers/RecipeHandlers/RecipeExecutionStepHandler.cs index b1d0edc61..0cb9e8ee9 100644 --- a/src/Orchard.Web/Modules/Orchard.Recipes/Providers/RecipeHandlers/RecipeExecutionStepHandler.cs +++ b/src/Orchard.Web/Modules/Orchard.Recipes/Providers/RecipeHandlers/RecipeExecutionStepHandler.cs @@ -9,13 +9,13 @@ namespace Orchard.Recipes.Providers.RecipeHandlers { /// Delegates execution of the step to the appropriate recipe execution step implementation. /// public class RecipeExecutionStepHandler : Component, IRecipeHandler { - private readonly IEnumerable _recipeExecutionSteps; - public RecipeExecutionStepHandler(IEnumerable recipeExecutionSteps) { - _recipeExecutionSteps = recipeExecutionSteps; + private readonly IRecipeExecutionStepResolver _recipeExecutionStepResolver; + public RecipeExecutionStepHandler(IRecipeExecutionStepResolver recipeExecutionStepResolver) { + _recipeExecutionStepResolver = recipeExecutionStepResolver; } public void ExecuteRecipeStep(RecipeContext recipeContext) { - var executionStep = _recipeExecutionSteps.FirstOrDefault(x => x.Names.Contains(recipeContext.RecipeStep.Name)); + var executionStep = _recipeExecutionStepResolver.Resolve(recipeContext.RecipeStep.Name); var recipeExecutionContext = new RecipeExecutionContext {ExecutionId = recipeContext.ExecutionId, RecipeStep = recipeContext.RecipeStep}; if (executionStep != null) { diff --git a/src/Orchard.Web/Modules/Orchard.Scripting.CSharp/Services/CSharpService.cs b/src/Orchard.Web/Modules/Orchard.Scripting.CSharp/Services/CSharpService.cs index bb8594d3e..dcc1c46c5 100644 --- a/src/Orchard.Web/Modules/Orchard.Scripting.CSharp/Services/CSharpService.cs +++ b/src/Orchard.Web/Modules/Orchard.Scripting.CSharp/Services/CSharpService.cs @@ -52,6 +52,7 @@ namespace Orchard.Scripting.CSharp.Services { Engine.Run("using System;"); Engine.Run("using System.Collections.Generic;"); + Engine.Run("using System.Linq;"); Engine.Run("var dictionary = new Dictionary();"); Dictionary = Engine.Evaluate("dictionary") as IDictionary; } diff --git a/src/Orchard.Web/Modules/Orchard.Users/Models/UserPart.cs b/src/Orchard.Web/Modules/Orchard.Users/Models/UserPart.cs index 7afb5819f..45722af93 100644 --- a/src/Orchard.Web/Modules/Orchard.Users/Models/UserPart.cs +++ b/src/Orchard.Web/Modules/Orchard.Users/Models/UserPart.cs @@ -6,7 +6,7 @@ using System.Web.Security; namespace Orchard.Users.Models { public sealed class UserPart : ContentPart, IUser { public const string EmailPattern = - @"^(?![\.@])(""([^""\r\\]|\\[""\r\\])*""|([-a-z0-9!#$%&'*+/=?^_`{|}~]|(? + + + + diff --git a/src/Orchard/Recipes/Services/IRecipeBuilderStepResolver.cs b/src/Orchard/Recipes/Services/IRecipeBuilderStepResolver.cs new file mode 100644 index 000000000..fa4f97f82 --- /dev/null +++ b/src/Orchard/Recipes/Services/IRecipeBuilderStepResolver.cs @@ -0,0 +1,10 @@ +using System.Collections.Generic; + +namespace Orchard.Recipes.Services +{ + public interface IRecipeBuilderStepResolver : IDependency + { + IRecipeBuilderStep Resolve(string exportStepName); + IEnumerable Resolve(IEnumerable exportStepNames); + } +} \ No newline at end of file diff --git a/src/Orchard/Recipes/Services/IRecipeExecutionStepResolver.cs b/src/Orchard/Recipes/Services/IRecipeExecutionStepResolver.cs new file mode 100644 index 000000000..bd57b2165 --- /dev/null +++ b/src/Orchard/Recipes/Services/IRecipeExecutionStepResolver.cs @@ -0,0 +1,10 @@ +using System.Collections.Generic; + +namespace Orchard.Recipes.Services +{ + public interface IRecipeExecutionStepResolver :IDependency + { + IRecipeExecutionStep Resolve(string importStepName); + IEnumerable Resolve(IEnumerable exportStepNames); + } +} \ No newline at end of file diff --git a/src/Orchard/Recipes/Services/RecipeBuilderStepResolver.cs b/src/Orchard/Recipes/Services/RecipeBuilderStepResolver.cs new file mode 100644 index 000000000..f75f19cb4 --- /dev/null +++ b/src/Orchard/Recipes/Services/RecipeBuilderStepResolver.cs @@ -0,0 +1,28 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Orchard.Recipes.Services +{ + public class RecipeBuilderStepResolver : IRecipeBuilderStepResolver + { + private readonly IEnumerable _recipeBuilderSteps; + + public RecipeBuilderStepResolver(IEnumerable recipeBuilderSteps) { + _recipeBuilderSteps = recipeBuilderSteps; + } + + public IRecipeBuilderStep Resolve(string exportStepName) { + return _recipeBuilderSteps.SingleOrDefault(x => x.Name == exportStepName); + } + + public IEnumerable Resolve(IEnumerable exportStepNames) { + return from name in exportStepNames + let provider = _recipeBuilderSteps.SingleOrDefault(x => x.Name == name) + where provider != null + select provider; + } + } +} diff --git a/src/Orchard/Recipes/Services/RecipeExecutionStepResolver.cs b/src/Orchard/Recipes/Services/RecipeExecutionStepResolver.cs new file mode 100644 index 000000000..564786ff7 --- /dev/null +++ b/src/Orchard/Recipes/Services/RecipeExecutionStepResolver.cs @@ -0,0 +1,28 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Orchard.Recipes.Services +{ + public class RecipeExecutionStepResolver : IRecipeExecutionStepResolver + { + private readonly IEnumerable _recipeExecutionSteps; + + public RecipeExecutionStepResolver(IEnumerable recipeExecutionSteps) { + _recipeExecutionSteps = recipeExecutionSteps; + } + + public IRecipeExecutionStep Resolve(string importStepName) { + return _recipeExecutionSteps.SingleOrDefault(x => x.Names.Contains(importStepName)); + } + + public IEnumerable Resolve(IEnumerable importStepNames) { + return from name in importStepNames + let provider = _recipeExecutionSteps.SingleOrDefault(x => x.Names.Contains(name)) + where provider != null + select provider; + } + } +}
    @T("Default")
    @Html.RadioButtonFor(m => m.DefaultPatterns[Model.SiteCultures.IndexOf(culture)].Culture, culture + "|" + patternCultureCount, patternCultureCount.ToString() == Model.DefaultPatterns[Model.SiteCultures.IndexOf(culture)].PatternIndex ? new { @checked = "checked" } : null)@Html.RadioButtonFor(m => m.DefaultPatterns[cultures.IndexOf(culture)].Culture, culture + "|" + patternCultureCount, patternCultureCount.ToString() == Model.DefaultPatterns[cultures.IndexOf(culture)].PatternIndex ? new { @checked = "checked" } : null) @Html.TextBoxFor(m => m.Patterns[patternCount].Name, new { @class = "text" }) @Html.TextBoxFor(m => m.Patterns[patternCount].Pattern, new { @class = "tokenized text" }) @Html.TextBoxFor(m => m.Patterns[patternCount].Description, new { @class = "text" })