Skip to content

Commit

Permalink
#8791: Merge 1.10.x into dev (#8792)
Browse files Browse the repository at this point in the history
* 6748: Stricter file and folder name validation (#6792)

* Media Library: More strict file and folder name validation, fixes #6748

* Resetting MediaLibraryService changes to 1.10.x

* Code styling in FileSystemStorageProvider

* Adding string file and folder name validation to FileSystemStorageProvider, so that MediaLibrary components don't need to do it separately

* Applying the same file and folder name validation to AzureFileSystem too

* Code styling and fixes in AzureFileSystem, MediaLibrary and IStorageProvider

* Simplifying invalid character detection

* Code styling

* Adding InvalidNameCharacterException to be able to handle invalid characters precisely at various user-facing components

* Updating MediaLibrary not to log an error when a file can't be uploaded due to invalid characters

---------

Co-authored-by: Lombiq <github@lombiq.com>

* #6793: Adding a content-independent culture selector shape for the front-end (#8784)

* Adds a new CultureSelector shape for front-end

* fixed query string culture change

* Moving NameValueCollectionExtensions from Orchard.DynamicForms and Orchard.Localization to Orchard.Framework

* Code styling

* Simplifying UserCultureSelectorController and removing the addition of the culture to the query string

* EOF empty lines and code styling

* Fixing that the main Orchard.Localization should depend on Orchard.Autoroute

* Code styling in LocalizationService

* Updating LocalizationService to not have to use IEnumerable.Single

* Matching culture name matching in LocalizationService culture- and casing-invariant

---------

Co-authored-by: Sergio Navarro <jersio@hotmail.com>
Co-authored-by: psp589 <pablosanchez589@gmail.com>

* #8640: Fixing consistency between different Enumeration Field flavors' data storage (#8789)

* Reworking EnumerationField's logic to store/retrieve its (selected) values

* Fixing exception when creating new item with CheckboxList flavor, adding more nullchecks and compactness

* Code styling in EnumerationFieldDriver

* Code styling in EnumerationField editor template

* Fixing that EnumerationFieldDriver and the EnumerationField editor template should read SelectedValues instead of Values directly

---------

Co-authored-by: Matteo Piovanelli <MatteoPiovanelli-Laser@users.noreply.github.com>

* Fixing merge

---------

Co-authored-by: Lombiq <github@lombiq.com>
Co-authored-by: Sergio Navarro <jersio@hotmail.com>
Co-authored-by: psp589 <pablosanchez589@gmail.com>
Co-authored-by: Matteo Piovanelli <MatteoPiovanelli-Laser@users.noreply.github.com>
  • Loading branch information
5 people authored Apr 19, 2024
1 parent 0faf196 commit e60a845
Show file tree
Hide file tree
Showing 27 changed files with 410 additions and 185 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -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");
Expand Down Expand Up @@ -148,10 +150,10 @@ public IEnumerable<IStorageFile> ListFiles(string path) {
}

return BlobClient.ListBlobs(prefix)
.OfType<CloudBlockBlob>()
.Where(blobItem => !blobItem.Uri.AbsoluteUri.EndsWith(FolderEntry))
.Select(blobItem => new AzureBlobFileStorage(blobItem, _absoluteRoot))
.ToArray();
.OfType<CloudBlockBlob>()
.Where(blobItem => !blobItem.Uri.AbsoluteUri.EndsWith(FolderEntry))
.Select(blobItem => new AzureBlobFileStorage(blobItem, _absoluteRoot))
.ToArray();
}

public IEnumerable<IStorageFolder> ListFolders(string path) {
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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() + "/";
Expand Down Expand Up @@ -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));

