diff --git a/src/Orchard.Core.Tests/Feeds/Controllers/FeedControllerTests.cs b/src/Orchard.Core.Tests/Feeds/Controllers/FeedControllerTests.cs index 56c07dc076f..151d9773a27 100644 --- a/src/Orchard.Core.Tests/Feeds/Controllers/FeedControllerTests.cs +++ b/src/Orchard.Core.Tests/Feeds/Controllers/FeedControllerTests.cs @@ -20,6 +20,7 @@ using Orchard.Tests.Modules; using Orchard.Tests.Stubs; using Orchard.Core.Title.Models; +using Orchard.Services; namespace Orchard.Core.Tests.Feeds.Controllers { [TestFixture] @@ -177,6 +178,7 @@ public void CorePartValuesAreExtracted() { builder.RegisterInstance(mockContentManager.Object).As(); builder.RegisterType().As(); builder.RegisterType().As(); + builder.RegisterType().As(); builder.RegisterInstance(query).As(); var container = builder.Build(); diff --git a/src/Orchard.Web/Core/Common/Drivers/BodyPartDriver.cs b/src/Orchard.Web/Core/Common/Drivers/BodyPartDriver.cs index 064dee05f1e..55c46eac227 100644 --- a/src/Orchard.Web/Core/Common/Drivers/BodyPartDriver.cs +++ b/src/Orchard.Web/Core/Common/Drivers/BodyPartDriver.cs @@ -1,12 +1,9 @@ -using System.Collections.Generic; -using System.Linq; -using System.Web; +using System.Web; using Orchard.Mvc.Html; using Orchard.ContentManagement; using Orchard.ContentManagement.Aspects; using Orchard.ContentManagement.Drivers; using Orchard.Core.Common.Models; -using Orchard.Core.Common.Settings; using Orchard.Core.Common.ViewModels; using Orchard.Services; using System.Web.Mvc; @@ -15,13 +12,13 @@ namespace Orchard.Core.Common.Drivers { public class BodyPartDriver : ContentPartDriver { - private readonly IEnumerable _htmlFilters; + private readonly IHtmlFilterProcessor _htmlFilterProcessor; private readonly RequestContext _requestContext; private const string TemplateName = "Parts.Common.Body"; - public BodyPartDriver(IOrchardServices services, IEnumerable htmlFilters, RequestContext requestContext) { - _htmlFilters = htmlFilters; + public BodyPartDriver(IOrchardServices services, IHtmlFilterProcessor htmlFilterProcessor, RequestContext requestContext) { + _htmlFilterProcessor = htmlFilterProcessor; Services = services; _requestContext = requestContext; } @@ -33,32 +30,27 @@ protected override string Prefix { } protected override DriverResult Display(BodyPart part, string displayType, dynamic shapeHelper) { + string GetProcessedBodyText() => _htmlFilterProcessor.ProcessFilters(part.Text, part.GetFlavor(), part); + return Combined( - ContentShape("Parts_Common_Body", - () => { - var bodyText = _htmlFilters.Aggregate(part.Text, (text, filter) => filter.ProcessContent(text, GetFlavor(part))); - return shapeHelper.Parts_Common_Body(Html: new HtmlString(bodyText)); - }), - ContentShape("Parts_Common_Body_Summary", - () => { - var bodyText = _htmlFilters.Aggregate(part.Text, (text, filter) => filter.ProcessContent(text, GetFlavor(part))); - return shapeHelper.Parts_Common_Body_Summary(Html: new HtmlString(bodyText)); - }) - ); + ContentShape("Parts_Common_Body", () => + shapeHelper.Parts_Common_Body(Html: new HtmlString(GetProcessedBodyText()))), + ContentShape("Parts_Common_Body_Summary", () => + shapeHelper.Parts_Common_Body_Summary(Html: new HtmlString(GetProcessedBodyText())))); } protected override DriverResult Editor(BodyPart part, dynamic shapeHelper) { var model = BuildEditorViewModel(part,_requestContext); - return ContentShape("Parts_Common_Body_Edit", - () => shapeHelper.EditorTemplate(TemplateName: TemplateName, Model: model, Prefix: Prefix)); + return ContentShape("Parts_Common_Body_Edit", () => + shapeHelper.EditorTemplate(TemplateName: TemplateName, Model: model, Prefix: Prefix)); } protected override DriverResult Editor(BodyPart part, IUpdateModel updater, dynamic shapeHelper) { var model = BuildEditorViewModel(part, _requestContext); updater.TryUpdateModel(model, Prefix, null, null); - return ContentShape("Parts_Common_Body_Edit", - () => shapeHelper.EditorTemplate(TemplateName: TemplateName, Model: model, Prefix: Prefix)); + return ContentShape("Parts_Common_Body_Edit", () => + shapeHelper.EditorTemplate(TemplateName: TemplateName, Model: model, Prefix: Prefix)); } protected override void Importing(BodyPart part, ContentManagement.Handlers.ImportContentContext context) { @@ -83,18 +75,11 @@ protected override void Cloning(BodyPart originalPart, BodyPart clonePart, Clone private static BodyEditorViewModel BuildEditorViewModel(BodyPart part,RequestContext requestContext) { return new BodyEditorViewModel { BodyPart = part, - EditorFlavor = GetFlavor(part), + EditorFlavor = part.GetFlavor(), AddMediaPath = new PathBuilder(part,requestContext).AddContentType().AddContainerSlug().ToString() }; } - private static string GetFlavor(BodyPart part) { - var typePartSettings = part.Settings.GetModel(); - return (typePartSettings != null && !string.IsNullOrWhiteSpace(typePartSettings.Flavor)) - ? typePartSettings.Flavor - : part.PartDefinition.Settings.GetModel().FlavorDefault; - } - class PathBuilder { private readonly IContent _content; private string _path; diff --git a/src/Orchard.Web/Core/Common/Drivers/TextFieldDriver.cs b/src/Orchard.Web/Core/Common/Drivers/TextFieldDriver.cs index b29b3cb4417..4761bb94867 100644 --- a/src/Orchard.Web/Core/Common/Drivers/TextFieldDriver.cs +++ b/src/Orchard.Web/Core/Common/Drivers/TextFieldDriver.cs @@ -1,6 +1,4 @@ using System; -using System.Collections.Generic; -using System.Linq; using System.Web; using Orchard.ContentManagement; using Orchard.ContentManagement.Drivers; @@ -14,10 +12,10 @@ namespace Orchard.Core.Common.Drivers { public class TextFieldDriver : ContentFieldDriver { - private readonly IEnumerable _htmlFilters; + private readonly IHtmlFilterProcessor _htmlFilterProcessor; - public TextFieldDriver(IOrchardServices services, IEnumerable htmlFilters) { - _htmlFilters = htmlFilters; + public TextFieldDriver(IOrchardServices services, IHtmlFilterProcessor htmlFilterProcessor) { + _htmlFilterProcessor = htmlFilterProcessor; Services = services; T = NullLocalizer.Instance; } @@ -37,8 +35,7 @@ protected override DriverResult Display(ContentPart part, TextField field, strin return ContentShape("Fields_Common_Text", GetDifferentiator(field, part), () => { var settings = field.PartFieldDefinition.Settings.GetModel(); - - object fieldValue = new HtmlString(_htmlFilters.Aggregate(field.Value, (text, filter) => filter.ProcessContent(text, settings.Flavor))); + var fieldValue = new HtmlString(_htmlFilterProcessor.ProcessFilters(field.Value, settings.Flavor, part)); return shapeHelper.Fields_Common_Text(Name: field.Name, Value: fieldValue); }); } @@ -75,12 +72,12 @@ protected override DriverResult Editor(ContentPart part, TextField field, IUpdat if (settings.MaxLength > 0) { - var value = new HtmlString(_htmlFilters.Aggregate(field.Value, (text, filter) => filter.ProcessContent(text, settings.Flavor))) + var value = new HtmlString(_htmlFilterProcessor.ProcessFilters(field.Value, settings.Flavor, part)) .ToString().RemoveTags(); if (value.Length > settings.MaxLength) { updater.AddModelError("Text", T("The maximum allowed length for the field {0} is {1}", T(field.DisplayName), settings.MaxLength)); - } + } } } diff --git a/src/Orchard.Web/Core/Common/Models/BodyPart.cs b/src/Orchard.Web/Core/Common/Models/BodyPart.cs index 7d191cce2c5..c797fb68638 100644 --- a/src/Orchard.Web/Core/Common/Models/BodyPart.cs +++ b/src/Orchard.Web/Core/Common/Models/BodyPart.cs @@ -1,4 +1,5 @@ using Orchard.ContentManagement; +using Orchard.Core.Common.Settings; namespace Orchard.Core.Common.Models { public class BodyPart : ContentPart { @@ -11,5 +12,12 @@ public string Format { get { return Retrieve(x => x.Format); } set { Store(x => x.Format, value); } } + + public string GetFlavor() { + var typePartSettings = Settings.GetModel(); + return string.IsNullOrWhiteSpace(typePartSettings?.Flavor) + ? PartDefinition.Settings.GetModel().FlavorDefault + : typePartSettings.Flavor; + } } } diff --git a/src/Orchard.Web/Core/Common/Services/BbcodeFilter.cs b/src/Orchard.Web/Core/Common/Services/BbcodeFilter.cs index 33c406cfded..78a2e4f89b7 100644 --- a/src/Orchard.Web/Core/Common/Services/BbcodeFilter.cs +++ b/src/Orchard.Web/Core/Common/Services/BbcodeFilter.cs @@ -5,27 +5,26 @@ using Orchard.Services; namespace Orchard.Core.Common.Services { - public class BbcodeFilter : IHtmlFilter { - public string ProcessContent(string text, string flavor) { - return BbcodeReplace(text); + public class BbcodeFilter : HtmlFilter { + public override string ProcessContent(string text, HtmlFilterContext context) { + return BbcodeReplace(text, context); } // Can be moved somewhere else once we have IoC enabled body text filters. - private static string BbcodeReplace(string text) { - if (string.IsNullOrEmpty(text)) - return string.Empty; + private static string BbcodeReplace(string text, HtmlFilterContext context) { + if (String.IsNullOrEmpty(text)) + return String.Empty; - // optimize code path if nothing to do + // Optimize code path if nothing to do. if (!text.Contains("[url]") && !text.Contains("[img]") && !text.Contains("[url=")) { return text; } var sb = new StringBuilder(text); - var index = -1; var allIndexes = new List(); - // process all [url] + // Process all [url]. while (-1 != (index = text.IndexOf("[url]", index + 1, StringComparison.Ordinal))) { allIndexes.Add(index); } @@ -63,7 +62,7 @@ private static string BbcodeReplace(string text) { var url = text.Substring(start + 5, urlEnd - start - 5); var title = text.Substring(urlEnd + 1, end - urlEnd - 1); - // substitue [url] by + // Substitute [url] by . sb.Remove(start, end - start + 6); sb.Insert(start, String.Format("{1}", url, title)); } @@ -85,7 +84,7 @@ private static string BbcodeReplace(string text) { var url = text.Substring(start + 5, end - start - 5); - // substitue [url] by + // Substitue [url] by . sb.Remove(start, end - start + 6); if (!string.IsNullOrEmpty(url)) { diff --git a/src/Orchard.Web/Core/Common/Services/TextFieldFilter.cs b/src/Orchard.Web/Core/Common/Services/TextFieldFilter.cs index 263d17af5bd..75713da339e 100644 --- a/src/Orchard.Web/Core/Common/Services/TextFieldFilter.cs +++ b/src/Orchard.Web/Core/Common/Services/TextFieldFilter.cs @@ -4,10 +4,10 @@ using Orchard.Utility.Extensions; namespace Orchard.Core.Common.Services { - public class TextFieldFilter : IHtmlFilter { - public string ProcessContent(string text, string flavor) { + public class TextFieldFilter : HtmlFilter { + public override string ProcessContent(string text, HtmlFilterContext context) { // Flavor is null for a normal input/text field - return flavor == null || string.Equals(flavor, "textarea", StringComparison.OrdinalIgnoreCase) ? ReplaceNewLines(text) : text; + return context.Flavor == null || String.Equals(context.Flavor, "textarea", StringComparison.OrdinalIgnoreCase) ? ReplaceNewLines(text) : text; } private static string ReplaceNewLines(string text) { diff --git a/src/Orchard.Web/Core/Feeds/StandardBuilders/CorePartsFeedItemBuilder.cs b/src/Orchard.Web/Core/Feeds/StandardBuilders/CorePartsFeedItemBuilder.cs index 79f36b8abce..84a9957ad8e 100644 --- a/src/Orchard.Web/Core/Feeds/StandardBuilders/CorePartsFeedItemBuilder.cs +++ b/src/Orchard.Web/Core/Feeds/StandardBuilders/CorePartsFeedItemBuilder.cs @@ -13,15 +13,15 @@ namespace Orchard.Core.Feeds.StandardBuilders { public class CorePartsFeedItemBuilder : IFeedItemBuilder { private readonly IContentManager _contentManager; private readonly RouteCollection _routes; - private readonly IEnumerable _htmlFilters; + private readonly IHtmlFilterProcessor _htmlFilterProcessor; public CorePartsFeedItemBuilder( IContentManager contentManager, RouteCollection routes, - IEnumerable htmlFilters) { + IHtmlFilterProcessor htmlFilterProcessor) { _contentManager = contentManager; _routes = routes; - _htmlFilters = htmlFilters; + _htmlFilterProcessor = htmlFilterProcessor; } public void Populate(FeedContext context) { @@ -29,8 +29,8 @@ public void Populate(FeedContext context) { var inspector = new ItemInspector( feedItem.Item, - _contentManager.GetItemMetadata(feedItem.Item), - _htmlFilters); + _contentManager.GetItemMetadata(feedItem.Item), + _htmlFilterProcessor); // author is intentionally left empty as it could result in unwanted spam diff --git a/src/Orchard.Web/Core/Feeds/StandardBuilders/ItemInspector.cs b/src/Orchard.Web/Core/Feeds/StandardBuilders/ItemInspector.cs index a382c0fe849..b05c5c8d042 100644 --- a/src/Orchard.Web/Core/Feeds/StandardBuilders/ItemInspector.cs +++ b/src/Orchard.Web/Core/Feeds/StandardBuilders/ItemInspector.cs @@ -12,17 +12,17 @@ namespace Orchard.Core.Feeds.StandardBuilders { public class ItemInspector { private readonly IContent _item; private readonly ContentItemMetadata _metadata; - private readonly IEnumerable _htmlFilters; + private readonly IHtmlFilterProcessor _htmlFilterProcessor; private readonly ICommonPart _common; private readonly ITitleAspect _titleAspect; private readonly BodyPart _body; - public ItemInspector(IContent item, ContentItemMetadata metadata) : this(item, metadata, Enumerable.Empty()) {} + public ItemInspector(IContent item, ContentItemMetadata metadata) : this(item, metadata, null) {} - public ItemInspector(IContent item, ContentItemMetadata metadata, IEnumerable htmlFilters) { + public ItemInspector(IContent item, ContentItemMetadata metadata, IHtmlFilterProcessor htmlFilterProcessor) { _item = item; _metadata = metadata; - _htmlFilters = htmlFilters; + _htmlFilterProcessor = htmlFilterProcessor; _common = item.Get(); _titleAspect = item.Get(); _body = item.Get(); @@ -49,8 +49,8 @@ public RouteValueDictionary Link { public string Description { get { - if (_body != null && !string.IsNullOrEmpty(_body.Text)) { - return _htmlFilters.Aggregate(_body.Text, (text, filter) => filter.ProcessContent(text, GetFlavor(_body))); + if (_htmlFilterProcessor != null && _body != null && !string.IsNullOrEmpty(_body.Text)) { + return _htmlFilterProcessor.ProcessFilters(_body.Text, GetFlavor(_body), _body); } return Title; } diff --git a/src/Orchard.Web/Core/Feeds/StandardQueries/ContainerFeedQuery.cs b/src/Orchard.Web/Core/Feeds/StandardQueries/ContainerFeedQuery.cs index c1550e7beb8..694831a1b72 100644 --- a/src/Orchard.Web/Core/Feeds/StandardQueries/ContainerFeedQuery.cs +++ b/src/Orchard.Web/Core/Feeds/StandardQueries/ContainerFeedQuery.cs @@ -1,5 +1,4 @@ using System; -using System.Collections.Generic; using System.Web.Mvc; using System.Xml.Linq; using Orchard.ContentManagement; @@ -8,16 +7,16 @@ using Orchard.Core.Feeds.StandardBuilders; using Orchard.Mvc.Extensions; using Orchard.Services; -using Orchard.Utility.Extensions; -namespace Orchard.Core.Feeds.StandardQueries { +namespace Orchard.Core.Feeds.StandardQueries +{ public class ContainerFeedQuery : IFeedQueryProvider, IFeedQuery { private readonly IContentManager _contentManager; - private readonly IEnumerable _htmlFilters; + private readonly IHtmlFilterProcessor _htmlFilterProcessor; - public ContainerFeedQuery(IContentManager contentManager, IEnumerable htmlFilters) { + public ContainerFeedQuery(IContentManager contentManager, IHtmlFilterProcessor htmlFilterProcessor) { _contentManager = contentManager; - _htmlFilters = htmlFilters; + _htmlFilterProcessor = htmlFilterProcessor; } public FeedQueryMatch Match(FeedContext context) { @@ -55,7 +54,7 @@ public void Execute(FeedContext context) { return; } - var inspector = new ItemInspector(container, _contentManager.GetItemMetadata(container), _htmlFilters); + var inspector = new ItemInspector(container, _contentManager.GetItemMetadata(container), _htmlFilterProcessor); if (context.Format == "rss") { var link = new XElement("link"); context.Response.Element.SetElementValue("title", inspector.Title); diff --git a/src/Orchard.Web/Core/Navigation/Views/MenuItemLink-HtmlMenuItem.cshtml b/src/Orchard.Web/Core/Navigation/Views/MenuItemLink-HtmlMenuItem.cshtml index 6ac21f40418..12a799df244 100644 --- a/src/Orchard.Web/Core/Navigation/Views/MenuItemLink-HtmlMenuItem.cshtml +++ b/src/Orchard.Web/Core/Navigation/Views/MenuItemLink-HtmlMenuItem.cshtml @@ -1 +1,10 @@ -@Html.Raw(Model.Content.BodyPart.Text) +@using Orchard.Core.Common.Models +@using Orchard.Services + +@{ + var htmlFilterProcessor = WorkContext.Resolve(); + var bodyPart = Model.Content.BodyPart as BodyPart; + var bodyText = htmlFilterProcessor.ProcessFilters(bodyPart.Text, bodyPart.GetFlavor(), bodyPart); +} + +@Html.Raw(bodyText) diff --git a/src/Orchard.Web/Modules/Markdown/Services/MarkdownFilter.cs b/src/Orchard.Web/Modules/Markdown/Services/MarkdownFilter.cs index cf5d1db3e5d..ec66bb30e28 100644 --- a/src/Orchard.Web/Modules/Markdown/Services/MarkdownFilter.cs +++ b/src/Orchard.Web/Modules/Markdown/Services/MarkdownFilter.cs @@ -1,15 +1,16 @@ using System; using Orchard.Services; -namespace Markdown.Services { - public class MarkdownFilter : IHtmlFilter { - public string ProcessContent(string text, string flavor) { - return String.Equals(flavor, "markdown", StringComparison.OrdinalIgnoreCase) ? MarkdownReplace(text) : text; +namespace Markdown.Services +{ + public class MarkdownFilter : HtmlFilter { + public override string ProcessContent(string text, HtmlFilterContext context) { + return String.Equals(context.Flavor, "markdown", StringComparison.OrdinalIgnoreCase) ? MarkdownReplace(text) : text; } private static string MarkdownReplace(string text) { - if (string.IsNullOrEmpty(text)) - return string.Empty; + if (String.IsNullOrEmpty(text)) + return String.Empty; return Markdig.Markdown.ToHtml(text); } diff --git a/src/Orchard.Web/Modules/Orchard.Azure.MediaServices/Orchard.Azure.MediaServices.csproj b/src/Orchard.Web/Modules/Orchard.Azure.MediaServices/Orchard.Azure.MediaServices.csproj index 7d1c324bb43..ab5b25bdeea 100644 --- a/src/Orchard.Web/Modules/Orchard.Azure.MediaServices/Orchard.Azure.MediaServices.csproj +++ b/src/Orchard.Web/Modules/Orchard.Azure.MediaServices/Orchard.Azure.MediaServices.csproj @@ -79,14 +79,14 @@ ..\..\..\packages\Microsoft.Data.Services.Client.5.8.4\lib\net40\Microsoft.Data.Services.Client.dll - - ..\..\..\packages\Microsoft.IdentityModel.JsonWebTokens.5.2.4\lib\net451\Microsoft.IdentityModel.JsonWebTokens.dll + + ..\..\..\packages\Microsoft.IdentityModel.JsonWebTokens.5.7.0\lib\net461\Microsoft.IdentityModel.JsonWebTokens.dll - - ..\..\..\packages\Microsoft.IdentityModel.Logging.5.2.4\lib\net451\Microsoft.IdentityModel.Logging.dll + + ..\..\..\packages\Microsoft.IdentityModel.Logging.5.7.0\lib\net461\Microsoft.IdentityModel.Logging.dll - - ..\..\..\packages\Microsoft.IdentityModel.Tokens.5.2.4\lib\net451\Microsoft.IdentityModel.Tokens.dll + + ..\..\..\packages\Microsoft.IdentityModel.Tokens.5.7.0\lib\net461\Microsoft.IdentityModel.Tokens.dll ..\..\..\packages\TransientFaultHandling.Core.5.1.1209.1\lib\NET4\Microsoft.Practices.TransientFaultHandling.Core.dll @@ -130,8 +130,8 @@ 3.5 - - ..\..\..\packages\System.IdentityModel.Tokens.Jwt.5.2.4\lib\net451\System.IdentityModel.Tokens.Jwt.dll + + ..\..\..\packages\System.IdentityModel.Tokens.Jwt.5.7.0\lib\net461\System.IdentityModel.Tokens.Jwt.dll diff --git a/src/Orchard.Web/Modules/Orchard.Azure.MediaServices/Services/Rendering/CloudVideoFilter.cs b/src/Orchard.Web/Modules/Orchard.Azure.MediaServices/Services/Rendering/CloudVideoFilter.cs index 56d475d85a4..b98ee97279a 100644 --- a/src/Orchard.Web/Modules/Orchard.Azure.MediaServices/Services/Rendering/CloudVideoFilter.cs +++ b/src/Orchard.Web/Modules/Orchard.Azure.MediaServices/Services/Rendering/CloudVideoFilter.cs @@ -12,7 +12,7 @@ using Orchard.Services; namespace Orchard.Azure.MediaServices.Services.Rendering { - public class CloudVideoFilter : IHtmlFilter { + public class CloudVideoFilter : HtmlFilter { private readonly IShapeFactory _shapeFactory; private readonly IContentManager _contentManager; private readonly IShapeDisplay _shapeDisplay; @@ -23,8 +23,8 @@ public CloudVideoFilter(IShapeFactory shapeFactory, IContentManager contentManag _shapeDisplay = shapeDisplay; } - public string ProcessContent(string text, string flavor) { - return String.Equals(flavor, "html", StringComparison.OrdinalIgnoreCase) ? ReplaceVideoPlaceholder(text) : text; + public override string ProcessContent(string text, HtmlFilterContext context) { + return String.Equals(context.Flavor, "html", StringComparison.OrdinalIgnoreCase) ? ReplaceVideoPlaceholder(text) : text; } private string ReplaceVideoPlaceholder(string text) { diff --git a/src/Orchard.Web/Modules/Orchard.Azure.MediaServices/packages.config b/src/Orchard.Web/Modules/Orchard.Azure.MediaServices/packages.config index 670d7e211cd..670821743e1 100644 --- a/src/Orchard.Web/Modules/Orchard.Azure.MediaServices/packages.config +++ b/src/Orchard.Web/Modules/Orchard.Azure.MediaServices/packages.config @@ -12,9 +12,9 @@ - - - + + + @@ -22,7 +22,7 @@ - + diff --git a/src/Orchard.Web/Modules/Orchard.Azure/Services/FileSystems/AzureFileSystem.cs b/src/Orchard.Web/Modules/Orchard.Azure/Services/FileSystems/AzureFileSystem.cs index 12b2030290e..f4d586b2471 100644 --- a/src/Orchard.Web/Modules/Orchard.Azure/Services/FileSystems/AzureFileSystem.cs +++ b/src/Orchard.Web/Modules/Orchard.Azure/Services/FileSystems/AzureFileSystem.cs @@ -96,6 +96,8 @@ private static string ConvertToRelativeUriPath(string path) { return newPath; } + private static string GetFolderName(string path) => path.Substring(path.LastIndexOf('/') + 1); + public string Combine(string path1, string path2) { if (path1 == null) { throw new ArgumentNullException("path1"); @@ -148,10 +150,10 @@ public IEnumerable ListFiles(string path) { } return BlobClient.ListBlobs(prefix) - .OfType() - .Where(blobItem => !blobItem.Uri.AbsoluteUri.EndsWith(FolderEntry)) - .Select(blobItem => new AzureBlobFileStorage(blobItem, _absoluteRoot)) - .ToArray(); + .OfType() + .Where(blobItem => !blobItem.Uri.AbsoluteUri.EndsWith(FolderEntry)) + .Select(blobItem => new AzureBlobFileStorage(blobItem, _absoluteRoot)) + .ToArray(); } public IEnumerable ListFolders(string path) { @@ -201,6 +203,11 @@ public bool TryCreateFolder(string path) { public void CreateFolder(string path) { path = ConvertToRelativeUriPath(path); + + if (FileSystemStorageProvider.FolderNameContainsInvalidCharacters(GetFolderName(path))) { + throw new InvalidNameCharacterException("The directory name contains invalid character(s)"); + } + Container.EnsureDirectoryDoesNotExist(String.Concat(_root, path)); // Creating a virtually hidden file to make the directory an existing concept @@ -232,7 +239,11 @@ public void RenameFolder(string path, string newPath) { path = ConvertToRelativeUriPath(path); newPath = ConvertToRelativeUriPath(newPath); - // Workaround for https://github.com/Azure/azure-storage-net/issues/892 + if (FileSystemStorageProvider.FolderNameContainsInvalidCharacters(GetFolderName(newPath))) { + throw new InvalidNameCharacterException("The new directory name contains invalid character(s)"); + } + + // Workaround for https://github.com/Azure/azure-storage-net/issues/892. // Renaming a folder by only changing the casing corrupts all the files in the folder. if (path.Equals(newPath, StringComparison.OrdinalIgnoreCase)) { var tempPath = Guid.NewGuid().ToString() + "/"; @@ -277,6 +288,10 @@ public void RenameFile(string path, string newPath) { path = ConvertToRelativeUriPath(path); newPath = ConvertToRelativeUriPath(newPath); + if (FileSystemStorageProvider.FileNameContainsInvalidCharacters(Path.GetFileName(newPath))) { + throw new InvalidNameCharacterException("The new file name contains invalid character(s)"); + } + Container.EnsureBlobExists(String.Concat(_root, path)); Container.EnsureBlobDoesNotExist(String.Concat(_root, newPath)); @@ -301,6 +316,10 @@ public void CopyFile(string path, string newPath) { public IStorageFile CreateFile(string path) { path = ConvertToRelativeUriPath(path); + if (FileSystemStorageProvider.FileNameContainsInvalidCharacters(Path.GetFileName(path))) { + throw new InvalidNameCharacterException("The file name contains invalid character(s)"); + } + if (Container.BlobExists(String.Concat(_root, path))) { throw new ArgumentException("File " + path + " already exists"); } @@ -388,10 +407,7 @@ public AzureBlobFolderStorage(CloudBlobDirectory blob, string rootPath) { _rootPath = rootPath; } - public string GetName() { - var path = GetPath(); - return path.Substring(path.LastIndexOf('/') + 1); - } + public string GetName() => GetFolderName(GetPath()); public string GetPath() { return _blob.Uri.ToString().Substring(_rootPath.Length).Trim('/'); @@ -416,11 +432,12 @@ private static long GetDirectorySize(CloudBlobDirectory directoryBlob) { long size = 0; foreach (var blobItem in directoryBlob.ListBlobs()) { - if (blobItem is CloudBlockBlob) - size += ((CloudBlockBlob)blobItem).Properties.Length; - - if (blobItem is CloudBlobDirectory) - size += GetDirectorySize((CloudBlobDirectory)blobItem); + if (blobItem is CloudBlockBlob blob) { + size += blob.Properties.Length; + } + else if (blobItem is CloudBlobDirectory directory) { + size += GetDirectorySize(directory); + } } return size; diff --git a/src/Orchard.Web/Modules/Orchard.DynamicForms/Helpers/NameValueCollectionExtensions.cs b/src/Orchard.Web/Modules/Orchard.DynamicForms/Helpers/NameValueCollectionExtensions.cs deleted file mode 100644 index cdd14b43d54..00000000000 --- a/src/Orchard.Web/Modules/Orchard.DynamicForms/Helpers/NameValueCollectionExtensions.cs +++ /dev/null @@ -1,12 +0,0 @@ -using System; -using System.Collections.Specialized; -using System.Linq; -using System.Web; - -namespace Orchard.DynamicForms.Helpers { - public static class NameValueCollectionExtensions { - public static string ToQueryString(this NameValueCollection nameValues) { - return String.Join("&", (from string name in nameValues select String.Concat(name, "=", HttpUtility.UrlEncode(nameValues[name]))).ToArray()); - } - } -} \ No newline at end of file diff --git a/src/Orchard.Web/Modules/Orchard.DynamicForms/Orchard.DynamicForms.csproj b/src/Orchard.Web/Modules/Orchard.DynamicForms/Orchard.DynamicForms.csproj index e1e332f1efc..c2cc01b7bcb 100644 --- a/src/Orchard.Web/Modules/Orchard.DynamicForms/Orchard.DynamicForms.csproj +++ b/src/Orchard.Web/Modules/Orchard.DynamicForms/Orchard.DynamicForms.csproj @@ -339,7 +339,6 @@ - diff --git a/src/Orchard.Web/Modules/Orchard.DynamicForms/Services/FormService.cs b/src/Orchard.Web/Modules/Orchard.DynamicForms/Services/FormService.cs index 7c602486905..603f5099d7e 100644 --- a/src/Orchard.Web/Modules/Orchard.DynamicForms/Services/FormService.cs +++ b/src/Orchard.Web/Modules/Orchard.DynamicForms/Services/FormService.cs @@ -467,4 +467,4 @@ private static bool IsFormElementType(IElementValidator validator, Type elementT return validatorElementType == elementType || validatorElementType.IsAssignableFrom(elementType); } } -} \ No newline at end of file +} diff --git a/src/Orchard.Web/Modules/Orchard.Fields/Drivers/EnumerationFieldDriver.cs b/src/Orchard.Web/Modules/Orchard.Fields/Drivers/EnumerationFieldDriver.cs index 2bbd1a90379..3240df55d17 100644 --- a/src/Orchard.Web/Modules/Orchard.Fields/Drivers/EnumerationFieldDriver.cs +++ b/src/Orchard.Web/Modules/Orchard.Fields/Drivers/EnumerationFieldDriver.cs @@ -4,46 +4,41 @@ using Orchard.Fields.Fields; using Orchard.Fields.Settings; using Orchard.Localization; -using System; -using System.Collections.Generic; -using System.Linq; namespace Orchard.Fields.Drivers { public class EnumerationFieldDriver : ContentFieldDriver { public IOrchardServices Services { get; set; } + private const string TemplateName = "Fields/Enumeration.Edit"; public EnumerationFieldDriver(IOrchardServices services) { Services = services; + T = NullLocalizer.Instance; } public Localizer T { get; set; } - private static string GetPrefix(ContentField field, ContentPart part) { - return part.PartDefinition.Name + "." + field.Name; - } + private static string GetPrefix(ContentField field, ContentPart part) => + part.PartDefinition.Name + "." + field.Name; - private static string GetDifferentiator(EnumerationField field, ContentPart part) { - return field.Name; - } + private static string GetDifferentiator(EnumerationField field) => field.Name; protected override DriverResult Display(ContentPart part, EnumerationField field, string displayType, dynamic shapeHelper) { - return ContentShape("Fields_Enumeration", GetDifferentiator(field, part), - () => shapeHelper.Fields_Enumeration()); + return ContentShape("Fields_Enumeration", GetDifferentiator(field), () => shapeHelper.Fields_Enumeration()); } protected override DriverResult Editor(ContentPart part, EnumerationField field, dynamic shapeHelper) { - return ContentShape("Fields_Enumeration_Edit", GetDifferentiator(field, part), - () => { - if (part.IsNew() && String.IsNullOrEmpty(field.Value)) { - var settings = field.PartFieldDefinition.Settings.GetModel(); - if (!String.IsNullOrWhiteSpace(settings.DefaultValue)) { - field.Value = settings.DefaultValue; - } + return ContentShape("Fields_Enumeration_Edit", GetDifferentiator(field), () => { + if (part.IsNew() && string.IsNullOrEmpty(field.Value)) { + var settings = field.PartFieldDefinition.Settings.GetModel(); + if (!string.IsNullOrWhiteSpace(settings.DefaultValue)) { + field.SelectedValues = new string[] { settings.DefaultValue }; } - return shapeHelper.EditorTemplate(TemplateName: TemplateName, Model: field, Prefix: GetPrefix(field, part)); - }); + } + + return shapeHelper.EditorTemplate(TemplateName: TemplateName, Model: field, Prefix: GetPrefix(field, part)); + }); } protected override DriverResult Editor(ContentPart part, EnumerationField field, IUpdateModel updater, dynamic shapeHelper) { diff --git a/src/Orchard.Web/Modules/Orchard.Fields/Fields/EnumerationField.cs b/src/Orchard.Web/Modules/Orchard.Fields/Fields/EnumerationField.cs index a81d6829e0a..ee572b66bd4 100644 --- a/src/Orchard.Web/Modules/Orchard.Fields/Fields/EnumerationField.cs +++ b/src/Orchard.Web/Modules/Orchard.Fields/Fields/EnumerationField.cs @@ -7,28 +7,16 @@ public class EnumerationField : ContentField { private const char Separator = ';'; public string Value { - get { return Storage.Get(); } - set { Storage.Set(value ?? String.Empty); } + get => Storage.Get()?.Trim(Separator) ?? ""; + set => Storage.Set(string.IsNullOrWhiteSpace(value) + ? string.Empty + // It is now the responsibility of this field to (re-)add the separators. + : Separator + value.Trim(Separator) + Separator); } public string[] SelectedValues { - get { - var value = Value; - if(string.IsNullOrWhiteSpace(value)) { - return new string[0]; - } - - return value.Split(new [] { Separator }, StringSplitOptions.RemoveEmptyEntries); - } - - set { - if (value == null || value.Length == 0) { - Value = String.Empty; - } - else { - Value = Separator + string.Join(Separator.ToString(), value) + Separator; - } - } + get => Value?.Split(new[] { Separator }, StringSplitOptions.RemoveEmptyEntries) ?? new string[0]; + set => Value = value?.Length > 0 ? string.Join(Separator.ToString(), value) : ""; } } } diff --git a/src/Orchard.Web/Modules/Orchard.Fields/Views/EditorTemplates/Fields/Enumeration.Edit.cshtml b/src/Orchard.Web/Modules/Orchard.Fields/Views/EditorTemplates/Fields/Enumeration.Edit.cshtml index 0d7b16dd8c7..0f3d1003600 100644 --- a/src/Orchard.Web/Modules/Orchard.Fields/Views/EditorTemplates/Fields/Enumeration.Edit.cshtml +++ b/src/Orchard.Web/Modules/Orchard.Fields/Views/EditorTemplates/Fields/Enumeration.Edit.cshtml @@ -1,22 +1,27 @@ @model Orchard.Fields.Fields.EnumerationField + @using Orchard.Fields.Settings; + @{ var settings = Model.PartFieldDefinition.Settings.GetModel(); string[] options = (!String.IsNullOrWhiteSpace(settings.Options)) ? settings.Options.Split(new string[] { System.Environment.NewLine }, StringSplitOptions.None) : new string[] { T("Select an option").ToString() }; } +
- + @switch (settings.ListMode) { case ListMode.Dropdown: - @Html.DropDownListFor(m => m.Value, new SelectList(options, Model.Value), settings.Required ? new { required = "required" } : null) + @Html.DropDownListFor(m => m.Value, new SelectList(options, Model.SelectedValues.FirstOrDefault()), settings.Required ? new { required = "required" } : null) break; case ListMode.Radiobutton: foreach (var option in options) { if (string.IsNullOrWhiteSpace(option)) { - } + + } else { - } + + } } break; diff --git a/src/Orchard.Web/Modules/Orchard.Glimpse/Web.config b/src/Orchard.Web/Modules/Orchard.Glimpse/Web.config index d4c2217570d..0ad5a744213 100644 --- a/src/Orchard.Web/Modules/Orchard.Glimpse/Web.config +++ b/src/Orchard.Web/Modules/Orchard.Glimpse/Web.config @@ -45,10 +45,6 @@ - - - - diff --git a/src/Orchard.Web/Modules/Orchard.Layouts/Drivers/HeadingElementDriver.cs b/src/Orchard.Web/Modules/Orchard.Layouts/Drivers/HeadingElementDriver.cs index 0bc14c531e9..d122e6eb57a 100644 --- a/src/Orchard.Web/Modules/Orchard.Layouts/Drivers/HeadingElementDriver.cs +++ b/src/Orchard.Web/Modules/Orchard.Layouts/Drivers/HeadingElementDriver.cs @@ -2,15 +2,15 @@ using Orchard.Layouts.Framework.Display; using Orchard.Layouts.Framework.Drivers; using Orchard.Layouts.Helpers; -using Orchard.Layouts.Services; using Orchard.Layouts.ViewModels; +using Orchard.Services; namespace Orchard.Layouts.Drivers { public class HeadingElementDriver : ElementDriver { - private readonly IElementFilterProcessor _processor; + private readonly IHtmlFilterProcessor _htmlFilterProcessor; - public HeadingElementDriver(IElementFilterProcessor processor) { - _processor = processor; + public HeadingElementDriver(IHtmlFilterProcessor htmlFilterProcessor) { + _htmlFilterProcessor = htmlFilterProcessor; } protected override EditorResult OnBuildEditor(Heading element, ElementEditorContext context) { @@ -30,7 +30,7 @@ protected override EditorResult OnBuildEditor(Heading element, ElementEditorCont } protected override void OnDisplaying(Heading element, ElementDisplayingContext context) { - context.ElementShape.ProcessedContent = _processor.ProcessContent(element.Content, "html", context.GetTokenData()); + context.ElementShape.ProcessedContent = _htmlFilterProcessor.ProcessFilters(element.Content, new HtmlFilterContext { Flavor = "html", Data = context.GetTokenData() }); context.ElementShape.Level = element.Level; } } diff --git a/src/Orchard.Web/Modules/Orchard.Layouts/Drivers/HtmlElementDriver.cs b/src/Orchard.Web/Modules/Orchard.Layouts/Drivers/HtmlElementDriver.cs index eb62d7616a8..8e7c050b6ca 100644 --- a/src/Orchard.Web/Modules/Orchard.Layouts/Drivers/HtmlElementDriver.cs +++ b/src/Orchard.Web/Modules/Orchard.Layouts/Drivers/HtmlElementDriver.cs @@ -2,15 +2,16 @@ using Orchard.Layouts.Framework.Display; using Orchard.Layouts.Framework.Drivers; using Orchard.Layouts.Helpers; -using Orchard.Layouts.Services; using Orchard.Layouts.ViewModels; +using Orchard.Services; -namespace Orchard.Layouts.Drivers { +namespace Orchard.Layouts.Drivers +{ public class HtmlElementDriver : ElementDriver { - private readonly IElementFilterProcessor _processor; + private readonly IHtmlFilterProcessor _htmlFilterProcessor; - public HtmlElementDriver(IElementFilterProcessor processor) { - _processor = processor; + public HtmlElementDriver(IHtmlFilterProcessor htmlFilterProcessor) { + _htmlFilterProcessor = htmlFilterProcessor; } protected override EditorResult OnBuildEditor(Html element, ElementEditorContext context) { @@ -29,7 +30,7 @@ protected override EditorResult OnBuildEditor(Html element, ElementEditorContext } protected override void OnDisplaying(Html element, ElementDisplayingContext context) { - context.ElementShape.ProcessedContent = _processor.ProcessContent(element.Content, "html", context.GetTokenData()); + context.ElementShape.ProcessedContent = _htmlFilterProcessor.ProcessFilters(element.Content, new HtmlFilterContext { Flavor = "html", Data = context.GetTokenData() }); } } } \ No newline at end of file diff --git a/src/Orchard.Web/Modules/Orchard.Layouts/Drivers/MarkdownElementDriver.cs b/src/Orchard.Web/Modules/Orchard.Layouts/Drivers/MarkdownElementDriver.cs index e988c89a17d..8e7f8d612e9 100644 --- a/src/Orchard.Web/Modules/Orchard.Layouts/Drivers/MarkdownElementDriver.cs +++ b/src/Orchard.Web/Modules/Orchard.Layouts/Drivers/MarkdownElementDriver.cs @@ -2,16 +2,17 @@ using Orchard.Layouts.Framework.Display; using Orchard.Layouts.Framework.Drivers; using Orchard.Layouts.Helpers; -using Orchard.Layouts.Services; using Orchard.Layouts.ViewModels; +using Orchard.Services; using MarkdownElement = Orchard.Layouts.Elements.Markdown; -namespace Orchard.Layouts.Drivers { +namespace Orchard.Layouts.Drivers +{ [OrchardFeature("Orchard.Layouts.Markdown")] public class MarkdownElementDriver : ElementDriver { - private readonly IElementFilterProcessor _processor; - public MarkdownElementDriver(IElementFilterProcessor processor) { - _processor = processor; + private readonly IHtmlFilterProcessor _htmlFilterProcessor; + public MarkdownElementDriver(IHtmlFilterProcessor htmlFilterProcessor) { + _htmlFilterProcessor = htmlFilterProcessor; } protected override EditorResult OnBuildEditor(MarkdownElement element, ElementEditorContext context) { @@ -29,7 +30,7 @@ protected override EditorResult OnBuildEditor(MarkdownElement element, ElementEd } protected override void OnDisplaying(MarkdownElement element, ElementDisplayingContext context) { - context.ElementShape.ProcessedContent = _processor.ProcessContent(element.Content, "markdown", context.GetTokenData()); + context.ElementShape.ProcessedContent = _htmlFilterProcessor.ProcessFilters(element.Content, new HtmlFilterContext { Flavor = "markdown", Data = context.GetTokenData() }); } } } \ No newline at end of file diff --git a/src/Orchard.Web/Modules/Orchard.Layouts/Drivers/ParagraphElementDriver.cs b/src/Orchard.Web/Modules/Orchard.Layouts/Drivers/ParagraphElementDriver.cs index 0d285207ad5..1aca94f2a77 100644 --- a/src/Orchard.Web/Modules/Orchard.Layouts/Drivers/ParagraphElementDriver.cs +++ b/src/Orchard.Web/Modules/Orchard.Layouts/Drivers/ParagraphElementDriver.cs @@ -2,15 +2,15 @@ using Orchard.Layouts.Framework.Display; using Orchard.Layouts.Framework.Drivers; using Orchard.Layouts.Helpers; -using Orchard.Layouts.Services; using Orchard.Layouts.ViewModels; +using Orchard.Services; namespace Orchard.Layouts.Drivers { public class ParagraphElementDriver : ElementDriver { - private readonly IElementFilterProcessor _processor; + private readonly IHtmlFilterProcessor _htmlFilterProcessor; - public ParagraphElementDriver(IElementFilterProcessor processor) { - _processor = processor; + public ParagraphElementDriver(IHtmlFilterProcessor htmlFilterProcessor) { + _htmlFilterProcessor = htmlFilterProcessor; } protected override EditorResult OnBuildEditor(Paragraph element, ElementEditorContext context) { @@ -28,7 +28,7 @@ protected override EditorResult OnBuildEditor(Paragraph element, ElementEditorCo } protected override void OnDisplaying(Paragraph element, ElementDisplayingContext context) { - context.ElementShape.ProcessedContent = _processor.ProcessContent(element.Content, "html", context.GetTokenData()); + context.ElementShape.ProcessedContent = _htmlFilterProcessor.ProcessFilters(element.Content, new HtmlFilterContext { Flavor = "html", Data = context.GetTokenData() }); } } } \ No newline at end of file diff --git a/src/Orchard.Web/Modules/Orchard.Layouts/Drivers/TextElementDriver.cs b/src/Orchard.Web/Modules/Orchard.Layouts/Drivers/TextElementDriver.cs index e85dad82c9c..87083dd7d6b 100644 --- a/src/Orchard.Web/Modules/Orchard.Layouts/Drivers/TextElementDriver.cs +++ b/src/Orchard.Web/Modules/Orchard.Layouts/Drivers/TextElementDriver.cs @@ -2,15 +2,16 @@ using Orchard.Layouts.Framework.Display; using Orchard.Layouts.Framework.Drivers; using Orchard.Layouts.Helpers; -using Orchard.Layouts.Services; using Orchard.Layouts.ViewModels; +using Orchard.Services; -namespace Orchard.Layouts.Drivers { +namespace Orchard.Layouts.Drivers +{ public class TextElementDriver : ElementDriver { - private readonly IElementFilterProcessor _processor; + private readonly IHtmlFilterProcessor _htmlFilterProcessor; - public TextElementDriver(IElementFilterProcessor processor) { - _processor = processor; + public TextElementDriver(IHtmlFilterProcessor htmlFilterProcessor) { + _htmlFilterProcessor = htmlFilterProcessor; } protected override EditorResult OnBuildEditor(Text element, ElementEditorContext context) { @@ -28,7 +29,7 @@ protected override EditorResult OnBuildEditor(Text element, ElementEditorContext } protected override void OnDisplaying(Text element, ElementDisplayingContext context) { - context.ElementShape.ProcessedContent = _processor.ProcessContent(element.Content, "textarea", context.GetTokenData()); + context.ElementShape.ProcessedContent = _htmlFilterProcessor.ProcessFilters(element.Content, new HtmlFilterContext { Flavor = "textarea", Data = context.GetTokenData() }); } } } \ No newline at end of file diff --git a/src/Orchard.Web/Modules/Orchard.Layouts/Filters/TokensFilter.cs b/src/Orchard.Web/Modules/Orchard.Layouts/Filters/TokensFilter.cs deleted file mode 100644 index 1dc8a27a5c5..00000000000 --- a/src/Orchard.Web/Modules/Orchard.Layouts/Filters/TokensFilter.cs +++ /dev/null @@ -1,34 +0,0 @@ -using System; -using Orchard.Environment.Extensions; -using Orchard.Layouts.Services; -using Orchard.Tokens; -using System.Collections.Generic; - -namespace Orchard.Layouts.Filters { - [OrchardFeature("Orchard.Layouts.Tokens")] - public class TokensFilter : IElementFilter { - - private readonly ITokenizer _tokenizer; - - public TokensFilter(ITokenizer tokenizer) { - _tokenizer = tokenizer; - } - - public string ProcessContent(string text, string flavor) { - return ProcessContent(text, flavor, new Dictionary()); - } - - public string ProcessContent(string text, string flavor, IDictionary context) { - if (String.IsNullOrEmpty(text)) - return ""; - - if (!text.Contains("#{")) { - return text; - } - - text = _tokenizer.Replace(text, context, new ReplaceOptions { Encoding = ReplaceOptions.NoEncode }); - - return text; - } - } -} \ No newline at end of file diff --git a/src/Orchard.Web/Modules/Orchard.Layouts/Module.txt b/src/Orchard.Web/Modules/Orchard.Layouts/Module.txt index 7dec211a0f2..7a42cf991a1 100644 --- a/src/Orchard.Web/Modules/Orchard.Layouts/Module.txt +++ b/src/Orchard.Web/Modules/Orchard.Layouts/Module.txt @@ -29,7 +29,7 @@ Features: Dependencies: Orchard.Layouts, Orchard.Projections Orchard.Layouts.Tokens: Name: Element Tokens - Description: Provides an element token provider that enables elements to be rendered using a token and enables tokens to be used inside of various elements such as Html, Text and Paragraph. + Description: Provides an element token provider that enables elements to be rendered using a token. Category: Layout Dependencies: Orchard.Layouts, Orchard.Tokens Orchard.Layouts.UI: diff --git a/src/Orchard.Web/Modules/Orchard.Layouts/Orchard.Layouts.csproj b/src/Orchard.Web/Modules/Orchard.Layouts/Orchard.Layouts.csproj index ff45ec93087..e207903e8a2 100644 --- a/src/Orchard.Web/Modules/Orchard.Layouts/Orchard.Layouts.csproj +++ b/src/Orchard.Web/Modules/Orchard.Layouts/Orchard.Layouts.csproj @@ -319,7 +319,6 @@ - @@ -375,9 +374,6 @@ - - - diff --git a/src/Orchard.Web/Modules/Orchard.Layouts/Services/ElementFilterProcessor.cs b/src/Orchard.Web/Modules/Orchard.Layouts/Services/ElementFilterProcessor.cs deleted file mode 100644 index 2dd58b24fea..00000000000 --- a/src/Orchard.Web/Modules/Orchard.Layouts/Services/ElementFilterProcessor.cs +++ /dev/null @@ -1,19 +0,0 @@ -using System.Collections.Generic; -using Orchard.Services; - -namespace Orchard.Layouts.Services { - public class ElementFilterProcessor : IElementFilterProcessor { - private readonly IEnumerable _filters; - public ElementFilterProcessor(IEnumerable filters) { - _filters = filters; - } - - public string ProcessContent(string text, string flavor, IDictionary context) { - foreach (var htmlFilter in _filters) { - var elementFilter = htmlFilter as IElementFilter; - text = elementFilter != null ? elementFilter.ProcessContent(text, flavor, context) : htmlFilter.ProcessContent(text, flavor); - } - return text; - } - } -} \ No newline at end of file diff --git a/src/Orchard.Web/Modules/Orchard.Layouts/Services/IElementFilter.cs b/src/Orchard.Web/Modules/Orchard.Layouts/Services/IElementFilter.cs deleted file mode 100644 index 23d474c7359..00000000000 --- a/src/Orchard.Web/Modules/Orchard.Layouts/Services/IElementFilter.cs +++ /dev/null @@ -1,8 +0,0 @@ -using System.Collections.Generic; -using Orchard.Services; - -namespace Orchard.Layouts.Services { - public interface IElementFilter : IHtmlFilter { - string ProcessContent(string text, string flavor, IDictionary context); - } -} \ No newline at end of file diff --git a/src/Orchard.Web/Modules/Orchard.Layouts/Services/IElementFilterProcessor.cs b/src/Orchard.Web/Modules/Orchard.Layouts/Services/IElementFilterProcessor.cs deleted file mode 100644 index f66dbf07a3d..00000000000 --- a/src/Orchard.Web/Modules/Orchard.Layouts/Services/IElementFilterProcessor.cs +++ /dev/null @@ -1,8 +0,0 @@ -using System.Collections.Generic; -using Orchard.Services; - -namespace Orchard.Layouts.Services { - public interface IElementFilterProcessor : IDependency { - string ProcessContent(string text, string flavor, IDictionary context); - } -} \ No newline at end of file diff --git a/src/Orchard.Web/Modules/Orchard.Localization/Controllers/UserCultureSelectorController.cs b/src/Orchard.Web/Modules/Orchard.Localization/Controllers/UserCultureSelectorController.cs new file mode 100644 index 00000000000..2a4543c289b --- /dev/null +++ b/src/Orchard.Web/Modules/Orchard.Localization/Controllers/UserCultureSelectorController.cs @@ -0,0 +1,48 @@ +using System; +using System.Web.Mvc; +using Orchard.Autoroute.Models; +using Orchard.CulturePicker.Services; +using Orchard.Environment.Extensions; +using Orchard.Localization.Providers; +using Orchard.Localization.Services; +using Orchard.Mvc.Extensions; + +namespace Orchard.Localization.Controllers { + [OrchardFeature("Orchard.Localization.CultureSelector")] + public class UserCultureSelectorController : Controller { + private readonly ILocalizationService _localizationService; + private readonly ICultureStorageProvider _cultureStorageProvider; + public IOrchardServices Services { get; set; } + + public UserCultureSelectorController( + IOrchardServices services, + ILocalizationService localizationService, + ICultureStorageProvider cultureStorageProvider) { + Services = services; + _localizationService = localizationService; + _cultureStorageProvider = cultureStorageProvider; + } + + public ActionResult ChangeCulture(string culture) { + if (string.IsNullOrEmpty(culture)) { + throw new ArgumentNullException(culture); + } + + var returnUrl = Utils.GetReturnUrl(Services.WorkContext.HttpContext.Request); + if (string.IsNullOrEmpty(returnUrl)) + returnUrl = ""; + + if (_localizationService.TryGetRouteForUrl(returnUrl, out AutoroutePart currentRoutePart) + && _localizationService.TryFindLocalizedRoute(currentRoutePart.ContentItem, culture, out AutoroutePart localizedRoutePart)) { + returnUrl = localizedRoutePart.Path; + } + + _cultureStorageProvider.SetCulture(culture); + if (!returnUrl.StartsWith("~/")) { + returnUrl = "~/" + returnUrl; + } + + return this.RedirectLocal(returnUrl); + } + } +} diff --git a/src/Orchard.Web/Modules/Orchard.Localization/Module.txt b/src/Orchard.Web/Modules/Orchard.Localization/Module.txt index 9f6e499f81d..2acd2c4b75a 100644 --- a/src/Orchard.Web/Modules/Orchard.Localization/Module.txt +++ b/src/Orchard.Web/Modules/Orchard.Localization/Module.txt @@ -9,7 +9,7 @@ Features: Orchard.Localization: Description: Enables localization of content items. Category: Content - Dependencies: Settings + Dependencies: Settings, Orchard.Autoroute Name: Content Localization Orchard.Localization.DateTimeFormat: Description: Enables PO-based translation of date/time formats and names of days and months. @@ -30,7 +30,7 @@ Features: Description: Enables transliteration of the autoroute slug when creating a piece of content. Category: Content Name: URL Transliteration - Dependencies: Orchard.Localization.Transliteration, Orchard.Autoroute + Dependencies: Orchard.Localization.Transliteration Orchard.Localization.CultureNeutralPartsAndFields: Description: Enables the synchronization among localizations of parts and fields specifically marked as "Culture Neutral". Category: Content diff --git a/src/Orchard.Web/Modules/Orchard.Localization/Orchard.Localization.csproj b/src/Orchard.Web/Modules/Orchard.Localization/Orchard.Localization.csproj index 9baa994b890..80b4bd1c9f5 100644 --- a/src/Orchard.Web/Modules/Orchard.Localization/Orchard.Localization.csproj +++ b/src/Orchard.Web/Modules/Orchard.Localization/Orchard.Localization.csproj @@ -89,10 +89,11 @@ + - + @@ -120,6 +121,7 @@ + @@ -196,8 +198,7 @@ - - + @@ -233,4 +234,4 @@ - + \ No newline at end of file diff --git a/src/Orchard.Web/Modules/Orchard.Localization/Selectors/CookieCultureSelector.cs b/src/Orchard.Web/Modules/Orchard.Localization/Selectors/CookieCultureSelector.cs index d39abbb5b67..00c82464f87 100644 --- a/src/Orchard.Web/Modules/Orchard.Localization/Selectors/CookieCultureSelector.cs +++ b/src/Orchard.Web/Modules/Orchard.Localization/Selectors/CookieCultureSelector.cs @@ -1,4 +1,3 @@ -using System; using System.Web; using Orchard.Environment.Configuration; using Orchard.Environment.Extensions; @@ -19,7 +18,8 @@ public class CookieCultureSelector : ICultureSelector, ICultureStorageProvider { private const string AdminCookieName = "OrchardCurrentCulture-Admin"; private const int DefaultExpireTimeYear = 1; - public CookieCultureSelector(IHttpContextAccessor httpContextAccessor, + public CookieCultureSelector( + IHttpContextAccessor httpContextAccessor, IClock clock, ShellSettings shellSettings) { _httpContextAccessor = httpContextAccessor; @@ -36,11 +36,10 @@ public void SetCulture(string culture) { var cookie = new HttpCookie(cookieName, culture) { Expires = _clock.UtcNow.AddYears(DefaultExpireTimeYear), + Domain = httpContext.Request.IsLocal ? null : httpContext.Request.Url.Host }; - cookie.Domain = !httpContext.Request.IsLocal ? httpContext.Request.Url.Host : null; - - if (!String.IsNullOrEmpty(_shellSettings.RequestUrlPrefix)) { + if (!string.IsNullOrEmpty(_shellSettings.RequestUrlPrefix)) { cookie.Path = GetCookiePath(httpContext); } @@ -73,4 +72,4 @@ private string GetCookiePath(HttpContextBase httpContext) { return cookiePath; } } -} \ No newline at end of file +} diff --git a/src/Orchard.Web/Modules/Orchard.Localization/Services/ILocalizationService.cs b/src/Orchard.Web/Modules/Orchard.Localization/Services/ILocalizationService.cs index f2ace3a9cb5..04cda8d60b6 100644 --- a/src/Orchard.Web/Modules/Orchard.Localization/Services/ILocalizationService.cs +++ b/src/Orchard.Web/Modules/Orchard.Localization/Services/ILocalizationService.cs @@ -1,4 +1,5 @@ using System.Collections.Generic; +using Orchard.Autoroute.Models; using Orchard.ContentManagement; using Orchard.Localization.Models; @@ -10,5 +11,7 @@ public interface ILocalizationService : IDependency { void SetContentCulture(IContent content, string culture); IEnumerable GetLocalizations(IContent content); IEnumerable GetLocalizations(IContent content, VersionOptions versionOptions); + bool TryFindLocalizedRoute(ContentItem routableContent, string cultureName, out AutoroutePart localizedRoute); + bool TryGetRouteForUrl(string url, out AutoroutePart route); } } diff --git a/src/Orchard.Web/Modules/Orchard.Localization/Services/LocalizationService.cs b/src/Orchard.Web/Modules/Orchard.Localization/Services/LocalizationService.cs index 8255ef292de..b0e1fccb526 100644 --- a/src/Orchard.Web/Modules/Orchard.Localization/Services/LocalizationService.cs +++ b/src/Orchard.Web/Modules/Orchard.Localization/Services/LocalizationService.cs @@ -1,47 +1,56 @@ -using System.Collections.Generic; +using System; +using System.Collections.Generic; using System.Linq; +using Orchard.Autoroute.Models; +using Orchard.Autoroute.Services; using Orchard.ContentManagement; +using Orchard.ContentManagement.Aspects; using Orchard.Localization.Models; namespace Orchard.Localization.Services { public class LocalizationService : ILocalizationService { private readonly IContentManager _contentManager; private readonly ICultureManager _cultureManager; + private readonly IHomeAliasService _homeAliasService; - - public LocalizationService(IContentManager contentManager, ICultureManager cultureManager) { + public LocalizationService(IContentManager contentManager, ICultureManager cultureManager, IHomeAliasService homeAliasService) { _contentManager = contentManager; _cultureManager = cultureManager; + _homeAliasService = homeAliasService; } + /// + /// Warning: Returns only the first item of same culture localizations. + /// + public LocalizationPart GetLocalizedContentItem(IContent content, string culture) => + GetLocalizedContentItem(content, culture, null); - public LocalizationPart GetLocalizedContentItem(IContent content, string culture) { - return GetLocalizedContentItem(content, culture, null); - } - + /// + /// Warning: Returns only the first item of same culture localizations. + /// public LocalizationPart GetLocalizedContentItem(IContent content, string culture, VersionOptions versionOptions) { var cultureRecord = _cultureManager.GetCultureByName(culture); - if (cultureRecord == null) return null; + if (cultureRecord == null) { + return null; + } var localized = content.As(); - if (localized == null) return null; + if (localized == null) { + return null; + } - if (localized?.Culture.Culture == culture) return localized; + if (localized.Culture?.Culture == culture) return localized; - // Warning: Returns only the first of same culture localizations. return GetLocalizationsQuery(localized, versionOptions) - .Where(l => l.CultureId == cultureRecord.Id) + .Where(localization => localization.CultureId == cultureRecord.Id) .Slice(1) .FirstOrDefault(); } - public string GetContentCulture(IContent content) { - var localized = content.As(); - - return localized?.Culture == null ? _cultureManager.GetSiteCulture() : localized.Culture.Culture; - } + public string GetContentCulture(IContent content) => + content.As()?.Culture?.Culture ?? _cultureManager.GetSiteCulture(); public void SetContentCulture(IContent content, string culture) { var localized = content.As(); @@ -51,30 +60,83 @@ public void SetContentCulture(IContent content, string culture) { localized.Culture = _cultureManager.GetCultureByName(culture); } - public IEnumerable GetLocalizations(IContent content) { - return GetLocalizations(content, null); - } + /// + /// Warning: May contain more than one localization of the same culture. + /// + public IEnumerable GetLocalizations(IContent content) => GetLocalizations(content, null); + /// + /// Warning: May contain more than one localization of the same culture. + /// public IEnumerable GetLocalizations(IContent content, VersionOptions versionOptions) { if (content.ContentItem.Id == 0) return Enumerable.Empty(); var localized = content.As(); return GetLocalizationsQuery(localized, versionOptions) - .Where(l => l.Id != localized.Id) // Exclude the current content. + .Where(localization => localization.Id != localized.Id) // Exclude the current content. .List(); } + public bool TryGetRouteForUrl(string url, out AutoroutePart route) { + route = _contentManager.Query() + .ForVersion(VersionOptions.Published) + .Where(r => r.DisplayAlias == url) + .List() + .FirstOrDefault(); + + route = route ?? _homeAliasService.GetHomePage(VersionOptions.Latest).As(); + + return route != null; + } + + public bool TryFindLocalizedRoute(ContentItem routableContent, string cultureName, out AutoroutePart localizedRoute) { + if (!routableContent.Parts.Any(p => p.Is())) { + localizedRoute = null; + + return false; + } + + IEnumerable localizations = GetLocalizations(routableContent, VersionOptions.Published); + + ILocalizableAspect localizationPart = null, siteCultureLocalizationPart = null; + foreach (var localization in localizations) { + if (localization.Culture.Culture.Equals(cultureName, StringComparison.InvariantCultureIgnoreCase)) { + localizationPart = localization; + + break; + } + + if (localization.Culture == null && siteCultureLocalizationPart == null) { + siteCultureLocalizationPart = localization; + } + } + if (localizationPart == null) { + localizationPart = siteCultureLocalizationPart; + } + + localizedRoute = localizationPart?.As(); + + return localizedRoute != null; + } + + /// + /// Warning: May contain more than one localization of the same culture. + /// private IContentQuery GetLocalizationsQuery(LocalizationPart localizationPart, VersionOptions versionOptions) { - var masterId = localizationPart.HasTranslationGroup ? - localizationPart.Record.MasterContentItemId : localizationPart.Id; + var masterId = localizationPart.HasTranslationGroup + ? localizationPart.Record.MasterContentItemId + : localizationPart.Id; + + var query = _contentManager.Query(localizationPart.ContentItem.ContentType); - var query = versionOptions == null ? - _contentManager.Query() : _contentManager.Query(versionOptions); + if (versionOptions == null) { + query = query.ForVersion(versionOptions); + } - // Warning: May contain more than one localization of the same culture. - return query.Where(l => l.Id == masterId || l.MasterContentItemId == masterId); + return query + .Where(localization => localization.Id == masterId || localization.MasterContentItemId == masterId); } } } diff --git a/src/Orchard.Web/Modules/Orchard.Localization/Services/Utils.cs b/src/Orchard.Web/Modules/Orchard.Localization/Services/Utils.cs new file mode 100644 index 00000000000..c69c4e5df33 --- /dev/null +++ b/src/Orchard.Web/Modules/Orchard.Localization/Services/Utils.cs @@ -0,0 +1,31 @@ +using System.Web; + +namespace Orchard.CulturePicker.Services { + public static class Utils { + public static string GetReturnUrl(HttpRequestBase request) { + if (request.UrlReferrer == null) { + return ""; + } + + string localUrl = GetAppRelativePath(request.UrlReferrer.AbsolutePath, request); + return HttpUtility.UrlDecode(localUrl); + } + + public static string GetAppRelativePath(string logicalPath, HttpRequestBase request) { + if (request.ApplicationPath == null) { + return ""; + } + + logicalPath = logicalPath.ToLower(); + string appPath = request.ApplicationPath.ToLower(); + if (appPath != "/") { + appPath += "/"; + } + else { + return logicalPath.Substring(1); + } + + return logicalPath.Replace(appPath, ""); + } + } +} diff --git a/src/Orchard.Web/Modules/Orchard.Localization/Views/UserCultureSelector.cshtml b/src/Orchard.Web/Modules/Orchard.Localization/Views/UserCultureSelector.cshtml new file mode 100644 index 00000000000..71f4f80844e --- /dev/null +++ b/src/Orchard.Web/Modules/Orchard.Localization/Views/UserCultureSelector.cshtml @@ -0,0 +1,34 @@ +@using Orchard.Localization.Services + +@{ + var currentCulture = WorkContext.CurrentCulture; + var supportedCultures = WorkContext.Resolve().ListCultures().ToList(); +} + + diff --git a/src/Orchard.Web/Modules/Orchard.MediaLibrary/Controllers/ClientStorageController.cs b/src/Orchard.Web/Modules/Orchard.MediaLibrary/Controllers/ClientStorageController.cs index 00d7aacf655..fc61e3c6348 100644 --- a/src/Orchard.Web/Modules/Orchard.MediaLibrary/Controllers/ClientStorageController.cs +++ b/src/Orchard.Web/Modules/Orchard.MediaLibrary/Controllers/ClientStorageController.cs @@ -111,10 +111,16 @@ public ActionResult Upload(string folderPath, string type) { url = mediaPart.FileName, }); } + catch (InvalidNameCharacterException) { + statuses.Add(new { + error = T("The file name contains invalid character(s)").Text, + progress = 1.0, + }); + } catch (Exception ex) { - Logger.Error(ex, "Unexpected exception when uploading a media."); + Logger.Error(ex, T("Unexpected exception when uploading a media.").Text); statuses.Add(new { - error = T(ex.Message).Text, + error = ex.Message, progress = 1.0, }); } @@ -134,7 +140,7 @@ public ActionResult Replace(int replaceId, string type) { return HttpNotFound(); // Check permission - if (!(_mediaLibraryService.CheckMediaFolderPermission(Permissions.EditMediaContent, replaceMedia.FolderPath) && _mediaLibraryService.CheckMediaFolderPermission(Permissions.ImportMediaContent, replaceMedia.FolderPath)) + if (!(_mediaLibraryService.CheckMediaFolderPermission(Permissions.EditMediaContent, replaceMedia.FolderPath) && _mediaLibraryService.CheckMediaFolderPermission(Permissions.ImportMediaContent, replaceMedia.FolderPath)) && !_mediaLibraryService.CanManageMediaFolder(replaceMedia.FolderPath)) { return new HttpUnauthorizedResult(); } @@ -142,7 +148,7 @@ public ActionResult Replace(int replaceId, string type) { var statuses = new List(); var settings = Services.WorkContext.CurrentSite.As(); - + // Loop through each file in the request for (int i = 0; i < HttpContext.Request.Files.Count; i++) { // Pointer to file @@ -150,7 +156,8 @@ public ActionResult Replace(int replaceId, string type) { var filename = Path.GetFileName(file.FileName); // if the file has been pasted, provide a default name - if (file.ContentType.Equals("image/png", StringComparison.InvariantCultureIgnoreCase) && !filename.EndsWith(".png", StringComparison.InvariantCultureIgnoreCase)) { + if (file.ContentType.Equals("image/png", StringComparison.InvariantCultureIgnoreCase) + && !filename.EndsWith(".png", StringComparison.InvariantCultureIgnoreCase)) { filename = "clipboard.png"; } @@ -180,13 +187,15 @@ public ActionResult Replace(int replaceId, string type) { if (mediaItemsUsingTheFile == 1) { // if the file is referenced only by the deleted media content, the file too can be removed. try { _mediaLibraryService.DeleteFile(replaceMedia.FolderPath, replaceMedia.FileName); - } catch (ArgumentException) { // File not found by FileSystemStorageProvider is thrown as ArgumentException. + } + catch (ArgumentException) { // File not found by FileSystemStorageProvider is thrown as ArgumentException. statuses.Add(new { error = T("Error when deleting file to replace: file {0} does not exist in folder {1}. Media has been updated anyway.", replaceMedia.FileName, replaceMedia.FolderPath).Text, progress = 1.0 }); } - } else { + } + else { // it changes the media file name replaceMedia.FileName = filename; } @@ -210,7 +219,7 @@ public ActionResult Replace(int replaceId, string type) { }); } catch (Exception ex) { - Logger.Error(ex, "Unexpected exception when uploading a media."); + Logger.Error(ex, T("Unexpected exception when uploading a media.").Text); statuses.Add(new { error = T(ex.Message).Text, diff --git a/src/Orchard.Web/Modules/Orchard.MediaLibrary/Controllers/FolderController.cs b/src/Orchard.Web/Modules/Orchard.MediaLibrary/Controllers/FolderController.cs index cf039106aaf..22036beb67d 100644 --- a/src/Orchard.Web/Modules/Orchard.MediaLibrary/Controllers/FolderController.cs +++ b/src/Orchard.Web/Modules/Orchard.MediaLibrary/Controllers/FolderController.cs @@ -1,9 +1,9 @@ using System; using System.IO; using System.Linq; -using System.Text.RegularExpressions; using System.Web.Mvc; using Orchard.ContentManagement; +using Orchard.FileSystems.Media; using Orchard.Localization; using Orchard.Logging; using Orchard.MediaLibrary.Models; @@ -36,7 +36,7 @@ IMediaLibraryService mediaManagerService public ActionResult Create(string folderPath) { if (!(_mediaLibraryService.CheckMediaFolderPermission(Permissions.ImportMediaContent, folderPath) || _mediaLibraryService.CheckMediaFolderPermission(Permissions.EditMediaContent, folderPath))) { Services.Notifier.Error(T("Couldn't create media folder")); - return RedirectToAction("Index", "Admin", new { area = "Orchard.MediaLibrary", folderPath = folderPath }); + return RedirectToAction("Index", "Admin", new { area = "Orchard.MediaLibrary", folderPath }); } // If the user is trying to access a folder above his boundaries, redirect him to his home folder @@ -68,28 +68,32 @@ public ActionResult Create() { return new HttpUnauthorizedResult(); } + var failed = false; try { - bool valid = String.IsNullOrWhiteSpace(viewModel.Name) || Regex.IsMatch(viewModel.Name, @"^[^:?#\[\]@!$&'()*+,.;=\s\""\<\>\\\|%]+$"); - if (!valid) { - throw new ArgumentException(T("Folder contains invalid characters").ToString()); - } - else { - _mediaLibraryService.CreateFolder(viewModel.FolderPath, viewModel.Name); - Services.Notifier.Information(T("Media folder created")); - } + _mediaLibraryService.CreateFolder(viewModel.FolderPath, viewModel.Name); + Services.Notifier.Information(T("Media folder created")); + } + catch (InvalidNameCharacterException) { + Services.Notifier.Error(T("The folder name contains invalid character(s).")); + failed = true; } catch (ArgumentException argumentException) { Services.Notifier.Error(T("Creating Folder failed: {0}", argumentException.Message)); + failed = true; + } + + if (failed) { Services.TransactionManager.Cancel(); return View(viewModel); } + return RedirectToAction("Index", "Admin", new { area = "Orchard.MediaLibrary" }); } public ActionResult Edit(string folderPath) { if (!(_mediaLibraryService.CheckMediaFolderPermission(Permissions.ImportMediaContent, folderPath) || _mediaLibraryService.CheckMediaFolderPermission(Permissions.EditMediaContent, folderPath))) { Services.Notifier.Error(T("Couldn't edit media folder")); - return RedirectToAction("Index", "Admin", new { area = "Orchard.MediaLibrary", folderPath = folderPath }); + return RedirectToAction("Index", "Admin", new { area = "Orchard.MediaLibrary", folderPath }); } if (!_mediaLibraryService.CanManageMediaFolder(folderPath)) { @@ -125,7 +129,7 @@ public ActionResult Edit() { var viewModel = new MediaManagerFolderEditViewModel(); UpdateModel(viewModel); - if (!(_mediaLibraryService.CheckMediaFolderPermission(Permissions.ImportMediaContent, viewModel.FolderPath) + if (!(_mediaLibraryService.CheckMediaFolderPermission(Permissions.ImportMediaContent, viewModel.FolderPath) || _mediaLibraryService.CheckMediaFolderPermission(Permissions.EditMediaContent, viewModel.FolderPath))) { return new HttpUnauthorizedResult(); } @@ -136,14 +140,12 @@ public ActionResult Edit() { } try { - bool valid = String.IsNullOrWhiteSpace(viewModel.Name) || Regex.IsMatch(viewModel.Name, @"^[^:?#\[\]@!$&'()*+,.;=\s\""\<\>\\\|%]+$"); - if (!valid) { - throw new ArgumentException(T("Folder contains invalid characters").ToString()); - } - else { - _mediaLibraryService.RenameFolder(viewModel.FolderPath, viewModel.Name); - Services.Notifier.Information(T("Media folder renamed")); - } + _mediaLibraryService.RenameFolder(viewModel.FolderPath, viewModel.Name); + Services.Notifier.Information(T("Media folder renamed")); + } + catch (InvalidNameCharacterException) { + Services.Notifier.Error(T("The folder name contains invalid character(s).")); + return View(viewModel); } catch (Exception exception) { Services.Notifier.Error(T("Editing Folder failed: {0}", exception.Message)); @@ -198,7 +200,7 @@ public ActionResult Move(string folderPath, int[] mediaItemIds) { // don't try to rename the file if there is no associated media file if (!string.IsNullOrEmpty(media.FileName)) { // check permission on source folder - if(!_mediaLibraryService.CheckMediaFolderPermission(Permissions.DeleteMediaContent, media.FolderPath)) { + if (!_mediaLibraryService.CheckMediaFolderPermission(Permissions.DeleteMediaContent, media.FolderPath)) { return new HttpUnauthorizedResult(); } diff --git a/src/Orchard.Web/Modules/Orchard.MediaLibrary/MediaFileName/MediaFileNameDriver.cs b/src/Orchard.Web/Modules/Orchard.MediaLibrary/MediaFileName/MediaFileNameDriver.cs index e99e26318aa..42353deeb9d 100644 --- a/src/Orchard.Web/Modules/Orchard.MediaLibrary/MediaFileName/MediaFileNameDriver.cs +++ b/src/Orchard.Web/Modules/Orchard.MediaLibrary/MediaFileName/MediaFileNameDriver.cs @@ -1,14 +1,14 @@ using System; using Orchard.ContentManagement; using Orchard.ContentManagement.Drivers; +using Orchard.FileSystems.Media; using Orchard.Localization; using Orchard.MediaLibrary.Models; using Orchard.MediaLibrary.Services; using Orchard.Security; using Orchard.UI.Notify; -namespace Orchard.MediaLibrary.MediaFileName -{ +namespace Orchard.MediaLibrary.MediaFileName { public class MediaFileNameDriver : ContentPartDriver { private readonly IAuthenticationService _authenticationService; private readonly IAuthorizationService _authorizationService; @@ -58,6 +58,8 @@ protected override DriverResult Editor(MediaPart part, IUpdateModel updater, dyn var priorFileName = model.FileName; if (updater.TryUpdateModel(model, Prefix, null, null)) { if (model.FileName != null && !model.FileName.Equals(priorFileName, StringComparison.OrdinalIgnoreCase)) { + var fieldName = "MediaFileNameEditorSettings.FileName"; + try { _mediaLibraryService.RenameFile(part.FolderPath, priorFileName, model.FileName); part.FileName = model.FileName; @@ -65,10 +67,13 @@ protected override DriverResult Editor(MediaPart part, IUpdateModel updater, dyn _notifier.Add(NotifyType.Success, T("File '{0}' was renamed to '{1}'", priorFileName, model.FileName)); } catch (OrchardException) { - updater.AddModelError("MediaFileNameEditorSettings.FileName", T("Unable to rename file. Invalid Windows file path.")); + updater.AddModelError(fieldName, T("Unable to rename file. Invalid Windows file path.")); + } + catch (InvalidNameCharacterException) { + updater.AddModelError(fieldName, T("The file name contains invalid character(s).")); } - catch (Exception) { - updater.AddModelError("MediaFileNameEditorSettings.FileName", T("Unable to rename file")); + catch (Exception exception) { + updater.AddModelError(fieldName, T("Unable to rename file: {0}", exception.Message)); } } } diff --git a/src/Orchard.Web/Modules/Orchard.MediaLibrary/Services/MediaLibraryService.cs b/src/Orchard.Web/Modules/Orchard.MediaLibrary/Services/MediaLibraryService.cs index bbf77b8447d..e5b3bf7c1c1 100644 --- a/src/Orchard.Web/Modules/Orchard.MediaLibrary/Services/MediaLibraryService.cs +++ b/src/Orchard.Web/Modules/Orchard.MediaLibrary/Services/MediaLibraryService.cs @@ -6,13 +6,13 @@ using Orchard.ContentManagement; using Orchard.ContentManagement.MetaData.Models; using Orchard.Core.Common.Models; +using Orchard.Core.Title.Models; using Orchard.FileSystems.Media; using Orchard.Localization; using Orchard.MediaLibrary.Factories; using Orchard.MediaLibrary.Models; -using Orchard.Core.Title.Models; -using Orchard.Validation; using Orchard.MediaLibrary.Providers; +using Orchard.Validation; namespace Orchard.MediaLibrary.Services { public class MediaLibraryService : IMediaLibraryService { @@ -21,7 +21,6 @@ public class MediaLibraryService : IMediaLibraryService { private readonly IStorageProvider _storageProvider; private readonly IEnumerable _mediaFactorySelectors; private readonly IMediaFolderProvider _mediaFolderProvider; - private static char[] HttpUnallowed = new char[] { '<', '>', '*', '%', '&', ':', '\\', '?', '#' }; public MediaLibraryService( IOrchardServices orchardServices, @@ -146,12 +145,6 @@ public MediaPart ImportMedia(Stream stream, string relativePath, string filename } public string GetUniqueFilename(string folderPath, string filename) { - - // remove any char which is unallowed in an HTTP request - foreach (var unallowedChar in HttpUnallowed) { - filename = filename.Replace(unallowedChar.ToString(), ""); - } - // compute a unique filename var uniqueFilename = filename; var index = 1; @@ -178,9 +171,9 @@ public MediaPart ImportMedia(string relativePath, string filename, string conten var mediaFile = BuildMediaFile(relativePath, storageFile); using (var stream = storageFile.OpenRead()) { - var mediaFactory = GetMediaFactory(stream, mimeType, contentType); - if (mediaFactory == null) - throw new Exception(T("No media factory available to handle this resource.").Text); + var mediaFactory = GetMediaFactory(stream, mimeType, contentType) + ?? throw new Exception(T("No media factory available to handle this resource.").Text); + var mediaPart = mediaFactory.CreateMedia(stream, mediaFile.Name, mimeType, contentType); if (mediaPart != null) { mediaPart.FolderPath = relativePath; @@ -257,7 +250,7 @@ public bool CheckMediaFolderPermission(Orchard.Security.Permissions.Permission p if (_orchardServices.Authorizer.Authorize(Permissions.ManageMediaContent)) { return true; } - if (_orchardServices.WorkContext.CurrentUser==null) + if (_orchardServices.WorkContext.CurrentUser == null) return _orchardServices.Authorizer.Authorize(permission); // determines the folder type: public, user own folder (my), folder of another user (private) var rootedFolderPath = this.GetRootedFolderPath(folderPath) ?? ""; @@ -269,7 +262,7 @@ public bool CheckMediaFolderPermission(Orchard.Security.Permissions.Permission p isMyfolder = true; } - if(isMyfolder) { + if (isMyfolder) { return _orchardServices.Authorizer.Authorize(Permissions.ManageOwnMedia); } else { // other diff --git a/src/Orchard.Web/Modules/Orchard.OpenId/Orchard.OpenId.csproj b/src/Orchard.Web/Modules/Orchard.OpenId/Orchard.OpenId.csproj index 109495d160b..5eb6ee7f5fa 100644 --- a/src/Orchard.Web/Modules/Orchard.OpenId/Orchard.OpenId.csproj +++ b/src/Orchard.Web/Modules/Orchard.OpenId/Orchard.OpenId.csproj @@ -84,23 +84,32 @@ ..\..\..\packages\Microsoft.IdentityModel.Clients.ActiveDirectory.3.19.8\lib\net45\Microsoft.IdentityModel.Clients.ActiveDirectory.Platform.dll - - ..\..\..\packages\Microsoft.IdentityModel.JsonWebTokens.5.2.4\lib\net451\Microsoft.IdentityModel.JsonWebTokens.dll + + ..\..\..\packages\Microsoft.IdentityModel.JsonWebTokens.5.7.0\lib\net461\Microsoft.IdentityModel.JsonWebTokens.dll - - ..\..\..\packages\Microsoft.IdentityModel.Logging.5.2.4\lib\net451\Microsoft.IdentityModel.Logging.dll + + ..\..\..\packages\Microsoft.IdentityModel.Logging.5.7.0\lib\net461\Microsoft.IdentityModel.Logging.dll ..\..\..\packages\Microsoft.IdentityModel.Protocol.Extensions.1.0.4.403061554\lib\net45\Microsoft.IdentityModel.Protocol.Extensions.dll - - ..\..\..\packages\Microsoft.IdentityModel.Protocols.5.2.4\lib\net451\Microsoft.IdentityModel.Protocols.dll + + ..\..\..\packages\Microsoft.IdentityModel.Protocols.5.7.0\lib\net461\Microsoft.IdentityModel.Protocols.dll - - ..\..\..\packages\Microsoft.IdentityModel.Protocols.OpenIdConnect.5.2.4\lib\net451\Microsoft.IdentityModel.Protocols.OpenIdConnect.dll + + ..\..\..\packages\Microsoft.IdentityModel.Protocols.OpenIdConnect.5.7.0\lib\net461\Microsoft.IdentityModel.Protocols.OpenIdConnect.dll - - ..\..\..\packages\Microsoft.IdentityModel.Tokens.5.2.4\lib\net451\Microsoft.IdentityModel.Tokens.dll + + ..\..\..\packages\Microsoft.IdentityModel.Protocols.WsFederation.5.7.0\lib\net461\Microsoft.IdentityModel.Protocols.WsFederation.dll + + + ..\..\..\packages\Microsoft.IdentityModel.Tokens.5.7.0\lib\net461\Microsoft.IdentityModel.Tokens.dll + + + ..\..\..\packages\Microsoft.IdentityModel.Tokens.Saml.5.7.0\lib\net461\Microsoft.IdentityModel.Tokens.Saml.dll + + + ..\..\..\packages\Microsoft.IdentityModel.Xml.5.7.0\lib\net461\Microsoft.IdentityModel.Xml.dll ..\..\..\packages\Microsoft.Owin.4.2.2\lib\net45\Microsoft.Owin.dll @@ -111,29 +120,29 @@ ..\..\..\packages\Microsoft.Owin.Security.4.2.2\lib\net45\Microsoft.Owin.Security.dll - - ..\..\..\packages\Microsoft.Owin.Security.ActiveDirectory.4.0.0\lib\net451\Microsoft.Owin.Security.ActiveDirectory.dll + + ..\..\..\packages\Microsoft.Owin.Security.ActiveDirectory.4.2.2\lib\net45\Microsoft.Owin.Security.ActiveDirectory.dll ..\..\..\packages\Microsoft.Owin.Security.Cookies.4.2.2\lib\net45\Microsoft.Owin.Security.Cookies.dll - - ..\..\..\packages\Microsoft.Owin.Security.Facebook.4.0.0\lib\net451\Microsoft.Owin.Security.Facebook.dll + + ..\..\..\packages\Microsoft.Owin.Security.Facebook.4.2.2\lib\net45\Microsoft.Owin.Security.Facebook.dll - - ..\..\..\packages\Microsoft.Owin.Security.Google.4.0.0\lib\net451\Microsoft.Owin.Security.Google.dll + + ..\..\..\packages\Microsoft.Owin.Security.Google.4.2.2\lib\net45\Microsoft.Owin.Security.Google.dll - - ..\..\..\packages\Microsoft.Owin.Security.Jwt.4.0.0\lib\net451\Microsoft.Owin.Security.Jwt.dll + + ..\..\..\packages\Microsoft.Owin.Security.Jwt.4.2.2\lib\net45\Microsoft.Owin.Security.Jwt.dll - - ..\..\..\packages\Microsoft.Owin.Security.OAuth.4.0.0\lib\net451\Microsoft.Owin.Security.OAuth.dll + + ..\..\..\packages\Microsoft.Owin.Security.OAuth.4.2.2\lib\net45\Microsoft.Owin.Security.OAuth.dll - - ..\..\..\packages\Microsoft.Owin.Security.OpenIdConnect.4.0.0\lib\net451\Microsoft.Owin.Security.OpenIdConnect.dll + + ..\..\..\packages\Microsoft.Owin.Security.OpenIdConnect.4.2.2\lib\net45\Microsoft.Owin.Security.OpenIdConnect.dll - - ..\..\..\packages\Microsoft.Owin.Security.Twitter.4.0.0\lib\net451\Microsoft.Owin.Security.Twitter.dll + + ..\..\..\packages\Microsoft.Owin.Security.Twitter.4.2.2\lib\net45\Microsoft.Owin.Security.Twitter.dll ..\..\..\packages\Microsoft.Web.Infrastructure.1.0.0.0\lib\net40\Microsoft.Web.Infrastructure.dll @@ -162,8 +171,8 @@ - - ..\..\..\packages\System.IdentityModel.Tokens.Jwt.5.2.4\lib\net451\System.IdentityModel.Tokens.Jwt.dll + + ..\..\..\packages\System.IdentityModel.Tokens.Jwt.5.7.0\lib\net461\System.IdentityModel.Tokens.Jwt.dll diff --git a/src/Orchard.Web/Modules/Orchard.OpenId/Web.config b/src/Orchard.Web/Modules/Orchard.OpenId/Web.config index 2e752185629..9e9001652e2 100644 --- a/src/Orchard.Web/Modules/Orchard.OpenId/Web.config +++ b/src/Orchard.Web/Modules/Orchard.OpenId/Web.config @@ -76,7 +76,7 @@ - + @@ -88,15 +88,15 @@ - + - + - + @@ -114,6 +114,22 @@ + + + + + + + + + + + + + + + + diff --git a/src/Orchard.Web/Modules/Orchard.OpenId/packages.config b/src/Orchard.Web/Modules/Orchard.OpenId/packages.config index d4e71c49d6c..c31b3098702 100644 --- a/src/Orchard.Web/Modules/Orchard.OpenId/packages.config +++ b/src/Orchard.Web/Modules/Orchard.OpenId/packages.config @@ -12,29 +12,32 @@ - - + + - - - + + + + + + - + - - - - - - + + + + + + - + \ No newline at end of file diff --git a/src/Orchard.Web/Modules/Orchard.Projections/FilterEditors/Forms/StringFilterForm.cs b/src/Orchard.Web/Modules/Orchard.Projections/FilterEditors/Forms/StringFilterForm.cs index 59656c4b6d5..4b88d245c2e 100644 --- a/src/Orchard.Web/Modules/Orchard.Projections/FilterEditors/Forms/StringFilterForm.cs +++ b/src/Orchard.Web/Modules/Orchard.Projections/FilterEditors/Forms/StringFilterForm.cs @@ -7,7 +7,6 @@ using Orchard.Localization; namespace Orchard.Projections.FilterEditors.Forms { - public class StringFilterForm : IFormProvider { public const string FormName = "StringFilter"; @@ -20,44 +19,44 @@ public StringFilterForm(IShapeFactory shapeFactory) { } public void Describe(DescribeContext context) { - Func form = - shape => { - - var f = Shape.Form( - Id: "StringFilter", - _Operator: Shape.SelectList( - Id: "operator", Name: "Operator", - Title: T("Operator"), - Size: 1, - Multiple: false - ), - _Value: Shape.TextBox( - Id: "value", Name: "Value", - Title: T("Value"), - Classes: new[] { "text medium", "tokenized" }, - Description: T("Enter the value the string should be.") - ) - ); + object form(IShapeFactory shape) { + var f = Shape.Form( + Id: "StringFilter", + _Operator: Shape.SelectList( + Id: "operator", Name: "Operator", + Title: T("Operator"), + Size: 1, + Multiple: false + ), + _Value: Shape.TextBox( + Id: "value", Name: "Value", + Title: T("Value"), + Classes: new[] { "text medium", "tokenized" }, + Description: T("Enter the value the string should be.") + ), + _IgnoreIfEmptyValue: Shape.Checkbox( + Id: "IgnoreFilterIfValueIsEmpty", + Name: "IgnoreFilterIfValueIsEmpty", + Title: T("Ignore filter if value is empty"), + Description: T("When enabled, the filter will not be applied if the provided value is or evaluates to empty."), + Value: "true" + )); - f._Operator.Add(new SelectListItem { Value = Convert.ToString(StringOperator.Equals), Text = T("Is equal to").Text }); - f._Operator.Add(new SelectListItem { Value = Convert.ToString(StringOperator.NotEquals), Text = T("Is not equal to").Text }); - f._Operator.Add(new SelectListItem { Value = Convert.ToString(StringOperator.Contains), Text = T("Contains").Text }); - f._Operator.Add(new SelectListItem { Value = Convert.ToString(StringOperator.ContainsAny), Text = T("Contains any word").Text }); - f._Operator.Add(new SelectListItem { Value = Convert.ToString(StringOperator.ContainsAll), Text = T("Contains all words").Text }); - f._Operator.Add(new SelectListItem { Value = Convert.ToString(StringOperator.Starts), Text = T("Starts with").Text }); - f._Operator.Add(new SelectListItem { Value = Convert.ToString(StringOperator.NotStarts), Text = T("Does not start with").Text }); - f._Operator.Add(new SelectListItem { Value = Convert.ToString(StringOperator.Ends), Text = T("Ends with").Text }); - f._Operator.Add(new SelectListItem { Value = Convert.ToString(StringOperator.NotEnds), Text = T("Does not end with").Text }); - f._Operator.Add(new SelectListItem { Value = Convert.ToString(StringOperator.NotContains), Text = T("Does not contain").Text }); - f._Operator.Add(new SelectListItem { - Value = Convert.ToString(StringOperator.ContainsAnyIfProvided), - Text = T("Contains any word (if any is provided)").Text - }); + f._Operator.Add(new SelectListItem { Value = Convert.ToString(StringOperator.Equals), Text = T("Is equal to").Text }); + f._Operator.Add(new SelectListItem { Value = Convert.ToString(StringOperator.NotEquals), Text = T("Is not equal to").Text }); + f._Operator.Add(new SelectListItem { Value = Convert.ToString(StringOperator.Contains), Text = T("Contains").Text }); + f._Operator.Add(new SelectListItem { Value = Convert.ToString(StringOperator.ContainsAny), Text = T("Contains any word").Text }); + f._Operator.Add(new SelectListItem { Value = Convert.ToString(StringOperator.ContainsAll), Text = T("Contains all words").Text }); + f._Operator.Add(new SelectListItem { Value = Convert.ToString(StringOperator.Starts), Text = T("Starts with").Text }); + f._Operator.Add(new SelectListItem { Value = Convert.ToString(StringOperator.NotStarts), Text = T("Does not start with").Text }); + f._Operator.Add(new SelectListItem { Value = Convert.ToString(StringOperator.Ends), Text = T("Ends with").Text }); + f._Operator.Add(new SelectListItem { Value = Convert.ToString(StringOperator.NotEnds), Text = T("Does not end with").Text }); + f._Operator.Add(new SelectListItem { Value = Convert.ToString(StringOperator.NotContains), Text = T("Does not contain").Text }); - return f; - }; + return f; + } - context.Form(FormName, form); + context.Form(FormName, (Func)form); } @@ -65,6 +64,11 @@ public static Action GetFilterPredicate(dynamic formState var op = (StringOperator)Enum.Parse(typeof(StringOperator), Convert.ToString(formState.Operator)); object value = Convert.ToString(formState.Value); + if (bool.TryParse(formState.IgnoreFilterIfValueIsEmpty?.ToString() ?? "", out bool ignoreIfEmpty) + && ignoreIfEmpty + && string.IsNullOrWhiteSpace(value as string)) + return (ex) => { }; + switch (op) { case StringOperator.Equals: return x => x.Eq(property, value); @@ -92,14 +96,6 @@ public static Action GetFilterPredicate(dynamic formState return y => y.Not(x => x.Like(property, Convert.ToString(value), HqlMatchMode.End)); case StringOperator.NotContains: return y => y.Not(x => x.Like(property, Convert.ToString(value), HqlMatchMode.Anywhere)); - case StringOperator.ContainsAnyIfProvided: - if (string.IsNullOrWhiteSpace((string)value)) - return x => x.IsNotEmpty("Id"); // basically, return every possible ContentItem - var values3 = Convert.ToString(value) - .Split(new[] { ' ' }, StringSplitOptions.RemoveEmptyEntries); - var predicates3 = values3.Skip(1) - .Select>(x => y => y.Like(property, x, HqlMatchMode.Anywhere)).ToArray(); - return x => x.Disjunction(y => y.Like(property, values3[0], HqlMatchMode.Anywhere), predicates3); default: throw new ArgumentOutOfRangeException(); } @@ -130,11 +126,6 @@ public static LocalizedString DisplayFilter(string fieldName, dynamic formState, return T("{0} does not end with '{1}'", fieldName, value); case StringOperator.NotContains: return T("{0} does not contain '{1}'", fieldName, value); - case StringOperator.ContainsAnyIfProvided: - return T("{0} contains any of '{1}' (or '{1}' is empty)", - fieldName, - new LocalizedString(string.Join("', '", - value.Split(new[] { ' ' }, StringSplitOptions.RemoveEmptyEntries)))); default: throw new ArgumentOutOfRangeException(); } @@ -151,7 +142,6 @@ public enum StringOperator { NotStarts, Ends, NotEnds, - NotContains, - ContainsAnyIfProvided + NotContains } -} \ No newline at end of file +} diff --git a/src/Orchard.Web/Modules/Orchard.Projections/Migrations.cs b/src/Orchard.Web/Modules/Orchard.Projections/Migrations.cs index 2c57885ef95..982520d91a8 100644 --- a/src/Orchard.Web/Modules/Orchard.Projections/Migrations.cs +++ b/src/Orchard.Web/Modules/Orchard.Projections/Migrations.cs @@ -15,6 +15,7 @@ public class Migrations : DataMigrationImpl { private readonly IRepository _memberBindingRepository; private readonly IRepository _layoutRepository; private readonly IRepository _propertyRecordRepository; + private readonly IRepository _filterRepository; /// /// When upgrading from "1.10.x" branch code committed after 1.10.3 to "dev" branch code or 1.11, merge @@ -28,10 +29,12 @@ public class Migrations : DataMigrationImpl { public Migrations( IRepository memberBindingRepository, IRepository layoutRepository, - IRepository propertyRecordRepository) { + IRepository propertyRecordRepository, + IRepository filterRepository) { _memberBindingRepository = memberBindingRepository; _layoutRepository = layoutRepository; _propertyRecordRepository = propertyRecordRepository; + _filterRepository = filterRepository; T = NullLocalizer.Instance; } @@ -402,6 +405,18 @@ public int UpdateFrom5() { } public int UpdateFrom6() { + // This change was originally UpdateFrom6 on 1.10.x and UpdateFrom6 on dev: Casting a somewhat wide net, but + // filters can't be queried by the form they are using and different types of filters can (and do) use + // StringFilterForm. However, the "Operator" parameter's value being "ContainsAnyIfProvided" is very + // specific. + var formStateToReplace = "ContainsAnyIfProvided"; + var filterRecordsToUpdate = _filterRepository.Table.Where(f => f.State.Contains(formStateToReplace)).ToList(); + foreach (var filter in filterRecordsToUpdate) { + filter.State = filter.State.Replace( + formStateToReplace, + "ContainsAnytrue"); + } + if (IsUpgradingFromOrchard_1_10_x_Version_6) { MigratePropertyRecordToRewriteOutputCondition(); } diff --git a/src/Orchard.Web/Modules/Orchard.Projections/StandardQueries/QueryFeedQuery.cs b/src/Orchard.Web/Modules/Orchard.Projections/StandardQueries/QueryFeedQuery.cs index d53d7228b5c..07b0d629ef7 100644 --- a/src/Orchard.Web/Modules/Orchard.Projections/StandardQueries/QueryFeedQuery.cs +++ b/src/Orchard.Web/Modules/Orchard.Projections/StandardQueries/QueryFeedQuery.cs @@ -1,5 +1,4 @@ using System; -using System.Collections.Generic; using System.Linq; using System.Web.Mvc; using System.Xml.Linq; @@ -11,22 +10,22 @@ using Orchard.Projections.Models; using Orchard.Projections.Services; using Orchard.Services; -using Orchard.Utility.Extensions; -namespace Orchard.Projections.StandardQueries { +namespace Orchard.Projections.StandardQueries +{ public class QueryFeedQuery : IFeedQueryProvider, IFeedQuery { private readonly IContentManager _contentManager; private readonly IProjectionManager _projectionManager; - private readonly IEnumerable _htmlFilters; + private readonly IHtmlFilterProcessor _htmlFilterProcessor; public QueryFeedQuery( IContentManager contentManager, IProjectionManager projectionManager, - IEnumerable htmlFilters) + IHtmlFilterProcessor htmlFilterProcessor) { _contentManager = contentManager; _projectionManager = projectionManager; - _htmlFilters = htmlFilters; + _htmlFilterProcessor = htmlFilterProcessor; } public FeedQueryMatch Match(FeedContext context) { @@ -55,7 +54,7 @@ public void Execute(FeedContext context) { return; } - var inspector = new ItemInspector(container, _contentManager.GetItemMetadata(container), _htmlFilters); + var inspector = new ItemInspector(container, _contentManager.GetItemMetadata(container), _htmlFilterProcessor); if (context.Format == "rss") { var link = new XElement("link"); context.Response.Element.SetElementValue("title", inspector.Title); diff --git a/src/Orchard.Web/Modules/Orchard.Tags/Feeds/TagFeedQuery.cs b/src/Orchard.Web/Modules/Orchard.Tags/Feeds/TagFeedQuery.cs index b5fd415c350..2e30e643ef8 100644 --- a/src/Orchard.Web/Modules/Orchard.Tags/Feeds/TagFeedQuery.cs +++ b/src/Orchard.Web/Modules/Orchard.Tags/Feeds/TagFeedQuery.cs @@ -1,34 +1,21 @@ using System; -using System.Collections.Generic; using System.Web.Mvc; +using System.Web.Routing; using System.Xml.Linq; -using Orchard.ContentManagement; -using Orchard.Core.Common.Models; +using Orchard.Core.Feeds; using Orchard.Core.Feeds.Models; -using Orchard.Core.Feeds.StandardBuilders; +using Orchard.Environment.Extensions; +using Orchard.Localization; using Orchard.Mvc.Extensions; -using Orchard.Services; -using Orchard.Utility.Extensions; -using Orchard.Core.Feeds; using Orchard.Tags.Services; -using Orchard.Localization; -using System.Web.Routing; -using Orchard.Environment.Extensions; namespace Orchard.Tags.Feeds { [OrchardFeature("Orchard.Tags.Feeds")] public class TagFeedQuery : IFeedQueryProvider, IFeedQuery { - private readonly IContentManager _contentManager; - private readonly IEnumerable _htmlFilters; private readonly ITagService _tagService; - public TagFeedQuery( - IContentManager contentManager, - IEnumerable htmlFilters, - ITagService tagService) { - _contentManager = contentManager; + public TagFeedQuery(ITagService tagService) { _tagService = tagService; - _htmlFilters = htmlFilters; T = NullLocalizer.Instance; } @@ -42,11 +29,11 @@ public FeedQueryMatch Match(FeedContext context) { var tagName = (string)tagIdValue.ConvertTo(typeof(string)); var tag = _tagService.GetTagByName(tagName); - + if (tag == null) { return null; } - + return new FeedQueryMatch { FeedQuery = this, Priority = -5 }; } @@ -57,10 +44,10 @@ public void Execute(FeedContext context) { var limitValue = context.ValueProvider.GetValue("limit"); var limit = 20; - if (limitValue != null) { + if (limitValue != null) { Int32.TryParse(Convert.ToString(limitValue), out limit); } - + limit = Math.Min(limit, 100); var tagName = (string)tagIdValue.ConvertTo(typeof(string)); diff --git a/src/Orchard.Web/Modules/Orchard.Tokens/Filters/TokensFilter.cs b/src/Orchard.Web/Modules/Orchard.Tokens/Filters/TokensFilter.cs index cab5c586b4e..fa301871a4c 100644 --- a/src/Orchard.Web/Modules/Orchard.Tokens/Filters/TokensFilter.cs +++ b/src/Orchard.Web/Modules/Orchard.Tokens/Filters/TokensFilter.cs @@ -1,49 +1,33 @@ using System; -using System.Collections.Generic; -using Orchard.ContentManagement; -using Orchard.ContentManagement.Handlers; using Orchard.Environment.Extensions; using Orchard.Services; namespace Orchard.Tokens.Filters { - [OrchardFeature("Orchard.Tokens.HtmlFilter")] - public class TokensFilter : ContentHandler, IHtmlFilter { + public class TokensFilter : HtmlFilter { private readonly ITokenizer _tokenizer; - private ContentItem _displayed; public TokensFilter(ITokenizer tokenizer) { _tokenizer = tokenizer; } - - protected override void BuildDisplayShape(BuildDisplayContext context) { - _displayed = context.ContentItem; - } - - public string ProcessContent(string text, string flavor) { - return TokensReplace(text); + + public override string ProcessContent(string text, HtmlFilterContext context) { + return TokensReplace(text, context); } - private string TokensReplace(string text) { + private string TokensReplace(string text, HtmlFilterContext context) { if (String.IsNullOrEmpty(text)) return String.Empty; // Optimize code path if nothing to do. - if (!text.Contains("#{")) { + if (!text.Contains("#{")) return text; - } - - var data = new Dictionary(); - - if (_displayed != null) - data["Content"] = _displayed; - - text = _tokenizer.Replace(text, data); - - _displayed = null; - return text; + return _tokenizer.Replace(text, context.Data, + String.Equals(context.Flavor, "html", StringComparison.OrdinalIgnoreCase) + ? new ReplaceOptions { Encoding = ReplaceOptions.HtmlEncode } + : new ReplaceOptions { Encoding = ReplaceOptions.NoEncode }); } } } \ No newline at end of file diff --git a/src/Orchard.Web/Modules/Orchard.Tokens/Module.txt b/src/Orchard.Web/Modules/Orchard.Tokens/Module.txt index 2be37cd1b70..d90c598cd2b 100644 --- a/src/Orchard.Web/Modules/Orchard.Tokens/Module.txt +++ b/src/Orchard.Web/Modules/Orchard.Tokens/Module.txt @@ -20,3 +20,4 @@ Features: Description: Evaluates tokens in a body. Category: Content Dependencies: Orchard.Tokens + Priority: -1 diff --git a/src/Orchard.Web/Web.config b/src/Orchard.Web/Web.config index d8fa011b2bb..ef54236949d 100644 --- a/src/Orchard.Web/Web.config +++ b/src/Orchard.Web/Web.config @@ -247,7 +247,7 @@ - + @@ -255,15 +255,15 @@ - + - + - + diff --git a/src/Orchard/Exceptions/DefaultExceptionPolicy.cs b/src/Orchard/Exceptions/DefaultExceptionPolicy.cs index db6de355985..08e7466e749 100644 --- a/src/Orchard/Exceptions/DefaultExceptionPolicy.cs +++ b/src/Orchard/Exceptions/DefaultExceptionPolicy.cs @@ -34,7 +34,7 @@ public bool HandleException(object sender, Exception exception) { return false; } - if (sender is IEventBus && exception is OrchardFatalException) { + if (sender is IEventBus && exception is OrchardFatalException) { return false; } @@ -49,7 +49,7 @@ public bool HandleException(object sender, Exception exception) { } private static bool IsFatal(Exception exception) { - return + return exception is OrchardSecurityException || exception is StackOverflowException || exception is AccessViolationException || diff --git a/src/Orchard/Exceptions/ExceptionExtensions.cs b/src/Orchard/Exceptions/ExceptionExtensions.cs index a66ba1a8b9d..534c774065e 100644 --- a/src/Orchard/Exceptions/ExceptionExtensions.cs +++ b/src/Orchard/Exceptions/ExceptionExtensions.cs @@ -1,11 +1,8 @@ using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using Orchard.Security; -using System.Threading; -using System.Security; using System.Runtime.InteropServices; +using System.Security; +using System.Threading; +using Orchard.Security; namespace Orchard.Exceptions { public static class ExceptionExtensions { diff --git a/src/Orchard/FileSystems/Media/FileSystemStorageProvider.cs b/src/Orchard/FileSystems/Media/FileSystemStorageProvider.cs index ab0abfd2652..34a7b505518 100644 --- a/src/Orchard/FileSystems/Media/FileSystemStorageProvider.cs +++ b/src/Orchard/FileSystems/Media/FileSystemStorageProvider.cs @@ -4,15 +4,22 @@ using System.Linq; using System.Web.Hosting; using Orchard.Environment.Configuration; +using Orchard.Exceptions; using Orchard.Localization; +using Orchard.Utility.Extensions; using Orchard.Validation; -using Orchard.Exceptions; namespace Orchard.FileSystems.Media { public class FileSystemStorageProvider : IStorageProvider { private readonly string _storagePath; // c:\orchard\media\default private readonly string _virtualPath; // ~/Media/Default/ private readonly string _publicPath; // /Orchard/Media/Default/ + public static readonly char[] HttpUnallowedCharacters = + new char[] { '<', '>', '*', '%', '&', ':', '\\', '/', '?', '#', '"', '{', '}', '|', '^', '[', ']', '`' }; + public static readonly char[] InvalidFolderNameCharacters = + Path.GetInvalidPathChars().Union(HttpUnallowedCharacters).ToArray(); + public static readonly char[] InvalidFileNameCharacters = + Path.GetInvalidFileNameChars().Union(HttpUnallowedCharacters).ToArray(); public FileSystemStorageProvider(ShellSettings settings) { var mediaPath = HostingEnvironment.IsHosted @@ -27,7 +34,7 @@ public FileSystemStorageProvider(ShellSettings settings) { appPath = HostingEnvironment.ApplicationVirtualPath; } if (!appPath.EndsWith("/")) - appPath = appPath + '/'; + appPath += '/'; if (!appPath.StartsWith("/")) appPath = '/' + appPath; @@ -39,21 +46,21 @@ public FileSystemStorageProvider(ShellSettings settings) { public Localizer T { get; set; } - public int MaxPathLength { - get; set; - // The public setter allows injecting this from Sites.MyTenant.Config or Sites.config, by using - // an AutoFac component: - /* - + /// + /// The public setter allows injecting this from Sites.MyTenant.Config or Sites.config, by using an AutoFac + /// component. See the example below. + /// + /* + - - */ - } + */ + public int MaxPathLength { get; set; } /// /// Maps a relative path into the storage path. @@ -215,6 +222,12 @@ public bool TryCreateFolder(string path) { /// The relative path to the folder to be created. /// If the folder already exists. public void CreateFolder(string path) { + // We are dealing with a folder here, but GetFileName returns the last path segment, which in this case is + // the folder name. + if (FolderNameContainsInvalidCharacters(Path.GetFileName(path))) { + throw new InvalidNameCharacterException(T("The directory name contains invalid character(s)").ToString()); + } + DirectoryInfo directoryInfo = new DirectoryInfo(MapStorage(path)); if (directoryInfo.Exists) { throw new ArgumentException(T("Directory {0} already exists", path).ToString()); @@ -248,6 +261,12 @@ public void RenameFolder(string oldPath, string newPath) { throw new ArgumentException(T("Directory {0} does not exist", oldPath).ToString()); } + // We are dealing with a folder here, but GetFileName returns the last path segment, which in this case is + // the folder name. + if (FolderNameContainsInvalidCharacters(Path.GetFileName(newPath))) { + throw new InvalidNameCharacterException(T("The new directory name contains invalid character(s)").ToString()); + } + DirectoryInfo targetDirectory = new DirectoryInfo(MapStorage(newPath)); if (targetDirectory.Exists) { throw new ArgumentException(T("Directory {0} already exists", newPath).ToString()); @@ -313,6 +332,10 @@ public void RenameFile(string oldPath, string newPath) { throw new ArgumentException(T("File {0} does not exist", oldPath).ToString()); } + if (FileNameContainsInvalidCharacters(Path.GetFileName(newPath))) { + throw new InvalidNameCharacterException(T("The new file name contains invalid character(s)").ToString()); + } + FileInfo targetFileInfo = new FileInfo(MapStorage(newPath)); if (targetFileInfo.Exists) { throw new ArgumentException(T("File {0} already exists", newPath).ToString()); @@ -342,6 +365,10 @@ public void CopyFile(string originalPath, string duplicatePath) { /// If the file already exists. /// The created file. public IStorageFile CreateFile(string path) { + if (FileNameContainsInvalidCharacters(Path.GetFileName(path))) { + throw new InvalidNameCharacterException(T("The file name contains invalid character(s)").ToString()); + } + FileInfo fileInfo = new FileInfo(MapStorage(path)); if (fileInfo.Exists) { throw new ArgumentException(T("File {0} already exists", fileInfo.Name).ToString()); @@ -427,6 +454,12 @@ private static bool IsHidden(FileSystemInfo di) { return (di.Attributes & FileAttributes.Hidden) != 0; } + public static bool FolderNameContainsInvalidCharacters(string folderName) => + folderName.IndexOfAny(InvalidFolderNameCharacters) > -1; + + public static bool FileNameContainsInvalidCharacters(string fileName) => + fileName.IndexOfAny(InvalidFileNameCharacters) > -1; + #endregion private class FileSystemStorageFile : IStorageFile { diff --git a/src/Orchard/FileSystems/Media/IStorageProvider.cs b/src/Orchard/FileSystems/Media/IStorageProvider.cs index 39501cdaa7d..b7d771e6b3f 100644 --- a/src/Orchard/FileSystems/Media/IStorageProvider.cs +++ b/src/Orchard/FileSystems/Media/IStorageProvider.cs @@ -128,7 +128,7 @@ public interface IStorageProvider : IDependency { void SaveStream(string path, Stream inputStream); /// - /// Combines to paths. + /// Combines two paths. /// /// The parent path. /// The child path. diff --git a/src/Orchard/FileSystems/Media/InvalidNameCharacterException.cs b/src/Orchard/FileSystems/Media/InvalidNameCharacterException.cs new file mode 100644 index 00000000000..53fcff0c550 --- /dev/null +++ b/src/Orchard/FileSystems/Media/InvalidNameCharacterException.cs @@ -0,0 +1,7 @@ +using System; + +namespace Orchard.FileSystems.Media { + public class InvalidNameCharacterException : ArgumentException { + public InvalidNameCharacterException(string message) : base(message) { } + } +} diff --git a/src/Orchard/FileSystems/Media/StorageProviderExtensions.cs b/src/Orchard/FileSystems/Media/StorageProviderExtensions.cs index 8380b7645fb..1a5e4bf96ea 100644 --- a/src/Orchard/FileSystems/Media/StorageProviderExtensions.cs +++ b/src/Orchard/FileSystems/Media/StorageProviderExtensions.cs @@ -1,9 +1,5 @@ using System; -using System.Collections.Generic; using System.IO; -using System.Linq; -using System.Text; -using System.Threading.Tasks; namespace Orchard.FileSystems.Media { public static class StorageProviderExtensions { diff --git a/src/Orchard/Orchard.Framework.csproj b/src/Orchard/Orchard.Framework.csproj index 94eb529a18f..7058ad861d0 100644 --- a/src/Orchard/Orchard.Framework.csproj +++ b/src/Orchard/Orchard.Framework.csproj @@ -45,7 +45,6 @@ ..\OrchardBasicCorrectness.ruleset false false - pdbonly @@ -169,6 +168,7 @@ + @@ -432,7 +432,11 @@ + + + + @@ -714,6 +718,7 @@ + diff --git a/src/Orchard/Services/HtmlFilter.cs b/src/Orchard/Services/HtmlFilter.cs new file mode 100644 index 00000000000..09b729bb78b --- /dev/null +++ b/src/Orchard/Services/HtmlFilter.cs @@ -0,0 +1,5 @@ +namespace Orchard.Services { + public abstract class HtmlFilter : Component, IHtmlFilter { + public abstract string ProcessContent(string text, HtmlFilterContext context); + } +} diff --git a/src/Orchard/Services/HtmlFilterContext.cs b/src/Orchard/Services/HtmlFilterContext.cs new file mode 100644 index 00000000000..cb821de97d3 --- /dev/null +++ b/src/Orchard/Services/HtmlFilterContext.cs @@ -0,0 +1,8 @@ +using System.Collections.Generic; + +namespace Orchard.Services { + public class HtmlFilterContext { + public string Flavor { get; set; } + public IDictionary Data { get; set; } = new Dictionary(); + } +} diff --git a/src/Orchard/Services/HtmlFilterProcessor.cs b/src/Orchard/Services/HtmlFilterProcessor.cs new file mode 100644 index 00000000000..f1d2cfe9eb2 --- /dev/null +++ b/src/Orchard/Services/HtmlFilterProcessor.cs @@ -0,0 +1,16 @@ +using System.Collections.Generic; +using System.Linq; + +namespace Orchard.Services { + public class HtmlFilterProcessor : IHtmlFilterProcessor { + private readonly IEnumerable _filters; + + public HtmlFilterProcessor(IEnumerable filters) { + _filters = filters; + } + + public string ProcessFilters(string text, HtmlFilterContext context) { + return _filters.Aggregate(text, (current, htmlFilter) => htmlFilter.ProcessContent(current, context)); + } + } +} diff --git a/src/Orchard/Services/IHtmlFilter.cs b/src/Orchard/Services/IHtmlFilter.cs index 6114dc3fb17..0e206dbb0b1 100644 --- a/src/Orchard/Services/IHtmlFilter.cs +++ b/src/Orchard/Services/IHtmlFilter.cs @@ -1,5 +1,5 @@ namespace Orchard.Services { public interface IHtmlFilter : IDependency { - string ProcessContent(string text, string flavor); + string ProcessContent(string text, HtmlFilterContext context); } -} \ No newline at end of file +} diff --git a/src/Orchard/Services/IHtmlFilterProcessor.cs b/src/Orchard/Services/IHtmlFilterProcessor.cs new file mode 100644 index 00000000000..aea5defa3b9 --- /dev/null +++ b/src/Orchard/Services/IHtmlFilterProcessor.cs @@ -0,0 +1,28 @@ +using System.Collections.Generic; +using Orchard.ContentManagement; + +namespace Orchard.Services { + public interface IHtmlFilterProcessor : IDependency { + string ProcessFilters(string text, HtmlFilterContext context); + } + + public static class HtmlFilterProcessorExtensions { + public static string ProcessFilters( + this IHtmlFilterProcessor processor, + string text, + string flavor, + IDictionary data) => + processor.ProcessFilters(text, new HtmlFilterContext { Flavor = flavor, Data = data }); + + public static string ProcessFilters(this IHtmlFilterProcessor processor, string text, string flavor) => + processor.ProcessFilters(text, new HtmlFilterContext { Flavor = flavor }); + + public static string ProcessFilters(this IHtmlFilterProcessor processor, string text, string flavor, IContent content) => + processor.ProcessFilters( + text, + new HtmlFilterContext { + Flavor = flavor, + Data = new Dictionary { { "Content", content.ContentItem } } + }); + } +} diff --git a/src/Orchard/Utility/Extensions/NameValueCollectionExtensions.cs b/src/Orchard/Utility/Extensions/NameValueCollectionExtensions.cs new file mode 100644 index 00000000000..f4606979042 --- /dev/null +++ b/src/Orchard/Utility/Extensions/NameValueCollectionExtensions.cs @@ -0,0 +1,12 @@ +using System.Collections.Specialized; +using System.Linq; +using System.Web; + +namespace Orchard.Utility.Extensions { + public static class NameValueCollectionExtensions { + public static string ToQueryString(this NameValueCollection nameValues) => + string.Join( + "&", + (from string name in nameValues select string.Concat(name, "=", HttpUtility.UrlEncode(nameValues[name]))).ToArray()); + } +}