Expand All @@ -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");
}
Expand Down Expand Up @@ -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('/');
Expand All @@ -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;
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -339,7 +339,6 @@
<Compile Include="Migrations.cs" />
<Compile Include="Handlers\ReadFormValuesHandler.cs" />
<Compile Include="Services\FormElementEventHandlerBase.cs" />
<Compile Include="Helpers\NameValueCollectionExtensions.cs" />
<Compile Include="Models\Submission.cs" />
<Compile Include="Permissions.cs" />
<Compile Include="Services\FormService.cs" />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -467,4 +467,4 @@ private static bool IsFormElementType(IElementValidator validator, Type elementT
return validatorElementType == elementType || validatorElementType.IsAssignableFrom(elementType);
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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<EnumerationField> {
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<EnumerationFieldSettings>();
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<EnumerationFieldSettings>();
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) {
Expand Down
26 changes: 7 additions & 19 deletions src/Orchard.Web/Modules/Orchard.Fields/Fields/EnumerationField.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,28 +7,16 @@ public class EnumerationField : ContentField {
private const char Separator = ';';

public string Value {
get { return Storage.Get<string>(); }
set { Storage.Set(value ?? String.Empty); }
get => Storage.Get<string>()?.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) : "";
}
}
}
Original file line number Diff line number Diff line change
@@ -1,22 +1,27 @@
@model Orchard.Fields.Fields.EnumerationField

@using Orchard.Fields.Settings;

@{
var settings = Model.PartFieldDefinition.Settings.GetModel<EnumerationFieldSettings>();
string[] options = (!String.IsNullOrWhiteSpace(settings.Options)) ? settings.Options.Split(new string[] { System.Environment.NewLine }, StringSplitOptions.None) : new string[] { T("Select an option").ToString() };
}

<fieldset>
<label for="@Html.FieldIdFor(m => m.Value)" @if (settings.Required) { <text> class="required" </text> }>@Model.DisplayName</label>
<label for="@Html.FieldIdFor(m => m.Value)" @if (settings.Required) { <text> class="required" </text> }>@Model.DisplayName</label>
@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)) {
<label>@Html.RadioButton("Value", "", string.IsNullOrWhiteSpace(Model.Value), settings.Required ? new { required = "required" } : null)<i>@T("unset")</i></label> }
<label>@Html.RadioButton("Value", "", string.IsNullOrWhiteSpace(Model.SelectedValues.FirstOrDefault()), settings.Required ? new { required = "required" } : null)<i>@T("unset")</i></label>
}
else {
<label>@Html.RadioButton("Value", option, (option == Model.Value), settings.Required ? new { required = "required" } : null)@option</label> }
<label>@Html.RadioButton("Value", option, (option == Model.SelectedValues.FirstOrDefault()), settings.Required ? new { required = "required" } : null)@option</label>
}
}
break;

Expand Down
Original file line number Diff line number Diff line change
@@ -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);
}
}
}
4 changes: 2 additions & 2 deletions src/Orchard.Web/Modules/Orchard.Localization/Module.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand All @@ -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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -89,10 +89,11 @@
</ItemGroup>
<ItemGroup>
<Compile Include="AdminMenu.cs" />
<Compile Include="Controllers\AdminCultureSelectorController.cs" />
<Compile Include="Extensions\Constants.cs" />
<Compile Include="Controllers\AdminController.cs" />
<Compile Include="Controllers\TransliterationAdminController.cs" />
<Compile Include="Controllers\AdminCultureSelectorController.cs" />
<Compile Include="Controllers\UserCultureSelectorController.cs" />
<Compile Include="Extensions\MetaDataExtensions.cs" />
<Compile Include="Handlers\LocalizationCultureNeutralityHandler.cs" />
<Compile Include="Models\TransliterationSpecificationRecord.cs" />
Expand Down Expand Up @@ -120,6 +121,7 @@
<Compile Include="Services\LocalizationService.cs" />
<Compile Include="Services\TransliterationService.cs" />
<Compile Include="Events\TransliterationSlugEventHandler.cs" />
<Compile Include="Services\Utils.cs" />
<Compile Include="Settings\LocalizationCultureNeutralityEditorEvents.cs" />
<Compile Include="Settings\LocalizationCultureNeutralitySettings.cs" />
<Compile Include="ViewModels\ContentLocalizationsViewModel.cs" />
Expand Down Expand Up @@ -196,8 +198,7 @@
</ItemGroup>
<ItemGroup>
<None Include="packages.config" />
</ItemGroup>
<ItemGroup>
<Content Include="Views\UserCultureSelector.cshtml" />
<Content Include="Views\DefinitionTemplates\LocalizationCultureNeutralitySettings.cshtml" />
</ItemGroup>
<PropertyGroup>
Expand Down Expand Up @@ -233,4 +234,4 @@
</FlavorProperties>
</VisualStudio>
</ProjectExtensions>
</Project>
</Project>
Loading

0 comments on commit e60a845

Please sign in to comment.